Skip to content

perf(serve): eliminate repeated per-term graph scoring in _query_graph_text #1889

Description

@Sirhan1

Summary

_query_graph_text() currently performs T+1 graph-scoring passes for a query with T unique terms:

This means every query rescans the graph once for the combined query and then once again for every distinct search term.

Proposal

Replace the current T+1 scoring flow with a single candidate-scoring traversal.

Introduce a private helper (for example _score_query()) that computes both:

  • the existing combined ranking;
  • the best matching node for each normalized query term.

_pick_seeds() would then consume the precomputed per-term winners instead of rescoring the graph once per token.

The goal is to preserve existing behaviour while eliminating the redundant scoring passes.

Why this should be equivalent

The combined trigram candidate set (norm_terms + [joined]) is a superset of every individual per-token candidate set.

Therefore every node that could receive a non-zero singleton score is already visited during the combined scoring traversal, allowing the per-term winners to be computed without additional graph scans.

The intention is to preserve:

  • combined ranking
  • per-term seed selection
  • existing tie-breaking behaviour
  • existing seed deduplication
  • existing trigram prefilter behaviour

while changing only the implementation.

Prototype results

I implemented a prototype to validate the approach.

Across synthetic graphs ranging from 1k to 100k nodes, together with Graphify's own ~11k node graph, I observed:

Scenario Improvement
1k–100k synthetic graphs 1.4×–2.4× faster
100k graph (3-term query) ~41% lower latency
100k graph (10-term query) ~35% lower latency
11k real graph 1.8×–2.0× faster

Single-term queries also improve because the current implementation still performs two scoring passes.

Scope

This would be an internal refactor only.

No intended changes to:

  • CLI
  • MCP
  • graph format
  • public API
  • query behaviour

Current PR

#1918

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions