Cypher is the query language of Neo4j, and as promised I’ll show you how you can use it to implement friend recommendations as well as degrees of separation.
We can send any cypher query to Neo4j via the REST API and neography using the execute_query command. Let’s implement suggestions_for so it sends a cypher query to the server:
def suggestions_for(node) node_id = node["self"].split('/').last.to_i @neo.execute_query("START me = node({node_id}) MATCH (me)-[:friends]->(friend)-[:friends]->(foaf) RETURN foaf.name", {:node_id => node_id})["data"] end puts "Johnathan should become friends with #{suggestions_for(johnathan).join(', ')}" # RESULT # Johnathan should become friends with Mary, Phil
Let’s go through the query:
START me = node({node_id})
is the place in the graph where we will begin. We are naming this node “me” and will reference it later. The
{node_id}
part defines a parameter which we will fill in later. Like before, we want to find nodes that are two friends relationships away
MATCH (me)-[:friends]->(friend)-[:friends]->(foaf)
and we want to return the name of these friends of a friend which we are labeling “foaf”, skipping over our known friends which were labeled “friend”
RETURN foaf.name
We then pass in our parameter
{:node_id => node_id}
This last bit just grabs the name data, ignoring the names of the columns which are also returned
["data"]
How about degrees of separation?
def degrees_of_separation(start_node, destination_node) start_node_id = start_node["self"].split('/').last.to_i destination_node_id = destination_node["self"].split('/').last.to_i @neo.execute_query("START me=node({start_node_id}), them=node({destination_node_id}) MATCH path = allShortestPaths( me-[?*]->them ) RETURN length(path), extract(person in nodes(path) : person.name)", {:start_node_id => start_node_id, :destination_node_id => destination_node_id })["data"] end degrees_of_separation(johnathan, mary).each do |path| nodes = path.last puts "#{path.first} degrees: " + nodes.join(' => friends => ') end # RESULT # 2 degrees: Johnathan => friends => Mark => friends => Mary
We are once again using some of the built in graph functions of Neo4j. In this case we are using allShortestPaths. We start with “me” as the start node and “them” as the destination node
START me=node({start_node_id}), them=node({destination_node_id})
We want to match any relationship between me and them, and get the shortest paths
MATCH path = allShortestPaths( me-[?*]->them )
then we want to return the length of the paths and the names of the nodes. We use the extract function to pull node data from the paths. You can think of extract like map or collect in Ruby
RETURN length(path), extract(person in nodes(path) : person.name)
We pass in our parameters
{:start_node_id => start_node_id, :destination_node_id => destination_node_id }
and we just want the data returned, ignoring the column names
["data"]
We do a bit of string wrangling at the end to get the format we want and that’s all there is too it.
Cypher is a fairly new language and is undergoing constant additions and improvements. Contribute and stay up to date by checking the Neo4j Google Group.
Max,
great post, thanks!
Perhaps choosing better names for the cypher variables would help make it more readable like (me, friend, foaf).
Your first query should perhaps show cyphers Index usage, like:
start me = node:users(name={username}) ….
Thanks
You are right, I’ll set a good example by using proper naming.
[…] In upcoming posts, I’ll show you two more ways to traverse the graph via Gremlin and Cypher as well as many more things you can do with […]
[…] of friends and degrees of separation with the Neo4j REST API and a little bit of the Gremlin and Cypher languages. However, all we’ve seen is a little bit of text output. We haven’t really […]
I am having issues passing this as a query string using execute_query and trying different escape sequences on quotes. i have a lot of indexes with spaces ion them. This works in neo4j console btw.. Thanks in advance
START a=node:i_search(lc_name=’abraham lincoln’)RETURN a
“START a=node:i_search(lc_name=\’abraham lincoln\’)RETURN a”
Jason,
I’ll take a look at this and add some specs to make sure it works as intended.
Thanks for the feedback.
Regards,
Max
Hi Max,
I am trying to search node on basis of property value so to do that I am trying to create index first and then adding node to that but it doesn’t seems working,
Do you any example code prepared for creating INdex and adding nodes to it and then search node on basis of property?