19
19
from neo4j_graphrag .filters import get_metadata_filter
20
20
from neo4j_graphrag .types import IndexType , SearchType
21
21
22
+ NODE_VECTOR_INDEX_QUERY = (
23
+ "CALL db.index.vector.queryNodes"
24
+ "($vector_index_name, $top_k * $effective_search_ratio, $query_vector) "
25
+ "YIELD node, score "
26
+ "WITH node, score LIMIT $top_k"
27
+ )
28
+
29
+ REL_VECTOR_INDEX_QUERY = (
30
+ "CALL db.index.vector.queryRelationships"
31
+ "($vector_index_name, $top_k * $effective_search_ratio, $query_vector) "
32
+ "YIELD relationship, score "
33
+ "WITH relationship, score LIMIT $top_k"
34
+ )
35
+
22
36
VECTOR_EXACT_QUERY = (
23
37
"WITH node, "
24
38
"vector.similarity.cosine(node.`{embedding_node_property}`, $query_vector) AS score "
31
45
"AND size(node.`{embedding_node_property}`) = toInteger($embedding_dimension)"
32
46
)
33
47
48
+ FULL_TEXT_SEARCH_QUERY = (
49
+ "CALL db.index.fulltext.queryNodes($fulltext_index_name, $query_text, {limit: $top_k}) "
50
+ "YIELD node, score"
51
+ )
34
52
35
53
UPSERT_NODE_QUERY = (
36
54
"UNWIND $rows AS row "
105
123
)
106
124
107
125
108
- def _get_vector_search_query (index_type : IndexType = IndexType .NODE ) -> str :
109
- procedure = "queryNodes" if index_type == IndexType .NODE else "queryRelationships"
110
- return (
111
- f"CALL db.index.vector.{ procedure } "
112
- "($vector_index_name, $top_k * $effective_search_ratio, $query_vector) "
113
- f"YIELD { index_type .value } , score "
114
- f"WITH { index_type .value } , score LIMIT $top_k"
115
- )
116
-
117
-
118
- def _get_full_text_search_query (index_type : IndexType = IndexType .NODE ) -> str :
119
- procedure = "queryNodes" if index_type == IndexType .NODE else "queryRelationships"
120
- return (
121
- f"CALL db.index.fulltext.{ procedure } "
122
- "($fulltext_index_name, $query_text, {limit: $top_k}) "
123
- f"YIELD { index_type .value } , score"
124
- )
125
-
126
-
127
- def _get_hybrid_query (
128
- neo4j_version_is_5_23_or_above : bool , index_type : IndexType = IndexType .NODE
129
- ) -> str :
126
+ def _get_hybrid_query (neo4j_version_is_5_23_or_above : bool ) -> str :
130
127
call_prefix = "CALL () { " if neo4j_version_is_5_23_or_above else "CALL { "
131
- vector_search_query = _get_vector_search_query (index_type = index_type )
132
- full_text_search_query = _get_full_text_search_query (index_type = index_type )
133
128
query_body = (
134
- f"{ vector_search_query } "
135
- f"WITH collect({{{ index_type .value } :{ index_type .value } , score:score}}) AS { index_type .value } s, "
136
- "max(score) AS vector_index_max_score "
137
- f"UNWIND { index_type .value } s AS n "
138
- f"RETURN n.{ index_type .value } AS { index_type .value } , (n.score / vector_index_max_score) AS score "
129
+ f"{ NODE_VECTOR_INDEX_QUERY } "
130
+ "WITH collect({node:node, score:score}) AS nodes, max(score) AS vector_index_max_score "
131
+ "UNWIND nodes AS n "
132
+ "RETURN n.node AS node, (n.score / vector_index_max_score) AS score "
139
133
"UNION "
140
- f"{ full_text_search_query } "
141
- f"WITH collect({{{ index_type .value } :{ index_type .value } , score:score}}) AS { index_type .value } s, "
142
- "max(score) AS ft_index_max_score "
143
- f"UNWIND { index_type .value } s AS n "
144
- f"RETURN n.{ index_type .value } AS { index_type .value } , (n.score / ft_index_max_score) AS score "
145
- "} "
146
- f"WITH { index_type .value } , max(score) AS score ORDER BY score DESC LIMIT $top_k"
134
+ f"{ FULL_TEXT_SEARCH_QUERY } "
135
+ "WITH collect({node:node, score:score}) AS nodes, max(score) AS ft_index_max_score "
136
+ "UNWIND nodes AS n "
137
+ "RETURN n.node AS node, (n.score / ft_index_max_score) AS score } "
138
+ "WITH node, max(score) AS score ORDER BY score DESC LIMIT $top_k"
147
139
)
148
140
return call_prefix + query_body
149
141
@@ -219,7 +211,7 @@ def get_search_query(
219
211
if index_type == IndexType .NODE :
220
212
if search_type == SearchType .HYBRID :
221
213
if filters :
222
- raise Exception ("Filters are not supported with Hybrid Search " )
214
+ raise Exception ("Filters are not supported with hybrid search " )
223
215
query = _get_hybrid_query (neo4j_version_is_5_23_or_above )
224
216
params : dict [str , Any ] = {}
225
217
elif search_type == SearchType .VECTOR :
@@ -240,24 +232,26 @@ def get_search_query(
240
232
"Vector Search with filters requires: node_label, embedding_node_property, embedding_dimension"
241
233
)
242
234
else :
243
- query , params = _get_vector_search_query ( index_type = index_type ) , {}
235
+ query , params = NODE_VECTOR_INDEX_QUERY , {}
244
236
else :
245
237
raise ValueError (f"Search type is not supported: { search_type } " )
246
238
fallback_return = (
247
239
f"RETURN node {{ .*, `{ embedding_node_property } `: null }} AS node, "
248
240
"labels(node) AS nodeLabels, elementId(node) AS elementId, score"
249
241
)
250
242
elif index_type == IndexType .RELATIONSHIP :
243
+ if filters :
244
+ raise Exception ("Filters are not supported for relationship indexes" )
251
245
if search_type == SearchType .HYBRID :
252
- raise Exception ("Hybrid search is not support for relationship indexes" )
246
+ raise Exception ("Hybrid search is not supported for relationship indexes" )
253
247
elif search_type == SearchType .VECTOR :
254
- query , params = _get_vector_search_query (index_type = index_type ), {}
248
+ query , params = REL_VECTOR_INDEX_QUERY , {}
249
+ fallback_return = (
250
+ f"RETURN relationship {{ .*, `{ embedding_node_property } `: null }} AS relationship, "
251
+ "elementId(relationship) AS elementId, score"
252
+ )
255
253
else :
256
254
raise ValueError (f"Search type is not supported: { search_type } " )
257
- fallback_return = (
258
- f"RETURN relationship {{ .*, `{ embedding_node_property } `: null }} AS relationship, "
259
- "elementId(relationship) AS elementId, score"
260
- )
261
255
else :
262
256
raise ValueError (f"Index type is not supported: { index_type } " )
263
257
0 commit comments