Skip to content

Commit 32b614b

Browse files
ZacZac
authored andcommitted
added cypher template example
1 parent 02d4d82 commit 32b614b

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Copyright (c) "Neo4j"
2+
# Neo4j Sweden AB [https://neo4j.com]
3+
# #
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
# #
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
# #
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
"""
16+
Example demonstrating how to use the ToolsRetriever with a Cypher template.
17+
18+
This example shows:
19+
1. How to create a tool from a Cypher template
20+
2. How to use the ToolsRetriever to select and execute tools based on a query
21+
"""
22+
23+
import os
24+
from typing import Any, Callable, Dict, Optional
25+
26+
import neo4j
27+
from dotenv import load_dotenv
28+
29+
from neo4j_graphrag.llm.openai_llm import OpenAILLM
30+
from neo4j_graphrag.retrievers.base import Retriever
31+
from neo4j_graphrag.retrievers.tools_retriever import ToolsRetriever
32+
from neo4j_graphrag.types import RawSearchResult, RetrieverResultItem
33+
34+
# Load environment variables from .env file (OPENAI_API_KEY required for this example)
35+
load_dotenv()
36+
37+
# Define database credentials
38+
URI = "neo4j+s://demo.neo4jlabs.com"
39+
AUTH = ("recommendations", "recommendations")
40+
41+
# Cypher template to count actors in a specific movie
42+
CYPHER_TEMPLATE = """
43+
MATCH (m:Movie {title: $title})
44+
OPTIONAL MATCH (m)<-[:ACTED_IN]-(a:Actor)
45+
WITH m, collect(a.name) AS actor_names, count(a) AS actor_count
46+
RETURN m.title AS movie_title,
47+
m.plot AS plot,
48+
m.released AS year,
49+
actor_count,
50+
actor_names
51+
"""
52+
53+
class CypherTemplateRetriever(Retriever):
54+
"""
55+
Custom retriever that executes a parameterized Cypher query template.
56+
"""
57+
58+
def __init__(
59+
self,
60+
driver: neo4j.Driver,
61+
cypher_template: str,
62+
neo4j_database: Optional[str] = None,
63+
result_formatter: Optional[callable] = None
64+
):
65+
"""
66+
Args:
67+
driver: Neo4j driver instance
68+
cypher_template: Cypher query with parameters (e.g., "MATCH (m:Movie {title: $title}) RETURN m")
69+
neo4j_database: Optional database name
70+
result_formatter: Optional function to format results
71+
"""
72+
super().__init__(driver, neo4j_database)
73+
self.cypher_template = cypher_template
74+
self.result_formatter = result_formatter
75+
76+
def get_search_results(
77+
self,
78+
query_vector: Optional[list[float]] = None,
79+
query_text: Optional[str] = None,
80+
top_k: int = 5,
81+
query_params: Optional[Dict[str, Any]] = None,
82+
**kwargs
83+
) -> RawSearchResult:
84+
"""
85+
Execute the Cypher template with provided parameters.
86+
87+
Args:
88+
query_text: Can be used as a parameter (e.g., for movie title)
89+
query_params: Dictionary of parameters for the Cypher query
90+
**kwargs: Additional parameters
91+
92+
Returns:
93+
RawSearchResult containing neo4j.Record objects
94+
"""
95+
# Prepare parameters for the Cypher query
96+
parameters = query_params or {}
97+
98+
# Optionally use query_text as a parameter
99+
if query_text and "query_text" not in parameters:
100+
parameters["query_text"] = query_text
101+
102+
# Add any additional kwargs as parameters
103+
parameters.update(kwargs)
104+
105+
# Execute the query
106+
try:
107+
records, summary, keys = self.driver.execute_query(
108+
self.cypher_template,
109+
parameters_=parameters,
110+
database_=self.neo4j_database,
111+
routing_=neo4j.RoutingControl.READ,
112+
)
113+
114+
return RawSearchResult(
115+
records=records,
116+
metadata={
117+
"cypher_query": self.cypher_template,
118+
"parameters": parameters
119+
}
120+
)
121+
except Exception as e:
122+
raise RuntimeError(f"Failed to execute Cypher template: {e}") from e
123+
124+
125+
126+
def main() -> None:
127+
"""Run the example."""
128+
driver = neo4j.GraphDatabase.driver(URI, auth=AUTH)
129+
try:
130+
neo4j_retriever = CypherTemplateRetriever(
131+
driver=driver,
132+
cypher_template=CYPHER_TEMPLATE,
133+
)
134+
135+
# Convert retriever to tool
136+
neo4j_tool = neo4j_retriever.convert_to_tool(
137+
name="movie_info_tool",
138+
description=(
139+
"Retrieves the total number of actors in a specific movie and returns "
140+
"aggregation metrics including actor count, list of actor names, and movie details. "
141+
"Use this when the user asks about cast size, number of actors, or movie cast information."
142+
),
143+
parameter_descriptions={
144+
"query_params": (
145+
"Dictionary containing 'title' key with the movie title to analyze. "
146+
"Example: {'title': 'The Matrix'}"
147+
)
148+
}
149+
)
150+
151+
llm = OpenAILLM(
152+
api_key=os.getenv("OPENAI_API_KEY"),
153+
model_name="gpt-4.1",
154+
model_params={"temperature": 0.2},
155+
)
156+
157+
tools_retriever = ToolsRetriever(
158+
driver=driver,
159+
llm=llm,
160+
tools=[neo4j_tool],
161+
)
162+
163+
query_text = "How many actors are there in Around the World in 80 Days?"
164+
print("Query:", query_text)
165+
result = tools_retriever.search(query_text=query_text, return_context=True)
166+
print("Result:", result)
167+
finally:
168+
driver.close()
169+
170+
171+
if __name__ == "__main__":
172+
main()

0 commit comments

Comments
 (0)