Skip to content

Commit

Permalink
Cypher docs fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
nawroth committed May 11, 2012
1 parent 5ec1e15 commit 3b06b74
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 62 deletions.
3 changes: 2 additions & 1 deletion cypher/src/docs/dev/expressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ An expression in Cypher can be:
* A collection of expressions -- `["a", "b"]`, `[1,2,3]`, `["a", 2, n.property, {param}]`, `[ ]`
* A function call -- `length(p)`, `nodes(p)`
* An aggregate function -- `avg(x.prop)`, `count(*)`
* Relationship types - :REL_TYPE, +:\`REL TYPE`+, :REL1|REL2
* Relationship types -- `:REL_TYPE`, +:\`REL TYPE`+, `:REL1|REL2`

22 changes: 11 additions & 11 deletions cypher/src/docs/dev/introduction.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
``Cypher'' is a declarative graph query language that allows for expressive and efficient querying and updating of the
_Cypher_ is a declarative graph query language that allows for expressive and efficient querying and updating of the
graph store without having to write traversals through the graph structure in code. Cypher is still growing and
maturing, and that means that there probably will be breaking syntax changes. It also means that it has not undergone
the same rigorous performance testing as the other components.
Expand All @@ -20,27 +20,27 @@ implementation detail not exposed to the user.

The query language is comprised of several distinct clauses.

* +START+: Starting points in the graph, obtained by element IDs or via index lookups
* +START+: Starting points in the graph, obtained via index lookups or by element IDs.

* +MATCH+: The graph pattern to match, bound to the starting points in +START+
* +MATCH+: The graph pattern to match, bound to the starting points in +START+.

* +WHERE+: Filtering criteria
* +WHERE+: Filtering criteria.

* +RETURN+: What to return
* +RETURN+: What to return.

* +CREATE+: Creates nodes and relationships
* +CREATE+: Creates nodes and relationships.

* +DELETE+: Removed nodes, relationships and properties
* +DELETE+: Removes nodes, relationships and properties.

* +SET+: Set values to properties
* +SET+: Set values to properties.

* +FOREACH+: Performs updating actions once per element in a list
* +FOREACH+: Performs updating actions once per element in a list.

* +WITH+: Divides a query into multiple, distinct parts
* +WITH+: Divides a query into multiple, distinct parts.


Let's see three of them in action:

include::intro-examples.txt[]

To use Cypher from Java, see <<tutorials-cypher-java>>. For more Cypher examples, see even <<cypher-cookbook>>.
To use Cypher from Java, see <<tutorials-cypher-java>>. For more Cypher examples, see even <<cypher-cookbook>>.
13 changes: 6 additions & 7 deletions cypher/src/docs/dev/operators.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@ Operators

Operators in Cypher are of three different varieties - mathematical, equality and relationships.

The mathematical operators are +, -, *, / and %. Of these, only the plus-sign works on strings.
The mathematical operators are `+`, `-`, `*`, `/` and `%`. Of these, only the plus-sign works on strings.

The equality operators are =, <>, <, >, `<=`, >=.
The equality operators are `=`, `<>`, `<`, `>`, `<=`, `>=`.

Since Neo4j is a schema less graph database, Cypher has two special operators - ? and !.
Since Neo4j is a schema-less graph database, Cypher has two special operators -- `?` and `!`.

They are used on properties, and are used to deal with missing values. A comparison on a property
that does not exist will cause an error. Instead of having to always check if the property exists
before comparing its value with something else, the question mark make the comparison always return
true if the property is missing, and the exclamation mark makes the comparator return false.

This predicate will evaluate to true if n.prop is missing.
This predicate will evaluate to true if +n.prop+ is missing.

+WHERE n.prop? = "foo"+


This predicate will evaluate to false if n.prop is missing.
This predicate will evaluate to false if +n.prop+ is missing.

+WHERE n.prop! = "foo"+

Warning: Mixing the two in the same comparison will lead to unpredictable results.

This is really syntatic sugar that expands to this:
This is really syntactic sugar that expands to this:

+WHERE n.prop? = "foo"+ => +WHERE (not(has(n.prop)) OR n.prop = "foo")+

Expand Down
15 changes: 10 additions & 5 deletions cypher/src/docs/dev/transactions.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
[[query-transactions]]
Transactions and Cypher
=======================

Any query that updates the graph will run in a transaction. An updating query will always either fully succeed,
or not succeed at all.

Cypher will either create a new transaction, and commit it once the query finishes. If a transaction already
exists in the running context, the query will run inside the it, and nothing will be persisted to disk until
Cypher will either create a new transaction, and commit it once the query finishes. Or if a transaction already
exists in the running context, the query will run inside it, and nothing will be persisted to disk until
the transaction is successfully committed.

This can be used to have multiple queries be committed as a single transaction - open a transaction, run multiple
updating Cypher queries, and commit all of them in one go.
This can be used to have multiple queries be committed as a single transaction:

. Open a transaction,
. run multiple updating Cypher queries,
. and commit all of them in one go.

Note that a query will hold the changes in heap until the whole query has finished executing. A large query will
consequently need a JVM with lots of heap space.
consequently need a JVM with lots of heap space.

29 changes: 18 additions & 11 deletions cypher/src/docs/dev/updating.txt
Original file line number Diff line number Diff line change
@@ -1,45 +1,52 @@
[[query-updating]]
Updating the graph with Cypher
==============================

Cypher can be used for both querying and updating your graph.

Updating query structure
== Updating query structure ==

A Cypher query part can't both match and update the graph at the same time. Every part can either read and match on the
graph, or make updates on it.

== Query Parts & Structure ==
If you read from the graph, and then update the graph, your query implicitly has two parts - the reading is the first

If you read from the graph, and then update the graph, your query implicitly has two parts -- the reading is the first
part, and the writing is the second. If your query is read-only, Cypher will be lazy, and not actually pattern match
until you ask for the results. Here, the semantics are that _all_ the reading will be done before any writing actually
happens. This is very important - without this it's easy to find cases where the pattern matcher runs into data that is
happens. This is very important -- without this it's easy to find cases where the pattern matcher runs into data that is
being created by the very same query, and all bets are off. That road leads to Heisenbugs, Brownian motion and cats that
are dead and alive at the same time.

First reading, and then writing, is the only pattern where the query parts are implicit - any other order and you
First reading, and then writing, is the only pattern where the query parts are implicit -- any other order and you
have to be explicit about your query parts. The parts are separated using the `WITH` statement. `WITH` is like the event
horizon - it's a barrier between a plan and the finished execution of that plan.
horizon -- it's a barrier between a plan and the finished execution of that plan.

When you want filter using aggregated data, you have to chain together two reading query parts - the first one does the
When you want filter using aggregated data, you have to chain together two reading query parts -- the first one does the
aggregating, and the second query filters on the results coming from the first one.

[source,cypher]
----
START n=node(...)
MATCH n-[:friend]-friend
WITH n, count(friend) as friendsCount
WHERE friendsCount > 3
RETURN n, friendsCount
----

Using `WITH`, you specify how you want the aggregation to happen, and that the aggregation has to be finished before
Cypher can start filtering.

You can chain together as many query parts as you have JVM heap for.

== Returning data ==
Any query can return data. If your query is read only, it has to return data - it serves no purpose if it doesn't, and

Any query can return data. If your query is read only, it has to return data -- it serves no purpose if it doesn't, and
it is not a valid Cypher query. Queries that update the graph don't have to return anything, but they can.

After all the parts of the query, comes one final `RETURN`-statement. `RETURN` is not part of any query part - it
is a period symbol after an eloquent statement. When `RETURN` is legal, it's also legal to use SKIP/LIMIT and ORDER BY.
After all the parts of the query, comes one final `RETURN` statement. `RETURN` is not part of any query part -- it
is a period symbol after an eloquent statement. When `RETURN` is legal, it's also legal to use `SKIP`/`LIMIT` and `ORDER BY`.

If you return graph elements from a query that has just deleted them - beware, you are holding a pointer that is no
longer valid. Operations on that node might fail mysteriously and unpredictably.
If you return graph elements from a query that has just deleted them -- beware, you are holding a pointer that is no
longer valid. Operations on that node might fail mysteriously and unpredictably.

Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ public void exampleWithStringLiteralAsParameter() throws Exception
// START SNIPPET: exampleWithStringLiteralAsParameter
Map<String, Object> params = new HashMap<String, Object>();
params.put( "name", "Johan" );
ExecutionResult result = engine.execute( "start n=node(0,1,2) where n.name = {name} return n", params );
ExecutionResult result =
engine.execute( "start n=node(0,1,2) where n.name = {name} return n", params );
// END SNIPPET: exampleWithStringLiteralAsParameter

assertEquals( asList( johanNode ), this.<Node>toList( result, "n" ) );
Expand All @@ -192,7 +193,8 @@ public void exampleWithParametersForIndexKeyAndValue() throws Exception
Map<String, Object> params = new HashMap<String, Object>();
params.put( "key", "name" );
params.put( "value", "Michaela" );
ExecutionResult result = engine.execute( "start n=node:people({key} = {value}) return n", params );
ExecutionResult result =
engine.execute( "start n=node:people({key} = {value}) return n", params );
// END SNIPPET: exampleWithParametersForIndexKeyAndValue

assertEquals( asList( michaelaNode ), this.<Node>toList( result, "n" ) );
Expand Down Expand Up @@ -231,7 +233,8 @@ public void exampleWithParameterForSkipAndLimit() throws Exception
Map<String, Object> params = new HashMap<String, Object>();
params.put( "s", 1 );
params.put( "l", 1 );
ExecutionResult result = engine.execute( "start n=node(0,1,2) return n.name skip {s} limit {l}", params );
ExecutionResult result =
engine.execute( "start n=node(0,1,2) return n.name skip {s} limit {l}", params );
// END SNIPPET: exampleWithParameterForSkipLimit

assertThat( result.columns(), hasItem( "n.name" ) );
Expand All @@ -245,7 +248,8 @@ public void exampleWithParameterRegularExpression() throws Exception
// START SNIPPET: exampleWithParameterRegularExpression
Map<String, Object> params = new HashMap<String, Object>();
params.put( "regex", ".*h.*" );
ExecutionResult result = engine.execute( "start n=node(0,1,2) where n.name =~ {regex} return n.name", params );
ExecutionResult result =
engine.execute( "start n=node(0,1,2) where n.name =~ {regex} return n.name", params );
// END SNIPPET: exampleWithParameterRegularExpression

assertThat( result.columns(), hasItem( "n.name" ) );
Expand Down
20 changes: 10 additions & 10 deletions cypher/src/test/scala/org/neo4j/cypher/docgen/MatchTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class MatchTest extends DocumentingTestBase {
@Test def relatedNodesByMultipleRelationshipTypes() {
testQuery(
title = "Match by multiple relationship types",
text = "If multiple types are acceptable, you can specify this by chaining them with the pipe symbol |",
text = "If multiple types are acceptable, you can specify this by chaining them with the pipe symbol `|`.",
queryText = """start n=node(%A%) match (n)-[:BLOCKS|KNOWS]->(x) return x""",
returns = """All nodes with a +BLOCK+ or +KNOWS+ relationship to A.""",
assertions = (p) => assertEquals(List(node("C"), node("B")), p.columnAs[Node]("x").toList)
Expand Down Expand Up @@ -122,7 +122,7 @@ class MatchTest extends DocumentingTestBase {
text = "Relationships can be expressed by using multiple statements in the form of `()--()`, or they can be strung together, " +
"like this:",
queryText = """start a=node(%A%) match (a)-[:KNOWS]->(b)-[:KNOWS]->(c) return a,b,c""",
returns = """The three nodes in the path.""",
returns = """The three nodes in the path are returned.""",
assertions = (p) => assertEquals(List(Map("a" -> node("A"), "b" -> node("B"), "c" -> node("E"))), p.toList)
)
}
Expand Down Expand Up @@ -180,7 +180,7 @@ class MatchTest extends DocumentingTestBase {
title = "Optional relationship",
text = "If a relationship is optional, it can be marked with a question mark. This is similar to how a SQL outer join " +
"works. If the relationship is there, it is returned. If it's not, +null+ is returned in it's place. Remember that " +
"anything hanging off an optional relationship, is in turn optional, unless it is connected with a bound node some other " +
"anything hanging off an optional relationship, is in turn optional, unless it is connected with a bound node through some other " +
"path.",
queryText = """start a=node(%E%) match a-[?]->x return a,x""",
returns = """A node, and +null+, since the node has no outgoing relationships.""",
Expand All @@ -193,7 +193,7 @@ class MatchTest extends DocumentingTestBase {
title = "Properties on optional elements",
text = "Returning a property from an optional element that is +null+ will also return +null+.",
queryText = """start a=node(%E%) match a-[?]->x return x, x.name""",
returns = """The element x (`null` in this query), and `null` as it's name.""",
returns = """This returns the element x (`null` in this query), and `null` as it's name.""",
assertions = (p) => assertEquals(List(Map("x" -> null, "x.name" -> null)), p.toList)
)
}
Expand All @@ -204,15 +204,15 @@ class MatchTest extends DocumentingTestBase {
text = "Just as with a normal relationship, you can decide which identifier it goes into, and what relationship type " +
"you need.",
queryText = """start a=node(%A%) match a-[r?:LOVES]->() return a,r""",
returns = """A node, and +null+, since the node has no outgoing `LOVES` relationships.""",
returns = """This returns a node, and +null+, since the node has no outgoing `LOVES` relationships.""",
assertions = (p) => assertEquals(List(Map("a" -> node("A"), "r" -> null)), p.toList)
)
}

@Test def shortestPathBetweenTwoNodes() {
testQuery(
title = "Shortest path",
text = "Finding a single shortest path between two nodes is as easy as using the `shortestPath`-function, like this:",
text = "Finding a single shortest path between two nodes is as easy as using the `shortestPath` function, like this:",
queryText = """start d=node(%D%), e=node(%E%) match p = shortestPath( d-[*..15]->e ) return p""",
returns = """This means: find a single shortest path between two nodes, as long as the path is max 15 relationships long. Inside of the parenthesis
you write a single link of a path -- the starting node, the connecting relationship and the end node. Characteristics describing the relationship
Expand All @@ -238,7 +238,7 @@ class MatchTest extends DocumentingTestBase {
queryText = """start a=node(%A%)
match (a)-[:KNOWS]->(b)-[:KNOWS]->(c), (a)-[:BLOCKS]-(d)-[:KNOWS]-(c)
return a,b,c,d""",
returns = """The four nodes in the paths.""",
returns = """This returns the four nodes in the paths.""",
assertions = p => assertEquals(List(Map("a" -> node("A"), "b" -> node("B"), "c" -> node("E"), "d" -> node("C"))), p.toList)
)
}
Expand All @@ -248,7 +248,7 @@ return a,b,c,d""",
title = "Named path",
text = "If you want to return or filter on a path in your pattern graph, you can a introduce a named path.",
queryText = """start a=node(%A%) match p = a-->b return p""",
returns = """The two paths starting from the first node.""",
returns = """This returns the two paths starting from the first node.""",
assertions = (p) => assertEquals(2, p.toSeq.length)
)
}
Expand All @@ -259,7 +259,7 @@ return a,b,c,d""",
text = """When your pattern contains a bound relationship, and that relationship pattern doesn't specify direction,
Cypher will try to match the relationship where the connected nodes switch sides.""",
queryText = """start r=rel(0) match a-[r]-b return a,b""",
returns = "This returns the two connected nodes, once as the start node, and once as the end node",
returns = "This returns the two connected nodes, once as the start node, and once as the end node.",
assertions = p => assertEquals(2, p.toSeq.length)
)
}
Expand Down Expand Up @@ -299,7 +299,7 @@ RETURN p"""
testQuery(
title = "introduction",
text = """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.
looking for. Cypher will then try to find patterns in the graph -- these are called matching subgraphs.
The description of the pattern is made up of one or more paths, separated by commas. A path is a sequence of nodes and
relationships that always start and end in nodes. An example path would be:
Expand Down
Loading

0 comments on commit 3b06b74

Please sign in to comment.