Cypher with Neography

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.

Tagged , , ,

7 thoughts on “Cypher with Neography

  1. 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

  2. […] 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 […]

  3. […] 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 […]

  4. Jason says:

    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”

    • maxdemarzi says:

      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

  5. Nitesh says:

    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?

Leave a comment