Skip to content

Commit 7575efd

Browse files
committed
feat(query): dedupe results and use transitive test lookup
- Dedupe callers/callees/inheritors by qualified_name (repeated CALLS edges were surfacing the same node multiple times). - tests_for: use store.get_transitive_tests (direct TESTED_BY + one CALLS hop) instead of only direct TESTED_BY edges. - Disambiguation: when multiple candidates match a bare name and exactly one is a production node (not is_test), prefer it silently instead of returning 'ambiguous'.
1 parent 17caf52 commit 7575efd

1 file changed

Lines changed: 31 additions & 18 deletions

File tree

code_review_graph/tools/query.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,20 @@ def query_graph(
198198
node = candidates[0]
199199
target = node.qualified_name
200200
elif len(candidates) > 1:
201-
return {
202-
"status": "ambiguous",
203-
"summary": (
204-
f"Multiple matches for '{target}'. "
205-
"Please use a qualified name."
206-
),
207-
"candidates": [node_to_dict(c) for c in candidates],
208-
}
201+
# Prefer non-test nodes when exactly one production candidate
202+
non_test = [c for c in candidates if not c.is_test]
203+
if len(non_test) == 1:
204+
node = non_test[0]
205+
target = node.qualified_name
206+
else:
207+
return {
208+
"status": "ambiguous",
209+
"summary": (
210+
f"Multiple matches for '{target}'. "
211+
"Please use a qualified name."
212+
),
213+
"candidates": [node_to_dict(c) for c in candidates],
214+
}
209215

210216
if not node and pattern != "file_summary":
211217
return {
@@ -216,10 +222,12 @@ def query_graph(
216222
qn = node.qualified_name if node else target
217223

218224
if pattern == "callers_of":
225+
seen_qn: set[str] = set()
219226
for e in store.get_edges_by_target(qn):
220227
if e.kind == "CALLS":
221228
caller = store.get_node(e.source_qualified)
222-
if caller:
229+
if caller and caller.qualified_name not in seen_qn:
230+
seen_qn.add(caller.qualified_name)
223231
results.append(node_to_dict(caller))
224232
edges_out.append(edge_to_dict(e))
225233
# Fallback: CALLS edges store unqualified target names
@@ -228,15 +236,18 @@ def query_graph(
228236
if not results and node:
229237
for e in store.search_edges_by_target_name(node.name):
230238
caller = store.get_node(e.source_qualified)
231-
if caller:
239+
if caller and caller.qualified_name not in seen_qn:
240+
seen_qn.add(caller.qualified_name)
232241
results.append(node_to_dict(caller))
233242
edges_out.append(edge_to_dict(e))
234243

235244
elif pattern == "callees_of":
245+
seen_callee: set[str] = set()
236246
for e in store.get_edges_by_source(qn):
237247
if e.kind == "CALLS":
238248
callee = store.get_node(e.target_qualified)
239-
if callee:
249+
if callee and callee.qualified_name not in seen_callee:
250+
seen_callee.add(callee.qualified_name)
240251
results.append(node_to_dict(callee))
241252
edges_out.append(edge_to_dict(e))
242253

@@ -270,25 +281,27 @@ def query_graph(
270281
results.append(node_to_dict(child))
271282

272283
elif pattern == "tests_for":
273-
for e in store.get_edges_by_target(qn):
274-
if e.kind == "TESTED_BY":
275-
test = store.get_node(e.source_qualified)
276-
if test:
277-
results.append(node_to_dict(test))
284+
# Use transitive lookup: direct TESTED_BY + 1-hop CALLS->TESTED_BY
285+
transitive = store.get_transitive_tests(qn)
286+
seen = {r["qualified_name"] for r in transitive}
287+
for r in transitive:
288+
results.append(r)
278289
# Also search by naming convention
279290
name = node.name if node else target
280291
test_nodes = store.search_nodes(f"test_{name}", limit=10)
281292
test_nodes += store.search_nodes(f"Test{name}", limit=10)
282-
seen = {r.get("qualified_name") for r in results}
283293
for t in test_nodes:
284294
if t.qualified_name not in seen and t.is_test:
285295
results.append(node_to_dict(t))
296+
seen.add(t.qualified_name)
286297

287298
elif pattern == "inheritors_of":
299+
seen_inheritor: set[str] = set()
288300
for e in store.get_edges_by_target(qn):
289301
if e.kind in ("INHERITS", "IMPLEMENTS"):
290302
child = store.get_node(e.source_qualified)
291-
if child:
303+
if child and child.qualified_name not in seen_inheritor:
304+
seen_inheritor.add(child.qualified_name)
292305
results.append(node_to_dict(child))
293306
edges_out.append(edge_to_dict(e))
294307
# Fallback: INHERITS/IMPLEMENTS edges store unqualified base names

0 commit comments

Comments
 (0)