10 Caveats Neo4j Users Should Be Familiar With
Recently I used the Neo4j graph database in GitMoon. I have gathered some of the tricky things I learned the hard way and I recommend any Neo4j user to take a look.
1. Execution guard
By default queries can run forever. This means that if you have accidently (or by purpose) sent the server a long running query with many nested relationships, your CPU may be busy for a while. The solution is to configure neo to terminate all queries longer than some threshold. Here's how you do this:
in neo4j.properties add this:
Then in neo4j-server.properties add this:
where the limit value is in milliseconds, so the above will terminate
each query that runs over 20 seconds. Your CPU will thank you for it!
2. ID (may) be volatile
Each node has a unique ID assigned to it by neo. so in your cypher you could do something like:
Early on I was tempted to use this ID in urls of my app:
This was very convinient since I did not have a numeric natual key for
nodes and I did not want the hassle of encoding strings in urls.
Bad idea.IDs are volatile. In theory, when you restart the db all nodes may be assigned with different IDs.
IDs of deleted nodes may be reused for new nodes. In practice, I have
not seen this happen, and I believe that with the current neo versions
this will never happen. However you should not take it as guaranteed and
should always come up with your own unique way to identify nodes.
3. ORDER BY lower case
There is no build in function that allows you to return results ordered by some field in lower case. You have to maintain a shadow field with the lower case values. For example:
4. Random rows
There is no built in mechanism to return a random row.
The easiest way is to use a two-phase randomization - first select the COUNT of available rows, then SKIP rows until you get to that row:
Where 20 is number you generated in your code. Of course this will never
be fully randomized, and also requires some knowledge on the values
distribution, but for many cases this may be good enough.
5. Use the WITH clause when cypher has expensive conditions
Take a look at this cypher:
Here we will calculate the shortest path for all noes. This is a cpu
intensive operation. How about separating concerns like this:
Now the path is only calculated on relevant nodes which is much cheaper.
6. Arbitrary depth is evil
Always strive to limit the max depth of queries you perform. Each depth level increases the query complexity:
7. Safe shutdown on windows
When you run Neo4j on windows in interactive mode (e.g. not a service) do not close the console with the x button. Instead, always use CTRL+C and then wait a few seconds until the db is safety closed and the window disappears. If by mistake you did not safely close it then the next start will be slower (can take a few minutes or more) since neo will do recovery. In that case the log (see #8) will show this message:
8. The log is your best friend
When crap hits the fan always turn out to /data/log. Especially if neo does not start you may find out that you have misconfigured some setting or recovery has started (see #7)
9. Prevent cypher injection
Take a look at this code:
if "search" comes from an interactive user then you can imagine what kind of injections are possible. The correct way is to use cypher parameters which any driver should expose an api for. If you use the awesome node-neo4j api by aseemk you could do it like this:
Published at DZone with permission of Yaron Naveh, author and DZone MVB. (source)1. Execution guard
By default queries can run forever. This means that if you have accidently (or by purpose) sent the server a long running query with many nested relationships, your CPU may be busy for a while. The solution is to configure neo to terminate all queries longer than some threshold. Here's how you do this:
in neo4j.properties add this:
execution_guard_enabled=true
org.neo4j.server.webserver.limit.executiontime=20000
2. ID (may) be volatile
Each node has a unique ID assigned to it by neo. so in your cypher you could do something like:
START n=node(1020) RETURN n START n=node(*) where ID(n)=1020 return nwhere both cyphers will return the same node.
Early on I was tempted to use this ID in urls of my app:
/projects/1020/users
Bad idea.
3. ORDER BY lower case
There is no build in function that allows you to return results ordered by some field in lower case. You have to maintain a shadow field with the lower case values. For example:
RETURN n.name ORDER BY n.name_lower
There is no built in mechanism to return a random row.
The easiest way is to use a two-phase randomization - first select the COUNT of available rows, then SKIP rows until you get to that row:
START n=node(*) WHERE n.type='project' RETURN count(*) // result is 1000 // now in your app code you make a draw and the random number is 512 START n=node(*) WHERE n.type='project' RETURN n SKIP 512 LIMIT 1An alternative is to use statistical randomization:
START n=node(*) WHERE n.type='project' AND ID(n)%20=0 RETURN n LIMIT 1
5. Use the WITH clause when cypher has expensive conditions
Take a look at this cypher:
START n=node(...), u=node(...) MATCH p = shortestPath( n<-[*..5]-u) WHERE n.forks>20 AND length(p)>2 RETURN n, u, p
START n=node(...), u=node(...) WHERE n.forks>20 AND length(p)>2 WITH n as n, u as u MATCH p = shortestPath( n<-[*..5]-u ) WHERE length(p)>2 RETURN n, u, p
6. Arbitrary depth is evil
Always strive to limit the max depth of queries you perform. Each depth level increases the query complexity:
... MATCH (n)<-[depends_on*0..4]-(x) ...
When you run Neo4j on windows in interactive mode (e.g. not a service) do not close the console with the x button. Instead, always use CTRL+C and then wait a few seconds until the db is safety closed and the window disappears. If by mistake you did not safely close it then the next start will be slower (can take a few minutes or more) since neo will do recovery. In that case the log (see #8) will show this message:
INFO: Non clean shutdown detected on log [C:\Program Files (x86)\neo4j-community-1.8.M03\data\graph.db\index\lucene.log.1]. Recovery started ...
When crap hits the fan always turn out to /data/log. Especially if neo does not start you may find out that you have misconfigured some setting or recovery has started (see #7)
9. Prevent cypher injection
Take a look at this code:
"START n=node(*) WHERE n='"+search+"' RETURN n"
qry = "START n=node(*) WHERE n={search} RETURN n"
db.query qry, {search: "async"}
10. Where to get help
The Neo4j Google group or the community github project are very friendly and responsive.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)
Tags:





