Skip to content

feat(search): signature search + callers-of / callees-of graph qualifiers#135

Closed
andreinknv wants to merge 2 commits into
colbymchenry:mainfrom
andreinknv:feat/search-signature-and-graph-qualifiers
Closed

feat(search): signature search + callers-of / callees-of graph qualifiers#135
andreinknv wants to merge 2 commits into
colbymchenry:mainfrom
andreinknv:feat/search-signature-and-graph-qualifiers

Conversation

@andreinknv
Copy link
Copy Markdown
Contributor

Summary

Three new search query qualifiers extending PR #131's field-qualified parser. All compose with the existing kind: / lang: / path: / name: filters.

sig: / signature: — substring of node.signature

Find functions by what they accept or return:

query "sig:Promise<User> kind:function"
query "sig:context.Context"
query "sig:io.Reader sig:io.Writer"   # OR semantics

callers-of:NAME — nodes that call NAME

query "callers-of:authenticate"
query "callers-of:authenticate name:Handler"
query "callers-of:Forward kind:function"

callees-of:NAME — nodes called BY NAME

query "callees-of:bootstrap"

Implementation notes

  • New nodesCallingAny(names) and nodesCalledByAny(names) SQL helpers; nodesByIds(ids) for bulk node fetching when seeded from the graph.
  • Graph-seeded candidate set when callers-of/callees-of is given without text — otherwise the filter-only path scans 50 arbitrary name-ordered nodes that rarely intersect with the graph result.
  • searchAllByFilters accepts signatureLike: string[] and OR's all entries in SQL — matches the OR semantics of path: / name: post-filters.
  • All IN-clause helpers chunk inputs to 900 per query (under SQLite's 999 variable cap) so pathological inputs can't trigger SQLITE_ERROR: too many SQL variables.
  • Graph-seeded path applies kinds/languages post-filter to the seed set so callers-of:foo kind:method correctly narrows.
  • Reviewer-driven: also memoises callersSet() / calleesSet() closures so the seed and the filter pass don't re-query the same names.

Test plan

Verified live on ollama/ollama@v0.22.0:

Query Result
sig:context.Context kind:function BuildLauncherState, Chat, ...
callers-of:Forward kind:function audioFeedForward, forwardConvBlock, ...
auth sig:error getAuthorizationToken (text + sig: composes)
  • 5 new integration tests in __tests__/search-qualifiers.test.ts covering sig:, multi-sig: OR, callers-of:, callees-of:, and callers-of: + kind: composition
  • 2 new parser tests for sig: and callers-of: / callees-of: parsing
  • npx vitest run410 passed (was 405)
  • npx tsc --noEmit clean
  • npm run build succeeds

Depends on

🤖 Generated with Claude Code

… callees-of)

Three additional query-parser fields on top of PR colbymchenry#131's
field-qualified-query foundation:

- sig:/signature: SUBSTR — case-insensitive substring of node.signature.
  Lets users find functions by what they return / accept (e.g.
  `sig:Promise<User>`, `sig:context.Context`). Pushed into SQL via a
  new `signatureLike` parameter on searchAllByFilters when sig: is
  the only criterion, so the filter-only path doesn't waste candidate
  slots on signatureless rows.

- callers-of:NAME — restricts results to nodes that have at least one
  outgoing `calls` edge to a node named NAME. Backed by a new
  nodesCallingAny() SQL helper. When this is the only criterion (no
  text), the candidate set is seeded FROM the graph (via nodesByIds)
  rather than scanning a name-ordered slice of arbitrary nodes — that
  way a 3-caller function correctly returns 3 candidates instead of
  zero.

- callees-of:NAME — symmetric: nodes that are called BY a node named
  NAME. Backed by nodesCalledByAny() and the same graph-seeded path.

Verified live against ollama/ollama@v0.22.0:
  query "sig:context.Context kind:function" → BuildLauncherState, Chat,...
  query "callers-of:Forward kind:function"  → audioFeedForward, forwardConvBlock,...
  query "auth sig:error"                    → getAuthorizationToken (filters compose with text)

Two new parser tests (sig: + alias, callers-of/callees-of). Full suite:
405 passed.
…ndant graph helpers, IN-clause cap, integration tests

Five reviewer fixes:

- searchAllByFilters: signatureLike is now a string[] (OR'd in SQL),
  not a single string. The previous version pre-filtered SQL on
  signatureFilters[0] only while the JS post-filter used `some()`
  (OR) on all filters — the intersection silently dropped every
  sig: token after the first. Multi-sig now works as proper OR
  (matching path:/name: filter semantics).

- callersSet() / calleesSet() are computed once via a memoised
  closure and reused by both the graph-seeded candidate path and
  the post-filter pass. Previously each was queried twice per
  filter-only callers-of:/callees-of: query.

- New chunkIds() helper splits IN-clause inputs into batches of 900
  (under SQLite's default SQLITE_LIMIT_VARIABLE_NUMBER of 999) so
  pathological inputs (large name lists or seeded id sets) can't
  trigger 'too many SQL variables'. Applied to nodesByIds,
  nodesCallingAny, nodesCalledByAny.

- Graph-seeded path now applies kinds/languages filters to the
  result. Previously `callers-of:foo kind:method` returned every
  caller regardless of kind because the post-filter pass only
  intersected the graph-derived set without re-filtering.

- 5 new integration tests in __tests__/search-qualifiers.test.ts:
  sig: filter, multi-sig OR, callers-of:, callees-of:,
  callers-of: + kind: composition. Catches the bugs above.

Note: an idx_nodes_signature SQL index was considered but skipped —
LIKE '%substr%' matches don't use a btree index, so adding one would
just bloat the DB without helping. Substring sig: queries are
intentionally full-table scans; FTS already covers signature for
text queries.

Full suite: 410 passed (was 405, +5).
andreinknv added a commit to andreinknv/codegraph that referenced this pull request Apr 28, 2026
… qualifiers

# Conflicts:
#	__tests__/search-query-parser.test.ts
#	src/db/queries.ts
#	src/search/query-parser.ts
andreinknv added a commit to andreinknv/codegraph that referenced this pull request Apr 29, 2026
Adds Steps K-O to walk the new PRs in dependency order:
  K: bug-fix wave (clean):    colbymchenry#128, colbymchenry#129
  L: resolution + search:     colbymchenry#130 (resolve), colbymchenry#131 (resolve)
  M: extraction edges:        colbymchenry#134 (resolve)
  N: biomarker stack:         colbymchenry#132, colbymchenry#133 (both resolve, on top of colbymchenry#125)
  O: search advanced:         colbymchenry#135 (resolve, on top of colbymchenry#131)

Also flips colbymchenry#125 from merge_clean to merge_resolve - it now hits a
queries.ts conflict after the Phase-4 stack lands (colbymchenry#111/colbymchenry#112/colbymchenry#123/colbymchenry#124
all extend the same QueryBuilder surface, so colbymchenry#125's biomarker columns
no longer apply cleanly without a resolution).

Validated end-to-end against colbymchenry/main HEAD: script ran
clean through all 43 PRs, npm run build succeeded, full test
suite reports 877/877 passing (was 829 before this wave: +48 from
new tests added by the new PRs plus the reviewer-driven follow-ups).
@colbymchenry
Copy link
Copy Markdown
Owner

Thanks for the contribution! Closing this one — the direction overlaps too much with what's already shipped rather than for any code-quality reason.

callers-of: / callees-of: duplicate the codegraph_callers and codegraph_callees MCP tools, which are the canonical entry points for graph traversal. Exposing the same traversals through a string DSL gives LLM consumers two ways to do the same thing, and the dedicated tools are easier to call correctly.

sig: is genuinely new and useful — if you'd like to land that piece, a smaller standalone PR (just signature filtering, rebased on main once #131 merges) would be very welcome.

Also noting that this PR is stacked on #131, which isn't merged yet — so it can't land in its current form regardless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants