Permalink
Browse files

Adding the ability to do ORDER BY, SKIP, LIMIT after WITH, before

another optional MATCH.
  • Loading branch information...
1 parent f9e37cd commit 5b0ab57ce272a4abd95578743e5e4bd5c9dc8485 @freeeve committed Oct 10, 2012
@@ -9,4 +9,6 @@ used to pipe the result from one query to the next.
include::cypher-with-graph.asciidoc[]
include::filter-on-aggregate-function-results.asciidoc[]
+include::sort-results-before-using-collect-on-them.asciidoc[]
+include::limit-branching-of-your-path-search.asciidoc[]
include::alternative-syntax-of-with.asciidoc[]
@@ -75,17 +75,21 @@ Thank you, the Neo4j Team.
val (pattern, namedPaths) = extractMatches(matching)
val returns = Return(List("*"), AllIdentifiers())
- BodyWith(updates, pattern, namedPaths, where, returns, None, startItems, paths, nextQ)
+ BodyWith(updates, pattern, namedPaths, None, where, Seq(), returns, None, startItems, paths, nextQ)
}
}
- def bodyWith: Parser[Body] = opt(matching) ~ opt(where) ~ WITH ~ opt(start) ~ updates ~ body ^^ {
- case matching ~ where ~ returns ~ start ~ updates ~ nextQ => {
+ def bodyWith: Parser[Body] = opt(matching) ~ opt(where) ~ WITH ~ opt(order) ~ opt(skip) ~ opt(limit) ~ opt(start) ~ updates ~ body ^^ {
+ case matching ~ where ~ returns ~ order ~ skip ~ limit ~ start ~ updates ~ nextQ => {
val (pattern, matchPaths) = extractMatches(matching)
val startItems = start.toSeq.flatMap(_._1)
val startPaths = start.toSeq.flatMap(_._2)
+ val slice = (skip, limit) match {
+ case (None, None) => None
+ case (s, l) => Some(Slice(s, l))
+ }
- BodyWith(updates._1, pattern, matchPaths ++ updates._2, where, returns._1, returns._2, startItems, startPaths, nextQ)
+ BodyWith(updates._1, pattern, matchPaths ++ updates._2, slice, where, order.toSeq.flatten, returns._1, returns._2, startItems, startPaths, nextQ)
}
}
@@ -113,7 +117,7 @@ Thank you, the Neo4j Team.
private def expandQuery(start: Seq[StartItem], namedPaths: Seq[NamedPath], updates: Seq[UpdateAction], body: Body): Query = body match {
case b: BodyWith => {
checkForAggregates(b.where)
- Query(b.returns, start, updates, b.matching, b.where, b.aggregate, Seq(), None, b.namedPath ++ namedPaths, Some(expandQuery(b.start, b.startPaths, b.updates, b.next)))
+ Query(b.returns, start, updates, b.matching, b.where, b.aggregate, b.order, b.slice, b.namedPath ++ namedPaths, Some(expandQuery(b.start, b.startPaths, b.updates, b.next)))
}
case b: BodyReturn => {
checkForAggregates(b.where)
@@ -193,11 +197,11 @@ If a Body is an intermediate part, either explicitly with WITH, or implicitly wh
This structure has three parts
*/
-case class BodyWith(updates:Seq[UpdateAction], matching: Seq[Pattern], namedPath: Seq[NamedPath], where: Option[Predicate], returns: Return, aggregate: Option[Seq[AggregationExpression]],// These items belong to the query part before the WITH delimiter
+case class BodyWith(updates:Seq[UpdateAction], matching: Seq[Pattern], namedPath: Seq[NamedPath], slice: Option[Slice], where: Option[Predicate], order:Seq[SortItem], returns: Return, aggregate: Option[Seq[AggregationExpression]],// These items belong to the query part before the WITH delimiter
start:Seq[StartItem], startPaths:Seq[NamedPath], // These are START or CREATE clauses directly following WITH
next: Body) extends Body // This is the pointer to the next query part
/*
This is the plug used when a query doesn't end in RETURN.
*/
-case class NoBody() extends Body
+case class NoBody() extends Body
@@ -1828,6 +1828,37 @@ foreach(x in [1,2,3] :
))
}
+ @Test def with_limit() {
+ testFrom_1_9("start n=node(0,1,2) with n limit 2 where ID(n) = 1 return n",
+ Query.
+ start(NodeById("n", 0, 1, 2)).
+ limit(2).
+ tail(Query.
+ start().
+ where(Equals(IdFunction(Identifier("n")), Literal(1))).
+ returns(ReturnItem(Identifier("n"), "n"))
+ ).
+ returns(
+ ReturnItem(Identifier("n"), "n")
+ ))
+ }
+
+ @Test def with_sort_limit() {
+ testFrom_1_9("start n=node(0,1,2) with n order by ID(n) desc limit 2 where ID(n) = 1 return n",
+ Query.
+ start(NodeById("n", 0, 1, 2)).
+ orderBy(SortItem(IdFunction(Identifier("n")), false)).
+ limit(2).
+ tail(Query.
+ start().
+ where(Equals(IdFunction(Identifier("n")), Literal(1))).
+ returns(ReturnItem(Identifier("n"), "n"))
+ ).
+ returns(
+ ReturnItem(Identifier("n"), "n")
+ ))
+ }
+
@Ignore("slow test") @Test def multi_thread_parsing() {
val q = """start root=node(0) return x"""
val parser = new CypherParser()
@@ -45,6 +45,24 @@ class WithTest extends DocumentingTestBase {
assertions = (p) => assertEquals(List(node("A")), p.columnAs[Node]("otherPerson").toList))
}
+ @Test def sort_collect_results() {
+ testQuery(
+ title = "Sort results before using collect on them",
+ text = "You can sort your results before passing them to collect, thus sorting the resulting collection.",
+ queryText = """start n=node(*) with n order by n.name desc limit 3 return collect(n.name)""",
+ returns = """A list of the names of people in reverse order, limited to 3, in a collection.""",
+ assertions = (p) => assertEquals(List(List("Emil", "David", "Cesar")), p.columnAs[Seq[String]]("collect(n.name)").toList))
+ }
+
+ @Test def limit_branching() {
+ testQuery(
+ title = "Limit branching of your path search",
+ text = "You can match paths, limit to a certain number, and then match again using those paths as a base As well as any number of similar limited searches.",
+ queryText = """start n=node(3) match n--m with m order by m.name desc limit 1 match m--o return o.name""",
+ returns = """Starting at Anders, find all matching nodes, order by name descending and get the top result, then find all the nodes connected to that top result, and return their names.""",
+ assertions = (p) => assertEquals(List("Anders", "Bossman"), p.columnAs[String]("o.name").toList))
+ }
+
@Test def alternative_way_to_write_with() {
testQuery(
title = "Alternative syntax of WITH",
@@ -58,4 +76,4 @@ set otherPerson.connection_count = foaf """,
returns = """For persons connected to David, the `connection_count` property is set to their number of outgoing relationships.""",
assertions = (p) => assertEquals(node("A").getProperty("connection_count"), 2L))
}
-}
+}

0 comments on commit 5b0ab57

Please sign in to comment.