-
Notifications
You must be signed in to change notification settings - Fork 276
Neo4j::Core Cypher
“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.
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
See Neo4j#query method.
This wiki page only contains an overview of what is possible to express with the neo4j.rb cypher DSL. For a complete description see the RSpecs or the YARD docs
You can use start
, match
, where
and ret
method if you want think it improves the readability.
The following:
Neo4j.query do
start(n=node(3,4,5))
where(n[:name] == 'kalle')
ret n
end
Is the same as
Neo4j.query{ n=node(3,4,5); n[:name] == 'kalle'; n }
# same as START n=node(1) RETURN n
Notice the last value in the block is the return value.
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.
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.
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
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
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) }
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}
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) > ':friend|knows' > 'bla'; :x}
# same as "START n0=node(3) MATCH (n0)-[:friend|knows]->(bla) RETURN x"
You can also use relationship as variables. Let say you want to return all the relationships instead from the query above. This can be done like this:
Neo4j.query{node(3) > (r=rel(':friend|knows')) > 'bla'; r}
# same as "START n0=node(3) MATCH (n0)-[r:friend|knows]->(bla) RETURN r"
Since you can inject your own nodes and relationship as argument 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.
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 >> other_node
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"
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"
You can specify the direction of relationship to match.
Example: match all outgoing relationship of any type
Neo4j.query{node(3, 4) >> :x; :x}
# Same as "START n0=node(3,4) MATCH (n0)-->(x) RETURN x"
By using the outgoing and incoming method you can use the declared has_n/has_one relationship types. Example, return friends of friends
Neo4j.query(Person.find(...)){|person| person.outgoing(Person.friends).outgoing(Person.friends)}
# Same as "START n0=node(some_id) MATCH (n0)-[:`knows`]->(v0),(v0)-[:`knows`]->(v1) RETURN v1"
A path specified with for example <<
has a number of useful methods.
For example, we can return the length of the path like this:
Neo4j.query { p = node(3) >> :b; p.length }
Available methods for paths, see MatchRelRight
It is often useful to assign a variable to a path (as seen above), relationships or nodes. By doing that you can use the variable as a condition in the WHERE clause or in the RETURN clause.
Example: return all outgoing nodes with property name equal 'andreas'
Neo4j.query { node(3) >> (x = node); x[:name] == 'andreas'; x }
You specify where clauses by using the ==, !=, not < comparable methods (Comparable) on properties (Property) or the is_a?, property?, exist? on node or relationship variables ([Neo4j::Core::Cypher::Variable|http://rdoc.info/github/andreasronge/neo4j-core/Neo4j/Core/Cypher/Variable])
Example:
Neo4j.query { n=node(3, 4); n[:desc] == "hej"; n }
# same as START n0=node(3,4) WHERE n0.desc = "hej" RETURN n0
Example for optinal properties
Neo4j.query { n=node(3, 4); n[:desc?] == "hej"; n }
# same as START n0=node(3,4) WHERE n0.desc? = "hej" RETURN n0
Checking if an property exists:
Neo4j.query { n=node(3, 1); n.property?(:belt); n }
# same as START n0=node(3,1) WHERE has(n0.belt) RETURN n0
Available methods for variables see Property
The none?, single? any? methods can be found on both path variable or property array.
Example:
Neo4j.query { p=node(3)>'*1..3'>:b; p.nodes.none? { |x| x[:age] == 25 }}
# same as START n0=node(3) MATCH m2 = (n0)-[*1..3]->(b) WHERE none(x in nodes(m2) WHERE x.age = 25) RETURN m2
The ruby &, | and ! (only for Ruby 1.9) and not can be used to make compound where statements.
Example:
Neo4j.query { n=node(3, 1); where((n[:age] < 30) & ((n[:name] == 'foo') | (n[:size] > n[:age]))); ret n }
# same as START n0=node(3,1) WHERE (n0.age < 30) and ((n0.name = "foo") or (n0.size > n0.age)) RETURN n0
(use where method below because it is easier to read)
It uses normal Ruby syntax.
Example: node(:x)[:desc] =~ /hej/
The DSL will take care of what is a match and what is a where clause. That means you can write queries like this:
node(1) > (rel(:knows)[:since] > 1994) > (node(:other)[:name] == 'foo'); :other
Which is same as :
START n0=node(1) MATCH (n0)-[v1:`knows`]->(other) WHERE v1.since > 1994 and other.name = "foo" RETURN other
The last statement will be used as the cypher return clause.
Example:
Neo4j.query { a=node(3); b = node(2); ret a[:age], b[:age], (a[:age] - b[:age]).abs}
# same as START n0=node(3),n1=node(2) RETURN n0.age,n1.age,abs(n0.age - n1.age)
# instead or ret you can simply return an array
Sorting can be done using the asc and desc methods.
Neo4j.query { n=node(3, 1, 2); ret(n, n[:name]).asc(n[:name], n[:age]) }
# same as START n0=node(3,1,2) RETURN n0,n0.name ORDER BY n0.name, n0.age
# instead or ret you can simply return an array
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
WARNING: Much of the information in this wiki is out of date. We are in the process of moving things to readthedocs
- Project Introduction
- Neo4j::ActiveNode
- Neo4j::ActiveRel
- Search and Scope
- Validation, Uniqueness, and Case Sensitivity
- Indexing VS Legacy Indexing
- Optimized Methods
- Inheritance
- Core: Nodes & Rels
- Introduction
- Persistence
- Find : Lucene
- Relationships
- Third Party Gems & extensions
- Scaffolding & Generators
- HA Cluster