Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions langchain-coordinode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ graph = CoordinodeGraph("localhost:7080", timeout=60.0)
| `query(query, params)` | Execute Cypher, returns `List[Dict[str, Any]]` |
| `refresh_schema()` | Reload node/relationship schema from database |
| `add_graph_documents(docs)` | Batch MERGE nodes + relationships from `GraphDocument` list |
| `keyword_search(query, k, label, fuzzy, language)` | Full-text BM25 search — returns `[{"id", "score", "snippet"}, …]` |
| `similarity_search(query_vector, k, label, property)` | Vector nearest-neighbour search — returns `[{"id", "node", "distance"}, …]` |
| `schema` | Schema string for LLM context |
| `structured_schema` | Structured schema dict for programmatic access |

## Related Packages

Expand Down
23 changes: 16 additions & 7 deletions langchain-coordinode/langchain_coordinode/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,22 @@ def keyword_search(
# Injected clients (e.g. bare coordinode-embedded LocalClient) may
# not implement text_search — return empty rather than AttributeError.
return []
results = self._client.text_search(
label,
query,
limit=k,
fuzzy=fuzzy,
language=language,
)
try:
results = self._client.text_search(
label,
query,
limit=k,
fuzzy=fuzzy,
language=language,
)
except Exception:
# Server may return UNIMPLEMENTED (feature not yet available in the
# deployed version) or NOT_FOUND (no text index for *label*).
# We catch broad Exception (rather than grpc.RpcError specifically)
# because langchain-coordinode does not take a hard grpc dependency.
# The exception is logged at DEBUG so it remains observable.
logger.debug("text_search() failed — returning empty list", exc_info=True)
return []
# Use "id" (not "node_id") for consistency with similarity_search() return
# format, so callers can write generic code over both methods.
return [{"id": r.node_id, "score": r.score, "snippet": r.snippet} for r in results]
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/test_langchain_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ def close(self) -> None:
return None


class _ClientWithRaisingTextSearch:
"""Fake client whose text_search raises (e.g. gRPC UNIMPLEMENTED)."""

def cypher(self, query: str, params: dict | None = None) -> list[dict]:
return []

def text_search(self, label: str, query: str, **kwargs: Any) -> list[Any]:
raise RuntimeError("StatusCode.UNIMPLEMENTED")

def close(self) -> None:
# No-op: keeps interface parity with real CoordinodeClient.
return None


# ── Tests: keyword_search ─────────────────────────────────────────────────────


Expand Down Expand Up @@ -150,3 +164,12 @@ def test_empty_snippet_preserved(self) -> None:
out = graph.keyword_search("test")

assert out[0]["snippet"] == ""

def test_returns_empty_when_text_search_raises(self) -> None:
"""Returns [] when text_search raises (e.g. gRPC UNIMPLEMENTED from older server)."""
client = _ClientWithRaisingTextSearch()
graph = CoordinodeGraph(client=client)

out = graph.keyword_search("query")

assert out == []
Loading