Skip to content

Commit fd169f2

Browse files
authored
Fix "NOT IN" operator (#327)
* Fix "NOT IN" operator * Update CHANGELOG * Ruff
1 parent f7ca762 commit fd169f2

File tree

4 files changed

+32
-23
lines changed

4 files changed

+32
-23
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
- Improved log output readability in Retrievers and GraphRAG and added embedded vector to retriever result metadata for debugging.
1515

16+
### Fixed
17+
18+
- Fixed a bug where the `$nin` operator for metadata pre-filtering in retrievers would create an invalid Cypher query.
19+
20+
1621
## 1.6.1
1722

1823
### Added

examples/customize/retrievers/use_pre_filters.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,29 @@
44
from neo4j_graphrag.embeddings import OpenAIEmbeddings
55
from neo4j_graphrag.retrievers import VectorRetriever
66

7-
URI = "neo4j://localhost:7687"
8-
AUTH = ("neo4j", "password")
9-
10-
INDEX_NAME = "embedding-name"
7+
URI = "neo4j+s://demo.neo4jlabs.com"
8+
AUTH = ("recommendations", "recommendations")
9+
DATABASE = "recommendations"
10+
INDEX_NAME = "moviePlotsEmbedding"
1111
DIMENSION = 1536
1212

13-
# Connect to Neo4j database
14-
driver = neo4j.GraphDatabase.driver(URI, auth=AUTH)
15-
1613

17-
# Initialize the retriever
18-
retriever = VectorRetriever(driver, INDEX_NAME, embedder=OpenAIEmbeddings())
14+
# Connect to Neo4j database
15+
with neo4j.GraphDatabase.driver(URI, auth=AUTH) as driver:
16+
# Initialize the retriever
17+
retriever = VectorRetriever(driver, INDEX_NAME, embedder=OpenAIEmbeddings())
1918

20-
# Perform the search
21-
query_text = "Find me a book about Fremen"
22-
pre_filters = {"int_property": {"$gt": 100}}
23-
print(
24-
retriever.search(
19+
# Perform the search
20+
query_text = "Find me a movie about love"
21+
pre_filters = {"int_property": {"$gt": 100}}
22+
# pre_filters = {
23+
# "year": {
24+
# "$nin": ["1999", "2000"]
25+
# }
26+
# }
27+
retriever_result = retriever.search(
2528
query_text=query_text,
2629
top_k=1,
2730
filters=pre_filters,
2831
)
29-
)
32+
print(retriever_result)

src/neo4j_graphrag/filters.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ def cleaned_value(self, value: list[Union[str, int, float]]) -> Any:
100100

101101

102102
class NinOperator(InOperator):
103-
CYPHER_OPERATOR = "NOT IN"
103+
def lhs(self, field: str) -> str:
104+
lhs = super().lhs(field)
105+
return f"NOT {lhs}"
104106

105107

106108
class LikeOperator(Operator):
@@ -114,8 +116,8 @@ def cleaned_value(self, value: str) -> str:
114116

115117
class ILikeOperator(LikeOperator):
116118
def lhs(self, field: str) -> str:
117-
safe_field_cypher = self.safe_field_cypher(field)
118-
return f"toLower({self.node_alias}.{safe_field_cypher})"
119+
lhs = super().lhs(field)
120+
return f"toLower({lhs})"
119121

120122
def cleaned_value(self, value: str) -> str:
121123
value = super().cleaned_value(value)
@@ -368,6 +370,5 @@ def get_metadata_filter(
368370
contains the query parameters
369371
"""
370372
param_store = ParameterStore()
371-
return _construct_metadata_filter(
372-
filter, param_store, node_alias=node_alias
373-
), param_store.params
373+
query = _construct_metadata_filter(filter, param_store, node_alias=node_alias)
374+
return query, param_store.params

tests/unit/test_filters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def test_single_condition_cypher_nin(param_store_empty: ParameterStore) -> None:
165165
generated = _single_condition_cypher(
166166
"field", NinOperator, ["a", "b", "c"], param_store=param_store_empty
167167
)
168-
assert generated == "node.field NOT IN $param_0"
168+
assert generated == "NOT node.field IN $param_0"
169169
assert param_store_empty.params == {"param_0": ["a", "b", "c"]}
170170

171171

@@ -552,7 +552,7 @@ def test_get_metadata_filter_in_operator() -> None:
552552
def test_get_metadata_filter_not_in_operator() -> None:
553553
filters = {"field": {"$nin": ["a", "b"]}}
554554
query, params = get_metadata_filter(filters)
555-
assert query == "node.field NOT IN $param_0"
555+
assert query == "NOT node.field IN $param_0"
556556
assert params == {"param_0": ["a", "b"]}
557557

558558

0 commit comments

Comments
 (0)