/** * Copyright (c) 2002-2013 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.cypherdsl; import static org.neo4j.cypherdsl.CypherQuery.abs; import static org.neo4j.cypherdsl.CypherQuery.allNodes; import static org.neo4j.cypherdsl.CypherQuery.as; import static org.neo4j.cypherdsl.CypherQuery.coalesce; import static org.neo4j.cypherdsl.CypherQuery.collect; import static org.neo4j.cypherdsl.CypherQuery.count; import static org.neo4j.cypherdsl.CypherQuery.create; import static org.neo4j.cypherdsl.CypherQuery.distinct; import static org.neo4j.cypherdsl.CypherQuery.has; import static org.neo4j.cypherdsl.CypherQuery.id; import static org.neo4j.cypherdsl.CypherQuery.identifier; import static org.neo4j.cypherdsl.CypherQuery.in; import static org.neo4j.cypherdsl.CypherQuery.isNull; import static org.neo4j.cypherdsl.CypherQuery.literal; import static org.neo4j.cypherdsl.CypherQuery.lookup; import static org.neo4j.cypherdsl.CypherQuery.max; import static org.neo4j.cypherdsl.CypherQuery.min; import static org.neo4j.cypherdsl.CypherQuery.node; import static org.neo4j.cypherdsl.CypherQuery.nodesById; import static org.neo4j.cypherdsl.CypherQuery.not; import static org.neo4j.cypherdsl.CypherQuery.order; import static org.neo4j.cypherdsl.CypherQuery.p; import static org.neo4j.cypherdsl.CypherQuery.param; import static org.neo4j.cypherdsl.CypherQuery.path; import static org.neo4j.cypherdsl.CypherQuery.range; import static org.neo4j.cypherdsl.CypherQuery.round; import static org.neo4j.cypherdsl.CypherQuery.start; import static org.neo4j.cypherdsl.CypherQuery.sum; import static org.neo4j.cypherdsl.CypherQuery.type; import static org.neo4j.cypherdsl.CypherQuery.value; import static org.neo4j.cypherdsl.Order.ASCENDING; import static org.neo4j.cypherdsl.Order.DESCENDING; import org.junit.Test; import org.neo4j.cypherdsl.grammar.Start; import org.neo4j.cypherdsl.query.Query; /** * Construct Cypher queries corresponding to the Cypher Cookbook in the manual */ public class CypherCookbookTest extends AbstractCypherTest { @Test public void test5_1_1() { // This test shows how to do partial queries. When the Query from toQuery() is passed into a new CypherQuery // it is cloned, so any modifications do not affect the original query Query query = start( lookup( "n", "node_auto_index", "name", "User1" ) ). match( node( "n" ).out( "hasRoleInGroup" ).node( "hyperEdge" ).out( "hasGroup" ).node( "group" ), node( "hyperEdge" ).out( "hasRole" ).node( "role" ) ).toQuery(); assertQueryEquals( CYPHER + "START n=node:node_auto_index(name=\"User1\") MATCH (n)-[:hasRoleInGroup]->" + "(hyperEdge)-[:hasGroup]->(group),(hyperEdge)-[:hasRole]->(role) WHERE group.name=\"Group2\" RETURN " + "role.name", CypherQuery.continueQuery( query, Start.class ).starts(). where( identifier( "group" ).string( "name" ).eq( "Group2" ) ). returns( identifier( "role" ).string( "name" ) ). toString() ); assertQueryEquals( CYPHER + "START n=node:node_auto_index(name=\"User1\") MATCH (n)-[:hasRoleInGroup]->" + "(hyperEdge)-[:hasGroup]->(group),(hyperEdge)-[:hasRole]->(role) RETURN role.name," + "group.name ORDER BY role.name ASCENDING", CypherQuery.continueQuery( query, Start.class ).starts(). returns( identifier( "role" ).property( "name" ), identifier( "group" ).property( "name" ) ). orderBy( order( identifier( "role" ).string( "name" ), ASCENDING ) ). toString() ); } @Test public void test5_1_2() { Identifier u1 = identifier( "u1" ); Identifier u2 = identifier( "u2" ); Identifier hyperEdge1 = identifier( "hyperEdge1" ); Identifier group = identifier( "group" ); Identifier role = identifier( "role" ); Identifier hyperEdge2 = identifier( "hyperEdge2" ); assertQueryEquals( CYPHER + "START u1=node:node_auto_index(name=\"User1\")," + "u2=node:node_auto_index(name=\"User2\") " + "MATCH (u1)-[:hasRoleInGroup]->(hyperEdge1)-[:hasGroup]->(group)," + "(hyperEdge1)-[:hasRole]->(role)," + "(u2)-[:hasRoleInGroup]->(hyperEdge2)-[:hasGroup]->(group)," + "(hyperEdge2)-[:hasRole]->(role) " + "RETURN group.name,count(role) " + "ORDER BY group.name ASCENDING", start( lookup( u1, identifier( "node_auto_index" ), identifier( "name" ), literal( "User1" ) ), lookup( u2, identifier( "node_auto_index" ), identifier( "name" ), literal( "User2" ) ) ). match( node( u1 ) .out( "hasRoleInGroup" ) .node( hyperEdge1 ) .out( "hasGroup" ) .node( group ), node( hyperEdge1 ).out( "hasRole" ).node( role ), node( u2 ) .out( "hasRoleInGroup" ) .node( hyperEdge2 ) .out( "hasGroup" ) .node( group ), node( hyperEdge2 ).out( "hasRole" ).node( role ) ). returns( group.property( "name" ), count( role ) ). orderBy( order( group.property( "name" ), ASCENDING ) ) .toString() ); } @Test public void test5_1_3() { assertQueryEquals( CYPHER + "START u1=node:node_auto_index(name=\"User1\")," + "u2=node:node_auto_index(name=\"User2\") " + "MATCH (u1)-[:hasRoleInGroup]->(hyperEdge1)-[:hasGroup]->(group)," + "(hyperEdge1)-[:hasRole]->(role)," + "(u2)-[:hasRoleInGroup]->(hyperEdge2)-[:hasGroup]->(group)," + "(hyperEdge2)-[:hasRole]->(role) " + "RETURN group.name,count(role) " + "ORDER BY group.name ASCENDING", start( lookup( "u1", "node_auto_index", "name", "User1" ), lookup( "u2", "node_auto_index", "name", "User2" ) ). match( node( "u1" ).out( "hasRoleInGroup" ) .node( "hyperEdge1" ) .out( "hasGroup" ) .node( "group" ), node( "hyperEdge1" ).out( "hasRole" ).node( "role" ), node( "u2" ).out( "hasRoleInGroup" ) .node( "hyperEdge2" ) .out( "hasGroup" ) .node( "group" ), node( "hyperEdge2" ).out( "hasRole" ).node( "role" ) ). returns( identifier( "group" ).property( "name" ), count( identifier( "role" ) ) ). orderBy( order( identifier( "group" ).property( "name" ), Order.ASCENDING ) ).toString() ); } @Test public void test5_2_1() { assertQueryEquals( CYPHER + "START joe=node:node_auto_index(name=\"Joe\") OPTIONAL 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(*) DESCENDING,friend_of_friend.name", start( lookup( "joe", "node_auto_index", "name", "Joe" ) ). match( node( "joe" ).out( "knows" ).node( "friend" ) .out( "knows" ).node( "friend_of_friend" ), node( "joe" ).out( "knows" ).as( "r" ).node( "friend_of_friend" ) ).optional(). where( isNull( identifier( "r" ) ) ). returns( identifier( "friend_of_friend" ).property( "name" ), count() ). orderBy( order( count(), DESCENDING ), identifier( "friend_of_friend" ).property( "name" ) ). toString() ); } @Test public void test5_3_1() { assertQueryEquals( CYPHER + "START place=node:node_auto_index(name=\"CoffeShop1\") MATCH (place)" + "<-[:favorite]-(person)-[:favorite]->(stuff) RETURN stuff.name,count(*) ORDER BY count(*) DESCENDING," + "stuff.name", start( lookup( "place", "node_auto_index", "name", "CoffeShop1" ) ). match( node( "place" ).in( "favorite" ).node( "person" ).out( "favorite" ).node( "stuff" ) ). returns( identifier( "stuff" ).property( "name" ), count() ). orderBy( order( count(), DESCENDING ), identifier( "stuff" ).property( "name" ) ). toString() ); assertQueryEquals( CYPHER + "START place=node:node_auto_index(name=\"CoffeShop1\") MATCH (place)-[:tagged]->" + "(tag)<-[:tagged]-(otherPlace) RETURN otherPlace.name,collect(tag.name) ORDER BY otherPlace.name " + "DESCENDING", start( lookup( "place", "node_auto_index", "name", "CoffeShop1" ) ). match( node( "place" ).out( "tagged" ).node( "tag" ).in( "tagged" ).node( "otherPlace" ) ). returns( identifier( "otherPlace" ).property( "name" ), collect( identifier( "tag" ).property ( "name" ) ) ). orderBy( order( identifier( "otherPlace" ).property( "name" ), DESCENDING ) ). toString() ); } @Test public void test5_3_2() { assertQueryEquals( CYPHER + "START place=node:node_auto_index(name=\"CoffeeShop1\") MATCH (place)-[:tagged]->" + "(tag)<-[:tagged]-(otherPlace) RETURN otherPlace.name,collect(tag.name) ORDER BY otherPlace.name " + "DESCENDING", start( lookup( "place", "node_auto_index", "name", "CoffeeShop1" ) ). match( node( "place" ).out( "tagged" ).node( "tag" ).in( "tagged" ).node( "otherPlace" ) ). returns( identifier( "otherPlace" ).property( "name" ), collect( identifier( "tag" ).property ( "name" ) ) ). orderBy( order( identifier( "otherPlace" ).property( "name" ), DESCENDING ) ).toString() ); } @Test public void test5_4_1() { assertQueryEquals( CYPHER + "START me=node:node_auto_index(name=\"Joe\") MATCH (me)-[:favorite]->(stuff)" + "<-[:favorite]-(person) WHERE not((me)-[:friend]-(person)) RETURN person.name," + "count(stuff) ORDER BY count(stuff) DESCENDING", start( lookup( "me", "node_auto_index", "name", "Joe" ) ). match( node( "me" ).out( "favorite" ).node( "stuff" ).in( "favorite" ).node( "person" ) ). where( not( node( "me" ).both( "friend" ).node( "person" ) ) ). returns( identifier( "person" ).property( "name" ), count( identifier( "stuff" ) ) ). orderBy( order( count( identifier( "stuff" ) ), DESCENDING ) ). toString() ); } @Test public void test5_5_1() { assertQueryEquals( CYPHER + "START me=node(5),other=node(4,3) " + "OPTIONAL MATCH pGroups=(me)-[:member_of_group]->(mg)<-[:member_of_group]-(other)," + "pMutualFriends=(me)-[:knows]->(mf)<-[:knows]-(other) " + "RETURN other.name AS name," + "count(DISTINCT pGroups) AS mutualGroups," + "count(DISTINCT pMutualFriends) AS mutualFriends ORDER BY mutualFriends DESCENDING", start( nodesById( "me", 5 ), nodesById( "other", 4, 3 ) ). match( path( "pGroups", node( "me" ).out( "member_of_group" ).node( "mg" ).in( "member_of_group" ).node( "other" ) ), path( "pMutualFriends", node( "me" ).out( "knows" ).node( "mf" ).in( "knows" ).node( "other" ) ) ).optional(). returns( as( identifier( "other" ).property( "name" ), "name" ), as( count( distinct( identifier( "pGroups" ) ) ), "mutualGroups" ), as( count( distinct( identifier( "pMutualFriends" ) ) ), "mutualFriends" ) ). orderBy( order( identifier( "mutualFriends" ), DESCENDING ) ).toString() ); } @Test public void test5_6_1() { assertQueryEquals( CYPHER + "START me=node(9) " + "MATCH (me)-[:favorite]->(myFavorites)-[:tagged]->(tag)<-[:tagged]-(theirFavorites)<-[:favorite]-" + "(people) " + "WHERE not(me=people) " + "RETURN people.name AS name,count(*) AS similar_favs " + "ORDER BY similar_favs DESCENDING", start( nodesById( "me", 9 ) ). match( node( "me" ).out( "favorite" ).node( "myFavorites" ).out( "tagged" ).node( "tag" ).in( "tagged" ).node( "theirFavorites" ).in( "favorite" ).node( "people" ) ). where( not( identifier( "me" ).eq( identifier( "people" ) ) ) ). returns( as( identifier( "people" ).property( "name" ), "name" ), as( count(), "similar_favs" ) ). orderBy( order( identifier( "similar_favs" ), DESCENDING ) ).toString() ); } @Test public void test5_7_1() { assertQueryEquals( CYPHER + "START me=node:node_auto_index(name=\"Joe\") " + "MATCH (me)-[r1]->(other)-[r2]->(me) " + "WHERE type(r1)=type(r2) and type(r1)=~\"FOLLOWS|LOVES\" " + "RETURN other.name,type(r1)", start( lookup( "me", "node_auto_index", "name", "Joe" ) ). match( node( "me" ).out().as( "r1" ).node( "other" ).out().as( "r2" ).node( "me" ) ). where( type( identifier( "r1" ) ).eq( type( identifier( "r2" ) ) ) .and( type( identifier( "r1" ) ).regexp( "FOLLOWS|LOVES" ) ) ). returns( identifier( "other" ).property( "name" ), type( identifier( "r1" ) ) ).toString() ); } @Test public void test5_7_1_1() { assertQueryEquals( CYPHER + "START me=node:node_auto_index(name=\"Joe\") " + "MATCH (me)-[r1]->(other)-[r2]->(me) " + "WHERE type(r1)=type(r2) and type(r1)=~{param1} " + "RETURN other.name,type(r1)", start( lookup( "me", "node_auto_index", "name", "Joe" ) ). match( node( "me" ).out().as( "r1" ).node( "other" ).out().as( "r2" ).node( "me" ) ). where( type( identifier( "r1" ) ).eq( type( identifier( "r2" ) ) ) .and( type( identifier( "r1" ) ).regexp( param( "param1" ) ) ) ). returns( identifier( "other" ).property( "name" ), type( identifier( "r1" ) ) ).toString() ); } @Test public void test5_8_1() { assertQueryEquals( CYPHER + "START origin=node(1) " + "MATCH (origin)-[r1:KNOWS|WORKSAT]-(c)-[r2:KNOWS|WORKSAT]-(candidate) " + "WHERE type(r1)=type(r2) and not((origin)-[:KNOWS]-(candidate)) " + "RETURN origin.name AS origin,candidate.name AS candidate,sum(round(r2.weight+" + "coalesce(r2.activity,0)*2)) AS boost " + "ORDER BY boost DESCENDING " + "LIMIT 10", start( nodesById( "origin", 1 ) ). match( node( "origin" ).both( "KNOWS", "WORKSAT" ).as( "r1" ).node( "c" ).both( "KNOWS", "WORKSAT" ).as( "r2" ).node( "candidate" ) ). where( type( identifier( "r1" ) ).eq( type( identifier( "r2" ) ) ).and( not( node( "origin" ) .both( "KNOWS" ).node( "candidate" ) ) ) ). returns( as( identifier( "origin" ).property( "name" ), "origin" ), as( identifier( "candidate" ).property( "name" ), "candidate" ), as( sum( round( identifier( "r2" ).property( "weight" ).add( coalesce( identifier( "r2" ) .property( "activity" ) , literal( 0 ) ) .times( 2 ) ) ) ), "boost" ) ). orderBy( order( identifier( "boost" ), DESCENDING ) ). limit( 10 ).toString() ); } @Test public void test5_8_1_1() { assertQueryEquals( CYPHER + "START origin=node(1) " + "MATCH (origin)-[r1:KNOWS|WORKSAT]-(c)-[r2:KNOWS|WORKSAT]-(candidate) " + "WHERE type(r1)=type(r2) and not((origin)-[:KNOWS]-(candidate)) " + "RETURN origin.name AS origin,candidate.name AS candidate,sum(round(r2.weight+" + "coalesce(r2.activity,0)*2)) AS boost " + "ORDER BY boost DESCENDING " + "LIMIT {limitParam}", start( nodesById( "origin", 1 ) ). match( node( "origin" ).both( "KNOWS", "WORKSAT" ).as( "r1" ).node( "c" ).both( "KNOWS", "WORKSAT" ).as( "r2" ).node( "candidate" ) ). where( type( identifier( "r1" ) ).eq( type( identifier( "r2" ) ) ).and( not( node( "origin" ) .both( "KNOWS" ).node( "candidate" ) ) ) ). returns( as( identifier( "origin" ).property( "name" ), "origin" ), as( identifier( "candidate" ).property( "name" ), "candidate" ), as( sum( round( identifier( "r2" ).property( "weight" ).add( coalesce( identifier( "r2" ) .property( "activity" ) , literal( 0 ) ) .times( 2 ) ) ) ), "boost" ) ). orderBy( order( identifier( "boost" ), DESCENDING ) ). limit( "limitParam" ).toString() ); } @Test public void test5_9_1() { assertQueryEquals( CYPHER + "START a=node(1) " + "MATCH (a)--(b) " + "WITH a,count(DISTINCT b) AS n " + "MATCH (a)--()-[r]-()--(a) " + "RETURN n,count(DISTINCT r) AS r", start( nodesById( "a", 1 ) ). match( node( "a" ).both().node( "b" ) ). with( identifier( "a" ), as( count( distinct( identifier( "b" ) ) ), "n" ) ). match( node( "a" ).both().node().both().as( "r" ).node().both().node( "a" ) ). returns( identifier( "n" ), as( count( distinct( identifier( "r" ) ) ), "r" ) ).toString() ); } @Test public void test5_10_1() { assertQueryEquals( CYPHER + "CREATE (center) " + "FOREACH(x in range(1,10)| CREATE (leaf),(center)-[:X]->(leaf)) " + "RETURN id(center) AS id", create( node( "center" ) ). forEach( in( identifier( "x" ), range( 1, 10 ) ).create( node( "leaf" ), node( "center" ).out( "X" ).node( "leaf" ) ) ). returns( as( id( identifier( "center" ) ), "id" ) ).toString() ); } @Test public void test5_10_2() { assertQueryEquals( CYPHER + "CREATE (center) " + "FOREACH(x in range(1,10)| CREATE (leaf {count:x}),(center)-[:X]->(leaf)) " + "WITH center " + "MATCH (large_leaf)<--(center)-->(small_leaf) " + "WHERE large_leaf.count=small_leaf.count+1 " + "CREATE (small_leaf)-[:X]->(large_leaf) " + "WITH center,min(small_leaf.count) AS min,max(large_leaf.count) AS max " + "MATCH (first_leaf)<--(center)-->(last_leaf) " + "WHERE first_leaf.count=min and last_leaf.count=max " + "CREATE (last_leaf)-[:X]->(first_leaf) " + "RETURN id(center) AS id", create( node( "center" ) ). forEach( in( "x", range( 1, 10 ) ).create( node( "leaf" ).values( value( "count", identifier( "x" ) ) ), node( "center" ).out( "X" ).node( "leaf" ) ) ). with( identifier( "center" ) ). match( node( "large_leaf" ).in().node( "center" ).out().node( "small_leaf" ) ). where( identifier( "large_leaf" ).property( "count" ).eq( identifier( "small_leaf" ).property ( "count" ).add( 1 ) ) ). create( node( "small_leaf" ).out( "X" ).node( "large_leaf" ) ). with( identifier( "center" ), as( min( identifier( "small_leaf" ).property( "count" ) ), "min" ), as( max( identifier( "large_leaf" ).property( "count" ) ), "max" ) ). match( node( "first_leaf" ).in().node( "center" ).out().node( "last_leaf" ) ). where( identifier( "first_leaf" ).property( "count" ).eq( identifier( "min" ) ).and( identifier( "last_leaf" ).property( "count" ).eq( identifier( "max" ) ) ) ). create( node( "last_leaf" ).out( "X" ).node( "first_leaf" ) ). returns( as( id( identifier( "center" ) ), "id" ) ).toString() ); } @Test public void test5_10_3() { assertQueryEquals( CYPHER + "CREATE (center) " + "FOREACH(x in range(1,10)| CREATE (leaf {count:x}),(center)-[:X]->(leaf)) " + "WITH center " + "MATCH (leaf1)<--(center)-->(leaf2) " + "WHERE id(leaf1)<id(leaf2) " + "CREATE (leaf1)-[:X]->(leaf2) " + "WITH center " + "MATCH (center)-[r]->() " + "DELETE center,r", create( node( "center" ) ). forEach( in( "x", range( 1, 10 ) ).create( node( "leaf" ).values( value( "count", identifier( "x" ) ) ), node( "center" ).out( "X" ).node( "leaf" ) ) ). with( identifier( "center" ) ). match( node( "leaf1" ).in().node( "center" ).out().node( "leaf2" ) ). where( id( "leaf1" ).lt( id( "leaf2" ) ) ). create( node( "leaf1" ).out( "X" ).node( "leaf2" ) ). with( identifier( "center" ) ). match( node( "center" ).out().as( "r" ).node() ). delete( identifier( "center" ), identifier( "r" ) ).toString() ); } @Test public void test5_10_4() { assertQueryEquals( CYPHER + "CREATE (center) " + "FOREACH(x in range(1,10)| CREATE (leaf1),(leaf2),(center)-[:X]->(leaf1),(center)-[:X]->(leaf2)," + "(leaf1)-[:X]->(leaf2)) " + "RETURN id(center) AS id", create( node( "center" ) ). forEach( in( "x", range( 1, 10 ) ).create( node( "leaf1" ), node( "leaf2" ), node( "center" ).out( "X" ).node( "leaf1" ), node( "center" ).out( "X" ).node( "leaf2" ), node( "leaf1" ).out( "X" ).node( "leaf2" ) ) ). returns( as( id( identifier( "center" ) ), "id" ) ).toString() ); } @Test public void test5_12_1() { assertQueryEquals( CYPHER + "CREATE (root)-[:LINK]->(root) RETURN root", create( node( "root" ).out( "LINK" ).node( "root" ) ).returns( identifier( "root" ) ).toString() ); } @Test public void test5_12_2() { assertQueryEquals( CYPHER + "START root=node(4) " + "MATCH (root)-[:LINK*0..]->(before),(after)-[:LINK*0..]->(root),(before)-[old:LINK]->(after) " + "WHERE before.value<25 and 25<after.value " + "CREATE (before)-[:LINK]->({value:25})-[:LINK]->(after) " + "DELETE old", start( nodesById( "root", 4 ) ). match( node( "root" ).out( "LINK" ).hops( 0, null ).node( "before" ), node( "after" ).out( "LINK" ).hops( 0, null ).node( "root" ), node( "before" ).out( "LINK" ).as( "old" ).node( "after" ) ). where( identifier( "before" ).property( "value" ).lt( 25 ).and( literal( 25 ).lt( identifier( "after" ).property( "value" ) ) ) ). create( node( "before" ).out( "LINK" ).node().values( value( "value", 25 ) ).out( "LINK" ).node( "after" ) ). delete( identifier( "old" ) ).toString() ); } @Test public void test5_12_3() { assertQueryEquals( CYPHER + "START root=node(4) " + "MATCH (root)-[:LINK*0..]->(before),(before)-[delBefore:LINK]->(del)-[delAfter:LINK]->(after)," + "(after)-[:LINK*0..]->(root) " + "WHERE del.value=10 " + "CREATE (before)-[:LINK]->(after) " + "DELETE del,delBefore,delAfter", start( nodesById( "root", 4 ) ). match( node( "root" ).out( "LINK" ).hops( 0, null ).node( "before" ), node( "before" ).out( "LINK" ).as( "delBefore" ).node( "del" ).out( "LINK" ).as( "delAfter" ).node( "after" ), node( "after" ).out( "LINK" ).hops( 0, null ).node( "root" ) ). where( identifier( "del" ).property( "value" ).eq( 10 ) ). create( node( "before" ).out( "LINK" ).node( "after" ) ). delete( identifier( "del" ), identifier( "delBefore" ), identifier( "delAfter" ) ).toString() ); } @Test public void test5_13_1() { assertQueryEquals( CYPHER + "START root=node:node_auto_index(name=\"Root\") " + "MATCH rootPath=(root)-[:`2010`]->()-[:`12`]->()-[:`31`]->(leaf),(leaf)-[:VALUE]->(event) " + "RETURN event.name " + "ORDER BY event.name ASCENDING", start( lookup( "root", "node_auto_index", "name", "Root" ) ). match( path( "rootPath", node( "root" ).out( "2010" ).node().out( "12" ).node().out( "31" ) .node( "leaf" ) ), node( "leaf" ).out( "VALUE" ).node( "event" ) ). returns( identifier( "event" ).property( "name" ) ). orderBy( order( identifier( "event" ).property( "name" ), ASCENDING ) ).toString() ); } @Test public void test5_13_2() { assertQueryEquals( CYPHER + "START root=node:node_auto_index(name=\"Root\") " + "MATCH startPath=(root)-[:`2010`]->()-[:`12`]->()-[:`31`]->(startLeaf)," + "endPath=(root)-[:`2011`]->()-[:`01`]->()-[:`03`]->(endLeaf)," + "valuePath=(startLeaf)-[:NEXT*0..]->(middle)-[:NEXT*0..]->(endLeaf)," + "values=(middle)-[:VALUE]->(event) " + "RETURN event.name " + "ORDER BY event.name ASCENDING", start( lookup( "root", "node_auto_index", "name", "Root" ) ). match( path( "startPath", node( "root" ).out( "2010" ).node().out( "12" ).node().out( "31" ) .node( "startLeaf" ) ), path( "endPath", node( "root" ).out( "2011" ).node().out( "01" ).node().out( "03" ) .node( "endLeaf" ) ), path( "valuePath", node( "startLeaf" ).out( "NEXT" ).hops( 0, null ).node( "middle" ).out( "NEXT" ).hops( 0, null ).node( "endLeaf" ) ), path( "values", node( "middle" ).out( "VALUE" ).node( "event" ) ) ). returns( identifier( "event" ).property( "name" ) ). orderBy( order( identifier( "event" ).property( "name" ), ASCENDING ) ).toString() ); } @Test public void test5_13_3() { assertQueryEquals( CYPHER + "START root=node:node_auto_index(name=\"Root\") " + "MATCH commonPath=(root)-[:`2011`]->()-[:`01`]->(commonRootEnd)," + "startPath=(commonRootEnd)-[:`01`]->(startLeaf)," + "endPath=(commonRootEnd)-[:`03`]->(endLeaf)," + "valuePath=(startLeaf)-[:NEXT*0..]->(middle)-[:NEXT*0..]->(endLeaf)," + "values=(middle)-[:VALUE]->(event) " + "RETURN event.name " + "ORDER BY event.name ASCENDING", start( lookup( "root", "node_auto_index", "name", "Root" ) ). match( path( "commonPath", node( "root" ).out( "2011" ).node().out( "01" ).node( "commonRootEnd" ) ), path( "startPath", node( "commonRootEnd" ).out( "01" ).node( "startLeaf" ) ), path( "endPath", node( "commonRootEnd" ).out( "03" ).node( "endLeaf" ) ), path( "valuePath", node( "startLeaf" ).out( "NEXT" ).hops( 0, null ).node( "middle" ).out( "NEXT" ).hops( 0, null ).node( "endLeaf" ) ), path( "values", node( "middle" ).out( "VALUE" ).node( "event" ) ) ). returns( identifier( "event" ).property( "name" ) ). orderBy( order( identifier( "event" ).property( "name" ), ASCENDING ) ).toString() ); } @Test public void test5_14_1() { assertQueryEquals( CYPHER + "START me=node(*) " + "MATCH (me)-[r1:ATE]->(food)<-[r2:ATE]-(you) " + "WHERE has(me.name) and me.name=\"me\" " + "WITH me,count(DISTINCT r1) AS H1,count(DISTINCT r2) AS H2,you " + "MATCH (me)-[r1:ATE]->(food)<-[r2:ATE]-(you) " + "RETURN sum((1-abs(r1.times/H1-r2.times/H2))*(r1.times+r2.times)/(H1+H2)) AS similarity", start( allNodes( "me" ) ). match( node( "me" ).out( "ATE" ).as( "r1" ).node( "food" ).in( "ATE" ).as( "r2" ).node( "you" ) ). where( has( identifier( "me" ).property( "name" ) ).and( identifier( "me" ).property( "name" ) .eq( "me" ) ) ). with( identifier( "me" ), as( count( distinct( identifier( "r1" ) ) ), "H1" ), as( count( distinct( identifier( "r2" ) ) ), "H2" ), identifier( "you" ) ). match( node( "me" ).out( "ATE" ).as( "r1" ).node( "food" ).in( "ATE" ).as( "r2" ).node( "you" ) ). returns( as( sum( p( literal( 1 ).subtract( abs( identifier( "r1" ).property( "times" ) .divideBy( identifier( "H1" ) ) .subtract( identifier( "r2" ).property( "times" ) .divideBy( identifier( "H2" ) ) ) ) ) ) . times( p( identifier( "r1" ).property( "times" ) .add( identifier( "r2" ).property( "times" ) ) ).divideBy( p( identifier( "H1" ) .add( identifier( "H2" ) ) ) ) ) ), "similarity" ) ).toString() ); } }