run-llama / llama_index

LlamaIndex is a data framework for your LLM applications
https://docs.llamaindex.ai
MIT License
35.21k stars 4.94k forks source link

[Bug]: Can't create Neo4jGraphStore using latest Neo4j docker container due to old cipher syntax #14797

Open liamdonnellymv opened 1 month ago

liamdonnellymv commented 1 month ago

Bug Description

When I try to create a graph store with:

from llama_index.graph_stores.neo4j import Neo4jGraphStore

graph_store = Neo4jGraphStore( username=username, password=password, url=url, database=database, )

I see an error around "Invalid constraint syntax, ON and ASSERT should not be used. Replace ON with FOR and ASSERT with REQUIRE."

Version

0.10.55

Steps to Reproduce

Pull neo4j from docker: docker pull neo4j

Create neo4j docker container compose yaml file with the following:

services: neofourj: image: neo4j ports:

Spin up the container with docker compose up.

Run the following in python script:

from llama_index.graph_stores.neo4j import Neo4jGraphStore

graph_store = Neo4jGraphStore( username=username, password=password, url=url, database=database, )

Relevant Logs/Tracbacks

{
    "name": "CypherSyntaxError",
    "message": "{code: Neo.ClientError.Statement.SyntaxError} {message: Invalid constraint syntax, ON and ASSERT should not be used. Replace ON with FOR and ASSERT with REQUIRE. (line 2, column 1 (offset: 17))
\"                CREATE CONSTRAINT IF NOT EXISTS ON (n:Entity) ASSERT n.id IS UNIQUE;\"
                 ^}",
    "stack": "---------------------------------------------------------------------------
DatabaseError                             Traceback (most recent call last)
File ~/.local/lib/python3.10/site-packages/llama_index/graph_stores/neo4j/base.py:77, in Neo4jGraphStore.__init__(self, username, password, url, database, node_label, **kwargs)
     76 try:  # Using Neo4j 5
---> 77     self.query(
     78         \"\"\"
     79         CREATE CONSTRAINT IF NOT EXISTS FOR (n:%s) REQUIRE n.id IS UNIQUE;
     80         \"\"\"
     81         % (self.node_label)
     82     )
     83 except Exception:  # Using Neo4j <5

File ~/.local/lib/python3.10/site-packages/llama_index/graph_stores/neo4j/base.py:254, in Neo4jGraphStore.query(self, query, param_map)
    253 with self._driver.session(database=self._database) as session:
--> 254     result = session.run(query, param_map)
    255     return [d.data() for d in result]

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/work/session.py:314, in Session.run(self, query, parameters, **kwargs)
    313 parameters = dict(parameters or {}, **kwargs)
--> 314 self._auto_result._run(
    315     query, parameters, self._config.database,
    316     self._config.impersonated_user, self._config.default_access_mode,
    317     bookmarks, self._config.notifications_min_severity,
    318     self._config.notifications_disabled_classifications,
    319 )
    321 return self._auto_result

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/work/result.py:221, in Result._run(self, query, parameters, db, imp_user, access_mode, bookmarks, notifications_min_severity, notifications_disabled_classifications)
    220 self._connection.send_all()
--> 221 self._attach()

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/work/result.py:409, in Result._attach(self)
    408 while self._attached is False:
--> 409     self._connection.fetch_message()

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_common.py:178, in ConnectionErrorHandler.__getattr__.<locals>.outer.<locals>.inner(*args, **kwargs)
    177 try:
--> 178     func(*args, **kwargs)
    179 except (Neo4jError, ServiceUnavailable, SessionExpired) as exc:

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_bolt.py:855, in Bolt.fetch_message(self)
    852 tag, fields = self.inbox.pop(
    853     hydration_hooks=self.responses[0].hydration_hooks
    854 )
--> 855 res = self._process_message(tag, fields)
    856 self.idle_since = monotonic()

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_bolt5.py:370, in Bolt5x0._process_message(self, tag, fields)
    369 try:
--> 370     response.on_failure(summary_metadata or {})
    371 except (ServiceUnavailable, DatabaseUnavailable):

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_common.py:245, in Response.on_failure(self, metadata)
    244 Util.callback(handler)
--> 245 raise Neo4jError.hydrate(**metadata)

DatabaseError: {code: Neo.DatabaseError.Schema.ConstraintCreationFailed} {message: Unable to create Constraint( name='constraint_1ed05907', type='UNIQUENESS', schema=(:Entity {id}) ):
Failed to populate index Index( id=3, name='constraint_1ed05907', type='RANGE', schema=(:Entity {id}), indexProvider='range-1.0' ). Note that only the first found violation is shown.}

During handling of the above exception, another exception occurred:

CypherSyntaxError                         Traceback (most recent call last)
Cell In[36], line 1
----> 1 graph_store = Neo4jGraphStore(
      2     username=username,
      3     password=password,
      4     url=url,
      5     database=database,
      6 )

File ~/.local/lib/python3.10/site-packages/llama_index/graph_stores/neo4j/base.py:84, in Neo4jGraphStore.__init__(self, username, password, url, database, node_label, **kwargs)
     77     self.query(
     78         \"\"\"
     79         CREATE CONSTRAINT IF NOT EXISTS FOR (n:%s) REQUIRE n.id IS UNIQUE;
     80         \"\"\"
     81         % (self.node_label)
     82     )
     83 except Exception:  # Using Neo4j <5
---> 84     self.query(
     85         \"\"\"
     86         CREATE CONSTRAINT IF NOT EXISTS ON (n:%s) ASSERT n.id IS UNIQUE;
     87         \"\"\"
     88         % (self.node_label)
     89     )

File ~/.local/lib/python3.10/site-packages/llama_index/graph_stores/neo4j/base.py:254, in Neo4jGraphStore.query(self, query, param_map)
    252 def query(self, query: str, param_map: Optional[Dict[str, Any]] = {}) -> Any:
    253     with self._driver.session(database=self._database) as session:
--> 254         result = session.run(query, param_map)
    255         return [d.data() for d in result]

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/work/session.py:314, in Session.run(self, query, parameters, **kwargs)
    312 bookmarks = self._get_bookmarks()
    313 parameters = dict(parameters or {}, **kwargs)
--> 314 self._auto_result._run(
    315     query, parameters, self._config.database,
    316     self._config.impersonated_user, self._config.default_access_mode,
    317     bookmarks, self._config.notifications_min_severity,
    318     self._config.notifications_disabled_classifications,
    319 )
    321 return self._auto_result

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/work/result.py:221, in Result._run(self, query, parameters, db, imp_user, access_mode, bookmarks, notifications_min_severity, notifications_disabled_classifications)
    219 self._pull()
    220 self._connection.send_all()
--> 221 self._attach()

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/work/result.py:409, in Result._attach(self)
    407 if self._exhausted is False:
    408     while self._attached is False:
--> 409         self._connection.fetch_message()

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_common.py:178, in ConnectionErrorHandler.__getattr__.<locals>.outer.<locals>.inner(*args, **kwargs)
    176 def inner(*args, **kwargs):
    177     try:
--> 178         func(*args, **kwargs)
    179     except (Neo4jError, ServiceUnavailable, SessionExpired) as exc:
    180         assert not asyncio.iscoroutinefunction(self.__on_error)

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_bolt.py:855, in Bolt.fetch_message(self)
    851 # Receive exactly one message
    852 tag, fields = self.inbox.pop(
    853     hydration_hooks=self.responses[0].hydration_hooks
    854 )
--> 855 res = self._process_message(tag, fields)
    856 self.idle_since = monotonic()
    857 return res

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_bolt5.py:370, in Bolt5x0._process_message(self, tag, fields)
    368 self._server_state_manager.state = self.bolt_states.FAILED
    369 try:
--> 370     response.on_failure(summary_metadata or {})
    371 except (ServiceUnavailable, DatabaseUnavailable):
    372     if self.pool:

File ~/.local/lib/python3.10/site-packages/neo4j/_sync/io/_common.py:245, in Response.on_failure(self, metadata)
    243 handler = self.handlers.get(\"on_summary\")
    244 Util.callback(handler)
--> 245 raise Neo4jError.hydrate(**metadata)

CypherSyntaxError: {code: Neo.ClientError.Statement.SyntaxError} {message: Invalid constraint syntax, ON and ASSERT should not be used. Replace ON with FOR and ASSERT with REQUIRE. (line 2, column 1 (offset: 17))
\"                CREATE CONSTRAINT IF NOT EXISTS ON (n:Entity) ASSERT n.id IS UNIQUE;\"
                 ^}"
}
dosubot[bot] commented 1 month ago

Hey there, @liamdonnellymv! I'm Dosu, here to assist you with any bugs, questions, or contributions. Let's work together to tackle this issue!

To resolve the Cypher syntax error when creating a Neo4jGraphStore using the latest Neo4j Docker container, you need to update the constraint creation query to comply with the latest Neo4j syntax. The error message indicates that the ON and ASSERT keywords should be replaced with FOR and REQUIRE.

Here's how you can modify the Neo4jGraphStore initialization to use the correct syntax:

  1. Update the Constraint Creation Query: Modify the query in the Neo4jGraphStore class to use the latest syntax:

    """
    CREATE CONSTRAINT IF NOT EXISTS FOR (n:%s) REQUIRE n.id IS UNIQUE;
    """
    % (self.node_label)
  2. Ensure Proper Initialization: Make sure the Neo4jGraphStore is properly initialized and connected to the Neo4j database. Here is an example:

    from llama_index.graph_stores.neo4j import Neo4jGraphStore
    
    # Initialize the Neo4jGraphStore
    graph_store = Neo4jGraphStore(
       username="neo4j",
       password="your_password",
       url="bolt://localhost:7687",
       database="neo4j"
    )
    
    # Verify connection and refresh schema
    graph_store.refresh_schema()
  3. Launch Neo4j Docker Container with Required Configurations: Ensure that the Neo4j Docker container is launched with the necessary configurations, including the APOC plugin:

    services:
     neofourj:
       image: neo4j
       ports:
         - 8102:7474
         - 8103:7687
       volumes:
         - ./neo4j_data:/data
       environment:
         - NEO4J_PLUGINS=["apoc"]
         - NEO4J_dbms_security_procedures_unrestricted=apoc.*

    Then, spin up the container with:

    docker compose up

By following these steps, you should be able to resolve the Cypher syntax error and successfully create a Neo4jGraphStore using the latest Neo4j Docker container [1][2].

To continue talking to Dosu, mention @dosu.

liamdonnellymv commented 1 month ago

Will try changing the llamaindex code but this is a work around and still requires a PR

liamdonnellymv commented 1 month ago

Turns out the Cipher syntax wasn't the root cause. It was symptom of the try except assuming syntax was the issue and then running Copher for neo4j < 5.

Suggestion: Line 83 in .../python3.10/site-packages/llama_index/graph_stores/neo4j/base.py should have some logic to check the exception from neo4j. In my case it was some pre existing constraints in the database I was pointing at.