Skip to content
Andreas Ronge edited this page Apr 25, 2012 · 36 revisions

“Cypher” is a declarative graph query language that allows for expressive and efficient querying of the graph store without having to write traversals through the graph structure in code. Neo4j.rb provides two ways of using cypher: express queries as Strings or use the Neo4j.rb Cypher DSL.

Cypher

The cypher language is described here: Neo4j Cypher Docuementation

q = Neo4j._query("START n=node(42) RETURN n")
q.first(:n) #=> the @node
q.columns.first => :n

Cypher DSL

See Neo4j#query method.

Using start,match,where,ret

You can use start, match, where and ret method if you want think it improves the readability.

The following:

Neo4j.query do
 start node(3)
 where n[:name] == 'kalle'
 ret n 
end

Is the same as

Neo4j.query{ node(3); n[:name] == 'kalle'; n }
# same as START n=node(1) RETURN n

Notice the last value in the block is the return value.

Start Nodes

Every query describes a pattern, and in that pattern one can have multiple start points. A start point is a relationship or a node that form the starting points for a pattern match. You can either introduce start points by id, or by index lookups. Note that trying to use an index that doesn’t exist will throw an exception.

Node By Id

Binding a node as a start point is done with the node(*) function.

Neo4j.query{ node(3) }
# same as START n=node(1) RETURN n

The node above will be assigned a variable automatically. To get the variable name: use the columns method, example:

result = Neo4j.query{ node(3) }
result.columns # => [:n0]
result.first[:n0] #=> returns the node 3

Notice that the query result is a once forward Enumerable object. To read the result more then once use #to_a to create an array of the Enumerable.

Change variable binding

There is a #as method which will change the name of the columns. Example:

result = Neo4j.query{ node(3).as(:x }
result.columns # => [:x]
result.first[:x] #=> returns the node 3

Using existing nodes and relationship

The query method allows parameters for start nodes and relationships.

Neo4j.query(node){|n| n }
# same as START n0=node(n.neo_id) RETURN n0

Multiple nodes by id

Selected by an array of nodes or relationships using query argument:

Neo4j.query([node1, node2]){|n| n }
# same as START n0=node(node1.neo_id, node2.neo_id) RETURN n0

Alternative: Neo4j.query{node(node_id1, node_id2) }

Variables

Nodes and Relationships can also be used in WHERE and MATCH clauses. To express that you want any node use the node or rel without any argument. Example:

Neo4j.query{x = node; n = node(3); match n <=> x; ret x}

which is same as: START n0=node(3) MATCH (n0)--(v0) RETURN v

Notice that the x variable will be assigned a cypher variable automatically (v0 above)

A shorter way of doing this is using ruby symbols:

Neo4j.query{ node(3) <=> :x; :x}

Relationships

You can start from a relationship just like you do with nodes.

Neo4j.query{ rel(3) }
# same as "START r0=relationship(3) RETURN r0"

Just like nodes you can use symbols to represent a relationship variable.

Neo4j.query{node(3) > :r > 'bla'; :x}
# same as "START n0=node(3) MATCH (n0)-[:r]->(bla) RETURN x"

If a relationship is a String it will be used in the match clause as is.

Neo4j.query{node(3) > ':r|knows' > 'bla'; :x}
# same as "START n0=node(3) MATCH (n0)-[:r|knows]->(bla) RETURN x"

Index Lookup

Since you can inject your own nodes and relationship to the Cypher DSL there is less needed to use the lookup and query methods.

Example of lookup

Neo4j.query { lookup(Person, "desc", "A")
# same as "START n0=node:person_fulltext(desc="A") RETURN n0"

Example of query

Neo4j.query { query(Person, "name:A", :fulltext) }
# same as "START n0=node:person_fulltext(name:A) RETURN n0])"

Example of using query argument:

# Notice that the neo4j-core and neo4j find method behaves differently.
# For Neo4j::Rails::Model.find the first method is not needed since it returns one node
Neo4j.query(Person.find("name: A").first) { |n| n}

The lookup and query methods knows where the lucene index is stored.

Match

Pattern matching is one of the pillars of Cypher. The pattern is used to describe the shape of the data that we are looking for. Cypher will then try to find patterns in the graph — these are called matching sub graphs.

In Neo4j.rb there are several methods to specify a match pattern by using the <, >, >>, <<, <=> and outgoing and incoming methods.

The <, > and - operators is used when you want to specify relationship type(s) between two nodes.

Example: node > ':knows|friend' > other_node

The <<, <=> and outgoing and incoming or used when you don't care what type of relationship the has.

Example: node > ':knows|friend' > other_node

Relationships

Many of the patterns

Related nodes

The method <=> will generate a pattern match without regard to type or direction.

Example:

Neo4j.query{node(3, 4) <=> :x; node(:x)[:desc] =~ /hej/; :x}
# Same as "START n0=node(3,4) MATCH (n0)--(x) WHERE x.desc =~ /hej/ RETURN x"

Outgoing or Incoming of a Type

If you don't want to specify the direction of the relationship but only the type of relationship you can use the - operator:

Neo4j.query{node(3) - ':knows|friends' - :foo; :foo}
# Same as "START n0=node(3) MATCH (n0)-[:knows|friends]-(foo) RETURN foo"

Outgoing relationships

When the direction of a relationship is interesting ...

Examples

Example 5.2. Basic Friend finding based on social neighborhood

See http://docs.neo4j.org/chunked/snapshot/cypher-cookbook-friend-finding.html Let say we want to express the following Cypher Query:

START joe=node(some node id)
MATCH joe-[:knows]->friend-[:knows]->friend_of_friend, joe-[r?:knows]->friend_of_friend
WHERE r IS NULL
RETURN friend_of_friend.name, COUNT(*)
ORDER BY COUNT(*) DESC, friend_of_friend.name

This can be done like this:

Neo4j.query(joe_node) do |joe|
  friends_of_friends = node(:friends_of_friends)
  joe > ':knows' > node(:friend) > ':knows' > friends_of_friends
  r = rel('r?:knows')
  joe > r > friends_of_friends
  r.exist?
  ret(friends_of_friends[:name], count).desc(count).asc(friends_of_friends[:name])
end

Or like this using the outgoing instead of the > operator

Neo4j.query(joe_node) do |joe|
 friends_of_friends = joe.outgoing(:knows).outgoing(:knows)
 r = rel('r?:knows').as(:r)
 joe > r > friends_of_friends
 r.exist?
 ret(friends_of_friends[:name], count).desc(count).asc(friends_of_friends[:name])
end
Clone this wiki locally