Repository: iansrobinson/graph-databases-use-cases Branch: master Commit: 020a4c95de2c Files: 83 Total size: 319.2 KB Directory structure: gitextract_0j5cs4sk/ ├── .gitignore ├── .gitmodules ├── README.md ├── configuration/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── neo4j/ │ └── graphdatabases/ │ ├── AccessControlConfig.java │ ├── AccessControlWithRelationshipPropertiesConfig.java │ ├── LogisticsConfig.java │ ├── SimpleSocialNetworkConfig.java │ └── SocialNetworkConfig.java ├── data-generation/ │ ├── pom.xml │ └── src/ │ └── test/ │ └── java/ │ └── org/ │ └── neo4j/ │ └── graphdatabases/ │ └── dataset_builders/ │ ├── AccessControl.java │ ├── AccessControlWithRelationshipProperties.java │ ├── Logistics.java │ ├── SimpleSocialNetwork.java │ ├── SocialNetwork.java │ ├── helpers/ │ │ └── SevenDays.java │ ├── properties/ │ │ ├── DurationOnProjectProperty.java │ │ ├── ProjectDuration.java │ │ ├── ProjectDurationGenerator.java │ │ └── ProjectDurationProperty.java │ └── traversers/ │ ├── FindAllColleagues.java │ ├── FindColleagues.java │ └── IsCompanyProject.java ├── performance-testing/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── neo4j/ │ │ └── graphdatabases/ │ │ └── performance_tests/ │ │ └── testing/ │ │ ├── DefaultExecutionEngineWrapper.java │ │ ├── DoNothingWithTestResults.java │ │ ├── MultipleTestRuns.java │ │ ├── ParamsGenerator.java │ │ ├── PrintTestResults.java │ │ ├── QueryType.java │ │ ├── ResultFormatter.java │ │ ├── ResultsContainSameElementsUnordered.java │ │ ├── SingleTest.java │ │ ├── SingleTestRunResultHandler.java │ │ ├── SingleTestRunResultHandlerFactory.java │ │ ├── SysOutWriter.java │ │ ├── TakeXTestResults.java │ │ └── TestResultsHandler.java │ └── test/ │ └── java/ │ └── org/ │ └── neo4j/ │ └── graphdatabases/ │ └── performance_tests/ │ ├── AccessControl.java │ ├── AccessControlWithRelationshipProperties.java │ ├── Logistics.java │ ├── SimpleSocialNetwork.java │ ├── SocialNetwork.java │ └── testing/ │ └── ResultsContainSameElementsUnorderedTest.java ├── pom.xml └── queries/ ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── org/ │ │ └── neo4j/ │ │ └── graphdatabases/ │ │ └── queries/ │ │ ├── AccessControlQueries.java │ │ ├── AccessControlWithRelationshipPropertiesQueries.java │ │ ├── EmailQueries.java │ │ ├── LogisticsQueries.java │ │ ├── ShakespeareQueries.java │ │ ├── ShakespeareQueriesUsingAutoIndexes.java │ │ ├── SimpleSocialNetworkQueries.java │ │ ├── SocialNetworkQueries.java │ │ ├── helpers/ │ │ │ ├── DbUtils.java │ │ │ ├── ExecutionEngineWrapper.java │ │ │ ├── ExecutionResultIterator.java │ │ │ ├── ExecutionResultsIterator.java │ │ │ ├── IndexNodeByOtherNodeIndexer.java │ │ │ ├── QueryUnionExecutionEngine.java │ │ │ └── QueryUnionExecutionResult.java │ │ ├── server/ │ │ │ └── SimpleSocialNetworkExtension.java │ │ ├── testing/ │ │ │ ├── IndexParam.java │ │ │ ├── IndexParams.java │ │ │ └── TestOutputWriter.java │ │ └── traversals/ │ │ ├── FriendOfAFriendDepth4.java │ │ ├── IndexResources.java │ │ ├── ParcelRouteCalculator.java │ │ └── SimpleParcelRouteCalculator.java │ └── resources/ │ └── META_INF/ │ └── services/ │ ├── javax.script.ScriptEngineFactory │ └── org.neo4j.server.plugins.ServerPlugin └── test/ └── java/ └── org/ └── neo4j/ └── graphdatabases/ └── queries/ ├── AccessControlQueriesTest.java ├── AccessControlWithRelationshipPropertiesQueriesTest.java ├── EmailQueriesTest.java ├── LogisticsQueriesTest.java ├── ShakespeareQueriesTest.java ├── SimpleSocialNetworkQueriesTest.java ├── SocialNetworkQueriesTest.java ├── helpers/ │ ├── Db.java │ ├── IndexNodeByOtherNodeIndexerTest.java │ ├── PrintingExecutionEngineWrapper.java │ └── QueryUnionExecutionEngineTest.java └── server/ └── SimpleSocialNetworkExtensionTest.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ datasets/ examples/ datasets-old/ .DS_Store build target .settings .project .idea .classpath *.iml ================================================ FILE: .gitmodules ================================================ [submodule "neode"] path = neode url = https://github.com/jexp/neode.git ================================================ FILE: README.md ================================================ Graph Databases Use Cases ========================= Example use case implementations from the O'Reilly book [Graph Databases](http://graphdatabases.com/) by [@iansrobinson](http://twitter.com/iansrobinson), [@jimwebber](http://twitter.com/jimwebber) and [@emileifrem](http://twitter.com/emileifrem). Setup ----- This repository contains a submodule, _neode_, which is used to build the performance datasets. After cloning the repository, you will need to initialize the submodule: git submodule init and then: git submodule update To run the use case queries: mvn clean install Overview -------- Queries are developed in a test-driven fashion against small, well-known representative graphs (as described pp.83-87 of the book). The queries can then be run against a much larger, randomly-generated graph (typically, 1-2 million nodes and several million relationships), to test their relative performance. (Note: these performance tests do not test production-like scenarios; rather, they act as a sanity check, ensuring that queries that run fast against a very small graph are still reasonably performant when run against a larger graph.) The project contains 3 modules (in addition to the _neode_ submodule): * _queries_ Contains the use case queries and the unit tests used to develop the queries. * _dataset_builders_ Builds larger, randomly-generated sample datasets. * _performance_tests_ Runs the queries against the large sample datasets. Running the Performance Tests ----------------------------- First, build the project as described in Setup. Before you run the performance tests you will need to generate sample datasets. To create a sample dataset run: mvn test -pl data-generation -DargLine="-Xms2g -Xmx2g" -Dtest=AccessControl|Logistics|SocialNetwork For example, to generate a sample dataset for the Logistics queries, run: mvn test -pl data-generation -DargLine="-Xms2g -Xmx2g" -Dtest=Logistics *WARNING:* Building the sample datasets takes a long time (several tens of minutes in some cases). To execute the performance tests against a sample dataset, run: mvn test -pl performance-testing -DargLine="-Xms2g -Xmx2g" -Dtest=AccessControl|Logistics|SocialNetwork ================================================ FILE: configuration/pom.xml ================================================ 4.0.0 org.neo4j.graphdatabases configuration 2.0-SNAPSHOT jar 1.7 1.7 UTF-8 joda-time joda-time 2.1 ================================================ FILE: configuration/src/main/java/org/neo4j/graphdatabases/AccessControlConfig.java ================================================ package org.neo4j.graphdatabases; public class AccessControlConfig { public static final String STORE_DIR = "../datasets/access-control-no-attributes/"; public static final String TITLE = "Access Control (fine-grained permission relationships)"; public static final int NUMBER_OF_ADMINS = 1000; public static final int NUMBER_OF_GROUPS = 2000; public static final int NUMBER_OF_COMPANIES = 30000; } ================================================ FILE: configuration/src/main/java/org/neo4j/graphdatabases/AccessControlWithRelationshipPropertiesConfig.java ================================================ package org.neo4j.graphdatabases; public class AccessControlWithRelationshipPropertiesConfig { public static final String STORE_DIR = "../datasets/access-control/"; public static final String TITLE = "Access Control"; public static final int NUMBER_OF_ADMINS = 1000; public static final int NUMBER_OF_GROUPS = 2000; public static final int NUMBER_OF_COMPANIES = 30000; } ================================================ FILE: configuration/src/main/java/org/neo4j/graphdatabases/LogisticsConfig.java ================================================ package org.neo4j.graphdatabases; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; public class LogisticsConfig { public static final String TITLE = "Logistics"; public static final String STORE_DIR = "../datasets/logistics/"; public static final DateTime START_DATE = new DateTime( 2012, 10, 15, 0, 0, 0, 0, DateTimeZone.UTC ); public static final int NUMBER_OF_PARCEL_CENTRES = 20; public static final int MIN_NUMBER_OF_DELIVERY_BASES_PER_PARCEL_CENTRE = 30; public static final int MAX_NUMBER_OF_DELIVERY_BASES_PER_PARCEL_CENTRE = 50; public static final int MIN_NUMBER_OF_DELIVERY_AREAS_PER_DELIVERY_BASE = 20; public static final int MAX_NUMBER_OF_DELIVERY_AREAS_PER_DELIVERY_BASE = 40; public static final int MIN_NUMBER_OF_DELIVERY_SEGMENTS_PER_DELIVERY_AREA = 50; public static final int MAX_NUMBER_OF_DELIVERY_SEGMENTS_PER_DELIVERY_AREA = 100; } ================================================ FILE: configuration/src/main/java/org/neo4j/graphdatabases/SimpleSocialNetworkConfig.java ================================================ package org.neo4j.graphdatabases; public class SimpleSocialNetworkConfig { public static final String STORE_DIR = "../datasets/simple-social-network/"; public static final String TITLE = "Simple Social Network"; public static final int NUMBER_USERS = 20900; public static final int MIN_NUMBER_OF_FRIENDS = 50; public static final int MAX_NUMBER_OF_FRIENDS = 100; } ================================================ FILE: configuration/src/main/java/org/neo4j/graphdatabases/SocialNetworkConfig.java ================================================ package org.neo4j.graphdatabases; public class SocialNetworkConfig { public static final String STORE_DIR = "../datasets/social-network"; public static final String TITLE = "Social Network"; public static final int NUMBER_USERS = 1000000; public static final int NUMBER_COMPANIES = 10000; public static final int NUMBER_TOPICS = 100; } ================================================ FILE: data-generation/pom.xml ================================================ 4.0.0 org.neo4j.graphdatabases data-generation 2.0-SNAPSHOT jar 1.7 1.7 UTF-8 2.0.1 org.neo4j neo4j-kernel ${neo4j.version} org.neo4j neo4j-enterprise ${neo4j.version} org.neo4j.graphdatabases configuration 2.0-SNAPSHOT org.neo4j.graphdatabases queries 2.0-SNAPSHOT neode neode 2.0 joda-time joda-time 2.1 junit junit 4.11 test ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/AccessControl.java ================================================ package org.neo4j.graphdatabases.dataset_builders; import java.io.File; import org.junit.Test; import org.neo4j.graphdatabases.AccessControlConfig; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.traversals.IndexResources; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.kernel.impl.util.FileUtils; import org.neo4j.neode.Dataset; import org.neo4j.neode.DatasetManager; import org.neo4j.neode.NodeCollection; import org.neo4j.neode.NodeSpecification; import org.neo4j.neode.Range; import org.neo4j.neode.RelationshipSpecification; import org.neo4j.neode.RelationshipUniqueness; import org.neo4j.neode.logging.SysOutLog; import org.neo4j.neode.statistics.AsciiDocFormatter; import org.neo4j.neode.statistics.GraphStatistics; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.TargetNodesStrategy.create; import static org.neo4j.neode.TargetNodesStrategy.getExisting; import static org.neo4j.neode.TargetNodesStrategy.getOrCreate; import static org.neo4j.neode.properties.Property.indexableProperty; import static org.neo4j.neode.properties.Property.property; public class AccessControl { public static final Range GROUPS_PER_ADMIN = minMax( 1, 3 ); public static final Range ALLOWED_COMPANIES_PER_GROUP = minMax( 10, 50 ); public static final Range DENIED_COMPANIES_PER_GROUP = minMax( 2, 10 ); public static final Range EMPLOYEES_PER_COMPANY = minMax( 5, 100 ); public static final Range ACCOUNTS_PER_EMPLOYEE = minMax( 1, 5 ); @Test public void buildAccessControl() throws Exception { File dir = new File( AccessControlConfig.STORE_DIR ); FileUtils.deleteRecursively( dir ); GraphDatabaseService db = new GraphDatabaseFactory() .newEmbeddedDatabaseBuilder( AccessControlConfig.STORE_DIR ) .setConfig( DbUtils.dbConfig() ) .newGraphDatabase(); DatasetManager dsm = new DatasetManager( db, SysOutLog.INSTANCE ); NodeSpecification adminSpec = dsm.nodeSpecification( "Administrator", indexableProperty( db, "Administrator", "name" ) ); NodeSpecification groupSpec = dsm.nodeSpecification( "Group", property( "name" ) ); NodeSpecification companySpec = dsm.nodeSpecification( "Company", indexableProperty( db, "Company", "name" ) ); NodeSpecification customerSpec = dsm.nodeSpecification( "Employee", indexableProperty( db, "Employee", "name", "Resource")); NodeSpecification accountSpec = dsm.nodeSpecification( "Account", indexableProperty( db, "Account", "name", "Resource")); RelationshipSpecification member_of = dsm.relationshipSpecification( "MEMBER_OF" ); RelationshipSpecification allowed_inherit = dsm.relationshipSpecification( "ALLOWED_INHERIT" ); RelationshipSpecification allowed_do_not_inherit = dsm.relationshipSpecification( "ALLOWED_DO_NOT_INHERIT" ); RelationshipSpecification denied = dsm.relationshipSpecification( "DENIED" ); RelationshipSpecification child_of = dsm.relationshipSpecification( "CHILD_OF" ); RelationshipSpecification works_for = dsm.relationshipSpecification( "WORKS_FOR" ); RelationshipSpecification has_account = dsm.relationshipSpecification( "HAS_ACCOUNT" ); Dataset dataset = dsm.newDataset( "Access Control (without using inherit attributes)" ); NodeCollection administrators = adminSpec.create( AccessControlConfig.NUMBER_OF_ADMINS ).update( dataset ); NodeCollection groups = administrators.createRelationshipsTo( getOrCreate( groupSpec, AccessControlConfig.NUMBER_OF_GROUPS ) .numberOfTargetNodes( GROUPS_PER_ADMIN ) .relationship( member_of ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1allowedInherit = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlConfig.NUMBER_OF_COMPANIES, 0.17 ) ) .numberOfTargetNodes( ALLOWED_COMPANIES_PER_GROUP ) .relationship( allowed_inherit ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1allowedDoNotInherit = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlConfig.NUMBER_OF_COMPANIES, 0.08 ) ) .numberOfTargetNodes( ALLOWED_COMPANIES_PER_GROUP ) .relationship( allowed_do_not_inherit ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1denied = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlConfig.NUMBER_OF_COMPANIES, 0.1 ) ) .numberOfTargetNodes( DENIED_COMPANIES_PER_GROUP ) .relationship( denied ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies2allowedInherit = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlConfig.NUMBER_OF_COMPANIES, 0.33 ) ) .numberOfTargetNodes( ALLOWED_COMPANIES_PER_GROUP ) .relationship( allowed_inherit ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies2allowedDoNotInherit = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlConfig.NUMBER_OF_COMPANIES, 0.17 ) ) .numberOfTargetNodes( ALLOWED_COMPANIES_PER_GROUP ) .relationship( allowed_do_not_inherit ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies2denied = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlConfig.NUMBER_OF_COMPANIES, 0.15 ) ) .numberOfTargetNodes( DENIED_COMPANIES_PER_GROUP ) .relationship( denied ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1 = companies1allowedInherit.combine( companies1allowedDoNotInherit ).combine( companies1denied ); NodeCollection companies2 = companies2allowedInherit.combine( companies2allowedDoNotInherit ).combine( companies2denied ); companies2.createRelationshipsTo( getExisting( companies1 ) .numberOfTargetNodes( 1 ) .relationship( child_of ) .exactlyOneRelationship() ) .updateNoReturn( dataset ); NodeCollection companies = companies1.combine( companies2 ); NodeCollection employees = companies.createRelationshipsTo( create( customerSpec ) .numberOfTargetNodes( EMPLOYEES_PER_COMPANY ) .relationship( works_for, Direction.INCOMING ) .exactlyOneRelationship() ) .update( dataset, 1000 ); employees.createRelationshipsTo( create( accountSpec ) .numberOfTargetNodes( ACCOUNTS_PER_EMPLOYEE ) .relationship( has_account, Direction.OUTGOING ) .exactlyOneRelationship() ) .updateNoReturn( dataset ); dataset.end(); new IndexResources( db ).execute(); GraphStatistics.create( db, AccessControlConfig.TITLE ) .describeTo( new AsciiDocFormatter( SysOutLog.INSTANCE ) ); db.shutdown(); } private int percentageOf( int i, double percentage ) { return (int) (i * percentage); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/AccessControlWithRelationshipProperties.java ================================================ package org.neo4j.graphdatabases.dataset_builders; import java.io.File; import java.util.Random; import org.junit.Test; import org.neo4j.graphdatabases.AccessControlWithRelationshipPropertiesConfig; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.traversals.IndexResources; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.kernel.impl.util.FileUtils; import org.neo4j.neode.Dataset; import org.neo4j.neode.DatasetManager; import org.neo4j.neode.NodeCollection; import org.neo4j.neode.NodeSpecification; import org.neo4j.neode.Range; import org.neo4j.neode.RelationshipSpecification; import org.neo4j.neode.RelationshipUniqueness; import org.neo4j.neode.logging.SysOutLog; import org.neo4j.neode.properties.Property; import org.neo4j.neode.statistics.AsciiDocFormatter; import org.neo4j.neode.statistics.GraphStatistics; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.TargetNodesStrategy.create; import static org.neo4j.neode.TargetNodesStrategy.getExisting; import static org.neo4j.neode.TargetNodesStrategy.getOrCreate; import static org.neo4j.neode.properties.Property.indexableProperty; import static org.neo4j.neode.properties.Property.property; public class AccessControlWithRelationshipProperties { public static final Range GROUPS_PER_ADMIN = minMax( 1, 3 ); public static final Range ALLOWED_COMPANIES_PER_GROUP = minMax( 10, 50 ); public static final Range DENIED_COMPANIES_PER_GROUP = minMax( 2, 10 ); public static final Range EMPLOYEES_PER_COMPANY = minMax( 5, 100 ); public static final Range ACCOUNTS_PER_EMPLOYEE = minMax( 1, 5 ); @Test public void buildAccessControl() throws Exception { File dir = new File( AccessControlWithRelationshipPropertiesConfig.STORE_DIR ); FileUtils.deleteRecursively( dir ); GraphDatabaseService db = new GraphDatabaseFactory() .newEmbeddedDatabaseBuilder( AccessControlWithRelationshipPropertiesConfig.STORE_DIR ) .setConfig( DbUtils.dbConfig() ) .newGraphDatabase(); DatasetManager dsm = new DatasetManager( db, SysOutLog.INSTANCE ); NodeSpecification adminSpec = dsm.nodeSpecification( "Administrator", indexableProperty( db, "Administrator", "name" ) ); NodeSpecification groupSpec = dsm.nodeSpecification( "Group", property( "name" ) ); NodeSpecification companySpec = dsm.nodeSpecification( "Company", indexableProperty( db, "Company", "name" ) ); NodeSpecification customerSpec = dsm.nodeSpecification( "Employee", indexableProperty( db, "Employee", "name", "Resource")); NodeSpecification accountSpec = dsm.nodeSpecification( "Account", indexableProperty( db, "Account", "name", "Resource")); Property inheritProperty = new Property() { private final Random random = new Random(); @Override public void setProperty( PropertyContainer propertyContainer, GraphDatabaseService graphDatabaseService, String label, int iteration ) { int i = random.nextInt( 3 ); boolean value = i < 2; propertyContainer.setProperty( "inherit", value ); } }; RelationshipSpecification member_of = dsm.relationshipSpecification( "MEMBER_OF" ); RelationshipSpecification allowed = dsm.relationshipSpecification( "ALLOWED", inheritProperty ); RelationshipSpecification denied = dsm.relationshipSpecification( "DENIED" ); RelationshipSpecification child_of = dsm.relationshipSpecification( "CHILD_OF" ); RelationshipSpecification works_for = dsm.relationshipSpecification( "WORKS_FOR" ); RelationshipSpecification has_account = dsm.relationshipSpecification( "HAS_ACCOUNT" ); Dataset dataset = dsm.newDataset( AccessControlWithRelationshipPropertiesConfig.TITLE ); NodeCollection administrators = adminSpec.create( AccessControlWithRelationshipPropertiesConfig .NUMBER_OF_ADMINS ).update( dataset ); NodeCollection groups = administrators.createRelationshipsTo( getOrCreate( groupSpec, AccessControlWithRelationshipPropertiesConfig.NUMBER_OF_GROUPS ) .numberOfTargetNodes( GROUPS_PER_ADMIN ) .relationship( member_of ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1allowed = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlWithRelationshipPropertiesConfig .NUMBER_OF_COMPANIES, 0.25 ) ) .numberOfTargetNodes( ALLOWED_COMPANIES_PER_GROUP ) .relationship( allowed ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1denied = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlWithRelationshipPropertiesConfig .NUMBER_OF_COMPANIES, 0.1 ) ) .numberOfTargetNodes( DENIED_COMPANIES_PER_GROUP ) .relationship( denied ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies2allowed = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlWithRelationshipPropertiesConfig .NUMBER_OF_COMPANIES, 0.50 ) ) .numberOfTargetNodes( ALLOWED_COMPANIES_PER_GROUP ) .relationship( allowed ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies2denied = groups.createRelationshipsTo( getOrCreate( companySpec, percentageOf( AccessControlWithRelationshipPropertiesConfig .NUMBER_OF_COMPANIES, 0.15 ) ) .numberOfTargetNodes( DENIED_COMPANIES_PER_GROUP ) .relationship( denied ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); NodeCollection companies1 = companies1allowed.combine( companies1denied ); NodeCollection companies2 = companies2allowed.combine( companies2denied ); companies2.createRelationshipsTo( getExisting( companies1 ) .numberOfTargetNodes( 1 ) .relationship( child_of ) .exactlyOneRelationship() ) .updateNoReturn( dataset ); NodeCollection companies = companies1.combine( companies2 ); NodeCollection employees = companies.createRelationshipsTo( create( customerSpec ) .numberOfTargetNodes( EMPLOYEES_PER_COMPANY ) .relationship( works_for, Direction.INCOMING ) .exactlyOneRelationship() ) .update( dataset, 1000 ); employees.createRelationshipsTo( create( accountSpec ) .numberOfTargetNodes( ACCOUNTS_PER_EMPLOYEE ) .relationship( has_account, Direction.OUTGOING ) .exactlyOneRelationship() ) .updateNoReturn( dataset ); dataset.end(); new IndexResources( db ).execute(); GraphStatistics.create( db, AccessControlWithRelationshipPropertiesConfig.TITLE ) .describeTo( new AsciiDocFormatter( SysOutLog.INSTANCE ) ); db.shutdown(); } private int percentageOf( int i, double percentage ) { return (int) (i * percentage); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/Logistics.java ================================================ package org.neo4j.graphdatabases.dataset_builders; import java.io.File; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Random; import org.joda.time.Interval; import org.junit.Test; import org.neo4j.graphdatabases.LogisticsConfig; import org.neo4j.graphdatabases.dataset_builders.helpers.SevenDays; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.kernel.impl.util.FileUtils; import org.neo4j.neode.Dataset; import org.neo4j.neode.DatasetManager; import org.neo4j.neode.NodeCollection; import org.neo4j.neode.NodeSpecification; import org.neo4j.neode.logging.SysOutLog; import org.neo4j.neode.properties.Property; import org.neo4j.neode.statistics.AsciiDocFormatter; import org.neo4j.neode.statistics.GraphStatistics; import static org.neo4j.neode.Range.exactly; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.TargetNodesStrategy.create; import static org.neo4j.neode.TargetNodesStrategy.getOrCreate; import static org.neo4j.neode.properties.Property.indexableProperty; public class Logistics { @Test public void buildLogistics() throws Exception { File dir = new File( LogisticsConfig.STORE_DIR ); FileUtils.deleteRecursively( dir ); GraphDatabaseService db = new GraphDatabaseFactory() .newEmbeddedDatabaseBuilder( LogisticsConfig.STORE_DIR ) .setConfig( DbUtils.dbConfig() ) .newGraphDatabase(); DatasetManager dsm = new DatasetManager( db, SysOutLog.INSTANCE ); NodeSpecification parcelCentreSpec = dsm.nodeSpecification( "ParcelCentre", indexableProperty( db, "ParcelCentre", "name", "Location" ) ); NodeSpecification deliveryBaseSpec = dsm.nodeSpecification( "DeliveryBase", indexableProperty( db, "DeliveryBase","name", "Location" ) ); NodeSpecification deliveryAreaSpec = dsm.nodeSpecification( "DeliveryArea", indexableProperty( db, "DeliveryArea", "name", "Location" ) ); NodeSpecification deliverySegmentSpec = dsm.nodeSpecification( "DeliverySegment", indexableProperty( db, "DeliverySegment", "name", "Location" ) ); Property costProperty = new CostProperty(); Dataset dataset = dsm.newDataset( LogisticsConfig.TITLE ); NodeCollection parcelCentres = parcelCentreSpec.create( LogisticsConfig.NUMBER_OF_PARCEL_CENTRES ).update( dataset ); NodeCollection deliveryBases = parcelCentres.createRelationshipsTo( getOrCreate( deliveryBaseSpec, 400 ) .numberOfTargetNodes( minMax( LogisticsConfig.MIN_NUMBER_OF_DELIVERY_BASES_PER_PARCEL_CENTRE, LogisticsConfig.MAX_NUMBER_OF_DELIVERY_BASES_PER_PARCEL_CENTRE ) ) .relationship( dsm.relationshipSpecification( "CONNECTED_TO", new IntervalProperty( 2 ), costProperty ) ) .relationshipConstraints( exactly( 2 ) ) ) .update( dataset ); NodeCollection deliveryAreas = deliveryBases.createRelationshipsTo( create( deliveryAreaSpec ) .numberOfTargetNodes( minMax( LogisticsConfig.MIN_NUMBER_OF_DELIVERY_AREAS_PER_DELIVERY_BASE, LogisticsConfig.MAX_NUMBER_OF_DELIVERY_AREAS_PER_DELIVERY_BASE ) ) .relationship( dsm.relationshipSpecification( "DELIVERY_ROUTE", new IntervalProperty( 3 ), costProperty ) ) .relationshipConstraints( exactly( 3 ) ) ) .update( dataset ); deliveryAreas.createRelationshipsTo( create( deliverySegmentSpec ) .numberOfTargetNodes( minMax( LogisticsConfig.MIN_NUMBER_OF_DELIVERY_SEGMENTS_PER_DELIVERY_AREA, LogisticsConfig.MAX_NUMBER_OF_DELIVERY_SEGMENTS_PER_DELIVERY_AREA ) ) .relationship( dsm.relationshipSpecification( "DELIVERY_ROUTE", new IntervalProperty( 3 ), costProperty ) ) .relationshipConstraints( exactly( 3 ) ) ) .updateNoReturn( dataset, 1000 ); dataset.end(); GraphStatistics.create( db, LogisticsConfig.TITLE ) .describeTo( new AsciiDocFormatter( SysOutLog.INSTANCE ) ); db.shutdown(); } private static class IntervalProperty extends Property { private final int numberOfIntervals; private final SevenDays sevenDays = new SevenDays( LogisticsConfig.START_DATE ); private Iterator intervals; private int counter; private IntervalProperty( int numberOfIntervals ) { this.numberOfIntervals = numberOfIntervals; this.counter = numberOfIntervals; } @Override public void setProperty( PropertyContainer propertyContainer, GraphDatabaseService graphDatabaseService, String label, int iteration ) { if ( counter++ >= numberOfIntervals ) { intervals = sevenDays.calculateIntervals( numberOfIntervals ).iterator(); counter = 1; } try { Interval nextInterval = intervals.next(); propertyContainer.setProperty( "start_date", nextInterval.getStartMillis() ); propertyContainer.setProperty( "end_date", nextInterval.getEndMillis() ); } catch ( NoSuchElementException e ) { throw new IllegalStateException( String.format( "counter: %s, numberOfIntervals: %s, iteration: %s", counter, numberOfIntervals, iteration ) ); } } } private static class CostProperty extends Property { private final Random random = new Random(); @Override public void setProperty( PropertyContainer propertyContainer, GraphDatabaseService graphDatabaseService, String s, int i ) { propertyContainer.setProperty( "cost", random.nextInt( 10 ) + 1 ); } } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/SimpleSocialNetwork.java ================================================ package org.neo4j.graphdatabases.dataset_builders; import java.io.File; import org.junit.Test; import org.neo4j.graphdatabases.SimpleSocialNetworkConfig; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.kernel.impl.util.FileUtils; import org.neo4j.neode.Dataset; import org.neo4j.neode.DatasetManager; import org.neo4j.neode.NodeCollection; import org.neo4j.neode.NodeSpecification; import org.neo4j.neode.RelationshipSpecification; import org.neo4j.neode.logging.Log; import org.neo4j.neode.logging.SysOutLog; import org.neo4j.neode.statistics.AsciiDocFormatter; import org.neo4j.neode.statistics.GraphStatistics; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.RelationshipUniqueness.BOTH_DIRECTIONS; import static org.neo4j.neode.TargetNodesStrategy.getExisting; import static org.neo4j.neode.properties.Property.indexableProperty; public class SimpleSocialNetwork { @Test public void buildSocialNetwork() throws Exception { File dir = new File( SimpleSocialNetworkConfig.STORE_DIR ); FileUtils.deleteRecursively( dir ); GraphDatabaseService db = new GraphDatabaseFactory() .newEmbeddedDatabaseBuilder( SimpleSocialNetworkConfig.STORE_DIR ) .setConfig( DbUtils.dbConfig() ) .newGraphDatabase(); createSampleDataset( db ); GraphStatistics.create( db, SimpleSocialNetworkConfig.TITLE ) .describeTo( new AsciiDocFormatter( SysOutLog.INSTANCE ) ); db.shutdown(); } private void createSampleDataset( GraphDatabaseService db ) { DatasetManager dsm = new DatasetManager( db, new Log() { @Override public void write( String value ) { System.out.println( value ); } } ); NodeSpecification userSpec = dsm.nodeSpecification( "User", indexableProperty( db, "User", "name" ) ); RelationshipSpecification friend = dsm.relationshipSpecification( "FRIEND" ); Dataset dataset = dsm.newDataset( "Simple social network example" ); NodeCollection users = userSpec.create( SimpleSocialNetworkConfig.NUMBER_USERS ) .update( dataset ); users.createRelationshipsTo( getExisting( users ) .numberOfTargetNodes( minMax( SimpleSocialNetworkConfig.MIN_NUMBER_OF_FRIENDS, SimpleSocialNetworkConfig.MAX_NUMBER_OF_FRIENDS ) ) .relationship( friend ) .relationshipConstraints( BOTH_DIRECTIONS ) ) .updateNoReturn( dataset, 20000 ); dataset.end(); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/SocialNetwork.java ================================================ package org.neo4j.graphdatabases.dataset_builders; import java.io.File; import org.junit.Test; import org.neo4j.graphdatabases.SocialNetworkConfig; import org.neo4j.graphdatabases.dataset_builders.properties.DurationOnProjectProperty; import org.neo4j.graphdatabases.dataset_builders.properties.ProjectDurationProperty; import org.neo4j.graphdatabases.dataset_builders.traversers.FindAllColleagues; import org.neo4j.graphdatabases.dataset_builders.traversers.IsCompanyProject; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.kernel.Traversal; import org.neo4j.kernel.Uniqueness; import org.neo4j.kernel.impl.util.FileUtils; import org.neo4j.neode.Dataset; import org.neo4j.neode.DatasetManager; import org.neo4j.neode.NodeCollection; import org.neo4j.neode.NodeSpecification; import org.neo4j.neode.RelationshipSpecification; import org.neo4j.neode.RelationshipUniqueness; import org.neo4j.neode.logging.SysOutLog; import org.neo4j.neode.properties.Property; import org.neo4j.neode.statistics.AsciiDocFormatter; import org.neo4j.neode.statistics.GraphStatistics; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import static org.neo4j.neode.GraphQuery.traversal; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.TargetNodesStrategy.getExisting; import static org.neo4j.neode.TargetNodesStrategy.getOrCreate; import static org.neo4j.neode.TargetNodesStrategy.queryBasedGetOrCreate; import static org.neo4j.neode.probabilities.ProbabilityDistribution.flatDistribution; import static org.neo4j.neode.probabilities.ProbabilityDistribution.normalDistribution; import static org.neo4j.neode.properties.Property.indexableProperty; import static org.neo4j.neode.properties.Property.property; public class SocialNetwork { @Test public void buildSocialNetwork() throws Exception { File dir = new File( SocialNetworkConfig.STORE_DIR ); FileUtils.deleteRecursively( dir ); GraphDatabaseService db = new GraphDatabaseFactory() .newEmbeddedDatabaseBuilder( SocialNetworkConfig.STORE_DIR ) .setConfig( DbUtils.dbConfig() ) .newGraphDatabase(); DatasetManager dsm = new DatasetManager( db, SysOutLog.INSTANCE ); TraversalDescription findCompanyProjects = createFindCompanyProjectsTraversalDescription(); Property projectDuration = new ProjectDurationProperty(); Property durationOnProject = new DurationOnProjectProperty(); NodeSpecification userSpec = dsm.nodeSpecification( "User", indexableProperty(db, "User", "name" ) ); NodeSpecification topicSpec = dsm.nodeSpecification( "Topic", indexableProperty(db, "Topic", "name" ) ); NodeSpecification companySpec = dsm.nodeSpecification( "Company", indexableProperty(db, "company", "name" ) ); NodeSpecification projectSpec = dsm.nodeSpecification( "Project", property( "name" ), projectDuration ); RelationshipSpecification interested_in = dsm.relationshipSpecification( "INTERESTED_IN" ); RelationshipSpecification works_for = dsm.relationshipSpecification( "WORKS_FOR" ); RelationshipSpecification worked_on = dsm.relationshipSpecification( "WORKED_ON", durationOnProject ); RelationshipSpecification worked_with = dsm.relationshipSpecification( "WORKED_WITH" ); Dataset dataset = dsm.newDataset( "Social network example" ); NodeCollection users = userSpec.create( SocialNetworkConfig.NUMBER_USERS ).update( dataset ); users.createRelationshipsTo( getOrCreate( topicSpec, SocialNetworkConfig.NUMBER_TOPICS, normalDistribution() ) .numberOfTargetNodes( minMax( 1, 3 ) ) .relationship( interested_in ) .exactlyOneRelationship() ) .updateNoReturn( dataset ); users.createRelationshipsTo( getOrCreate( companySpec, SocialNetworkConfig.NUMBER_COMPANIES, flatDistribution() ) .numberOfTargetNodes( 1 ) .relationship( works_for ) .exactlyOneRelationship() ) .updateNoReturn( dataset ); NodeCollection allProjects = users.createRelationshipsTo( queryBasedGetOrCreate( projectSpec, traversal( findCompanyProjects ), 5.0 ) .numberOfTargetNodes( minMax( 1, 5 ), normalDistribution() ) .relationship( worked_on ) .exactlyOneRelationship() ) .update( dataset ); users.approxPercentage( 30 ).createRelationshipsTo( getExisting( allProjects ) .numberOfTargetNodes( minMax( 1, 2 ), normalDistribution() ) .relationship( worked_on ) .relationshipConstraints( RelationshipUniqueness.BOTH_DIRECTIONS ) ) .update( dataset ); users.createRelationshipsTo( getExisting( new FindAllColleagues() ) .numberOfTargetNodes( 1 ) .relationship( worked_with ) .exactlyOneRelationship() ) .updateNoReturn( dataset, 5000 ); dataset.end(); GraphStatistics.create( db, SocialNetworkConfig.TITLE ).describeTo( new AsciiDocFormatter( SysOutLog.INSTANCE ) ); db.shutdown(); } private TraversalDescription createFindCompanyProjectsTraversalDescription() { return Traversal.description() .depthFirst() .uniqueness( Uniqueness.NODE_GLOBAL ) .relationships( withName( "WORKS_FOR" ), Direction.BOTH ) .relationships( withName( "WORKED_ON" ), Direction.OUTGOING ) .evaluator( new IsCompanyProject() ); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/helpers/SevenDays.java ================================================ package org.neo4j.graphdatabases.dataset_builders.helpers; import static java.util.Arrays.asList; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import org.joda.time.DateTime; import org.joda.time.Interval; public class SevenDays { private final DateTime start; private final Random random; @SuppressWarnings( "unchecked" ) private static final List> days = asList( asList( 1, 1, 5 ), asList( 1, 2, 4 ), asList( 1, 3, 3 ), asList( 2, 2, 3 ) ); public SevenDays( DateTime start ) { this.start = start; this.random = new Random(); } public Iterable calculateIntervals( int numberOfIntervals ) { if ( numberOfIntervals < 1 || numberOfIntervals > 3 ) { throw new IllegalArgumentException( "numberOfIntervals must be 1 or 3" ); } List intervals = new ArrayList(); if ( numberOfIntervals == 1 ) { intervals.add( new Interval( start, start.plusDays( 7 ) ) ); } else if ( numberOfIntervals == 2 ) { int numberOfDays = random.nextInt( 6 ) + 1; DateTime mid = start.plusDays( numberOfDays ); intervals.add( new Interval( start, mid ) ); intervals.add( new Interval( mid, start.plusDays( 7 ) ) ); } else { int i = random.nextInt( days.size() ); List plusDays = days.get( i ); Collections.shuffle( plusDays, random ); DateTime mid1 = start.plusDays( plusDays.get( 0 ) ); DateTime mid2 = mid1.plusDays( plusDays.get( 1 ) ); intervals.add( new Interval( start, mid1 ) ); intervals.add( new Interval( mid1, mid2 ) ); intervals.add( new Interval( mid2, start.plusDays( 7 ) ) ); } return intervals; } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/properties/DurationOnProjectProperty.java ================================================ package org.neo4j.graphdatabases.dataset_builders.properties; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import org.neo4j.neode.properties.Property; public class DurationOnProjectProperty extends Property { @Override public void setProperty( PropertyContainer propertyContainer, GraphDatabaseService graphDatabaseService, String s, int i ) { Node endNode = ((Relationship) propertyContainer).getEndNode(); Long startDateTime = (Long) endNode.getProperty( "start_date" ); Long endDateTime = (Long) endNode.getProperty( "end_date" ); ProjectDuration projectDuration = new ProjectDuration( startDateTime, endDateTime ); ProjectDuration durationOnProject = projectDuration.getSubDuration(); propertyContainer.setProperty( "duration", durationOnProject.toString() ); propertyContainer.setProperty( "start_date", durationOnProject.getStartDateMs() ); propertyContainer.setProperty( "end_date", durationOnProject.getEndDateMs() ); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/properties/ProjectDuration.java ================================================ package org.neo4j.graphdatabases.dataset_builders.properties; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.probabilities.ProbabilityDistribution.normalDistribution; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.Duration; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.neo4j.neode.probabilities.ProbabilityDistribution; public class ProjectDuration { private static final ProbabilityDistribution normalDistribution = normalDistribution(); private static final DateTimeFormatter fmt = DateTimeFormat.forPattern( "dd-MM-yyyy" ); private final Long startMs; private final Long endMs; public ProjectDuration( Long startMs, Long endMs ) { this.endMs = endMs; this.startMs = startMs; } public Long getStartDateMs() { return startMs; } public Long getEndDateMs() { return endMs; } public ProjectDuration getSubDuration() { DateTime startDateTime = new DateTime( startMs, DateTimeZone.UTC ); DateTime endDateTime = new DateTime( endMs, DateTimeZone.UTC ); int durationInDays = (int) new Duration( startDateTime, endDateTime ).getStandardDays(); int offsetDaysFromStart = normalDistribution.generateSingle( minMax( 0, (int) (durationInDays * 0.75) ) ); int remainingDays = durationInDays - offsetDaysFromStart; int subDurationInDays = (int) ((remainingDays * 0.75) + (normalDistribution.generateSingle( minMax( 0, (int) (remainingDays * (0.25)) ) ))); DateTime subDurationStartDateTime = startDateTime.plusDays( offsetDaysFromStart ); DateTime subDurationEndDateTime = subDurationStartDateTime.plusDays( subDurationInDays ); return new ProjectDuration( subDurationStartDateTime.getMillis(), subDurationEndDateTime.getMillis() ); } public String toString() { return "Start: " + new DateTime( startMs, DateTimeZone.UTC ).toString( fmt ) + ", End : " + new DateTime( endMs, DateTimeZone.UTC ).toString( fmt ); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/properties/ProjectDurationGenerator.java ================================================ package org.neo4j.graphdatabases.dataset_builders.properties; import java.util.Random; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; public class ProjectDurationGenerator { private final static DateTime durationLowerLimit = new DateTime(2000, 1, 1, 0, 0, DateTimeZone.UTC); private final Random rand = new Random(); public ProjectDuration getNextProjectDuration() { DateTime startDateTime = durationLowerLimit.plusMonths( rand.nextInt( 9 *12 ) ); DateTime endDateTime = startDateTime.plusMonths( 3 + (rand.nextInt(33)) ) ; return new ProjectDuration( startDateTime.getMillis(), endDateTime.getMillis() ); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/properties/ProjectDurationProperty.java ================================================ package org.neo4j.graphdatabases.dataset_builders.properties; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.neode.properties.Property; public class ProjectDurationProperty extends Property { private final ProjectDurationGenerator generator = new ProjectDurationGenerator(); @Override public void setProperty( PropertyContainer propertyContainer, GraphDatabaseService graphDatabaseService, String s, int i ) { ProjectDuration projectDuration = generator.getNextProjectDuration(); propertyContainer.setProperty( "duration", projectDuration.toString() ); propertyContainer.setProperty( "start_date", projectDuration.getStartDateMs() ); propertyContainer.setProperty( "end_date", projectDuration.getEndDateMs() ); } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/traversers/FindAllColleagues.java ================================================ package org.neo4j.graphdatabases.dataset_builders.traversers; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import org.neo4j.graphalgo.GraphAlgoFactory; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.PathExpander; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipExpander; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.traversal.BranchState; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.kernel.Traversal; import org.neo4j.kernel.Uniqueness; import org.neo4j.neode.GraphQuery; public class FindAllColleagues extends GraphQuery { private static final RelationshipType WORKED_ON = withName( "WORKED_ON" ); private static PathFinder workedWithPathFinder = GraphAlgoFactory.shortestPath( Traversal.expanderForTypes( withName( "WORKED_WITH" )), 1 ); private static final TraversalDescription traversal = Traversal.description() .depthFirst() .uniqueness( Uniqueness.NODE_GLOBAL ) .expand( new WorkOnPathExpander() ) .evaluator( new Evaluator() { @Override public Evaluation evaluate( Path path ) { if ( path.length() == 2 ) { if ( workedWithPathFinder.findSinglePath( path.startNode(), path.endNode() ) == null) { return Evaluation.INCLUDE_AND_PRUNE; } else { return Evaluation.EXCLUDE_AND_PRUNE; } } return Evaluation.EXCLUDE_AND_CONTINUE; } } ); @Override public Iterable execute( Node node ) { return traversal.traverse( node ).nodes(); } private static class WorkOnPathExpander implements PathExpander { @Override public Iterable expand( Path path, BranchState branchState ) { if (path.length() == 0) { return path.endNode().getRelationships( WORKED_ON, Direction.OUTGOING ); } else { return path.endNode().getRelationships( WORKED_ON, Direction.INCOMING ); } } @Override public PathExpander reverse() { return null; } } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/traversers/FindColleagues.java ================================================ package org.neo4j.graphdatabases.dataset_builders.traversers; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import java.util.ArrayList; import java.util.List; import org.joda.time.Duration; import org.joda.time.Interval; import org.joda.time.Period; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.DynamicRelationshipType; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.PathExpander; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.traversal.BranchState; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.kernel.Traversal; import org.neo4j.kernel.Uniqueness; import org.neo4j.neode.GraphQuery; public class FindColleagues extends GraphQuery { private static final TraversalDescription traversal = Traversal.description() .depthFirst() .uniqueness( Uniqueness.NODE_GLOBAL ) .relationships( withName( "WORKED_ON" ), Direction.BOTH ) .expand( new OverlappingWorkedOnRels() ) .evaluator( new IsColleague() ); @Override public Iterable execute( Node node ) { return traversal.traverse( node ).nodes(); } private static class IsColleague implements Evaluator { @Override public Evaluation evaluate( Path path ) { if ( path.length() == 2 ) { return Evaluation.INCLUDE_AND_PRUNE; } return Evaluation.EXCLUDE_AND_CONTINUE; } } private static class OverlappingWorkedOnRels implements PathExpander { private static final DynamicRelationshipType WORKED_ON = withName( "WORKED_ON" ); private static final Duration ONE_YEAR = new Period( 0, 0, 0, 365, 0, 0, 0, 0 ).toStandardDuration(); @Override public Iterable expand( Path path, BranchState branchState ) { if ( path.length() == 1 ) { List rels = new ArrayList(); Long start_date = (Long) path.lastRelationship().getProperty( "start_date" ); Long end_date = (Long) path.lastRelationship().getProperty( "end_date" ); Interval interval = new Interval( start_date, end_date ); for ( Relationship rel : path.endNode().getRelationships( WORKED_ON, Direction.INCOMING ) ) { if ( !rel.equals( path.lastRelationship() ) ) { Long rel_start_date = (Long) rel.getProperty( "start_date" ); Long rel_end_date = (Long) rel.getProperty( "end_date" ); Interval rel_interval = new Interval( rel_start_date, rel_end_date ); if ( intervalsOverlapByDuration( interval, rel_interval ) ) { rels.add( rel ); } } } return rels; } return path.endNode().getRelationships( WORKED_ON, Direction.OUTGOING ); } @Override public PathExpander reverse() { return null; } private boolean intervalsOverlapByDuration( Interval myInterval, Interval otherInterval ) { return otherInterval.overlaps( myInterval ) && otherInterval.overlap( myInterval ).toDuration().compareTo( ONE_YEAR ) > 0; } } } ================================================ FILE: data-generation/src/test/java/org/neo4j/graphdatabases/dataset_builders/traversers/IsCompanyProject.java ================================================ package org.neo4j.graphdatabases.dataset_builders.traversers; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; public class IsCompanyProject implements Evaluator { private static final RelationshipType WORKED_ON = withName( "WORKED_ON" ); @Override public Evaluation evaluate( Path path ) { if ( path.length() == 0 ) { return Evaluation.EXCLUDE_AND_CONTINUE; } if ( path.lastRelationship().isType( WORKED_ON ) ) { return Evaluation.INCLUDE_AND_PRUNE; } return Evaluation.EXCLUDE_AND_CONTINUE; } } ================================================ FILE: performance-testing/pom.xml ================================================ 4.0.0 org.neo4j.graphdatabases performance-testing 2.0-SNAPSHOT jar 1.7 1.7 UTF-8 2.0.1 org.neo4j neo4j-enterprise ${neo4j.version} org.neo4j.graphdatabases queries 2.0-SNAPSHOT org.neo4j.graphdatabases configuration 2.0-SNAPSHOT neode neode 2.0 junit junit 4.11 test ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/DefaultExecutionEngineWrapper.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; import org.neo4j.graphdb.GraphDatabaseService; public class DefaultExecutionEngineWrapper implements ExecutionEngineWrapper { private final ExecutionEngine executionEngine; public DefaultExecutionEngineWrapper( GraphDatabaseService db ) { this.executionEngine = new ExecutionEngine( db ); } @Override public ExecutionResult execute( String query, Map params ) { return executionEngine.execute( query, params ); } @Override public ExecutionResult execute( String query, Map params, int index ) { return execute( query, params ); } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/DoNothingWithTestResults.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class DoNothingWithTestResults implements TestResultsHandler { public static TestResultsHandler doNothing() { return new DoNothingWithTestResults(); } @Override public void handle( String queryType, Object results, SingleTestRunResultHandler singleTestRunResultHandler ) { //Do nothing } @Override public void writeTo( TestOutputWriter writer ) { // Do nothing } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/MultipleTestRuns.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.probabilities.ProbabilityDistribution.flatDistribution; import java.util.HashMap; import java.util.List; import java.util.Map; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class MultipleTestRuns { private final int numberOfRuns; private final TestOutputWriter writer; public MultipleTestRuns( int numberOfRuns, TestOutputWriter writer ) { this.numberOfRuns = numberOfRuns; this.writer = writer; } public void execute( String title, ParamsGenerator paramsGenerator, SingleTestRunResultHandlerFactory singleTestRunResultHandlerFactory, TestResultsHandler testResultsHandler, SingleTest... tests ) { writer.writeln( title ); testResultsHandler.writeTo( writer ); Map totalTimes = new HashMap(); for ( SingleTest singleTest : tests ) { totalTimes.put( singleTest.queryType(), 0L ); } for ( long i = 0; i < numberOfRuns; i++ ) { SingleTestRunResultHandler singleTestRunResultHandler = singleTestRunResultHandlerFactory .createSingleTestRunResultHandler(); writer.writeln( String.format( "\nTest run %s of %s", i + 1, numberOfRuns ) ); Map params = paramsGenerator.generateParams(); if ( !params.isEmpty() ) { writer.write( "Params: " ); } for ( String key : params.keySet() ) { writer.write( String.format( "[%s: %s] ", key, params.get( key ) ) ); } writer.writeln( "" ); //Randomize the order in which tests are executed each run List testIndexes = flatDistribution() .generateList( tests.length, minMax( 0, tests.length - 1 ) ); for ( Integer testIndex : testIndexes ) { SingleTest singleTest = tests[testIndex]; writer.writeln( String.format( "\n %s", singleTest.queryType() ) ); long startTime = System.nanoTime(); Object lastResult = singleTest.execute( params ); testResultsHandler.handle( singleTest.queryType(), lastResult, singleTestRunResultHandler ); long endTime = System.nanoTime(); long duration = endTime - startTime; writer.writeln( String.format( " Duration (ms): %s", duration / 1000000 ) ); Long currentTotalTime = totalTimes.get( singleTest.queryType() ); totalTimes.put( singleTest.queryType(), currentTotalTime + duration ); } singleTestRunResultHandler.summarize( writer ); } writer.writeln( "\n======================================" ); writer.writeln( title ); writer.writeln( "Average times (ms)" ); for ( SingleTest singleTest : tests ) { long avgTime = totalTimes.get( singleTest.queryType() ) / numberOfRuns / 1000000; writer.writeln( String.format( " %s: %s", singleTest.queryType(), avgTime ) ); } writer.writeln( "======================================" ); } public void execute( String title, ParamsGenerator paramsGenerator, TestResultsHandler testResultsHandler, SingleTest... tests ) { execute( title, paramsGenerator, new NullSingleTestRunResultHandlerFactory(), testResultsHandler, tests ); } private static class NullSingleTestRunResultHandler implements SingleTestRunResultHandler { @Override public void handle( String queryType, String formattedResult ) { // Do nothing } @Override public void summarize( TestOutputWriter writer ) { // Do nothing } } private static class NullSingleTestRunResultHandlerFactory implements SingleTestRunResultHandlerFactory { private final SingleTestRunResultHandler instance = new NullSingleTestRunResultHandler(); @Override public SingleTestRunResultHandler createSingleTestRunResultHandler() { return instance; } } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/ParamsGenerator.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; import java.util.Map; public interface ParamsGenerator { Map generateParams(); } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/PrintTestResults.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import java.util.Iterator; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class PrintTestResults implements TestResultsHandler { private static ResultFormatter DEFAULT_FORMATTER = new ResultFormatter() { @Override public String format( Object result ) { return result.toString(); } }; public static TestResultsHandler printResults( int numberOfResults, ResultFormatter resultFormatter, TestOutputWriter writer ) { return new PrintTestResults( numberOfResults, resultFormatter, writer ); } public static TestResultsHandler printResults( int numberOfResults, TestOutputWriter writer ) { return new PrintTestResults( numberOfResults, DEFAULT_FORMATTER, writer ); } private final int numberOfResults; private final ResultFormatter resultFormatter; private final TestOutputWriter writer; private PrintTestResults( int numberOfResults, ResultFormatter resultFormatter, TestOutputWriter writer ) { this.numberOfResults = numberOfResults; this.resultFormatter = resultFormatter; this.writer = writer; } @Override public void handle( String queryType, Object results, SingleTestRunResultHandler singleTestRunResultHandler ) { if ( Iterable.class.isAssignableFrom( results.getClass() ) ) { Iterator iterator = ((Iterable) results).iterator(); int count = 0; if ( !iterator.hasNext() ) { writer.write( " {EMPTY}" ); } else { while ( iterator.hasNext() && count < numberOfResults ) { String formattedResult = resultFormatter.format( iterator.next() ); singleTestRunResultHandler.handle( queryType, formattedResult ); writer.writeln( String.format( " [%s]", formattedResult ) ); count++; } writer.writeln(""); writer.writeln(String.format( " Total: %s", count ) ); writer.writeln(""); } } else { String formattedResult = resultFormatter.format( results ); singleTestRunResultHandler.handle( queryType, formattedResult ); writer.writeln( formattedResult ); } } @Override public void writeTo( TestOutputWriter writer ) { writer.writeln( String.format( "NUMBER_OF_RESULTS: %s", numberOfResults ) ); } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/QueryType.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; public enum QueryType { CoreAPI, Cypher, AltCypher, Gremlin, Traversal, GraphAlgo } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/ResultFormatter.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; public interface ResultFormatter { String format(Object result); } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/ResultsContainSameElementsUnordered.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import static java.util.Arrays.asList; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class ResultsContainSameElementsUnordered implements SingleTestRunResultHandler { public static SingleTestRunResultHandlerFactory newFactory(final String... cleanResultRegExes) { return new SingleTestRunResultHandlerFactory() { @Override public SingleTestRunResultHandler createSingleTestRunResultHandler() { return new ResultsContainSameElementsUnordered(cleanResultRegExes); } }; } private final List cleanResultRegExes; private final Map> results; private boolean newlineWritten = false; public ResultsContainSameElementsUnordered( String... cleanResultRegExes ) { this.cleanResultRegExes = asList( cleanResultRegExes ); results = new TreeMap>(); } @Override public void handle( String queryType, String formattedResult ) { if ( !results.containsKey( queryType ) ) { results.put( queryType, new ArrayList() ); } for ( String cleanResultRegEx : cleanResultRegExes ) { formattedResult = formattedResult.replaceAll( cleanResultRegEx, "" ); } results.get( queryType ).add( formattedResult ); } @Override public void summarize( TestOutputWriter writer ) { List queryTypes = new ArrayList( results.keySet() ); for ( int i = 0; i < queryTypes.size() - 1; i++ ) { String queryType1 = queryTypes.get( i ); for ( int j = i + 1; j < queryTypes.size(); j++ ) { String queryType2 = queryTypes.get( j ); compareCollections( queryType1, results.get( queryType1 ), queryType2, results.get( queryType2 ), writer ); } } } private void compareCollections( String queryType1, Collection results1, String queryType2, Collection results2, TestOutputWriter writer ) { compare( queryType1, results1, queryType2, results2, writer ); compare( queryType2, results2, queryType1, results1, writer ); } private void compare( String queryType1, Collection results1, String queryType2, Collection results2, TestOutputWriter writer ) { Collection copyOfResults1 = new HashSet( results1 ); copyOfResults1.removeAll( results2 ); if ( !copyOfResults1.isEmpty() ) { if (!newlineWritten) { writer.writeln( "" ); newlineWritten = true; } writer.writeln( String.format( " %s v. %s: %s contains additional elements: %s", queryType1, queryType2, queryType1, copyOfResults1 ) ); } } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/SingleTest.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; import java.util.Map; public interface SingleTest { String queryType(); Object execute( Map params ); } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/SingleTestRunResultHandler.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public interface SingleTestRunResultHandler { void handle( String queryType, String formattedResult ); void summarize( TestOutputWriter writer ); } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/SingleTestRunResultHandlerFactory.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; public interface SingleTestRunResultHandlerFactory { SingleTestRunResultHandler createSingleTestRunResultHandler(); } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/SysOutWriter.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class SysOutWriter implements TestOutputWriter { public static final TestOutputWriter INSTANCE = new SysOutWriter(); private SysOutWriter(){} @Override public void begin() { } @Override public void write( String value ) { System.out.print(value); } @Override public void writeln( String value ) { System.out.println( value ); } @Override public void flush() { } @Override public void end() { } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/TakeXTestResults.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import java.util.Iterator; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class TakeXTestResults implements TestResultsHandler { public static TestResultsHandler take( int quantity ) { return new TakeXTestResults( quantity ); } private final int quantity; public TakeXTestResults( int quantity ) { this.quantity = quantity; } @Override public void handle( String queryType, Object results, SingleTestRunResultHandler singleTestRunResultHandler ) { int i = 0; if ( Iterable.class.isAssignableFrom( results.getClass() ) ) { Iterator iterator = ((Iterable) results).iterator(); while ( iterator.hasNext() && ++i < quantity ) { iterator.next(); } } } @Override public void writeTo( TestOutputWriter writer ) { writer.writeln( String.format( "NUMBER_OF_RESULTS: %s", quantity ) ); } } ================================================ FILE: performance-testing/src/main/java/org/neo4j/graphdatabases/performance_tests/testing/TestResultsHandler.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.performance_tests.testing; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public interface TestResultsHandler { void handle( String queryType, Object results, SingleTestRunResultHandler singleTestRunResultHandler ); void writeTo(TestOutputWriter writer); } ================================================ FILE: performance-testing/src/test/java/org/neo4j/graphdatabases/performance_tests/AccessControl.java ================================================ package org.neo4j.graphdatabases.performance_tests; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Random; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.AccessControlConfig; import org.neo4j.graphdatabases.AccessControlWithRelationshipPropertiesConfig; import org.neo4j.graphdatabases.performance_tests.testing.DefaultExecutionEngineWrapper; import org.neo4j.graphdatabases.performance_tests.testing.MultipleTestRuns; import org.neo4j.graphdatabases.performance_tests.testing.ParamsGenerator; import org.neo4j.graphdatabases.performance_tests.testing.SingleTest; import org.neo4j.graphdatabases.performance_tests.testing.SysOutWriter; import org.neo4j.graphdatabases.queries.AccessControlQueries; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; import org.neo4j.graphdb.GraphDatabaseService; import static org.neo4j.graphdatabases.performance_tests.testing.PrintTestResults.printResults; public class AccessControl { private static final int NUMBER_OF_TEST_RUNS = 20; private static final int NUMBER_OF_RESULTS = 15; private static int numberOfAccounts; private static int numberOfEmployees; private static GraphDatabaseService db; private static AccessControlQueries queries; private static MultipleTestRuns multipleTestRuns; private static Random random; private static TestOutputWriter writer = SysOutWriter.INSTANCE; @BeforeClass public static void init() { try { db = DbUtils.existingDB( AccessControlConfig.STORE_DIR ); queries = new AccessControlQueries( new DefaultExecutionEngineWrapper( db ) ); multipleTestRuns = new MultipleTestRuns( NUMBER_OF_TEST_RUNS, writer ); random = new Random(); numberOfAccounts = DbUtils.numberOfItemsWithLabel(db, "Account"); numberOfEmployees = DbUtils.numberOfItemsWithLabel(db, "Employee"); } catch ( Exception ex ) { System.out.println( ex.getMessage() ); } } @AfterClass public static void teardown() { db.shutdown(); } @Test public void findAccessibleResources() throws Exception { // when multipleTestRuns.execute( "Find accessible resources for admin", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.findAccessibleResources( params.get( "admin" ) ); } } ); } @Test public void findAccessibleCompanies() throws Exception { // when multipleTestRuns.execute( "Find accessible companies for admin", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.findAccessibleCompanies( params.get( "admin" ) ); } } ); } @Test public void findAccessibleAccountsForCompany() throws Exception { // when multipleTestRuns.execute( "Find accessible accounts for company for admin", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.findAccessibleAccountsForCompany( params.get( "admin" ), params.get( "company" ) ); } } ); } @Test public void findAdminForCompany() throws Exception { // when multipleTestRuns.execute( "Find admins for company", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.findAdminForCompany( params.get( "company" ) ); } } ); } @Test public void findAdminForResource() throws Exception { // when multipleTestRuns.execute( "Find admins for resource", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.findAdminForResource( params.get( "resource" ) ); } } ); } @Test public void hasAccessToResource() throws Exception { // when multipleTestRuns.execute( "Does admin have access to resource?", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher (indexed resources)"; } @Override public ExecutionResult execute( Map params ) { return queries.hasAccessToIndexedResource( params.get( "admin" ), params.get( "resource" ) ); } } ); } @Test public void hasAccessToResourceBakeoff() throws Exception { // when multipleTestRuns.execute( "Does admin have access to resource?", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.hasAccessToResource( params.get( "admin" ), params.get( "resource" ) ); } }, new SingleTest() { @Override public String queryType() { return "Cypher (indexed resources)"; } @Override public ExecutionResult execute( Map params ) { return queries.hasAccessToIndexedResource( params.get( "admin" ), params.get( "resource" ) ); } } ); } private ParamsGenerator createParams() { return new ParamsGenerator() { @Override public Map generateParams() { HashMap params = new HashMap(); String adminName = String.format( "Administrator-%s", random.nextInt( AccessControlWithRelationshipPropertiesConfig.NUMBER_OF_ADMINS ) + 1 ); String resourceName; if ( random.nextInt( 2 ) < 1 ) { resourceName = String.format( "Account-%s", random.nextInt( numberOfAccounts ) + 1 ); } else { resourceName = String.format( "Customer-%s", random.nextInt( numberOfEmployees ) + 1 ); } ExecutionResult result = queries.findAccessibleCompanies( adminName ); Iterator> iterator = result.iterator(); String companyName = (String) iterator.next().get( "company" ); params.put( "admin", adminName ); params.put( "company", companyName ); params.put( "resource", resourceName ); return params; } }; } } ================================================ FILE: performance-testing/src/test/java/org/neo4j/graphdatabases/performance_tests/AccessControlWithRelationshipProperties.java ================================================ package org.neo4j.graphdatabases.performance_tests; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Random; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.graphdatabases.AccessControlWithRelationshipPropertiesConfig; import org.neo4j.graphdatabases.performance_tests.testing.DefaultExecutionEngineWrapper; import org.neo4j.graphdatabases.performance_tests.testing.MultipleTestRuns; import org.neo4j.graphdatabases.performance_tests.testing.ParamsGenerator; import org.neo4j.graphdatabases.performance_tests.testing.SingleTest; import org.neo4j.graphdatabases.performance_tests.testing.SysOutWriter; import org.neo4j.graphdatabases.queries.AccessControlWithRelationshipPropertiesQueries; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.helpers.QueryUnionExecutionResult; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import static org.neo4j.graphdatabases.performance_tests.testing.PrintTestResults.printResults; public class AccessControlWithRelationshipProperties { private static final int NUMBER_OF_TEST_RUNS = 20; private static final int NUMBER_OF_RESULTS = 15; private static int numberOfAccounts; private static int numberOfCustomers; private static GraphDatabaseService db; private static AccessControlWithRelationshipPropertiesQueries queries; private static MultipleTestRuns multipleTestRuns; private static Random random; private static TestOutputWriter writer = SysOutWriter.INSTANCE; @BeforeClass public static void init() { db = DbUtils.existingDB( AccessControlWithRelationshipPropertiesConfig.STORE_DIR ); queries = new AccessControlWithRelationshipPropertiesQueries( new DefaultExecutionEngineWrapper( db ) ); multipleTestRuns = new MultipleTestRuns( NUMBER_OF_TEST_RUNS, writer ); random = new Random(); numberOfAccounts = DbUtils.numberOfItemsWithLabel(db, "Account"); numberOfCustomers = DbUtils.numberOfItemsWithLabel(db, "Employee"); } @AfterClass public static void teardown() { db.shutdown(); } @Test public void findAccessibleResources() throws Exception { // when multipleTestRuns.execute( "Find accessible resources for admin", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.findAccessibleResources( params.get( "admin" ) ); } } ); } @Test public void findAccessibleCompanies() throws Exception { // when multipleTestRuns.execute( "Find accessible companies for admin", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.findAccessibleCompanies( params.get( "admin" ) ); } } ); } @Test public void findAccessibleAccountsForCompany() throws Exception { // when multipleTestRuns.execute( "Find accessible accounts for company for admin", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.findAccessibleAccountsForCompany( params.get( "admin" ), params.get( "company" ) ); } } ); } @Test public void findAdminForCompany() throws Exception { // when multipleTestRuns.execute( "Find admins for company", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.findAdminForCompany( params.get( "company" ) ); } } ); } @Test public void findAdminForResource() throws Exception { // when multipleTestRuns.execute( "Find admins for resource", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.findAdminForResource( params.get( "resource" ) ); } } ); } @Test public void hasAccessToResource() throws Exception { // when multipleTestRuns.execute( "Does admin have access to resource?", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher (indexed resources)"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.hasAccessToIndexedResource( params.get( "admin" ), params.get( "resource" ) ); } } ); } @Test public void hasAccessToResourceBakeoff() throws Exception { // when multipleTestRuns.execute( "Does admin have access to resource?", createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.hasAccessToResource( params.get( "admin" ), params.get( "resource" ) ); } }, new SingleTest() { @Override public String queryType() { return "Cypher (indexed resources)"; } @Override public QueryUnionExecutionResult execute( Map params ) { return queries.hasAccessToIndexedResource( params.get( "admin" ), params.get( "resource" ) ); } } ); } private ParamsGenerator createParams() { return new ParamsGenerator() { @Override public Map generateParams() { HashMap params = new HashMap(); String adminName = String.format( "Administrator-%s", random.nextInt( AccessControlWithRelationshipPropertiesConfig.NUMBER_OF_ADMINS ) + 1 ); String resourceName; if ( random.nextInt( 2 ) < 1 ) { resourceName = String.format( "Account-%s", random.nextInt( numberOfAccounts ) + 1 ); } else { resourceName = String.format( "Customer-%s", random.nextInt( numberOfCustomers ) + 1 ); } QueryUnionExecutionResult result = queries.findAccessibleCompanies( adminName ); Iterator> iterator = result.iterator(); String companyName = ((Node) iterator.next().get( "company" )).getProperty( "name" ).toString(); params.put( "admin", adminName ); params.put( "company", companyName ); params.put( "resource", resourceName ); return params; } }; } } ================================================ FILE: performance-testing/src/test/java/org/neo4j/graphdatabases/performance_tests/Logistics.java ================================================ package org.neo4j.graphdatabases.performance_tests; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.joda.time.DateTime; import org.joda.time.Interval; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.LogisticsConfig; import org.neo4j.graphdatabases.performance_tests.testing.DefaultExecutionEngineWrapper; import org.neo4j.graphdatabases.performance_tests.testing.MultipleTestRuns; import org.neo4j.graphdatabases.performance_tests.testing.ParamsGenerator; import org.neo4j.graphdatabases.performance_tests.testing.SingleTest; import org.neo4j.graphdatabases.performance_tests.testing.SysOutWriter; import org.neo4j.graphdatabases.queries.LogisticsQueries; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; import org.neo4j.graphdb.DynamicLabel; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Transaction; import org.neo4j.helpers.collection.IteratorUtil; import org.neo4j.tooling.GlobalGraphOperations; import static java.util.Arrays.asList; import static org.neo4j.graphdatabases.performance_tests.testing.PrintTestResults.printResults; public class Logistics { public static final int NUMBER_OF_TEST_RUNS = 20; private static final int NUMBER_OF_RESULTS = 15; private static GraphDatabaseService db; private static LogisticsQueries queries; private static MultipleTestRuns multipleTestRuns; private static Random random; private static TestOutputWriter writer = SysOutWriter.INSTANCE; @BeforeClass public static void init() { db = DbUtils.existingDB( LogisticsConfig.STORE_DIR ); queries = new LogisticsQueries( db, new DefaultExecutionEngineWrapper( db ) ); multipleTestRuns = new MultipleTestRuns( NUMBER_OF_TEST_RUNS, writer ); random = new Random(); } @AfterClass public static void teardown() { if ( db != null ) { db.shutdown(); } } @Test public void queryTest() throws Exception { List customIds = asList( 6053, 210, 6, 56, 8, 87, 2, 95, 9, 256, 7476, 545580 ); List cypherIds = asList( 6053, 210, 6, 192, 9, 256, 7476, 545580 ); Interval interval = Interval.parse( "2012-10-17T00:00:00.000+01:00/2012-10-18T00:00:00.000+01:00" ); getPath( customIds, interval ); getPath( cypherIds, interval ); } private void getPath( List ids, Interval interval ) { StringBuilder sbWhere = new StringBuilder(); StringBuilder sbMatch = new StringBuilder(); for ( int i = 0; i < ids.size(); i++ ) { sbWhere.append("id(n"); sbWhere.append(i); sbWhere.append(")="); sbWhere.append(ids.get(i)); if ( i < (ids.size() - 1) ) { sbWhere.append(" AND "); } sbWhere.append(" "); sbMatch.append( "n" ); sbMatch.append( i ); if ( i < (ids.size() - 1) ) { sbMatch.append( "--" ); } } String q = String.format( "MATCH p = %s %n" + "WHERE %s AND ALL(r in relationships(p) where r.start_date <= %s and r.end_date >= %s) %n" + "RETURN REDUCE(weight=0, r in relationships(p) | weight+r.cost) AS score, p", sbMatch.toString(), sbWhere.toString(), interval.getStartMillis(), interval.getEndMillis()); writer.writeln(q); ExecutionResult result = new ExecutionEngine( db ).execute( q ); writer.writeln(result.toString()); } @After public void flush() { writer.flush(); } @Test public void shortestRoute() throws Exception { TestRunParams testRunParams = new TestRunParams( db, writer ); // when multipleTestRuns.execute( "Shortest route through the parcel system", testRunParams.createParams(), printResults( NUMBER_OF_RESULTS, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public Object execute( Map params ) { return queries.findShortestPathWithCypherReduce( params.get( "start" ), params.get( "end" ), Interval.parse( params.get( "interval" ) ) ); } }, new SingleTest() { @Override public String queryType() { return "ParcelRouteCalculator"; } @Override public Object execute( Map params ) { return queries.findShortestPathWithParcelRouteCalculator( params.get( "start" ), params.get( "end" ), Interval.parse( params.get( "interval" ) ) ); } } ); } @Test public void testSingleCypherQuery() throws Exception { Map params = new HashMap(); params.put( "start", "delivery-segment-181622" ); params.put( "end", "delivery-segment-611694" ); params.put( "interval", "2012-10-17T00:00:00.000-07:00/2012-10-18T00:00:00.000-07:00" ); Interval interval = Interval.parse( params.get( "interval" ) ); System.out.println( "Start: " + interval.getStartMillis() ); System.out.println( "End: " + interval.getEndMillis() ); ExecutionResult result = queries.findShortestPathWithCypherReduce( params.get( "start" ), params.get( "end" ), interval ); writer.writeln( result.toString() ); } private class TestRunParams { private int deliveryAreaCount; private int deliverySegmentCount; public TestRunParams( GraphDatabaseService db, TestOutputWriter writer ) { GlobalGraphOperations ops = GlobalGraphOperations.at(db); try ( Transaction tx = db.beginTx()) { deliveryAreaCount = IteratorUtil.count(ops.getAllNodesWithLabel(DynamicLabel.label("DeliveryArea"))); deliverySegmentCount = IteratorUtil.count(ops.getAllNodesWithLabel(DynamicLabel.label("DeliverySegment"))); writer.writeln( "deliveryAreaCount " + deliveryAreaCount ); writer.writeln( "deliverySegmentCount " + deliverySegmentCount ); tx.success(); } } public ParamsGenerator createParams() { return new ParamsGenerator() { @Override public final Map generateParams() { Map params = new HashMap(); if ( random.nextInt( 2 ) < 1 ) { params.put( "start", String.format( "DeliverySegment-%s", random.nextInt( deliverySegmentCount ) + 1 ) ); } else { params.put( "start", String.format( "DeliveryArea-%s", random.nextInt( deliveryAreaCount ) + 1 ) ); } params.put( "end", String.format( "DeliverySegment-%s", random.nextInt( deliverySegmentCount ) + 1 ) ); DateTime startDtm = LogisticsConfig.START_DATE.plusDays( random.nextInt( 6 ) ); params.put( "interval", new Interval( startDtm, startDtm.plusDays( 1 ) ).toString() ); return params; } }; } } } ================================================ FILE: performance-testing/src/test/java/org/neo4j/graphdatabases/performance_tests/SimpleSocialNetwork.java ================================================ package org.neo4j.graphdatabases.performance_tests; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.SimpleSocialNetworkConfig; import org.neo4j.graphdatabases.performance_tests.testing.MultipleTestRuns; import org.neo4j.graphdatabases.performance_tests.testing.ParamsGenerator; import org.neo4j.graphdatabases.performance_tests.testing.SingleTest; import org.neo4j.graphdatabases.performance_tests.testing.SysOutWriter; import org.neo4j.graphdatabases.queries.SimpleSocialNetworkQueries; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; import org.neo4j.graphdatabases.queries.traversals.FriendOfAFriendDepth4; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import static org.neo4j.graphdatabases.performance_tests.testing.PrintTestResults.printResults; public class SimpleSocialNetwork { private static GraphDatabaseService db; private static SimpleSocialNetworkQueries queries; private static MultipleTestRuns multipleTestRuns; private static Random random; private static TestOutputWriter writer = SysOutWriter.INSTANCE; public static final int NUMBER_OF_TEST_RUNS = 20; @BeforeClass public static void init() { db = DbUtils.existingDB( SimpleSocialNetworkConfig.STORE_DIR ); queries = new SimpleSocialNetworkQueries( db ); multipleTestRuns = new MultipleTestRuns( NUMBER_OF_TEST_RUNS, writer ); random = new Random(); } @AfterClass public static void teardown() { db.shutdown(); } @Test public void foafToDepthFour() throws Exception { try ( Transaction tx = db.beginTx() ) { // when multipleTestRuns.execute( "Foaf to depth 4", createParams(), printResults( 1, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.pathBetweenTwoFriends( params.get( "first-user" ), params.get( "second-user" ) ); } } ); tx.success(); } } @Test public void friendOfAFriendToDepth4() throws Exception { try ( Transaction tx = db.beginTx() ) { // when multipleTestRuns.execute( "Friend of a friend to depth 4", createParams(), printResults( 100, writer ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.friendOfAFriendToDepth4( params.get( "first-user" ) ); } } ); tx.success(); } } @Test public void onlyFriendsAtDepth4UsingTraversalFramework() throws Exception { try ( Transaction tx = db.beginTx() ) { final FriendOfAFriendDepth4 traversal = new FriendOfAFriendDepth4( db ); // when multipleTestRuns.execute( "Only friends at depth 4 using Traversal Framework", createParams(), printResults( 300000, writer ), new SingleTest() { @Override public String queryType() { return "Traversal Framework (custom class)"; } @Override public Iterable execute( Map params ) { return traversal.getFriends( params.get( "first-user" ) ); } } ); tx.success(); } } private ParamsGenerator createParams() { return new ParamsGenerator() { @Override public Map generateParams() { Map params = new HashMap(); params.put( "first-user", String.format( "User-%s", random.nextInt( SimpleSocialNetworkConfig .NUMBER_USERS ) + 1 ) ); params.put( "second-user", String.format( "User-%s", random.nextInt( SimpleSocialNetworkConfig .NUMBER_USERS ) + 1 ) ); return params; } }; } } ================================================ FILE: performance-testing/src/test/java/org/neo4j/graphdatabases/performance_tests/SocialNetwork.java ================================================ package org.neo4j.graphdatabases.performance_tests; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.junit.*; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.SocialNetworkConfig; import org.neo4j.graphdatabases.performance_tests.testing.DefaultExecutionEngineWrapper; import org.neo4j.graphdatabases.performance_tests.testing.MultipleTestRuns; import org.neo4j.graphdatabases.performance_tests.testing.ParamsGenerator; import org.neo4j.graphdatabases.performance_tests.testing.ResultFormatter; import org.neo4j.graphdatabases.performance_tests.testing.ResultsContainSameElementsUnordered; import org.neo4j.graphdatabases.performance_tests.testing.SingleTest; import org.neo4j.graphdatabases.performance_tests.testing.SysOutWriter; import org.neo4j.graphdatabases.queries.SocialNetworkQueries; import org.neo4j.graphdatabases.queries.helpers.DbUtils; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import static org.neo4j.graphdatabases.performance_tests.testing.DoNothingWithTestResults.doNothing; import static org.neo4j.graphdatabases.performance_tests.testing.PrintTestResults.printResults; import static org.neo4j.graphdatabases.performance_tests.testing.TakeXTestResults.take; import static org.neo4j.neode.Range.minMax; import static org.neo4j.neode.probabilities.ProbabilityDistribution.flatDistribution; public class SocialNetwork { public static final int NUMBER_OF_RESULTS = 5; public static final int NUMBER_OF_TEST_RUNS = 20; private GraphDatabaseService db; private SocialNetworkQueries queries; private MultipleTestRuns multipleTestRuns; private Random random; private TestOutputWriter writer = SysOutWriter.INSTANCE; @Rule public TestName name = new TestName(); @Before public void init() { db = DbUtils.existingDB( SocialNetworkConfig.STORE_DIR ); queries = new SocialNetworkQueries( db, new DefaultExecutionEngineWrapper( db ) ); multipleTestRuns = new MultipleTestRuns( NUMBER_OF_TEST_RUNS, writer ); random = new Random(); } @After public void teardown() { db.shutdown(); writer.flush(); } @Test public void sharedInterestsSameCompany() throws Exception { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user" ), take( NUMBER_OF_RESULTS ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public Object execute( Map params ) { return queries.sharedInterestsSameCompany( params.get( "user" ) ); } } ); } @Test public void sharedInterestsAllCompanies() throws Exception { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user" ), take( NUMBER_OF_RESULTS ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public Object execute( Map params ) { return queries.sharedInterestsAllCompanies( params.get( "user" ), NUMBER_OF_RESULTS ); } } ); } @Test public void sharedInterestsAlsoInterestedInTopic() throws Exception { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user", "topic1" ), take( NUMBER_OF_RESULTS ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public Object execute( Map params ) { return queries.sharedInterestsAlsoInterestedInTopic( params.get( "user" ), params.get( "topic1" ) ); } } ); } @Test public void friendOfAFriendWithInterest() throws Exception { try ( Transaction tx = db.beginTx() ) { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user", "topic1" ), take( NUMBER_OF_RESULTS ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public Object execute( Map params ) { return queries.friendOfAFriendWithInterest( params.get( "user" ), params.get( "topic1" ), NUMBER_OF_RESULTS ); } } ); tx.success(); } } @Test public void friendOfAFriendWithInterestTraversalFramework() throws Exception { try ( Transaction tx = db.beginTx() ) { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user", "topic1" ), doNothing(), new SingleTest() { @Override public String queryType() { return "Traversal Framework"; } @Override public Object execute( Map params ) { return queries.friendOfAFriendWithInterestTraversalFramework( params.get( "user" ), params.get( "topic1" ), NUMBER_OF_RESULTS ); } } ); tx.success(); } } // @Test // public void shouldFindColleagueOfAColleagueOfAColleagueEtcWithAParticularInterestUsingGremlin() throws Exception // { // // when // multipleTestRuns.execute( "Colleagues of colleagues interested in particular topic (gremlin)", // createParams(), printResults( 2 ), new SingleTestRun() // { // @Override // public QueryType queryType() // { // return QueryType.Gremlin; // } // // @Override // public Object execute( Map params ) // { // try // { // return queries.friendOfAFriendWithParticularInterestGremlin( params.get( "user" ), // params.get( "topic" ) ); // } // catch ( ScriptException e ) // { // throw new RuntimeException( e ); // } // } // } ); // } @Test public void queryBakeoff() throws Exception { try ( Transaction tx = db.beginTx() ) { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user", "topic1" ), printResults( NUMBER_OF_RESULTS, resultFormatter(), writer ), new SingleTest() { @Override public String queryType() { return "Traversal Framework"; } @Override public Object execute( Map params ) { return queries.friendOfAFriendWithInterestTraversalFramework( params.get( "user" ), params.get( "topic1" ), NUMBER_OF_RESULTS ); } } // , new SingleTestRun() // { // @Override // public QueryType queryType() // { // return QueryType.Gremlin; // } // // @Override // public Object execute( Map params ) // { // try // { // return queries.friendOfAFriendWithParticularInterestGremlin( params.get( "user" ), // params.get( "topic" ) ); // } // catch ( ScriptException e ) // { // throw new RuntimeException( e ); // } // } // } , new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public Object execute( Map params ) { return queries.friendOfAFriendWithInterest( params.get( "user" ), params.get( "topic1" ), NUMBER_OF_RESULTS ); } } , new SingleTest() { @Override public String queryType() { return "Cypher2"; } @Override public Object execute( Map params ) { return queries.friendWorkedWithFriendWithInterests( params.get( "user" ), NUMBER_OF_RESULTS, params.get( "topic1" ) ); } } ); tx.success(); } } @Test public void friendWorkedWithFriendWithInterests() throws Exception { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user", "topic1", "topic2", "topic3" ), take( NUMBER_OF_RESULTS ), new SingleTest() { @Override public String queryType() { return "Cypher"; } @Override public ExecutionResult execute( Map params ) { return queries.friendWorkedWithFriendWithInterests( params.get( "user" ), NUMBER_OF_RESULTS, params.get( "topic1" ), params.get( "topic2" ), params.get( "topic3" ) ); } } ); } @Test public void friendOfAFriendWithMultipleInterestsBakeoff() throws Exception { // when multipleTestRuns.execute( name.getMethodName(), createParams( "user", "topic1", "topic2", "topic3", "topic4", "topic5" ), ResultsContainSameElementsUnordered.newFactory(), printResults( NUMBER_OF_RESULTS, resultFormatter(), writer ), new SingleTest() { @Override public String queryType() { return "friendOfAFriendWithMultipleInterest"; } @Override public ExecutionResult execute( Map params ) { return queries.friendOfAFriendWithMultipleInterest( params.get( "user" ), NUMBER_OF_RESULTS, params.get( "topic1" ), params.get( "topic2" ), params.get( "topic3" ), params.get( "topic4" ), params.get( "topic5" ) ); } }, new SingleTest() { @Override public String queryType() { return "friendWorkedWithFriendWithInterests"; } @Override public ExecutionResult execute( Map params ) { return queries.friendWorkedWithFriendWithInterests( params.get( "user" ), NUMBER_OF_RESULTS, params.get( "topic1" ), params.get( "topic2" ), params.get( "topic3" ), params.get( "topic4" ), params.get( "topic5" ) ); } } ); } private ParamsGenerator createParams( final String... keys ) { return new ParamsGenerator() { @Override public final Map generateParams() { List topicIds = flatDistribution().generateList( 5, minMax( 1, SocialNetworkConfig.NUMBER_TOPICS ) ); Map params = new HashMap(); for ( String key : keys ) { if ( key.equals( "user" ) ) { params.put( "user", String.format( "User-%s", random.nextInt( SocialNetworkConfig.NUMBER_USERS ) + 1 ) ); } if ( key.equals( "topic1" ) ) { params.put( "topic1", String.format( "Topic-%s", topicIds.get( 0 ) ) ); } if ( key.equals( "topic2" ) ) { params.put( "topic2", String.format( "Topic-%s", topicIds.get( 1 ) ) ); } if ( key.equals( "topic3" ) ) { params.put( "topic3", String.format( "Topic-%s", topicIds.get( 2 ) ) ); } if ( key.equals( "topic4" ) ) { params.put( "topic4", String.format( "Topic-%s", topicIds.get( 3 ) ) ); } if ( key.equals( "topic5" ) ) { params.put( "topic5", String.format( "Topic-%s", topicIds.get( 4 ) ) ); } } return params; } }; } private ResultFormatter resultFormatter() { return new ResultFormatter() { @Override public String format( Object result ) { if ( Node.class.isAssignableFrom( result.getClass() ) ) { return ((Node) result).getProperty( "name" ).toString(); } else { return result.toString(); } } }; } @Test @Ignore public void testSingleCypherQuery() throws Exception { Map params = new HashMap(); params.put( "person", "User-309491" ); params.put( "colleague", "User-558252" ); params.put( "topic", "Topic-78" ); String cypher = "MATCH (person:User {name:{person}}), (colleague:User {name:{colleague}}), " + "(topic:Topic {name:{topic}})\n" + "MATCH p = person-[:WORKED_ON*2..4]-colleague-[:INTERESTED_IN]->topic\n" + "RETURN p, LENGTH(p) AS pathLength ORDER BY pathLength ASC"; ExecutionResult result = new ExecutionEngine( db ).execute( cypher, params ); writer.writeln( result.toString() ); } @Test @Ignore public void testTraversal() throws Exception { Collection nodes = queries.friendOfAFriendWithInterestTraversalFramework( "User-309491", "Topic-78", NUMBER_OF_RESULTS ); for ( Node node : nodes ) { } } } ================================================ FILE: performance-testing/src/test/java/org/neo4j/graphdatabases/performance_tests/testing/ResultsContainSameElementsUnorderedTest.java ================================================ package org.neo4j.graphdatabases.performance_tests.testing; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; public class ResultsContainSameElementsUnorderedTest { @Test public void shouldReportDiscrepanciesBetweenTwoQueries() throws Exception { // given ResultsContainSameElementsUnordered handler = new ResultsContainSameElementsUnordered( ); // when handler.handle( "queryType1", "a" ); handler.handle( "queryType2", "C" ); handler.handle( "queryType1", "b" ); handler.handle( "queryType2", "b" ); handler.handle( "queryType1", "c" ); handler.handle( "queryType2", "a" ); MyTestOutputWriter writer = new MyTestOutputWriter(); handler.summarize( writer ); // then String expected = "\n" + " queryType1 v. queryType2: queryType1 contains additional elements: [c]\n" + " queryType2 v. queryType1: queryType2 contains additional elements: [C]\n"; assertEquals( expected, writer.toString()); } @Test public void shouldReportDiscrepanciesBetweenThreeQueries() throws Exception { // given ResultsContainSameElementsUnordered handler = new ResultsContainSameElementsUnordered( ); // when handler.handle( "queryType1", "a" ); handler.handle( "queryType2", "b" ); handler.handle( "queryType3", "c" ); MyTestOutputWriter writer = new MyTestOutputWriter(); handler.summarize( writer ); // then String expected = "\n" + " queryType1 v. queryType2: queryType1 contains additional elements: [a]\n" + " queryType2 v. queryType1: queryType2 contains additional elements: [b]\n" + " queryType1 v. queryType3: queryType1 contains additional elements: [a]\n" + " queryType3 v. queryType1: queryType3 contains additional elements: [c]\n" + " queryType2 v. queryType3: queryType2 contains additional elements: [b]\n" + " queryType3 v. queryType2: queryType3 contains additional elements: [c]\n"; assertEquals( expected, writer.toString()); } private static class MyTestOutputWriter implements TestOutputWriter { private final StringBuilder builder = new StringBuilder( ); @Override public void begin() { } @Override public void write( String value ) { builder.append( value ); } @Override public void writeln( String value ) { builder.append( value ); builder.append( "\n" ); } @Override public void flush() { } @Override public void end() { } @Override public String toString() { return builder.toString(); } } } ================================================ FILE: pom.xml ================================================ 4.0.0 graph-databases-use-cases graph-databases-use-cases 2.0-SNAPSHOT pom 1.7 1.7 UTF-8 queries neode configuration data-generation performance-testing ================================================ FILE: queries/pom.xml ================================================ 4.0.0 org.neo4j.graphdatabases queries 2.0-SNAPSHOT jar 1.7 1.7 UTF-8 2.0.1 org.neo4j neo4j ${neo4j.version} org.neo4j neo4j-kernel ${neo4j.version} test-jar test org.neo4j neo4j-graphviz ${neo4j.version} org.neo4j.app neo4j-server ${neo4j.version} org.neo4j.app neo4j-server ${neo4j.version} test-jar junit junit 4.11 test com.sun.jersey jersey-server 1.17 com.sun.jersey jersey-servlet 1.17 com.sun.jersey jersey-client 1.17 com.sun.jersey jersey-core 1.17 org.eclipse.jetty jetty-server 9.0.5.v20130815 joda-time joda-time 2.1 ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/AccessControlQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; public class AccessControlQueries { private final ExecutionEngineWrapper executionEngine; public AccessControlQueries( ExecutionEngineWrapper executionEngineWrapper ) { this.executionEngine = executionEngineWrapper; } public ExecutionResult findAccessibleResources( String adminName ) { String query = "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH paths=(admin)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()\n" + " <-[:CHILD_OF*0..3]-(company)<-[:WORKS_FOR]-(employee)\n" + " -[:HAS_ACCOUNT]->(account)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))\n" + "RETURN employee.name AS employee, account.name AS account\n" + "UNION\n" + "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH paths=(admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->()\n" + " <-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)\n" + "RETURN employee.name AS employee, account.name AS account"; Map params = new HashMap<>(); params.put( "adminName", adminName ); return executionEngine.execute( query, params ); } public ExecutionResult findAccessibleCompanies( String adminName ) { String query = "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH (admin)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()<-[:CHILD_OF*0..3]-(company)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))\n" + "RETURN company.name AS company\n" + "UNION\n" + "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH (admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->(company)\n" + "RETURN company.name AS company"; Map params = new HashMap<>(); params.put( "adminName", adminName ); return executionEngine.execute( query, params ); } public ExecutionResult findAccessibleAccountsForCompany( String adminName, String companyName ) { String query = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (company:Company {name:{companyName}})\n" + "MATCH (admin)-[:MEMBER_OF]->(group)-[:ALLOWED_INHERIT]->(company)\n" + " <-[:CHILD_OF*0..3]-(subcompany)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(subcompany))\n" + "RETURN account.name AS account\n" + "UNION\n" + "MATCH (admin:Administrator {name:{adminName}}),\n" + " (company:Company {name:{companyName}})\n" + "MATCH (admin)-[:MEMBER_OF]->(group)-[:ALLOWED_DO_NOT_INHERIT]->(company)\n" + " <-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)\n" + "RETURN account.name AS account"; Map params = new HashMap<>(); params.put( "adminName", adminName ); params.put( "companyName", companyName ); return executionEngine.execute( query, params ); } public ExecutionResult findAdminForResource( String resourceName ) { String query = "MATCH (resource:Resource {name:{resourceName}})\n" + "MATCH p=(resource)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(company)\n" + " -[:CHILD_OF*0..3]->()<-[:ALLOWED_INHERIT]-()<-[:MEMBER_OF]-(admin)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))\n" + "RETURN admin.name AS admin\n" + "UNION\n" + "MATCH (resource:Resource {name:{resourceName}})\n" + "MATCH p=(resource)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(company)\n" + " <-[:ALLOWED_DO_NOT_INHERIT]-()<-[:MEMBER_OF]-(admin)\n" + "RETURN admin.name AS admin"; Map params = new HashMap<>(); params.put( "resourceName", resourceName ); return executionEngine.execute( query, params ); } public ExecutionResult findAdminForCompany( String companyName ) { String query = "MATCH (company:Company {name:{companyName}})\n" + "MATCH (company)-[:CHILD_OF*0..3]->()<-[:ALLOWED_INHERIT]-()<-[:MEMBER_OF]-(admin)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))\n" + "RETURN admin.name AS admin\n" + "UNION\n" + "MATCH (company:Company {name:{companyName}})\n" + "MATCH (company)<-[:ALLOWED_DO_NOT_INHERIT]-()<-[:MEMBER_OF]-(admin)\n" + "RETURN admin.name AS admin"; Map params = new HashMap<>(); params.put( "companyName", companyName ); return executionEngine.execute( query, params ); } public ExecutionResult hasAccessToResource( String adminName, String resourceName ) { String query = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (resource:Resource {name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()<-[:CHILD_OF*0." + ".3]-(company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))\n" + "RETURN count(p) AS accessCount\n" + "UNION\n" + "MATCH (admin:Administrator {name:{adminName}}),\n" + " (resource:Resource {name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->(company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource)\n" + "RETURN count(p) AS accessCount"; Map params = new HashMap<>(); params.put( "adminName", adminName ); params.put( "resourceName", resourceName ); return executionEngine.execute( query, params ); } public ExecutionResult hasAccessToIndexedResource( String adminName, String resourceName ) { String query = "MATCH (admin:Administrator {name:{adminName}}),\n" + " c1=(company)<-[:CHILD_OF*0..3]-(:Company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource:Resource {name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->(company)\n" + "WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->(company))\n" + "RETURN count(p) AS accessCount\n" + // "RETURN p, company, admin, resource,c1\n" + "UNION\n" + "MATCH (admin:Administrator {name:{adminName}}),\n" + " c1=(company:Company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource:Resource {name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->(company)\n" + "RETURN count(p) AS accessCount\n"; // "RETURN p, company, admin, resource,c1\n"; Map params = new HashMap<>(); params.put( "adminName", adminName ); params.put( "resourceName", resourceName ); return executionEngine.execute( query, params ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/AccessControlWithRelationshipPropertiesQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.helpers.QueryUnionExecutionEngine; import org.neo4j.graphdatabases.queries.helpers.QueryUnionExecutionResult; public class AccessControlWithRelationshipPropertiesQueries { private final QueryUnionExecutionEngine executionEngine; public AccessControlWithRelationshipPropertiesQueries( ExecutionEngineWrapper executionEngine ) { this.executionEngine = new QueryUnionExecutionEngine( executionEngine ); } public QueryUnionExecutionResult findAccessibleResources( String adminName ) { String inheritedQuery = "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH paths=(admin)-[:MEMBER_OF]->()-[permission:ALLOWED]->()<-[:CHILD_OF*0..3]-()" + "<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)\n" + "WHERE (permission.inherit=true) AND NOT (admin-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-()" + "<-[:WORKS_FOR]-employee-[:HAS_ACCOUNT]->account)\n" + "RETURN paths"; String notInheritedQuery = "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH paths=(admin)-[:MEMBER_OF]->()-[permission:ALLOWED]->()" + "<-[:WORKS_FOR]-employee-[:HAS_ACCOUNT]->account\n" + "WHERE (permission.inherit=false)\n" + "RETURN paths"; Map params = new HashMap<>(); params.put( "adminName", adminName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } // todo no result for query1 ? public QueryUnionExecutionResult findAccessibleCompanies( String adminName ) { String inheritedQuery = "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH admin-[:MEMBER_OF]->()-[permission:ALLOWED]->()<-[:CHILD_OF*0..3]-company\n" + "WHERE (permission.inherit=true) AND NOT (admin-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0." + ".3]-company)\n" + "RETURN company"; String notInheritedQuery = "MATCH (admin:Administrator {name:{adminName}})\n" + "MATCH admin-[:MEMBER_OF]->()-[permission:ALLOWED]->(company)\n" + "WHERE (permission.inherit=false)\n" + "RETURN company"; Map params = new HashMap<>(); params.put( "adminName", adminName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } public QueryUnionExecutionResult findAccessibleAccountsForCompany( String adminName, String companyName ) { String inheritedQuery = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (company:Company{name:{companyName}})\n" + "MATCH admin-[:MEMBER_OF]->group-[permission:ALLOWED]->company<-[:CHILD_OF*0." + ".3]-subcompany<-[:WORKS_FOR]-employee-[:HAS_ACCOUNT]->account\n" + "WHERE (permission.inherit=true) AND NOT (admin-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0." + ".3]-subcompany)\n" + "RETURN account"; String notInheritedQuery = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (company:Company{name:{companyName}})\n" + "MATCH admin-[:MEMBER_OF]->group-[permission:ALLOWED]->company<-[:WORKS_FOR]-employee-[:HAS_ACCOUNT" + "]->account\n" + "WHERE (permission.inherit=false)\n" + "RETURN account"; Map params = new HashMap<>(); params.put( "adminName", adminName ); params.put( "companyName", companyName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } // todo no result for query2 public QueryUnionExecutionResult findAdminForResource( String resourceName ) { String inheritedQuery = "MATCH (resource:Resource {name:{resourceName}}) \n" + "MATCH p=resource-[:WORKS_FOR|HAS_ACCOUNT*1..2]-company-[:CHILD_OF*0..3]->()<-[permission:ALLOWED]-()" + "<-[:MEMBER_OF]-admin\n" + "WHERE (permission.inherit=true) AND NOT (admin-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-company)\n" + "RETURN DISTINCT admin, p"; String notInheritedQuery = "MATCH (resource:Resource{name:{resourceName}})\n" + "MATCH p=resource-[:WORKS_FOR|HAS_ACCOUNT*1..2]-company<-[permission:ALLOWED]-()" + "<-[:MEMBER_OF]-admin\n" + "WHERE (permission.inherit=false)\n" + "RETURN DISTINCT admin, p"; Map params = new HashMap<>(); params.put( "resourceName", resourceName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } public QueryUnionExecutionResult findAdminForCompany( String companyName ) { String inheritedQuery = "MATCH (company:Company{name:{companyName}})\n" + "MATCH p=company-[:CHILD_OF*0..3]->()<-[permission:ALLOWED]-()<-[:MEMBER_OF]-admin\n" + "WHERE (permission.inherit=true) AND NOT (admin-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-company)\n" + "RETURN DISTINCT admin, p"; String notInheritedQuery = "MATCH (company:Company{name:{companyName}})\n" + "MATCH p=company<-[permission:ALLOWED]-()<-[:MEMBER_OF]-admin\n" + "WHERE (permission.inherit=false)\n" + "RETURN DISTINCT admin, p"; Map params = new HashMap<>(); params.put( "companyName", companyName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } public QueryUnionExecutionResult hasAccessToResource( String adminName, String resourceName ) { String inheritedQuery = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (resource:Resource{name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[permission:ALLOWED]->()<-[:CHILD_OF*0." + ".3]-(company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource)\n" + "WHERE (permission.inherit=true) AND NOT (admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company)\n" + "RETURN COUNT(p) AS accessCount"; String notInheritedQuery = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (resource:Resource{name:{resourceName}})\n" + "MATCH p=admin-[:MEMBER_OF]->()-[permission:ALLOWED]->company-[:WORKS_FOR|HAS_ACCOUNT*1..2]-resource\n" + "WHERE (permission.inherit=false)\n" + "RETURN COUNT(p) AS accessCount"; Map params = new HashMap<>(); params.put( "adminName", adminName ); params.put( "resourceName", resourceName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } public QueryUnionExecutionResult hasAccessToIndexedResource( String adminName, String resourceName ) { String inheritedQuery = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (company)<-[:CHILD_OF*0..3]-(:Company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource:Resource {name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[permission:ALLOWED]->(company)\n" + "WHERE (permission.inherit=true) AND NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))\n" + "RETURN COUNT(p) AS accessCount"; String notInheritedQuery = "MATCH (admin:Administrator {name:{adminName}}),\n" + " (company)<-[:CHILD_OF*0..3]-(:Company)-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(resource:Resource {name:{resourceName}})\n" + "MATCH p=(admin)-[:MEMBER_OF]->()-[permission:ALLOWED]->(company)\n" + "WHERE (permission.inherit=false)\n" + "RETURN COUNT(p) AS accessCount"; Map params = new HashMap<>(); params.put( "adminName", adminName ); params.put( "resourceName", resourceName ); return executionEngine.execute( params, inheritedQuery, notInheritedQuery ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/EmailQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; import org.neo4j.graphdb.GraphDatabaseService; public class EmailQueries { private final GraphDatabaseService db; private final ExecutionEngineWrapper executionEngineWrapper; public EmailQueries( GraphDatabaseService db, ExecutionEngineWrapper executionEngineWrapper ) { this.db = db; this.executionEngineWrapper = executionEngineWrapper; } public ExecutionResult suspectBehaviour() { String query = "MATCH (bob:User {username:'Bob'})-[:SENT]->(email)-[:CC]->(alias),\n" + " (alias)-[:ALIAS_OF]->(bob)\n" + "RETURN email.id"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult suspectBehaviour2() { String query = "MATCH p=(email:Email {id:'6'})<-[:REPLY_TO*1..4]-()<-[:SENT]-(replier)\n" + "RETURN replier.username AS replier, length(p) - 1 AS depth ORDER BY depth"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult suspectBehaviour3() { String query = "MATCH (email:Email {id:'11'})<-[f:FORWARD_OF*]-() \n" + "RETURN count(f)"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult lossyDb() { String query = "MATCH (bob:User {username:'Bob'})-[e:EMAILED]->(charlie:User {username:'Charlie'})\n" + "RETURN e"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/LogisticsQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.joda.time.Interval; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.traversals.ParcelRouteCalculator; import org.neo4j.graphdatabases.queries.traversals.SimpleParcelRouteCalculator; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; public class LogisticsQueries { private final ExecutionEngineWrapper executionEngineWrapper; private final ParcelRouteCalculator parcelRouteCalculator; private final SimpleParcelRouteCalculator simpleParcelRouteCalculator; public LogisticsQueries( GraphDatabaseService db, ExecutionEngineWrapper executionEngineWrapper ) { this.executionEngineWrapper = executionEngineWrapper; this.parcelRouteCalculator = new ParcelRouteCalculator( db ); this.simpleParcelRouteCalculator = new SimpleParcelRouteCalculator( db ); } public Iterable findShortestPathWithParcelRouteCalculator( String start, String end, Interval interval ) { return parcelRouteCalculator.calculateRoute( start, end, interval ); } public Iterable findShortestPathWithSimpleParcelRouteCalculator( String start, String end, Interval interval ) { return simpleParcelRouteCalculator.calculateRoute( start, end, interval ); } public ExecutionResult findShortestPathWithCypherReduce( String start, String end, Interval interval ) { String query = "MATCH (s:Location {name:{startLocation}}),\n" + " (e:Location {name:{endLocation}})\n" + "MATCH upLeg = (s)<-[:DELIVERY_ROUTE*1..2]-(db1)\n" + "WHERE all(r in relationships(upLeg)\n" + " WHERE r.start_date <= {intervalStart}\n" + " AND r.end_date >= {intervalEnd})\n" + "WITH e, upLeg, db1\n" + "MATCH downLeg = (db2)-[:DELIVERY_ROUTE*1..2]->(e)\n" + "WHERE all(r in relationships(downLeg)\n" + " WHERE r.start_date <= {intervalStart}\n" + " AND r.end_date >= {intervalEnd})\n" + "WITH db1, db2, upLeg, downLeg\n" + "MATCH topRoute = (db1)<-[:CONNECTED_TO]-()-[:CONNECTED_TO*1..3]-(db2)\n" + "WHERE all(r in relationships(topRoute)\n" + " WHERE r.start_date <= {intervalStart}\n" + " AND r.end_date >= {intervalEnd})\n" + "WITH upLeg, downLeg, topRoute,\n" + " reduce(weight=0, r in relationships(topRoute) | weight+r.cost) AS score\n" + " ORDER BY score ASC\n" + " LIMIT 1\n" + "RETURN (nodes(upLeg) + tail(nodes(topRoute)) + tail(nodes(downLeg))) AS n"; Map params = new HashMap(); params.put( "startLocation", start ); params.put( "endLocation", end ); params.put( "intervalStart", interval.getStartMillis() ); params.put( "intervalEnd", interval.getEndMillis() ); return executionEngineWrapper.execute( query, params ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/ShakespeareQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; public class ShakespeareQueries { private final ExecutionEngineWrapper executionEngineWrapper; public ShakespeareQueries( ExecutionEngineWrapper executionEngineWrapper ) { this.executionEngineWrapper = executionEngineWrapper; } public ExecutionResult theatreCityBard() { String query = "MATCH (theater:Venue {name:'Theatre Royal'}), \n" + " (newcastle:City {name:'Newcastle'}), \n" + " (bard:Author {lastname:'Shakespeare'})\n" + "RETURN theater.name AS theater, newcastle.name AS city, bard.lastname AS bard"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult allPlays() { String query = "MATCH (theater:Venue {name:'Theatre Royal'}), \n" + " (newcastle:City {name:'Newcastle'}), \n" + " (bard:Author {lastname:'Shakespeare'})\n" + "MATCH (newcastle)<-[:STREET|CITY*1..2]-(theater)\n" + " <-[:VENUE]-()-[:PERFORMANCE_OF]->()-[:PRODUCTION_OF]->\n" + " (play)<-[:WROTE_PLAY]-(bard)\n" + "RETURN DISTINCT play.title AS play"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult latePeriodPlays() { String query = "MATCH (theater:Venue {name:'Theatre Royal'}), \n" + " (newcastle:City {name:'Newcastle'}), \n" + " (bard:Author {lastname:'Shakespeare'})\n" + "MATCH (newcastle)<-[:STREET|CITY*1..2]-(theater)<-[:VENUE]-()-[:PERFORMANCE_OF]->()\n" + " -[:PRODUCTION_OF]->(play)<-[w:WROTE_PLAY]-(bard)\n" + "WHERE w.year > 1608\n" + "RETURN DISTINCT play.title AS play"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult orderedByPerformance() { String query = "MATCH (theater:Venue {name:'Theatre Royal'}), \n" + " (newcastle:City {name:'Newcastle'}), \n" + " (bard:Author {lastname:'Shakespeare'})\n" + "MATCH (newcastle)<-[:STREET|CITY*1..2]-(theater)<-[:VENUE]-()-[p:PERFORMANCE_OF]->()\n" + " -[:PRODUCTION_OF]->(play)<-[:WROTE_PLAY]-(bard)\n" + "RETURN play.title AS play, count(p) AS performance_count \n" + "ORDER BY performance_count DESC"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult exampleOfWith() { String query = "MATCH (bard:Author {lastname:'Shakespeare'})\n" + "MATCH (bard)-[w:WROTE_PLAY]->(play)\n" + "WITH play \n" + "ORDER BY w.year DESC \n" + "RETURN collect(play.title) AS plays"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/ShakespeareQueriesUsingAutoIndexes.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; public class ShakespeareQueriesUsingAutoIndexes { private final ExecutionEngineWrapper executionEngineWrapper; public ShakespeareQueriesUsingAutoIndexes( ExecutionEngineWrapper executionEngineWrapper ) { this.executionEngineWrapper = executionEngineWrapper; } public ExecutionResult theatreCityBard() { String query = "START theater=node:node_auto_index(name='Theatre Royal'), \n" + " newcastle=node:node_auto_index(name='Newcastle'), \n" + " bard=node:node_auto_index(lastname='Shakespeare')\n" + "RETURN theater.name AS theater, newcastle.name AS city, bard.lastname AS bard"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult allPlays() { String query = "START theater=node:node_auto_index(name='Theatre Royal'), \n" + " newcastle=node:node_auto_index(name='Newcastle'), \n" + " bard=node:node_auto_index(lastname='Shakespeare')\n" + "MATCH (newcastle)<-[:STREET|CITY*1..2]-(theater)\n" + " <-[:VENUE]-()-[:PERFORMANCE_OF]->()-[:PRODUCTION_OF]->\n" + " (play)<-[:WROTE_PLAY]-(bard)\n" + "RETURN DISTINCT play.title AS play"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult latePeriodPlays() { String query = "START theater=node:node_auto_index(name='Theatre Royal'), \n" + " newcastle=node:node_auto_index(name='Newcastle'), \n" + " bard=node:node_auto_index(lastname='Shakespeare')\n" + "MATCH (newcastle)<-[:STREET|CITY*1..2]-(theater)<-[:VENUE]-()-[:PERFORMANCE_OF]->()\n" + " -[:PRODUCTION_OF]->(play)<-[w:WROTE_PLAY]-(bard)\n" + "WHERE w.year > 1608\n" + "RETURN DISTINCT play.title AS play"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult orderedByPerformance() { String query = "START theater=node:node_auto_index(name='Theatre Royal'), \n" + " newcastle=node:node_auto_index(name='Newcastle'), \n" + " bard=node:node_auto_index(lastname='Shakespeare')\n" + "MATCH (newcastle)<-[:STREET|CITY*1..2]-(theater)<-[:VENUE]-()-[p:PERFORMANCE_OF]->()\n" + " -[:PRODUCTION_OF]->(play)<-[:WROTE_PLAY]-(bard)\n" + "RETURN play.title AS play, count(p) AS performance_count \n" + "ORDER BY performance_count DESC"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } public ExecutionResult exampleOfWith() { String query = "START bard=node:node_auto_index(lastname='Shakespeare')\n" + "MATCH (bard)-[w:WROTE_PLAY]->(play)\n" + "WITH play \n" + "ORDER BY w.year DESC \n" + "RETURN collect(play.title) AS plays"; Map params = new HashMap(); return executionEngineWrapper.execute( query, params ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/SimpleSocialNetworkQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.HashMap; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdb.GraphDatabaseService; public class SimpleSocialNetworkQueries { private final ExecutionEngine executionEngine; public SimpleSocialNetworkQueries( GraphDatabaseService db ) { this.executionEngine = new ExecutionEngine( db ); } public ExecutionResult pathBetweenTwoFriends( String firstUser, String secondUser ) { String query = "MATCH (first:User{name:{firstUser}}),\n" + " (second:User{name:{secondUser}})\n" + "MATCH p=shortestPath(first-[*..4]-second)\n" + "RETURN length(p) AS depth"; Map params = new HashMap(); params.put( "firstUser", firstUser ); params.put( "secondUser", secondUser ); return executionEngine.execute( query, params ); } public ExecutionResult friendOfAFriendToDepth4(String name) { String query = "MATCH (person:User {name:{name}})-[:FRIEND]-()-[:FRIEND]-()-[:FRIEND]-()-[:FRIEND]-(friend)\n" + "RETURN friend.name AS name"; Map params = new HashMap(); params.put( "name", name ); return executionEngine.execute( query, params ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/SocialNetworkQueries.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.ExecutionEngineWrapper; import org.neo4j.graphdb.*; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.graphdb.traversal.Uniqueness; import org.neo4j.helpers.collection.IteratorUtil; import static org.neo4j.graphdb.DynamicRelationshipType.withName; public class SocialNetworkQueries { public static final Label USER = DynamicLabel.label("User"); public static final Label TOPIC = DynamicLabel.label("Topic"); private final GraphDatabaseService db; private final ExecutionEngineWrapper executionEngineWrapper; public SocialNetworkQueries( GraphDatabaseService db, ExecutionEngineWrapper executionEngineWrapper ) { this.db = db; this.executionEngineWrapper = executionEngineWrapper; } public ExecutionResult sharedInterestsSameCompany( String userName ) { String query = "MATCH (subject:User {name:{name}})\n" + "MATCH (subject)-[:WORKS_FOR]->(company)<-[:WORKS_FOR]-(person),\n" + " (subject)-[:INTERESTED_IN]->(interest)<-[:INTERESTED_IN]-(person)\n" + "RETURN person.name AS name,\n" + " count(interest) AS score,\n" + " collect(interest.name) AS interests\n" + "ORDER BY score DESC"; Map params = new HashMap(); params.put( "name", userName ); return executionEngineWrapper.execute( query, params ); } public ExecutionResult sharedInterestsAllCompanies( String userName, int limit ) { String query = "MATCH (subject:User {name:{name}})\n" + "MATCH (subject)-[:INTERESTED_IN]->(interest)<-[:INTERESTED_IN]-(person),\n" + " (person)-[:WORKS_FOR]->(company)\n" + "RETURN person.name AS name,\n" + " company.name AS company,\n" + " count(interest) AS score,\n" + " collect(interest.name) AS interests\n" + "ORDER BY score DESC"; Map params = new HashMap(); params.put( "name", userName ); params.put( "resultLimit", limit ); return executionEngineWrapper.execute( query, params ); } public ExecutionResult sharedInterestsAlsoInterestedInTopic( String userName, String topicLabel ) { String query = "MATCH (person:User {name:{name}})\n" + "MATCH (person)-[:INTERESTED_IN]->()<-[:INTERESTED_IN]-(colleague)-[:INTERESTED_IN]->(topic)\n" + "WHERE topic.name={topic}\n" + "WITH colleague\n" + "MATCH (colleague)-[:INTERESTED_IN]->(allTopics)\n" + "RETURN colleague.name AS name, collect(distinct(allTopics.name)) AS topics"; Map params = new HashMap(); params.put( "name", userName ); params.put( "topicQuery", "name:" + topicLabel ); params.put( "topic", topicLabel ); return executionEngineWrapper.execute( query, params ); } public ExecutionResult friendOfAFriendWithInterest( String userName, String topicLabel, int limit ) { String query = "MATCH (subject:User {name:{name}})\n" + "MATCH p=(subject)-[:WORKED_ON]->()-[:WORKED_ON*0..2]-()\n" + " <-[:WORKED_ON]-(person)-[:INTERESTED_IN]->(interest)\n" + "WHERE person<>subject AND interest.name={topic}\n" + "WITH DISTINCT person.name AS name,\n" + " min(length(p)) as pathLength\n" + "ORDER BY pathLength ASC\n" + "LIMIT {resultLimit}\n" + "RETURN name, pathLength"; Map params = new HashMap(); params.put( "name", userName ); params.put( "topicQuery", "name:" + topicLabel ); params.put( "topic", topicLabel ); params.put( "resultLimit", limit ); return executionEngineWrapper.execute( query, params ); } public Collection friendOfAFriendWithInterestTraversalFramework( String userName, final String topicLabel, int limit ) { Node user = IteratorUtil.single(db.findNodesByLabelAndProperty(USER, "name", userName)); final Node topic = IteratorUtil.single(db.findNodesByLabelAndProperty(TOPIC, "name", topicLabel)); final RelationshipType interested_in = withName( "INTERESTED_IN" ); final RelationshipType worked_on = withName( "WORKED_ON" ); TraversalDescription traversalDescription = db.traversalDescription() .breadthFirst() .uniqueness( Uniqueness.NODE_GLOBAL ) .relationships( worked_on ) .evaluator( new Evaluator() { @Override public Evaluation evaluate( Path path ) { if ( path.length() == 0 ) { return Evaluation.EXCLUDE_AND_CONTINUE; } Node currentNode = path.endNode(); if ( path.length() % 2 == 0 ) { for ( Relationship rel : currentNode.getRelationships( interested_in, Direction.OUTGOING ) ) { if ( rel.getEndNode().equals( topic ) ) { if ( path.length() % 4 == 0 ) { return Evaluation.INCLUDE_AND_PRUNE; } else { return Evaluation.INCLUDE_AND_CONTINUE; } } } } if ( path.length() % 4 == 0 ) { return Evaluation.EXCLUDE_AND_PRUNE; } else { return Evaluation.EXCLUDE_AND_CONTINUE; } } } ); Iterable nodes = traversalDescription.traverse( user ).nodes(); Iterator iterator = nodes.iterator(); int nodeCount = 0; List results = new ArrayList(); while ( iterator.hasNext() && nodeCount++ < limit ) { results.add( iterator.next() ); } return results; } public ExecutionResult friendOfAFriendWithMultipleInterest( String userName, int limit, String... interestLabels ) { String query = "MATCH (subject:User {name:{name}})\n" + "MATCH p=(subject)-[:WORKED_ON]->()-[:WORKED_ON*0..2]-()\n" + " <-[:WORKED_ON]-(person)-[:INTERESTED_IN]->(interest)\n" + "WHERE person<>subject AND interest.name IN {interests}\n" + "WITH person, interest, min(length(p)) as pathLength\n" + "ORDER BY interest.name\n"+ "RETURN person.name AS name,\n" + " count(interest) AS score,\n" + " collect(interest.name) AS interests,\n" + " ((pathLength - 1)/2) AS distance\n" + "ORDER BY score DESC\n" + "LIMIT {resultLimit}"; Map params = new HashMap(); params.put( "name", userName ); params.put( "interests", interestLabels ); params.put( "resultLimit", limit ); return executionEngineWrapper.execute( query, params ); } public ExecutionResult friendWorkedWithFriendWithInterests( String userName, int limit, String... interestLabels ) { String query = "MATCH (subject:User {name:{name}})\n" + "MATCH p=(subject)-[:WORKED_WITH*0..1]-()-[:WORKED_WITH]-(person)\n" + " -[:INTERESTED_IN]->(interest)\n" + "WHERE person<>subject AND interest.name IN {interests}\n" + "WITH person, interest, min(length(p)) as pathLength\n" + "RETURN person.name AS name,\n" + " count(interest) AS score,\n" + " collect(interest.name) AS interests,\n" + " (pathLength - 1) AS distance\n" + "ORDER BY score DESC\n" + "LIMIT {resultLimit}"; Map params = new HashMap(); params.put( "name", userName ); params.put( "userQuery", "name:" + userName ); params.put( "interests", interestLabels ); params.put( "resultLimit", limit ); StringBuilder builder = new StringBuilder(); builder.append( "[" ); for ( int i = 0; i < interestLabels.length; i++ ) { builder.append( "'" ); builder.append( interestLabels[i] ); builder.append( "'" ); if ( i < interestLabels.length - 1 ) { builder.append( "," ); } } builder.append( "]" ); params.put( "topicQuery", builder.toString() ); return executionEngineWrapper.execute( query, params ); } // todo no result? public ExecutionResult createWorkedWithRelationships( String userName ) { String query = "MATCH (subject:User {name:{name}})\n" + "MATCH (subject)-[:WORKED_ON]->()<-[:WORKED_ON]-(person)\n" + "WHERE NOT((subject)-[:WORKED_WITH]-(person))\n" + "WITH DISTINCT subject, person\n" + "CREATE UNIQUE (subject)-[:WORKED_WITH]-(person)\n" + "RETURN subject.name AS startName, person.name AS endName"; Map params = new HashMap(); params.put( "name", userName ); return executionEngineWrapper.execute( query, params ); } public ExecutionResult getAllUsers() { String query = "MATCH (subject:User)\n" + "RETURN subject.name AS name"; return executionEngineWrapper.execute( query, new HashMap() ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/DbUtils.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.util.HashMap; import java.util.Map; import org.neo4j.graphdatabases.queries.testing.TestOutputWriter; import org.neo4j.graphdb.*; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.tooling.GlobalGraphOperations; import static org.neo4j.helpers.collection.IteratorUtil.count; public class DbUtils { public static GraphDatabaseService existingDB( String dir ) { GraphDatabaseService db = new GraphDatabaseFactory() .newEmbeddedDatabaseBuilder( dir ) .setConfig( DbUtils.dbConfig() ) .newGraphDatabase(); if ( countRelTypes(db) == 0 ) { throw new IllegalStateException( "Performance dataset does not exist. See the Readme for instructions on " + "generating a sample dataset." ); } return db; } public static int countRelTypes(GraphDatabaseService db) { try ( Transaction tx = db.beginTx() ) { int count = count(GlobalGraphOperations.at(db).getAllRelationshipTypes()); tx.success(); return count; } } public static Map dbConfig() { Map params = new HashMap(); params.put( "dump_configuration", "true" ); params.put( "cache_type", "gcr" ); params.put( "allow_store_upgrade", "true" ); params.put( "online_backup_enabled", "false" ); return params; } public static void warmCache( GraphDatabaseService db, TestOutputWriter writer ) { writer.writeln( "BEGIN: Warming cache" ); try ( Transaction tx = db.beginTx() ) { for ( Relationship r : GlobalGraphOperations.at( db ).getAllRelationships() ) { r.getPropertyKeys(); r.getStartNode(); } for ( Node n : GlobalGraphOperations.at( db ).getAllNodes() ) { n.getPropertyKeys(); for ( Relationship relationship : n.getRelationships() ) { relationship.getStartNode(); } } tx.success(); } writer.writeln( "\nEND : Warming cache\n" ); } public static int numberOfItemsWithLabel(GraphDatabaseService db, String labelName) { try ( Transaction tx = db.beginTx() ) { GlobalGraphOperations ops = GlobalGraphOperations.at(db); int count = count(ops.getAllNodesWithLabel(DynamicLabel.label(labelName))); tx.success(); return count; } } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/ExecutionEngineWrapper.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionResult; public interface ExecutionEngineWrapper { ExecutionResult execute(String query, Map params); ExecutionResult execute(String query, Map params, int index); } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/ExecutionResultIterator.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import org.neo4j.cypher.javacompat.ExecutionResult; public class ExecutionResultIterator { public static ExecutionResultIterator newQueryIterators( List queries, ExecutionEngineWrapper executionEngine, Map params ) { Queue queue = new LinkedList( queries ); String query = queue.poll(); ExecutionResult executionResult = executionEngine.execute( query, params, 1 ); return new ExecutionResultIterator( executionResult, queue, executionEngine, params, 2 ); } private final ExecutionResult currentResult; private final Queue queries; private final ExecutionEngineWrapper executionEngine; private final Map params; private final int queryIndex; private ExecutionResultIterator( ExecutionResult currentResult, Queue queries, ExecutionEngineWrapper executionEngine, Map params, int queryIndex ) { this.currentResult = currentResult; this.queries = queries; this.executionEngine = executionEngine; this.params = params; this.queryIndex = queryIndex; } public Iterator> iterator() { return currentResult.iterator(); } public boolean hasNextIterator() { return !queries.isEmpty(); } public ExecutionResultIterator getNextIterator() { String query = queries.poll(); ExecutionResult executionResult = executionEngine.execute( query, params, queryIndex ); return new ExecutionResultIterator( executionResult, queries, executionEngine, params, queryIndex + 1 ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/ExecutionResultsIterator.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; public class ExecutionResultsIterator implements Iterator> { private ExecutionResultIterator executionResultIterator; public ExecutionResultsIterator( List queries, ExecutionEngineWrapper executionEngine, Map params ) { executionResultIterator = ExecutionResultIterator.newQueryIterators( queries, executionEngine, params ); } @Override public boolean hasNext() { if ( executionResultIterator.iterator().hasNext() ) { return true; } if ( !executionResultIterator.hasNextIterator() ) { return false; } executionResultIterator = executionResultIterator.getNextIterator(); return hasNext(); } @Override public Map next() { if ( hasNext() ) { return executionResultIterator.iterator().next(); } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/IndexNodeByOtherNodeIndexer.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import org.neo4j.graphdb.*; import org.neo4j.graphdb.index.Index; public class IndexNodeByOtherNodeIndexer { private final GraphTraversal findIndexableNodes; private final GraphTraversal findOtherNodesForIndexableNode; private final String indexName; private final String indexKey; private final String nodeToIndexByPropertyName; public IndexNodeByOtherNodeIndexer( GraphTraversal findIndexableNodes, GraphTraversal findOtherNodesForIndexableNode, String indexName, String indexKey, String nodeToIndexByPropertyName ) { this.findIndexableNodes = findIndexableNodes; this.findOtherNodesForIndexableNode = findOtherNodesForIndexableNode; this.indexName = indexName; this.indexKey = indexKey; this.nodeToIndexByPropertyName = nodeToIndexByPropertyName; } public void execute( GraphDatabaseService db, Node startNode, int batchSize ) { Transaction tx = db.beginTx(); Label label = DynamicLabel.label(indexName); Iterable indexableNodes = findIndexableNodes.execute( startNode ); int currentBatchSize = 0; try { for ( Node indexableNode : indexableNodes ) { Iterable nodesToIndexBy = findOtherNodesForIndexableNode.execute( indexableNode ); for ( Node node : nodesToIndexBy ) { indexableNode.addLabel(label); indexableNode.setProperty(indexKey, node.getProperty( nodeToIndexByPropertyName ) ); if ( currentBatchSize++ > batchSize ) { tx.success(); tx.close(); tx = db.beginTx(); currentBatchSize = 0; } } } tx.success(); } finally { tx.close(); } } public interface GraphTraversal { Iterable execute( Node startNode ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/QueryUnionExecutionEngine.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import static java.util.Arrays.asList; import java.util.HashMap; import java.util.Map; public class QueryUnionExecutionEngine { private final ExecutionEngineWrapper executionEngine; public QueryUnionExecutionEngine( ExecutionEngineWrapper executionEngine ) { this.executionEngine = executionEngine; } public QueryUnionExecutionResult execute( final Map params, final String... queries ) { if ( queries.length == 0 ) { throw new IllegalArgumentException( "Must supply one or more queries." ); } return new QueryUnionExecutionResult( asList( queries ), executionEngine, params ); } public Iterable> execute( final String... queries ) { return execute( new HashMap(), queries ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/helpers/QueryUnionExecutionResult.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.util.Iterator; import java.util.List; import java.util.Map; public class QueryUnionExecutionResult implements Iterable> { private final List queries; private final ExecutionEngineWrapper executionEngine; private final Map params; public QueryUnionExecutionResult( List queries, ExecutionEngineWrapper executionEngine, Map params ) { this.queries = queries; this.executionEngine = executionEngine; this.params = params; } @Override public Iterator> iterator() { return new ExecutionResultsIterator( queries, executionEngine, params ); } @Override public String toString() { StringBuilder builder = new StringBuilder( ); for ( String query : queries ) { builder.append( executionEngine.execute( query, params ).dumpToString()); } return builder.toString(); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/server/SimpleSocialNetworkExtension.java ================================================ package org.neo4j.graphdatabases.queries.server; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.SimpleSocialNetworkQueries; import org.neo4j.graphdb.GraphDatabaseService; @Path("/distance") public class SimpleSocialNetworkExtension { private final SimpleSocialNetworkQueries queries; public SimpleSocialNetworkExtension( @Context GraphDatabaseService db ) { this.queries = new SimpleSocialNetworkQueries( db ); } @GET @Path("/{name1}/{name2}") public String getDistance ( @PathParam("name1") String name1, @PathParam("name2") String name2 ) { ExecutionResult result = queries.pathBetweenTwoFriends( name1, name2 ); return String.valueOf( result.columnAs( "depth" ).next() ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/testing/IndexParam.java ================================================ package org.neo4j.graphdatabases.queries.testing; public class IndexParam { public static IndexParam indexParam( String nodeLabel, String propertyName ) { return new IndexParam( nodeLabel, propertyName); } private final String nodeLabel; private final String propertyName; private IndexParam(String nodeLabel, String propertyName) { this.nodeLabel = nodeLabel; this.propertyName = propertyName; } public String nodeLabel() { return nodeLabel; } public String propertyName() { return propertyName; } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/testing/IndexParams.java ================================================ package org.neo4j.graphdatabases.queries.testing; import static java.lang.String.format; import static java.util.Arrays.asList; import java.util.List; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.graphdb.DynamicLabel; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.tooling.GlobalGraphOperations; public class IndexParams { private final List indexParams; public IndexParams( IndexParam... params ) { indexParams = asList( params ); } public void index(GraphDatabaseService db) { try (Transaction tx = db.beginTx()) { for (IndexParam indexParam : indexParams) { db.schema().indexFor(DynamicLabel.label(indexParam.nodeLabel())).on(indexParam.propertyName()).create(); // db.schema().constraintFor(DynamicLabel.label(indexParam.nodeLabel())).assertPropertyIsUnique(indexParam.propertyName()).create(); // engine.execute(format("CREATE INDEX ON :%s(%s)", indexParam.nodeLabel(), indexParam.propertyName())); } tx.success(); } } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/testing/TestOutputWriter.java ================================================ /* * Copyright (C) 2012 Neo Technology * All rights reserved */ package org.neo4j.graphdatabases.queries.testing; public interface TestOutputWriter { void begin(); void write(String value); void writeln( String value ); void flush(); void end(); } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/traversals/FriendOfAFriendDepth4.java ================================================ package org.neo4j.graphdatabases.queries.traversals; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import static org.neo4j.graphdb.traversal.Uniqueness.*; import org.neo4j.graphdb.*; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.helpers.collection.IteratorUtil; import org.neo4j.kernel.Traversal; import org.neo4j.kernel.Uniqueness; public class FriendOfAFriendDepth4 { public static final Label USER = DynamicLabel.label("User"); private final TraversalDescription traversalDescription; private final GraphDatabaseService db; public FriendOfAFriendDepth4( GraphDatabaseService db ) { this.db = db; traversalDescription = traversalDescription(db); } private TraversalDescription traversalDescription(GraphDatabaseService db) { return db.traversalDescription() .breadthFirst() .uniqueness( NODE_GLOBAL ) .relationships( withName( "FRIEND" ) ) .evaluator( new Evaluator() { @Override public Evaluation evaluate( Path path ) { if ( path.length() == 4 ) { return Evaluation.INCLUDE_AND_PRUNE; } return Evaluation.EXCLUDE_AND_CONTINUE; } } ); } public Iterable getFriends( String name ) { ResourceIterable users = db.findNodesByLabelAndProperty(USER, "name", name); Node startNode = IteratorUtil.single(users); return traversalDescription.traverse(startNode).nodes(); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/traversals/IndexResources.java ================================================ package org.neo4j.graphdatabases.queries.traversals; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import org.neo4j.graphdatabases.queries.helpers.IndexNodeByOtherNodeIndexer; import org.neo4j.graphdb.*; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.kernel.Traversal; import org.neo4j.tooling.GlobalGraphOperations; public class IndexResources { private final GraphDatabaseService db; private final TraversalDescription traversalDescription = Traversal.description() .breadthFirst() .relationships( withName( "WORKS_FOR" ), Direction.INCOMING ) .relationships( withName( "HAS_ACCOUNT" ), Direction.OUTGOING ) .evaluator( new Evaluator() { @Override public Evaluation evaluate( Path path ) { if ( path.endNode().equals( path.startNode() ) ) { return Evaluation.EXCLUDE_AND_CONTINUE; } return Evaluation.INCLUDE_AND_CONTINUE; } } ); public IndexResources( GraphDatabaseService db ) { this.db = db; } public void execute() { IndexNodeByOtherNodeIndexer.GraphTraversal traversal1 = new IndexNodeByOtherNodeIndexer.GraphTraversal() { @Override public Iterable execute( Node startNode ) { return GlobalGraphOperations.at(db).getAllNodesWithLabel(DynamicLabel.label("company")); } }; IndexNodeByOtherNodeIndexer.GraphTraversal traversal2 = new IndexNodeByOtherNodeIndexer.GraphTraversal() { @Override public Iterable execute( Node startNode ) { return traversalDescription.traverse( startNode ).nodes(); } }; IndexNodeByOtherNodeIndexer indexer = new IndexNodeByOtherNodeIndexer( traversal1, traversal2, "company", "resourceName", "name" ); indexer.execute( db, null, 1000 ); } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/traversals/ParcelRouteCalculator.java ================================================ package org.neo4j.graphdatabases.queries.traversals; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.joda.time.Interval; import org.neo4j.graphalgo.CommonEvaluators; import org.neo4j.graphalgo.CostEvaluator; import org.neo4j.graphalgo.GraphAlgoFactory; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphalgo.WeightedPath; import org.neo4j.graphdb.*; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.BranchState; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.InitialBranchState; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.helpers.collection.IteratorUtil; import org.neo4j.kernel.Traversal; public class ParcelRouteCalculator { private static final PathExpander DELIVERY_ROUTE_EXPANDER = new IntervalPathExpander( withName( "DELIVERY_ROUTE" ), Direction.INCOMING ); private static final PathExpander CONNECTED_TO_EXPANDER = new IntervalPathExpander( withName( "CONNECTED_TO" ), Direction.BOTH ); private static final TraversalDescription DELIVERY_BASE_FINDER = Traversal.description() .depthFirst() .evaluator( new Evaluator() { private final RelationshipType DELIVERY_ROUTE = withName( "DELIVERY_ROUTE"); @Override public Evaluation evaluate( Path path ) { if ( isDeliveryBase( path ) ) { return Evaluation.INCLUDE_AND_PRUNE; } return Evaluation.EXCLUDE_AND_CONTINUE; } private boolean isDeliveryBase( Path path ) { return !path.endNode().hasRelationship( DELIVERY_ROUTE, Direction.INCOMING ); } } ); private static final CostEvaluator COST_EVALUATOR = CommonEvaluators.doubleCostEvaluator( "cost" ); public static final Label LOCATION = DynamicLabel.label("Location"); private GraphDatabaseService db; public ParcelRouteCalculator( GraphDatabaseService db ) { this.db = db; } public Iterable calculateRoute( String start, String end, Interval interval ) { try ( Transaction tx = db.beginTx() ) { TraversalDescription deliveryBaseFinder = createDeliveryBaseFinder( interval ); Path upLeg = findRouteToDeliveryBase( start, deliveryBaseFinder ); Path downLeg = findRouteToDeliveryBase( end, deliveryBaseFinder ); Path topRoute = findRouteBetweenDeliveryBases( upLeg.endNode(), downLeg.endNode(), interval ); Set routes = combineRoutes(upLeg, downLeg, topRoute); tx.success(); return routes; } } private TraversalDescription createDeliveryBaseFinder( Interval interval ) { return DELIVERY_BASE_FINDER.expand( DELIVERY_ROUTE_EXPANDER, new InitialBranchState.State( interval, interval ) ); } private Set combineRoutes( Path upLeg, Path downLeg, Path topRoute ) { LinkedHashSet results = new LinkedHashSet(); results.addAll( IteratorUtil.asCollection( upLeg.nodes() )); results.addAll( IteratorUtil.asCollection( topRoute.nodes() )); results.addAll( IteratorUtil.asCollection( downLeg.reverseNodes() )); return results; } private Path findRouteBetweenDeliveryBases( Node deliveryBase1, Node deliveryBase2, Interval interval ) { PathFinder routeBetweenDeliveryBasesFinder = GraphAlgoFactory.dijkstra( CONNECTED_TO_EXPANDER, new InitialBranchState.State( interval, interval ), COST_EVALUATOR ); return routeBetweenDeliveryBasesFinder.findSinglePath( deliveryBase1, deliveryBase2 ); } private Path findRouteToDeliveryBase( String startPosition, TraversalDescription deliveryBaseFinder ) { Node startNode = IteratorUtil.single(db.findNodesByLabelAndProperty(LOCATION, "name", startPosition)); return deliveryBaseFinder.traverse( startNode ).iterator().next(); } private static class IntervalPathExpander implements PathExpander { private final RelationshipType relationshipType; private final Direction direction; private IntervalPathExpander( RelationshipType relationshipType, Direction direction ) { this.relationshipType = relationshipType; this.direction = direction; } @Override public Iterable expand( Path path, BranchState deliveryInterval ) { List results = new ArrayList(); for ( Relationship r : path.endNode().getRelationships( relationshipType, direction ) ) { Interval relationshipInterval = new Interval( (Long) r.getProperty( "start_date" ), (Long) r.getProperty( "end_date" ) ); if ( relationshipInterval.contains( deliveryInterval.getState() ) ) { results.add( r ); } } return results; } @Override public PathExpander reverse() { return null; } } } ================================================ FILE: queries/src/main/java/org/neo4j/graphdatabases/queries/traversals/SimpleParcelRouteCalculator.java ================================================ package org.neo4j.graphdatabases.queries.traversals; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import java.util.ArrayList; import java.util.List; import org.joda.time.Interval; import org.neo4j.graphalgo.CommonEvaluators; import org.neo4j.graphalgo.CostEvaluator; import org.neo4j.graphalgo.GraphAlgoFactory; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphalgo.WeightedPath; import org.neo4j.graphdb.*; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.BranchState; import org.neo4j.graphdb.traversal.InitialBranchState; import org.neo4j.helpers.collection.IteratorUtil; public class SimpleParcelRouteCalculator { private static final CostEvaluator COST_EVALUATOR = CommonEvaluators.doubleCostEvaluator( "cost" ); private static final PathExpander PATH_EXPANDER = new ValidPathExpander(); public static final Label LOCATION = DynamicLabel.label("Location"); private GraphDatabaseService db; public SimpleParcelRouteCalculator( GraphDatabaseService db ) { this.db = db; } public Iterable calculateRoute( String start, String end, Interval interval ) { Node startNode = findByLocation ( start ); Node endNode = findByLocation( end ); PathFinder routeBetweenDeliveryBasesFinder = GraphAlgoFactory.dijkstra( PATH_EXPANDER, new InitialBranchState.State( interval, interval ), COST_EVALUATOR ); return IteratorUtil.asCollection( routeBetweenDeliveryBasesFinder.findSinglePath( startNode, endNode ).nodes() ); } private Node findByLocation(String location) { return IteratorUtil.single( db.findNodesByLabelAndProperty( LOCATION, "name", location ) ); } private static class ValidPathExpander implements PathExpander { @Override public Iterable expand( Path path, BranchState deliveryInterval ) { List results = new ArrayList(); for ( Relationship r : path.endNode().getRelationships( Direction.BOTH, withName( "CONNECTED_TO" ), withName( "DELIVERY_ROUTE" ) ) ) { Interval relationshipInterval = new Interval( (Long) r.getProperty( "start_date" ), (Long) r.getProperty( "end_date" ) ); if ( relationshipInterval.contains( deliveryInterval.getState() ) ) { results.add( r ); } } return results; } @Override public PathExpander reverse() { return null; } } } ================================================ FILE: queries/src/main/resources/META_INF/services/javax.script.ScriptEngineFactory ================================================ com.tinkerpop.gremlin.jsr223.GremlinGroovyScriptEngineFactory ================================================ FILE: queries/src/main/resources/META_INF/services/org.neo4j.server.plugins.ServerPlugin ================================================ org.neo4j.graphdatabases.queries.server.SimpleSocialNetworkExtension ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/AccessControlQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import static org.neo4j.graphdatabases.queries.testing.IndexParam.indexParam; import java.util.*; import org.junit.*; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.PrintingExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.traversals.IndexResources; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.helpers.collection.IteratorUtil; public class AccessControlQueriesTest { @Rule public TestName testName = new TestName(); private GraphDatabaseService db; private AccessControlQueries queries; @Before public void init() { db = createDatabase(); queries = new AccessControlQueries( new PrintingExecutionEngineWrapper( db, "access-control-revised", testName ) ); } @After public void shutdown() { db.shutdown(); } @Test public void allowedWithInheritTrueGivesAccessToSubcompaniesAndAccounts() throws Exception { // Ben is member of two groups, both of which have ALLOWED_INHERIT. // He should, therefore, see all results below the companies to which // these permissions are attached. // when ExecutionResult results = queries.findAccessibleResources( "Ben" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-1", iterator.next().get( "account" ) ); assertEquals( "Account-2", iterator.next().get( "account" ) ); assertEquals( "Account-3", iterator.next().get( "account" ) ); assertEquals( "Account-6", iterator.next().get( "account" ) ); assertEquals( "Account-4", iterator.next().get( "account" ) ); assertEquals( "Account-5", iterator.next().get( "account" ) ); assertEquals( "Account-7", iterator.next().get( "account" ) ); assertFalse( iterator.hasNext() ); } @Test public void deniedExcludesCompanyFromPermissionsTree() throws Exception { // Sarah is a member of Group-2, which has DENIED on Skunkworx. // Therefore Account-7 should not appear in the results. // Group-2 also has ALLOWED_DO_NOT_INHERIT on Acme, so Spinoff accounts, // a child of Acme, should not be included in results // when ExecutionResult results = queries.findAccessibleResources( "Sarah" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-4", iterator.next().get( "account" ) ); assertEquals( "Account-5", iterator.next().get( "account" ) ); assertEquals( "Account-1", iterator.next().get( "account" ) ); assertEquals( "Account-2", iterator.next().get( "account" ) ); assertEquals( "Account-3", iterator.next().get( "account" ) ); assertFalse( iterator.hasNext() ); } @Test public void deniedExcludesCompanyFromPermissionsTree2() throws Exception { // Liz is a member of Group-4, which has ALLOWED_INHERIT on BigCo. // However, she is also a member of Group-5, which has DENIED on AcquiredLtd. // This DENIED also debars Liz from Subsidiary and DevShop. // Liz's membership of Group-6, with its ALLOWED_DO_NOT_INHERIT on One-Man Shop // gives Liz access to One-Man Shop // when ExecutionResult results = queries.findAccessibleResources( "Liz" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-8", iterator.next().get( "account" ) ); assertEquals( "Account-10", iterator.next().get( "account" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldGetAccessibleCompaniesForAdmin() throws Exception { // Sarah is a member of groups that have ALLOWED_INHERIT, ALLOWED_DO_NOT_INHERIT and DENIED // This tests this combination (for a 2-layer organizational structure). // given ExecutionResult results = queries.findAccessibleCompanies( "Sarah" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Startup", iterator.next().get( "company" ) ); assertEquals( "Acme", iterator.next().get( "company" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldGetAccessibleCompaniesForAdminWhereNoAllowedInheritFalse() throws Exception { // Ben is a member of groups that have ALLOWED_INHERIT // given ExecutionResult results = queries.findAccessibleCompanies( "Ben" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Acme", iterator.next().get( "company" ) ); assertEquals( "Spinoff", iterator.next().get( "company" ) ); assertEquals( "Startup", iterator.next().get( "company" ) ); assertEquals( "Skunkworkz", iterator.next().get( "company" ) ); assertFalse( iterator.hasNext() ); } @Test public void moreComplexShouldGetAccessibleCompaniesForAdmin() throws Exception { // Liz has ALLOWED_INHERIT at the top of a 3-layer org structure, // DENIED at the next level, and ALLOWED_DO_NOT_INHERIT at the bottom layer. // given ExecutionResult results = queries.findAccessibleCompanies( "Liz" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "BigCompany", iterator.next().get( "company" ) ); assertEquals( "One-ManShop", iterator.next().get( "company" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAccessibleAccountsForAdminAndCompany() throws Exception { // given ExecutionResult results = queries.findAccessibleAccountsForCompany( "Sarah", "Startup" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-4", iterator.next().get( "account" ) ); assertEquals( "Account-5", iterator.next().get( "account" ) ); assertFalse( iterator.hasNext() ); } @Test public void moreComplexShouldFindAccessibleAccountsForAdminAndCompany() throws Exception { // given ExecutionResult results = queries.findAccessibleAccountsForCompany( "Liz", "BigCompany" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-8", iterator.next().get( "account" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAccessibleAccountsForAdminAndCompanyWhenNoAllowedWithInheritFalse() throws Exception { // given ExecutionResult results = queries.findAccessibleAccountsForCompany( "Ben", "Startup" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-4", iterator.next().get( "account" ) ); assertEquals( "Account-5", iterator.next().get( "account" ) ); assertEquals( "Account-7", iterator.next().get( "account" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForAccountResourceWhereAllowedInheritAndAllowedNotInherit() throws Exception { // Account-10 is associated with One-ManShop // One-ManShop-CHILD_OF->Subsidiary-CHILD_OF->AcquiredLtd->CHILD_OF->BigCompany // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to OneManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She has access to Account-10 by virtue of Group-6 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He has access to Account-10 by virtue of Group-7 // given ExecutionResult results = queries.findAdminForResource( "Account-10" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Phil", iterator.next().get( "admin" ) ); assertEquals( "Liz", iterator.next().get( "admin" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForEmployeeResourceWhereAllowedInheritAndDenied() throws Exception { // Kate works for Skunkworkz // Skunkworkz-CHILD_OF->Startup // Sarah is a member of: // Group-2, which has DENIED on Skunkworkz // Group-3, which has ALLOWED_INHERIT on Startup // She is denied access to Kate by virtue of Group-2 // Ben is a member of: // Group-3, which has ALLOWED_INHERIT on Startup // Group-1, which has no access to Kate's company chain // He has access by to Kate virtue of Group-3 // given ExecutionResult results = queries.findAdminForResource( "Kate" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Ben", iterator.next().get( "admin" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithAllowedInherit() throws Exception { // BigCompany<-CHILD_OF-AcquiredLtd<-CHILD_OF-Subsidiary<-CHILD_OF-One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She has access to BigCompany by virtue of Group-4 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He does not have access to BigCompany // given ExecutionResult results = queries.findAdminForCompany( "BigCompany" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Liz", iterator.next().get( "admin" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithDenied() throws Exception { // AcquiredLtd<-CHILD_OF-Subsidiary<-CHILD_OF-One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She is denied access to AcquiredLtd by virtue of Group-5 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He does not have access to AcquiredLtd // given ExecutionResult results = queries.findAdminForCompany( "AcquiredLtd" ); // then Iterator> iterator = results.iterator(); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithAllowedInheritAndAllowedDoNotInheritTooLowInTree() throws Exception { //Subsidiary<-CHILD_OF-One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She is denied access to Subsidiary by virtue of Group-5 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He has access to Subsidiary by virtue of Group-7 // given ExecutionResult results = queries.findAdminForCompany( "Subsidiary" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Phil", iterator.next().get( "admin" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithAllowedInheritAndAllowedAllowedDoNotInherit() throws Exception { //One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She has access to One-ManShop by virtue of Group-6 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He has access to One-ManShop by virtue of Group-7 // given ExecutionResult results = queries.findAdminForCompany( "One-ManShop" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Phil", iterator.next().get( "admin" ) ); assertEquals( "Liz", iterator.next().get( "admin" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldDetermineWhetherAdminHasAccessToResource() throws Exception { Map> testData = new LinkedHashMap<>(); testData.put( "Alistair", asList( 1L, 0L ) ); testData.put( "Account-8", asList( 1L, 0L ) ); testData.put( "Eve", asList( 0L ) ); testData.put( "Account-9", asList( 0L ) ); testData.put( "Mary", asList( 0L ) ); testData.put( "Account-12", asList( 0L ) ); testData.put( "Gary", asList( 0L ) ); testData.put( "Account-11", asList( 0L ) ); testData.put( "Bill", asList( 0L, 1L ) ); testData.put( "Account-10", asList( 0L, 1L ) ); for ( String resourceName : testData.keySet() ) { List expectedResults = testData.get( resourceName ); Iterator expectedResultsIterator = expectedResults.iterator(); // given ExecutionResult results = queries.hasAccessToResource( "Liz", resourceName ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( expectedResultsIterator.next(), iterator.next().get( "accessCount" ) ); if ( expectedResultsIterator.hasNext() ) { assertEquals( expectedResultsIterator.next(), iterator.next().get( "accessCount" ) ); } assertFalse( iterator.hasNext() ); } } @Test @Ignore("does conceptually not work") public void shouldDetermineWhetherAdminHasAccessToIndexedResource() throws Exception { Map testData = new LinkedHashMap<>(); testData.put( "Alistair", true ); testData.put( "Account-8", true ); testData.put( "Eve", false ); testData.put( "Account-9", false ); testData.put( "Mary", false ); testData.put( "Account-12", false ); testData.put( "Gary", false ); testData.put( "Account-11", false ); testData.put( "Bill", true ); testData.put( "Account-10", true ); for ( Map.Entry entry : testData.entrySet() ) { // given ExecutionResult results = queries.hasAccessToIndexedResource( "Liz", entry.getKey() ); // System.out.println(results.dumpToString()); // then assertEquals( entry.getKey(), entry.getValue(), isAuthorized( results ) ); } } private boolean isAuthorized( ExecutionResult result ) { Iterator accessCountIterator = result.columnAs( "accessCount" ); boolean isAuthorized = false; while ( accessCountIterator.hasNext() ) { isAuthorized |= accessCountIterator.next() > 0L; } return isAuthorized; } private static GraphDatabaseService createDatabase() { String cypher = "CREATE\n" + "(ben:Administrator {name:'Ben'}),\n" + "(sarah:Administrator {name:'Sarah'}),\n" + "(liz:Administrator {name:'Liz'}),\n" + "(phil:Administrator {name:'Phil'}),\n" + "(arnold:Employee:Resource {name:'Arnold'}),\n" + "(charlie:Employee:Resource {name:'Charlie'}),\n" + "(gordon:Employee:Resource {name:'Gordon'}),\n" + "(lucy:Employee:Resource {name:'Lucy'}),\n" + "(emily:Employee:Resource {name:'Emily'}),\n" + "(kate:Employee:Resource {name:'Kate'}),\n" + "(alistair:Employee:Resource {name:'Alistair'}),\n" + "(eve:Employee:Resource {name:'Eve'}),\n" + "(bill:Employee:Resource {name:'Bill'}),\n" + "(gary:Employee:Resource {name:'Gary'}),\n" + "(mary:Employee:Resource {name:'Mary'}),\n" + "(group1:Group {name:'Group-1'}),\n" + "(group2:Group {name:'Group-2'}),\n" + "(group3:Group {name:'Group-3'}),\n" + "(group4:Group {name:'Group-4'}),\n" + "(group5:Group {name:'Group-5'}),\n" + "(group6:Group {name:'Group-6'}),\n" + "(group7:Group {name:'Group-7'}),\n" + "(acme:Company {name:'Acme'}),\n" + "(spinoff:Company {name:'Spinoff'}),\n" + "(startup:Company {name:'Startup'}),\n" + "(skunkworkz:Company {name:'Skunkworkz'}),\n" + "(bigco:Company {name:'BigCompany'}),\n" + "(acquired:Company {name:'AcquiredLtd'}),\n" + "(subsidiary:Company {name:'Subsidiary'}),\n" + "(devshop:Company {name:'DevShop'}),\n" + "(onemanshop:Company {name:'One-ManShop'}),\n" + "(account1:Account:Resource {name:'Account-1'}),\n" + "(account2:Account:Resource {name:'Account-2'}),\n" + "(account3:Account:Resource {name:'Account-3'}),\n" + "(account4:Account:Resource {name:'Account-4'}),\n" + "(account5:Account:Resource {name:'Account-5'}),\n" + "(account6:Account:Resource {name:'Account-6'}),\n" + "(account7:Account:Resource {name:'Account-7'}),\n" + "(account8:Account:Resource {name:'Account-8'}),\n" + "(account9:Account:Resource {name:'Account-9'}),\n" + "(account10:Account:Resource {name:'Account-10'}),\n" + "(account11:Account:Resource {name:'Account-11'}),\n" + "(account12:Account:Resource {name:'Account-12'}),\n" + "ben-[:MEMBER_OF]->group1,\n" + "ben-[:MEMBER_OF]->group3,\n" + "sarah-[:MEMBER_OF]->group2,\n" + "sarah-[:MEMBER_OF]->group3,\n" + "liz-[:MEMBER_OF]->group4,\n" + "liz-[:MEMBER_OF]->group5,\n" + "liz-[:MEMBER_OF]->group6,\n" + "phil-[:MEMBER_OF]->group7,\n" + "spinoff-[:CHILD_OF]->acme,\n" + "skunkworkz-[:CHILD_OF]->startup,\n" + "acquired-[:CHILD_OF]->bigco,\n" + "subsidiary-[:CHILD_OF]->acquired,\n" + "onemanshop-[:CHILD_OF]->subsidiary,\n" + "devshop-[:CHILD_OF]->subsidiary,\n" + "arnold-[:WORKS_FOR]->acme,\n" + "charlie-[:WORKS_FOR]->acme,\n" + "gordon-[:WORKS_FOR]->startup,\n" + "lucy-[:WORKS_FOR]->startup,\n" + "emily-[:WORKS_FOR]->spinoff,\n" + "kate-[:WORKS_FOR]->skunkworkz,\n" + "alistair-[:WORKS_FOR]->bigco,\n" + "eve-[:WORKS_FOR]->acquired,\n" + "gary-[:WORKS_FOR]->subsidiary,\n" + "mary-[:WORKS_FOR]->devshop,\n" + "bill-[:WORKS_FOR]->onemanshop,\n" + "arnold-[:HAS_ACCOUNT]->account1,\n" + "arnold-[:HAS_ACCOUNT]->account2,\n" + "charlie-[:HAS_ACCOUNT]->account3,\n" + "gordon-[:HAS_ACCOUNT]->account4,\n" + "lucy-[:HAS_ACCOUNT]->account5,\n" + "emily-[:HAS_ACCOUNT]->account6,\n" + "kate-[:HAS_ACCOUNT]->account7,\n" + "alistair-[:HAS_ACCOUNT]->account8,\n" + "eve-[:HAS_ACCOUNT]->account9,\n" + "bill-[:HAS_ACCOUNT]->account10,\n" + "gary-[:HAS_ACCOUNT]->account11,\n" + "mary-[:HAS_ACCOUNT]->account12,\n" + "group1-[:ALLOWED_INHERIT]->acme,\n" + "group2-[:ALLOWED_DO_NOT_INHERIT]->acme,\n" + "group2-[:DENIED]->skunkworkz,\n" + "group3-[:ALLOWED_INHERIT]->startup,\n" + "group4-[:ALLOWED_INHERIT]->bigco,\n" + "group5-[:DENIED]->acquired,\n" + "group6-[:ALLOWED_DO_NOT_INHERIT]->onemanshop,\n" + "group7-[:ALLOWED_INHERIT]->subsidiary"; GraphDatabaseService graph = createFromCypher( "Access Control Revised", cypher, indexParam( "Administrator", "name" ), indexParam( "Employee", "name" ), indexParam( "Company", "name" ), indexParam( "Account", "name" ), indexParam( "Resource", "name" )); new IndexResources( graph ).execute(); return graph; } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/AccessControlWithRelationshipPropertiesQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import static org.neo4j.graphdatabases.queries.testing.IndexParam.indexParam; import java.util.*; import org.junit.*; import org.junit.rules.TestName; import org.neo4j.graphdatabases.queries.helpers.PrintingExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.helpers.QueryUnionExecutionResult; import org.neo4j.graphdatabases.queries.traversals.IndexResources; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Transaction; public class AccessControlWithRelationshipPropertiesQueriesTest { @Rule public TestName name = new TestName(); private GraphDatabaseService db; private AccessControlWithRelationshipPropertiesQueries queries; private Transaction tx; @Before public void init() { db = createDatabase(); queries = new AccessControlWithRelationshipPropertiesQueries( new PrintingExecutionEngineWrapper( db, "access-control", name ) ); tx = db.beginTx(); } @After public void shutdown() { if ( tx!=null ) { tx.success(); tx.close(); } db.shutdown(); } @Test public void allowedWithInheritTrueGivesAccessToSubcompaniesAndAccounts() throws Exception { // Ben is member of two groups, both of which have ALLOWED_INHERIT. // He should, therefore, see all results below the companies to which // these permissions are attached. // when QueryUnionExecutionResult results = queries.findAccessibleResources( "Ben" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-1", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-2", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-3", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-6", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-4", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-5", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-7", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void deniedExcludesCompanyFromPermissionsTree() throws Exception { // Sarah is a member of Group-2, which has DENIED on Skunkworx. // Therefore Account-7 should not appear in the results. // Group-2 also has ALLOWED_DO_NOT_INHERIT on Acme, so Spinoff accounts, // a child of Acme, should not be included in results // when QueryUnionExecutionResult results = queries.findAccessibleResources( "Sarah" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-4", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-5", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-1", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-2", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertEquals( "Account-3", ((Path) iterator.next().get( "paths" )).endNode().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldGetAccessibleCompaniesForAdmin() throws Exception { // Sarah is a member of groups that have ALLOWED_INHERIT, ALLOWED_DO_NOT_INHERIT and DENIED // This tests this combination (for a 2-layer organizational structure). // given QueryUnionExecutionResult results = queries.findAccessibleCompanies( "Sarah" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Startup", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertEquals( "Acme", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldGetAccessibleCompaniesForAdminWhereNoAllowedInheritFalse() throws Exception { // Ben is a member of groups that have ALLOWED_INHERIT // given QueryUnionExecutionResult results = queries.findAccessibleCompanies( "Ben" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Acme", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertEquals( "Spinoff", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertEquals( "Startup", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertEquals( "Skunkworkz", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void moreComplexShouldGetAccessibleCompaniesForAdmin() throws Exception { // Liz has ALLOWED_INHERIT at the top of a 3-layer org structure, // DENIED at the next level, and ALLOWED_DO_NOT_INHERIT at the bottom layer. // given QueryUnionExecutionResult results = queries.findAccessibleCompanies( "Liz" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "BigCompany", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertEquals( "One-ManShop", ((Node) iterator.next().get( "company" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAccessibleAccountsForAdminAndCompany() throws Exception { // given QueryUnionExecutionResult results = queries.findAccessibleAccountsForCompany( "Sarah", "Startup" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-4", ((Node) iterator.next().get( "account" )).getProperty( "name" ) ); assertEquals( "Account-5", ((Node) iterator.next().get( "account" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void moreComplexShouldFindAccessibleAccountsForAdminAndCompany() throws Exception { // given QueryUnionExecutionResult results = queries.findAccessibleAccountsForCompany( "Liz", "BigCompany" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-8", ((Node) iterator.next().get( "account" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAccessibleAccountsForAdminAndCompanyWhenNoAllowedWithInheritFalse() throws Exception { // given QueryUnionExecutionResult results = queries.findAccessibleAccountsForCompany( "Ben", "Startup" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Account-4", ((Node) iterator.next().get( "account" )).getProperty( "name" ) ); assertEquals( "Account-5", ((Node) iterator.next().get( "account" )).getProperty( "name" ) ); assertEquals( "Account-7", ((Node) iterator.next().get( "account" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForAccountResourceWhereAllowedInheritAndAllowedNotInherit() throws Exception { // Account-10 is associated with One-ManShop // One-ManShop-CHILD_OF->Subsidiary-CHILD_OF->AcquiredLtd->CHILD_OF->BigCompany // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to OneManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She has access to Account-10 by virtue of Group-6 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He has access to Account-10 by virtue of Group-7 // given QueryUnionExecutionResult results = queries.findAdminForResource( "Account-10" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Phil", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertEquals( "Liz", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForEmployeeResourceWhereAllowedInheritAndDenied() throws Exception { // Kate works for Skunkworkz // Skunkworkz-CHILD_OF->Startup // Sarah is a member of: // Group-2, which has DENIED on Skunkworkz // Group-3, which has ALLOWED_INHERIT on Startup // She is denied access to Kate by virtue of Group-2 // Ben is a member of: // Group-3, which has ALLOWED_INHERIT on Startup // Group-1, which has no access to Kate's company chain // He has access by to Kate virtue of Group-3 // given QueryUnionExecutionResult results = queries.findAdminForResource( "Kate" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Ben", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithAllowedInherit() throws Exception { // BigCompany<-CHILD_OF-AcquiredLtd<-CHILD_OF-Subsidiary<-CHILD_OF-One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She has access to BigCompany by virtue of Group-4 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He does not have access to BigCompany // given QueryUnionExecutionResult results = queries.findAdminForCompany( "BigCompany" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Liz", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithDenied() throws Exception { // AcquiredLtd<-CHILD_OF-Subsidiary<-CHILD_OF-One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She is denied access to AcquiredLtd by virtue of Group-5 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He does not have access to AcquiredLtd // given QueryUnionExecutionResult results = queries.findAdminForCompany( "AcquiredLtd" ); // then Iterator> iterator = results.iterator(); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithAllowedInheritAndAllowedDoNotInheritTooLowInTree() throws Exception { //Subsidiary<-CHILD_OF-One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She is denied access to Subsidiary by virtue of Group-5 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He has access to Subsidiary by virtue of Group-7 // given QueryUnionExecutionResult results = queries.findAdminForCompany( "Subsidiary" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Phil", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldFindAdminForCompanyWithAllowedInheritAndAllowedAllowedDoNotInherit() throws Exception { //One-ManShop // Liz is a member of: // Group-6, which has ALLOWED_DO_NOT_INHERIT to One-ManShop // Group-5, which has DENIED on AcquiredLtd // Group-4, which has ALLOWED_INHERIT on BigCompany // She has access to One-ManShop by virtue of Group-6 // Phil is a member of: // Group-7, which has ALLOWED_INHERIT on Subsidiary // He has access to One-ManShop by virtue of Group-7 // given QueryUnionExecutionResult results = queries.findAdminForCompany( "One-ManShop" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( "Phil", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertEquals( "Liz", ((Node) iterator.next().get( "admin" )).getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldDetermineWhetherAdminHasAccessToResource() throws Exception { Map> testData = new HashMap>(); testData.put( "Alistair", asList( 1L, 0L ) ); testData.put( "Account-8", asList( 1L, 0L ) ); testData.put( "Eve", asList( 0L, 0L ) ); testData.put( "Account-9", asList( 0L, 0L ) ); testData.put( "Mary", asList( 0L, 0L ) ); testData.put( "Account-12", asList( 0L, 0L ) ); testData.put( "Gary", asList( 0L, 0L ) ); testData.put( "Account-11", asList( 0L, 0L ) ); testData.put( "Bill", asList( 0L, 1L ) ); testData.put( "Account-10", asList( 0L, 1L ) ); for ( String resourceName : testData.keySet() ) { List expectedResults = testData.get( resourceName ); Iterator expectedResultsIterator = expectedResults.iterator(); // given QueryUnionExecutionResult results = queries.hasAccessToResource( "Liz", resourceName ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( resourceName + " inherited", expectedResultsIterator.next(), iterator.next().get( "accessCount" ) ); assertEquals( resourceName + " not inherited", expectedResultsIterator.next(), iterator.next().get( "accessCount" ) ); assertFalse( iterator.hasNext() ); assertFalse( expectedResultsIterator.hasNext() ); } } @Test @Ignore("does not work conceptually") public void shouldDetermineWhetherAdminHasAccessToIndexedResource() throws Exception { Map> testData = new LinkedHashMap<>(); testData.put( "Alistair", asList( 1L, 0L ) ); testData.put( "Account-8", asList( 1L, 0L ) ); testData.put( "Eve", asList( 0L, 0L ) ); testData.put( "Account-9", asList( 0L, 0L ) ); testData.put( "Mary", asList( 0L, 0L ) ); testData.put( "Account-12", asList( 0L, 0L ) ); testData.put( "Gary", asList( 0L, 0L ) ); testData.put( "Account-11", asList( 0L, 0L ) ); testData.put( "Bill", asList( 0L, 1L ) ); testData.put( "Account-10", asList( 0L, 1L ) ); for ( String resourceName : testData.keySet() ) { List expectedResults = testData.get( resourceName ); Iterator expectedResultsIterator = expectedResults.iterator(); // given QueryUnionExecutionResult results = queries.hasAccessToIndexedResource( "Liz", resourceName ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); assertEquals( resourceName + " inherited", expectedResultsIterator.next(), iterator.next().get( "accessCount" ) ); assertEquals( resourceName + " not inherited", expectedResultsIterator.next(), iterator.next().get( "accessCount" ) ); assertFalse( iterator.hasNext() ); assertFalse( expectedResultsIterator.hasNext() ); } } private static GraphDatabaseService createDatabase() { String cypher = "CREATE\n" + "(ben:Administrator {name:'Ben'}),\n" + "(sarah:Administrator {name:'Sarah'}),\n" + "(liz:Administrator {name:'Liz'}),\n" + "(phil:Administrator {name:'Phil'}),\n" + "(arnold:Employee:Resource {name:'Arnold'}),\n" + "(charlie:Employee:Resource {name:'Charlie'}),\n" + "(gordon:Employee:Resource {name:'Gordon'}),\n" + "(lucy:Employee:Resource {name:'Lucy'}),\n" + "(emily:Employee:Resource {name:'Emily'}),\n" + "(kate:Employee:Resource {name:'Kate'}),\n" + "(alistair:Employee:Resource {name:'Alistair'}),\n" + "(eve:Employee:Resource {name:'Eve'}),\n" + "(bill:Employee:Resource {name:'Bill'}),\n" + "(gary:Employee:Resource {name:'Gary'}),\n" + "(mary:Employee:Resource {name:'Mary'}),\n" + "(group1:Group {name:'Group-1'}),\n" + "(group2:Group {name:'Group-2'}),\n" + "(group3:Group {name:'Group-3'}),\n" + "(group4:Group {name:'Group-4'}),\n" + "(group5:Group {name:'Group-5'}),\n" + "(group6:Group {name:'Group-6'}),\n" + "(group7:Group {name:'Group-7'}),\n" + "(acme:Company {name:'Acme'}),\n" + "(spinoff:Company {name:'Spinoff'}),\n" + "(startup:Company {name:'Startup'}),\n" + "(skunkworkz:Company {name:'Skunkworkz'}),\n" + "(bigco:Company {name:'BigCompany'}),\n" + "(acquired:Company {name:'AcquiredLtd'}),\n" + "(subsidiary:Company {name:'Subsidiary'}),\n" + "(devshop:Company {name:'DevShop'}),\n" + "(onemanshop:Company {name:'One-ManShop'}),\n" + "(account1:Account:Resource {name:'Account-1'}),\n" + "(account2:Account:Resource {name:'Account-2'}),\n" + "(account3:Account:Resource {name:'Account-3'}),\n" + "(account4:Account:Resource {name:'Account-4'}),\n" + "(account5:Account:Resource {name:'Account-5'}),\n" + "(account6:Account:Resource {name:'Account-6'}),\n" + "(account7:Account:Resource {name:'Account-7'}),\n" + "(account8:Account:Resource {name:'Account-8'}),\n" + "(account9:Account:Resource {name:'Account-9'}),\n" + "(account10:Account:Resource {name:'Account-10'}),\n" + "(account11:Account:Resource {name:'Account-11'}),\n" + "(account12:Account:Resource {name:'Account-12'}),\n" + "ben-[:MEMBER_OF]->group1,\n" + "ben-[:MEMBER_OF]->group3,\n" + "sarah-[:MEMBER_OF]->group2,\n" + "sarah-[:MEMBER_OF]->group3,\n" + "liz-[:MEMBER_OF]->group4,\n" + "liz-[:MEMBER_OF]->group5,\n" + "liz-[:MEMBER_OF]->group6,\n" + "phil-[:MEMBER_OF]->group7,\n" + "spinoff-[:CHILD_OF]->acme,\n" + "skunkworkz-[:CHILD_OF]->startup,\n" + "acquired-[:CHILD_OF]->bigco,\n" + "subsidiary-[:CHILD_OF]->acquired,\n" + "onemanshop-[:CHILD_OF]->subsidiary,\n" + "devshop-[:CHILD_OF]->subsidiary,\n" + "arnold-[:WORKS_FOR]->acme,\n" + "charlie-[:WORKS_FOR]->acme,\n" + "gordon-[:WORKS_FOR]->startup,\n" + "lucy-[:WORKS_FOR]->startup,\n" + "emily-[:WORKS_FOR]->spinoff,\n" + "kate-[:WORKS_FOR]->skunkworkz,\n" + "alistair-[:WORKS_FOR]->bigco,\n" + "eve-[:WORKS_FOR]->acquired,\n" + "gary-[:WORKS_FOR]->subsidiary,\n" + "mary-[:WORKS_FOR]->devshop,\n" + "bill-[:WORKS_FOR]->onemanshop,\n" + "arnold-[:HAS_ACCOUNT]->account1,\n" + "arnold-[:HAS_ACCOUNT]->account2,\n" + "charlie-[:HAS_ACCOUNT]->account3,\n" + "gordon-[:HAS_ACCOUNT]->account4,\n" + "lucy-[:HAS_ACCOUNT]->account5,\n" + "emily-[:HAS_ACCOUNT]->account6,\n" + "kate-[:HAS_ACCOUNT]->account7,\n" + "alistair-[:HAS_ACCOUNT]->account8,\n" + "eve-[:HAS_ACCOUNT]->account9,\n" + "bill-[:HAS_ACCOUNT]->account10,\n" + "gary-[:HAS_ACCOUNT]->account11,\n" + "mary-[:HAS_ACCOUNT]->account12,\n" + "group1-[:ALLOWED {inherit:true}]->acme,\n" + "group2-[:ALLOWED {inherit:false}]->acme,\n" + "group2-[:DENIED]->skunkworkz,\n" + "group3-[:ALLOWED {inherit:true}]->startup,\n" + "group4-[:ALLOWED {inherit:true}]->bigco,\n" + "group5-[:DENIED]->acquired,\n" + "group6-[:ALLOWED {inherit:false}]->onemanshop,\n" + "group7-[:ALLOWED {inherit:true}]->subsidiary"; GraphDatabaseService graph = createFromCypher( "Access Control Revised", cypher, indexParam( "Administrator", "name" ), indexParam( "Employee", "name" ), indexParam( "Company", "name" ), indexParam( "Account", "name" ), indexParam( "Resource", "name" )); new IndexResources( graph ).execute(); return graph; } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/EmailQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.Iterator; import java.util.Map; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.PrintingExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import static org.neo4j.helpers.collection.IteratorUtil.count; public class EmailQueriesTest { @Rule public TestName name = new TestName(); @Test public void suspectBehaviour() throws Exception { GraphDatabaseService db = createDatabase(); EmailQueries queries = new EmailQueries( db, new PrintingExecutionEngineWrapper( db, "email", name ) ); ExecutionResult result = queries.suspectBehaviour(); Iterator> iterator = result.iterator(); Map next = iterator.next(); assertEquals( "1", next.get( "email.id" )); assertFalse( iterator.hasNext() ); db.shutdown(); } @Test public void suspectBehaviour2() throws Exception { GraphDatabaseService db = createDatabase2(); EmailQueries queries = new EmailQueries( db, new PrintingExecutionEngineWrapper( db, "email", name ) ); ExecutionResult result = queries.suspectBehaviour2(); Iterator> iterator = result.iterator(); Map next = iterator.next(); assertEquals( 1L, next.get( "depth" ) ); assertEquals( "Davina", next.get( "replier" ) ); next = iterator.next(); assertEquals( 1L, next.get( "depth" ) ); assertEquals( "Bob", next.get( "replier" ) ); next = iterator.next(); assertEquals( 2L, next.get( "depth" ) ); assertEquals( "Charlie", next.get( "replier" ) ); next = iterator.next(); assertEquals( 3L, next.get( "depth" ) ); assertEquals( "Bob", next.get( "replier" ) ); assertFalse( iterator.hasNext() ); db.shutdown(); } @Test public void suspectBehaviour3() throws Exception { GraphDatabaseService db = createDatabase3(); EmailQueries queries = new EmailQueries( db, new PrintingExecutionEngineWrapper( db, "email", name ) ); ExecutionResult result = queries.suspectBehaviour3(); Iterator objectIterator = result.columnAs( "count(f)" ); assertEquals( 2L, objectIterator.next() ); assertFalse( objectIterator.hasNext() ); db.shutdown(); } @Test public void lossyDb() throws Exception { GraphDatabaseService db = createDatabase4(); EmailQueries queries = new EmailQueries( db, new PrintingExecutionEngineWrapper( db, "email", name ) ); ExecutionResult result = queries.lossyDb(); assertEquals(1, count(result.iterator())); db.shutdown(); } private static GraphDatabaseService createDatabase() { String cypher = "CREATE \n" + "(charlie:User {username:'Charlie'}),\n" + "(davina:User {username:'Davina'}),\n" + "(edward:User {username:'Edward'}),\n" + "(alice:User {username:'Alice'}),\n" + "(bob:User {username:'Bob'}),\n" + "(alice)-[:ALIAS_OF]->(bob),\n" + "\n" + "(email_1:Email {id: '1', content: 'email contents'}),\n" + "(bob)-[:SENT]->(email_1),\n" + "(email_1)-[:TO]->(charlie),\n" + "(email_1)-[:CC]->(davina),\n" + "(email_1)-[:CC]->(alice),\n" + "(email_1)-[:BCC]->(edward),\n" + "\n" + "(email_2:Email {id: '2', content: 'email contents'}),\n" + "(bob)-[:SENT]->(email_2),\n" + "(email_2)-[:TO]->(davina),\n" + "(email_2)-[:BCC]->(edward),\n" + "\n" + "(email_3:Email {id: '3', content: 'email contents'}),\n" + "(davina)-[:SENT]->(email_3),\n" + "(email_3)-[:TO]->(bob),\n" + "(email_3)-[:CC]->(edward),\n" + "\n" + "(email_4:Email {id: '4', content: 'email contents'}),\n" + "(charlie)-[:SENT]->(email_4),\n" + "(email_4)-[:TO]->(bob),\n" + "(email_4)-[:TO]->(davina),\n" + "(email_4)-[:TO]->(edward),\n" + "\n" + "(email_5:Email {id: '5', content: 'email contents'}),\n" + "(davina)-[:SENT]->(email_5),\n" + "(email_5)-[:TO]->(alice),\n" + "(email_5)-[:BCC]->(bob),\n" + "(email_5)-[:BCC]->(edward)"; return createFromCypher( "Email", cypher, IndexParam.indexParam( "User", "username" ), IndexParam.indexParam( "Email", "id" ) ); } private static GraphDatabaseService createDatabase2() { String cypher = "CREATE \n" + "(charlie:User {username:'Charlie'}),\n" + "(davina:User {username:'Davina'}),\n" + "(edward:User {username:'Edward'}),\n" + "(alice:User {username:'Alice'}),\n" + "(bob:User {username:'Bob'}),\n" + "(alice)-[:ALIAS_OF]->(bob),\n" + "\n" + "(email_6:Email {id: '6', content: 'email'}),\n" + "(bob)-[:SENT]->(email_6),\n" + "(email_6)-[:TO]->(charlie),\n" + "(email_6)-[:TO]->(davina),\n" + "\n" + "(reply_1:Email {id: '7', content: 'response'}),\n" + "(reply_1)-[:REPLY_TO]->(email_6),\n" + "(davina)-[:SENT]->(reply_1),\n" + "(reply_1)-[:TO]->(bob),\n" + "(reply_1)-[:TO]->(charlie),\n" + "\n" + "(reply_2:Email {id: '8', content: 'response'}),\n" + "(reply_2)-[:REPLY_TO]->(email_6),\n" + "(bob)-[:SENT]->(reply_2),\n" + "(reply_2)-[:TO]->(davina),\n" + "(reply_2)-[:TO]->(charlie),\n" + "(reply_2)-[:CC]->(alice),\n" + "\n" + "(reply_3:Email {id: '9', content: 'response'}),\n" + "(reply_3)-[:REPLY_TO]->(reply_1),\n" + "(charlie)-[:SENT]->(reply_3),\n" + "(reply_3)-[:TO]->(bob),\n" + "(reply_3)-[:TO]->(davina),\n" + "\n" + "(reply_4:Email {id: '10', content: 'response'}),\n" + "(reply_4)-[:REPLY_TO]->(reply_3),\n" + "(bob)-[:SENT]->(reply_4),\n" + "(reply_4)-[:TO]->(charlie),\n" + "(reply_4)-[:TO]->(davina)"; return createFromCypher( "Email", cypher, IndexParam.indexParam( "User", "username" ), IndexParam.indexParam( "Email", "id" ) ); } private static GraphDatabaseService createDatabase3() { String cypher = "CREATE \n" + "(charlie:User {username:'Charlie'}),\n" + "(davina:User {username:'Davina'}),\n" + "(edward:User {username:'Edward'}),\n" + "(alice:User {username:'Alice'}),\n" + "(bob:User {username:'Bob'}),\n" + "(alice)-[:ALIAS_OF]->(bob),\n" + "\n" + "(email_11:Email {id: '11', content: 'email'}),\n" + " (alice)-[:SENT]->(email_11)-[:TO]->(bob),\n" + "\n" + "(email_12:Email {id: '12', content: 'email'}),\n" + " (email_12)-[:FORWARD_OF]->(email_11),\n" + " (bob)-[:SENT]->(email_12)-[:TO]->(charlie),\n" + "\n" + "(email_13:Email {id: '13', content: 'email'}),\n" + " (email_13)-[:FORWARD_OF]->(email_12),\n" + " (charlie)-[:SENT]->(email_13)-[:TO]->(davina)"; return createFromCypher( "Email", cypher, IndexParam.indexParam( "User", "username" ), IndexParam.indexParam( "Email", "id" ) ); } private static GraphDatabaseService createDatabase4() { String cypher = "CREATE (alice:User {username: 'Alice'}),\n" + "(bob:User {username: 'Bob'}),\n" + "(charlie:User {username: 'Charlie'}),\n" + "(davina:User {username: 'Davina'}),\n" + "(edward:User {username: 'Edward'}),\n" + "(alice)-[:ALIAS_OF]->(bob),\n" + "(bob)-[:EMAILED]->(charlie),\n" + "(bob)-[:CC]->(davina),\n" + "(bob)-[:BCC]->(edward)"; return createFromCypher( "Email", cypher, IndexParam.indexParam( "User", "username" ) ); } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/LogisticsQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import static org.neo4j.graphdatabases.queries.testing.IndexParam.indexParam; import java.util.Iterator; import org.joda.time.DateTime; import org.joda.time.Interval; import org.junit.*; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.PrintingExecutionEngineWrapper; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; public class LogisticsQueriesTest { @Rule public TestName name = new TestName(); private GraphDatabaseService db; private LogisticsQueries queries; private static Interval interval1 = Interval.parse( "2012-10-15T00:00:00.000+01:00/2012-10-22T00:00:00.000+01:00" ); private static Interval interval2 = Interval.parse( "2012-10-22T00:00:00.000+01:00/2012-10-29T00:00:00.000+01:00" ); private static Interval interval3 = Interval.parse( "2012-10-29T00:00:00.000+01:00/2012-11-05T00:00:00.000+01:00" ); private Transaction tx; @Before public void init() { try { db = createDatabase(); queries = new LogisticsQueries( db, new PrintingExecutionEngineWrapper( db, "logistics", name ) ); tx = db.beginTx(); } catch ( Exception e ) { e.printStackTrace(); System.out.println( e.getMessage() ); } } @After public void shutdown() { if ( tx != null ) { tx.success(); tx.close(); } db.shutdown(); } @Test public void parcelRoutingUsingParcelRouteCalculator() throws Exception { // given DateTime startDtm = interval1.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when Iterable results = queries.findShortestPathWithParcelRouteCalculator( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator iterator = results.iterator(); assertEquals( "DeliveryArea-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", iterator.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-2", iterator.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-2", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-2", iterator.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", iterator.next().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void parcelRoutingUsingParcelRouteCalculatorChoosesShortestRouteBetweenDeliveryBases() throws Exception { // given DateTime startDtm = interval2.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when Iterable results = queries.findShortestPathWithParcelRouteCalculator( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator iterator = results.iterator(); assertEquals( "DeliveryArea-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", iterator.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-2", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-2", iterator.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", iterator.next().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void parcelRoutingUsingParcelRouteCalculatorRespectsIntervals() throws Exception { // given DateTime startDtm = interval3.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when Iterable results = queries.findShortestPathWithParcelRouteCalculator( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator iterator = results.iterator(); assertEquals( "DeliveryArea-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", iterator.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-3", iterator.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", iterator.next().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void parcelRoutingUsingSimpleParcelRouteCalculatorRespectsIntervals() throws Exception { // given DateTime startDtm = interval3.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when Iterable results = queries.findShortestPathWithSimpleParcelRouteCalculator( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator iterator = results.iterator(); assertEquals( "DeliveryArea-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", iterator.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-3", iterator.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", iterator.next().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void withinScopeOfSingleParcelCentreParcelRouteCalculator() throws Exception { // given DateTime startDtm = interval2.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when Iterable results = queries.findShortestPathWithParcelRouteCalculator( "DeliveryArea-1", "DeliverySegment-8", queryInterval ); // then Iterator iterator = results.iterator(); assertEquals( "DeliveryArea-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", iterator.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-2", iterator.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-4", iterator.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-8", iterator.next().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void parcelRoutingUsingCypherReduce() throws Exception { // given DateTime startDtm = interval1.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when ExecutionResult result = queries.findShortestPathWithCypherReduce( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator> rows = result.columnAs( "n" ); Iterator nodes = rows.next().iterator(); assertEquals( "DeliveryArea-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", nodes.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-2", nodes.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-2", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-2", nodes.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", nodes.next().getProperty( "name" ) ); assertFalse( nodes.hasNext() ); } @Test public void parcelRoutingUsingCypherReduceChoosesShortestRouteBetweenDeliveryBases() throws Exception { // given DateTime startDtm = interval2.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when ExecutionResult result = queries.findShortestPathWithCypherReduce( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator> rows = result.columnAs( "n" ); Iterator nodes = rows.next().iterator(); assertEquals( "DeliveryArea-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", nodes.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-2", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-2", nodes.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", nodes.next().getProperty( "name" ) ); assertFalse( nodes.hasNext() ); } @Test public void parcelRoutingUsingCypherReduceRespectsIntervals() throws Exception { // given DateTime startDtm = interval3.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when ExecutionResult result = queries.findShortestPathWithCypherReduce( "DeliveryArea-1", "DeliverySegment-3", queryInterval ); // then Iterator> rows = result.columnAs( "n" ); Iterator nodes = rows.next().iterator(); assertEquals( "DeliveryArea-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", nodes.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-3", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-3", nodes.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-3", nodes.next().getProperty( "name" ) ); assertFalse( nodes.hasNext() ); } @Test public void withinScopeOfSingleParcelCentreCypher() throws Exception { // given DateTime startDtm = interval2.getStart().plusDays( 2 ); Interval queryInterval = new Interval( startDtm, startDtm.plusDays( 1 ) ); // when ExecutionResult result = queries.findShortestPathWithCypherReduce( "DeliveryArea-1", "DeliverySegment-8", queryInterval ); // then Iterator> rows = result.columnAs( "n" ); Iterator nodes = rows.next().iterator(); assertEquals( "DeliveryArea-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-1", nodes.next().getProperty( "name" ) ); assertEquals( "ParcelCentre-1", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryBase-2", nodes.next().getProperty( "name" ) ); assertEquals( "DeliveryArea-4", nodes.next().getProperty( "name" ) ); assertEquals( "DeliverySegment-8", nodes.next().getProperty( "name" ) ); assertFalse( nodes.hasNext() ); } private static GraphDatabaseService createDatabase() { String cypher = "CREATE\n" + "(pc1:ParcelCentre:Location {name:'ParcelCentre-1'}),\n" + "(pc2:ParcelCentre:Location {name:'ParcelCentre-2'}),\n" + "(db1:DeliveryBase:Location {name:'DeliveryBase-1'}),\n" + "(db2:DeliveryBase:Location {name:'DeliveryBase-2'}),\n" + "(db3:DeliveryBase:Location {name:'DeliveryBase-3'}),\n" + "(da1:DeliveryArea:Location {name:'DeliveryArea-1'}),\n" + "(da2:DeliveryArea:Location {name:'DeliveryArea-2'}),\n" + "(da3:DeliveryArea:Location {name:'DeliveryArea-3'}),\n" + "(da4:DeliveryArea:Location {name:'DeliveryArea-4'}),\n" + "(ds1:DeliverySegment:Location {name:'DeliverySegment-1'}),\n" + "(ds2:DeliverySegment:Location {name:'DeliverySegment-2'}),\n" + "(ds3:DeliverySegment:Location {name:'DeliverySegment-3'}),\n" + "(ds4:DeliverySegment:Location {name:'DeliverySegment-4'}),\n" + "(ds5:DeliverySegment:Location {name:'DeliverySegment-5'}),\n" + "(ds6:DeliverySegment:Location {name:'DeliverySegment-6'}),\n" + "(ds7:DeliverySegment:Location {name:'DeliverySegment-7'}),\n" + "(ds8:DeliverySegment:Location {name:'DeliverySegment-8'}),\n" + "pc1-[:CONNECTED_TO {cost:3, " + intervalProperties( interval1 ) + "}]->db1,\n" + "pc1-[:CONNECTED_TO {cost:3, " + intervalProperties( interval1 ) + "}]->db2,\n" + "pc2-[:CONNECTED_TO {cost:3, " + intervalProperties( interval1 ) + "}]->db2,\n" + "pc2-[:CONNECTED_TO {cost:3, " + intervalProperties( interval1 ) + "}]->db3,\n" + "db1-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->da1,\n" + "db1-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->da4,\n" + "db2-[:DELIVERY_ROUTE {cost:5, " + intervalProperties( interval1 ) + "}]->da3,\n" + "db3-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->da2,\n" + "da1-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds1,\n" + "da1-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds2,\n" + "da2-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds3,\n" + "da2-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds4,\n" + "da4-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds5,\n" + "da3-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds6,\n" + "da1-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds7,\n" + "da1-[:DELIVERY_ROUTE {cost:3, " + intervalProperties( interval1 ) + "}]->ds8,\n" + "pc1-[:CONNECTED_TO {cost:2, " + intervalProperties( interval2 ) + "}]->db1,\n" + "pc1-[:CONNECTED_TO {cost:2, " + intervalProperties( interval2 ) + "}]->db2,\n" + "pc2-[:CONNECTED_TO {cost:2, " + intervalProperties( interval2 ) + "}]->db2,\n" + "pc2-[:CONNECTED_TO {cost:2, " + intervalProperties( interval2 ) + "}]->db3,\n" + //Parcel centre connected directly to db1 for 2nd interval "pc2-[:CONNECTED_TO {cost:5, " + intervalProperties( interval2 ) + "}]->db1,\n" + "db1-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->da1,\n" + "db2-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->da4,\n" + "db3-[:DELIVERY_ROUTE {cost:5, " + intervalProperties( interval2 ) + "}]->da3,\n" + "db3-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->da2,\n" + "da4-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds1,\n" + "da4-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds2,\n" + "da2-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds3,\n" + "da2-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds4,\n" + "da3-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds5,\n" + "da3-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds6,\n" + "da4-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds7,\n" + "da4-[:DELIVERY_ROUTE {cost:2, " + intervalProperties( interval2 ) + "}]->ds8,\n" + "pc1-[:CONNECTED_TO {cost:6, " + intervalProperties( interval3 ) + "}]->db1,\n" + "pc1-[:CONNECTED_TO {cost:6, " + intervalProperties( interval3 ) + "}]->db2,\n" + "pc2-[:CONNECTED_TO {cost:6, " + intervalProperties( interval3 ) + "}]->db2,\n" + "pc1-[:CONNECTED_TO {cost:6, " + intervalProperties( interval3 ) + "}]->db3,\n" + "db1-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->da1,\n" + "db2-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->da4,\n" + "db3-[:DELIVERY_ROUTE {cost:5, " + intervalProperties( interval3 ) + "}]->da3,\n" + "db2-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->da2,\n" + "da1-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds1,\n" + "da1-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds2,\n" + //Different delivery centre for ds3 for 3rd interval "da3-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds3,\n" + "da3-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds4,\n" + "da3-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds5,\n" + "da3-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds6,\n" + "da4-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds7,\n" + "da4-[:DELIVERY_ROUTE {cost:6, " + intervalProperties( interval3 ) + "}]->ds8"; return createFromCypher( "Logistics", cypher, indexParam( "Location", "name" ), indexParam( "ParcelCentre", "name" ), indexParam( "DeliveryBase", "name" ), indexParam( "DeliveryArea", "name" ), indexParam( "DeliverySegment", "name" ) ); } private static String intervalProperties( Interval interval ) { StringBuilder builder = new StringBuilder(); builder.append( "start_date:" ); builder.append( interval.getStartMillis() ); builder.append( ", end_date:" ); builder.append( interval.getEndMillis() ); return builder.toString(); } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/ShakespeareQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import java.util.Iterator; import java.util.Map; import org.junit.*; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.Db; import org.neo4j.graphdatabases.queries.helpers.PrintingExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdb.*; import org.neo4j.graphdb.index.Index; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypherWithAutoIndexing; import static org.neo4j.graphdb.DynamicLabel.label; import static org.neo4j.graphdb.DynamicRelationshipType.withName; public class ShakespeareQueriesTest { @Rule public TestName name = new TestName(); private GraphDatabaseService db; private GraphDatabaseService dbUsingCoreApi; private ShakespeareQueries queries; private ShakespeareQueries queries2; private ShakespeareQueriesUsingAutoIndexes queriesUsingAutoIndexes; @Before public void init() { db = createDatabase(); dbUsingCoreApi = createDatabaseUsingCoreApi(); queries = new ShakespeareQueries( new PrintingExecutionEngineWrapper( db, "shakespeare", name ) ); queries2 = new ShakespeareQueries( new PrintingExecutionEngineWrapper( dbUsingCoreApi, "shakespeare", name ) ); queriesUsingAutoIndexes = new ShakespeareQueriesUsingAutoIndexes( new PrintingExecutionEngineWrapper( db, "shakespeare-auot-indexes", name ) ); } @After public void shutdown() { db.shutdown(); dbUsingCoreApi.shutdown(); } @Test public void theatreCityBard() throws Exception { try ( Transaction tx = db.beginTx() ) { assertTheatreCityBard( queries.theatreCityBard() ); assertTheatreCityBard( queries2.theatreCityBard() ); assertTheatreCityBard( queriesUsingAutoIndexes.theatreCityBard() ); tx.success(); } } private void assertTheatreCityBard( ExecutionResult results ) { Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Theatre Royal", result.get( "theater" ) ); assertEquals( "Newcastle", result.get( "city" ) ); assertEquals( "Shakespeare", result.get( "bard" ) ); assertFalse( iterator.hasNext() ); } @Test public void exampleOfWith() throws Exception { try ( Transaction tx = db.beginTx() ) { assertExampleOfWith( queries.exampleOfWith() ); assertExampleOfWith( queries2.exampleOfWith() ); assertExampleOfWith( queriesUsingAutoIndexes.exampleOfWith() ); tx.success(); } } private void assertExampleOfWith( ExecutionResult results ) { Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( asList( "The Tempest", "Julius Caesar" ), result.get( "plays" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldReturnAllPlays() throws Exception { try ( Transaction tx = db.beginTx() ) { assertAllPlays( queries.allPlays() ); assertAllPlays( queries2.allPlays() ); assertAllPlays( queriesUsingAutoIndexes.allPlays() ); } } private void assertAllPlays( ExecutionResult result ) { try ( Transaction tx = db.beginTx() ) { Iterator plays = result.columnAs( "play" ); assertEquals( "Julius Caesar", plays.next() ); assertEquals( "The Tempest", plays.next() ); assertFalse( plays.hasNext() ); tx.success(); } } @Test public void shouldReturnLatePeriodPlays() throws Exception { try ( Transaction tx = db.beginTx() ) { assertLatePeriodPlays( queries.latePeriodPlays() ); assertLatePeriodPlays( queries2.latePeriodPlays() ); assertLatePeriodPlays( queriesUsingAutoIndexes.latePeriodPlays() ); tx.success(); } } private void assertLatePeriodPlays( ExecutionResult result ) { try ( Transaction tx = db.beginTx() ) { Iterator plays = result.columnAs( "play" ); assertEquals( "The Tempest", plays.next() ); assertFalse( plays.hasNext() ); tx.success(); } } @Test public void orderedByPerformance() throws Exception { try ( Transaction tx = db.beginTx() ) { assertOrderedByPerformance( queries.orderedByPerformance() ); assertOrderedByPerformance( queries2.orderedByPerformance() ); assertOrderedByPerformance( queriesUsingAutoIndexes.orderedByPerformance() ); tx.success(); } } private void assertOrderedByPerformance( ExecutionResult result ) { try ( Transaction tx = db.beginTx() ) { Iterator> plays = result.iterator(); Map row = plays.next(); assertEquals( "Julius Caesar", row.get( "play" ) ); assertEquals( 2L, row.get( "performance_count" ) ); row = plays.next(); assertEquals( "The Tempest", row.get( "play" ) ); assertEquals( 1L, row.get( "performance_count" ) ); assertFalse( plays.hasNext() ); tx.success(); } } private static GraphDatabaseService createDatabase() { String cypher = "CREATE (shakespeare:Author { firstname: 'William', lastname: 'Shakespeare' }),\n" + " (juliusCaesar:Play { title: 'Julius Caesar' }),\n" + " (shakespeare)-[:WROTE_PLAY { year: 1599 }]->(juliusCaesar),\n" + " (theTempest:Play { title: 'The Tempest' }),\n" + " (shakespeare)-[:WROTE_PLAY { year: 1610}]->(theTempest),\n" + " (rsc:Company { name: 'RSC' }),\n" + " (production1:Production { name: 'Julius Caesar' }),\n" + " (rsc)-[:PRODUCED]->(production1),\n" + " (production1)-[:PRODUCTION_OF]->(juliusCaesar),\n" + " (performance1:Performance { date: 20120729 }),\n" + " (performance1)-[:PERFORMANCE_OF]->(production1),\n" + " (production2:Production { name: 'The Tempest' }),\n" + " (rsc)-[:PRODUCED]->(production2),\n" + " (production2)-[:PRODUCTION_OF]->(theTempest),\n" + " (performance2:Performance { date: 20061121 }),\n" + " (performance2)-[:PERFORMANCE_OF]->(production2),\n" + " (performance3:Performance { date: 20120730 }),\n" + " (performance3)-[:PERFORMANCE_OF]->(production1),\n" + " (billy:User { name: 'Billy' }),\n" + " (review:Review { rating: 5, review: 'This was awesome!' }),\n" + " (billy)-[:WROTE_REVIEW]->(review),\n" + " (review)-[:RATED]->(performance1),\n" + " (theatreRoyal:Venue { name: 'Theatre Royal' }),\n" + " (performance1)-[:VENUE]->(theatreRoyal),\n" + " (performance2)-[:VENUE]->(theatreRoyal),\n" + " (performance3)-[:VENUE]->(theatreRoyal),\n" + " (greyStreet:Street { name: 'Grey Street' }),\n" + " (theatreRoyal)-[:STREET]->(greyStreet),\n" + " (newcastle:City { name: 'Newcastle' }),\n" + " (greyStreet)-[:CITY]->(newcastle),\n" + " (tyneAndWear:County { name: 'Tyne and Wear' }),\n" + " (newcastle)-[:COUNTY]->(tyneAndWear),\n" + " (england:Country { name: 'England' }),\n" + " (tyneAndWear)-[:COUNTRY]->(england),\n" + " (stratford:City { name: 'Stratford upon Avon' }),\n" + " (stratford)-[:COUNTRY]->(england),\n" + " (rsc)-[:BASED_IN]->(stratford),\n" + " (shakespeare)-[:BORN_IN]->stratford"; return createFromCypherWithAutoIndexing( "Shakespeare", cypher, IndexParam.indexParam( "Venue", "name" ), IndexParam.indexParam( "Author", "lastname" ), IndexParam.indexParam( "City", "name" ) ); } private static GraphDatabaseService createDatabaseUsingCoreApi() { GraphDatabaseService db = Db.tempDb(); try (Transaction tx = db.beginTx()) { db.schema().indexFor(label("Author")).on("lastname").create(); db.schema().indexFor(label("City")).on("name").create(); db.schema().indexFor(label("Venue")).on("name").create(); tx.success(); } try (Transaction tx = db.beginTx()) { Node shakespeare = db.createNode(label("Author")); shakespeare.setProperty("firstname", "William"); shakespeare.setProperty("lastname", "Shakespeare"); Node juliusCaesar = db.createNode(label("Play")); juliusCaesar.setProperty("title", "Julius Caesar"); Relationship wrote_play_jc = shakespeare.createRelationshipTo(juliusCaesar, withName("WROTE_PLAY")); wrote_play_jc.setProperty("year", 1599); Node tempest = db.createNode(label("Play")); tempest.setProperty("title", "The Tempest"); Relationship wrote_play_t = shakespeare.createRelationshipTo(tempest, withName("WROTE_PLAY")); wrote_play_t.setProperty("year", 1610); Node rsc = db.createNode(label("Company")); rsc.setProperty("name", "RSC"); Node production1 = db.createNode(label("Production")); production1.setProperty("name", "Julius Caesar"); rsc.createRelationshipTo(production1, withName("PRODUCED")); production1.createRelationshipTo(juliusCaesar, withName("PRODUCTION_OF")); Node performance1 = db.createNode(label("Performance")); performance1.setProperty("date", 20120729); performance1.createRelationshipTo(production1, withName("PERFORMANCE_OF")); Node production2 = db.createNode(label("Production")); production2.setProperty("name", "The Tempest"); production2.createRelationshipTo(tempest, withName("PRODUCTION_OF")); rsc.createRelationshipTo(production2, withName("PRODUCED")); Node performance2 = db.createNode(label("Performance")); performance2.setProperty("date", 20061121); performance2.createRelationshipTo(production2, withName("PERFORMANCE_OF")); Node performance3 = db.createNode(label("Performance")); performance3.setProperty("date", 20120730); performance3.createRelationshipTo(production1, withName("PERFORMANCE_OF")); Node billy = db.createNode(label("User")); billy.setProperty("name", "Billy"); Node review = db.createNode(label("Review")); review.setProperty("rating", 5); review.setProperty("review", "This was awesome!"); review.createRelationshipTo(performance1, withName("RATED")); billy.createRelationshipTo(review, withName("WROTE_REVIEW")); Node theatreRoyal = db.createNode(label("Venue")); theatreRoyal.setProperty("name", "Theatre Royal"); performance1.createRelationshipTo(theatreRoyal, withName("VENUE")); performance2.createRelationshipTo(theatreRoyal, withName("VENUE")); performance3.createRelationshipTo(theatreRoyal, withName("VENUE")); Node greyStreet = db.createNode(label("Street")); greyStreet.setProperty("name", "Grey Street"); theatreRoyal.createRelationshipTo(greyStreet, withName("STREET")); Node newcastle = db.createNode(label("City")); newcastle.setProperty("name", "Newcastle"); greyStreet.createRelationshipTo(newcastle, withName("CITY")); Node tyneAndWear = db.createNode(label("County")); tyneAndWear.setProperty("name", "Tyne and Wear"); newcastle.createRelationshipTo(tyneAndWear, withName("COUNTY")); Node england = db.createNode(label("Country")); england.setProperty("name", "England"); tyneAndWear.createRelationshipTo(england, withName("COUNTRY")); Node stratford = db.createNode(label("City")); stratford.setProperty("name", "Stratford upon Avon"); stratford.createRelationshipTo(england, withName("COUNTRY")); rsc.createRelationshipTo(stratford, withName("BASED_IN")); shakespeare.createRelationshipTo(stratford, withName("BORN_IN")); tx.success(); } return db; } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/SimpleSocialNetworkQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdb.GraphDatabaseService; public class SimpleSocialNetworkQueriesTest { private static GraphDatabaseService db; private static SimpleSocialNetworkQueries queries; @BeforeClass public static void init() { db = createDatabase(); queries = new SimpleSocialNetworkQueries( db ); } @AfterClass public static void shutdown() { db.shutdown(); } @Test public void shouldReturnShortestPathBetweenTwoFriends() throws Exception { // when ExecutionResult results = queries.pathBetweenTwoFriends( "Ben", "Mike" ); // then assertTrue( results.iterator().hasNext() ); assertEquals( 4, results.iterator().next().get( "depth" ) ); } @Test public void friendOfAFriendToDepth4() throws Exception { // when ExecutionResult results = queries.friendOfAFriendToDepth4( "Ben" ); // then assertTrue( results.iterator().hasNext() ); assertEquals( "Mike", results.iterator().next().get( "name" ) ); assertFalse( results.iterator().hasNext() ); } @Test public void shouldReturnNoResultsWhenThereIsNotAPathBetweenTwoFriends() throws Exception { // when ExecutionResult results = queries.pathBetweenTwoFriends( "Ben", "Arnold" ); // then assertFalse( results.iterator().hasNext() ); } private static GraphDatabaseService createDatabase() { String cypher = "CREATE\n" + "(ben:User {name:'Ben'}),\n" + "(arnold:User {name:'Arnold'}),\n" + "(charlie:User {name:'Charlie'}),\n" + "(gordon:User {name:'Gordon'}),\n" + "(lucy:User {name:'Lucy'}),\n" + "(emily:User {name:'Emily'}),\n" + "(sarah:User {name:'Sarah'}),\n" + "(kate:User {name:'Kate'}),\n" + "(mike:User {name:'Mike'}),\n" + "(paula:User {name:'Paula'}),\n" + "ben-[:FRIEND]->charlie,\n" + "charlie-[:FRIEND]->lucy,\n" + "lucy-[:FRIEND]->sarah,\n" + "sarah-[:FRIEND]->mike,\n" + "arnold-[:FRIEND]->gordon,\n" + "gordon-[:FRIEND]->emily,\n" + "emily-[:FRIEND]->kate,\n" + "kate-[:FRIEND]->paula"; return createFromCypher( "Simple Social Network", cypher, IndexParam.indexParam( "User", "name" ) ); } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/SocialNetworkQueriesTest.java ================================================ package org.neo4j.graphdatabases.queries; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import java.util.Collection; import java.util.Iterator; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.helpers.PrintingExecutionEngineWrapper; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; public class SocialNetworkQueriesTest { @Rule public TestName name = new TestName(); private GraphDatabaseService db; private SocialNetworkQueries queries; @Before public void init() { db = createDatabase(); queries = new SocialNetworkQueries( db, new PrintingExecutionEngineWrapper( db, "social-network", name ) ); } @After public void shutdown() { db.shutdown(); } @Test public void sharedInterestsSameCompany() throws Exception { // when ExecutionResult results = queries.sharedInterestsSameCompany( "Sarah" ); // then Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Ben", result.get( "name" ) ); assertEquals( 2l, result.get( "score" ) ); assertEquals( asList( "Graphs", "REST" ), result.get( "interests" ) ); result = iterator.next(); assertEquals( "Charlie", result.get( "name" ) ); assertEquals( 1l, result.get( "score" ) ); assertEquals( asList( "Graphs" ), result.get( "interests" ) ); assertFalse( iterator.hasNext() ); } @Test public void sharedInterestsAllCompanies() throws Exception { // when ExecutionResult results = queries.sharedInterestsAllCompanies( "Sarah", 10 ); // then Iterator> iterator = results.iterator(); Map result; result = iterator.next(); assertEquals( "Arnold", result.get( "name" ) ); assertEquals( "Startup, Ltd", result.get( "company" ) ); assertEquals( 3l, result.get( "score" ) ); assertEquals( asList( "Java", "Graphs", "REST" ), result.get( "interests" ) ); result = iterator.next(); assertEquals( "Ben", result.get( "name" ) ); assertEquals( "Acme, Inc", result.get( "company" ) ); assertEquals( 2l, result.get( "score" ) ); assertEquals( asList( "Graphs", "REST" ), result.get( "interests" ) ); result = iterator.next(); assertEquals( "Gordon", result.get( "name" ) ); assertEquals( "Startup, Ltd", result.get( "company" ) ); assertEquals( 1l, result.get( "score" ) ); assertEquals( asList( "Graphs" ), result.get( "interests" ) ); result = iterator.next(); assertEquals( "Charlie", result.get( "name" ) ); assertEquals( "Acme, Inc", result.get( "company" ) ); assertEquals( 1l, result.get( "score" ) ); assertEquals( asList( "Graphs" ), result.get( "interests" ) ); assertFalse( iterator.hasNext() ); } @Test public void sharedInterestsAlsoInterestedInTopic() throws Exception { // when ExecutionResult results = queries.sharedInterestsAlsoInterestedInTopic( "Ben", "Travel" ); // then Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Arnold", result.get( "name" ) ); assertEquals( asList( "Graphs", "Java", "REST", "Travel" ), result.get( "topics" ) ); assertFalse( iterator.hasNext() ); } @Test public void friendOfAFriendWithInterest() throws Exception { // when ExecutionResult results = queries.friendOfAFriendWithInterest( "Sarah", "Java", 3 ); // then Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Arnold", result.get( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void friendOfAFriendWithInterestTraversalFramework() throws Exception { try ( Transaction tx = db.beginTx() ) { // when Collection results = queries.friendOfAFriendWithInterestTraversalFramework( "Arnold", "Art", 5 ); // then Iterator iterator = results.iterator(); assertEquals( "Emily", iterator.next().getProperty( "name" ) ); assertFalse( iterator.hasNext() ); tx.success(); } } @Test public void friendWorkedWithFriendWithInterests() throws Exception { // when createAllWorkedWithRelationships(); ExecutionResult results = queries.friendWorkedWithFriendWithInterests( "Arnold", 5, "Art", "Design" ); // then Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Emily", result.get( "name" ) ); assertFalse( iterator.hasNext() ); } @Test public void friendOfAFriendWithMultipleInterest() throws Exception { // when ExecutionResult results = queries.friendOfAFriendWithMultipleInterest( "Arnold", 5, "Art", "Design" ); // then Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Emily", result.get( "name" ) ); assertEquals( 2L, result.get( "score" ) ); assertEquals( 2L, result.get( "distance" ) ); assertEquals( asList( "Art", "Design" ), result.get( "interests" ) ); assertFalse( iterator.hasNext() ); } @Test public void friendOfAFriendWithMultipleInterestShouldOrderByScore() throws Exception { // when ExecutionResult results = queries.friendOfAFriendWithMultipleInterest( "Sarah", 5, "Java", "Travel", "Medicine" ); // then Iterator> iterator = results.iterator(); Map result = iterator.next(); assertEquals( "Arnold", result.get( "name" ) ); assertEquals( 2L, result.get( "score" ) ); assertEquals( 2L, result.get( "distance" ) ); assertEquals( asList( "Java", "Travel" ), result.get( "interests" ) ); result = iterator.next(); assertEquals( "Charlie", result.get( "name" ) ); assertEquals( 1L, result.get( "score" ) ); assertEquals( 1L, result.get( "distance" ) ); assertEquals( asList( "Medicine" ), result.get( "interests" ) ); assertFalse( iterator.hasNext() ); } @Test public void shouldCreateNewWorkedWithRelationships() throws Exception { // given String cypher = "MATCH (sarah:User {name:'Sarah'}),\n" + " (ben:User {name:'Ben'})\n" + "CREATE sarah-[:WORKED_WITH]->ben"; createFromCypher( db, "Enriched Social Network", cypher ); // when ExecutionResult results = queries.createWorkedWithRelationships( "Sarah" ); // then Iterator> iterator = results.iterator(); assertTrue( iterator.hasNext() ); Map result = iterator.next(); assertEquals( "Emily", result.get( "endName" ) ); result = iterator.next(); assertEquals( "Charlie", result.get( "endName" ) ); result = iterator.next(); assertEquals( "Kate", result.get( "endName" ) ); assertFalse( iterator.hasNext() ); } private void createAllWorkedWithRelationships() { ExecutionResult allUsers = queries.getAllUsers(); Iterator> iterator = allUsers.iterator(); while (iterator.hasNext()) { queries.createWorkedWithRelationships( iterator.next().get( "name" ).toString() ); } } // @Test // public void runGremlinQuery() throws Exception // { // // given // GremlinPipeline result = queries.friendOfAFriendWithParticularInterestGremlin( "Arnold", "Art" ); // // assertTrue( Iterable.class.isAssignableFrom( result.getClass() ) ); // // // then // Iterator iterator = result.iterator(); // // Object next = iterator.next(); // assertEquals( "Emily", next ); // assertFalse( iterator.hasNext() ); // } private static GraphDatabaseService createDatabase() { String cypher = "CREATE\n" + "(ben:User {name:'Ben'}),\n" + "(arnold:User {name:'Arnold'}),\n" + "(charlie:User {name:'Charlie'}),\n" + "(gordon:User {name:'Gordon'}),\n" + "(lucy:User {name:'Lucy'}),\n" + "(emily:User {name:'Emily'}),\n" + "(sarah:User {name:'Sarah'}),\n" + "(kate:User {name:'Kate'}),\n" + "(acme:User {name:'Acme, Inc'}),\n" + "(startup:Company {name:'Startup, Ltd'}),\n" + "(graphs:Topic {name:'Graphs'}),\n" + "(rest:Topic {name:'REST'}),\n" + "(art:Topic {name:'Art'}),\n" + "(design:Topic {name:'Design'}),\n" + "(medicine:Topic {name:'Medicine'}),\n" + "(drama:Topic {name:'Drama'}),\n" + "(java:Topic {name:'Java'}),\n" + "(music:Topic {name:'Music'}),\n" + "(cars:Topic {name:'Cars'}),\n" + "(travel:Topic {name:'Travel'}),\n" + "(phoenix:Topic {name:'Phoenix'}),\n" + "(quantumLeap:Topic {name:'Quantum Leap'}),\n" + "(nextGenPlatform:Topic {name:'Next Gen Platform'}),\n" + "ben-[:WORKS_FOR]->acme,\n" + "charlie-[:WORKS_FOR]->acme,\n" + "lucy-[:WORKS_FOR]->acme,\n" + "sarah-[:WORKS_FOR]->acme,\n" + "arnold-[:WORKS_FOR]->startup,\n" + "gordon-[:WORKS_FOR]->startup,\n" + "emily-[:WORKS_FOR]->startup,\n" + "kate-[:WORKS_FOR]->startup,\n" + "ben-[:INTERESTED_IN]->graphs,\n" + "ben-[:INTERESTED_IN]->rest,\n" + "arnold-[:INTERESTED_IN]->graphs,\n" + "arnold-[:INTERESTED_IN]->java,\n" + "arnold-[:INTERESTED_IN]->rest,\n" + "arnold-[:INTERESTED_IN]->travel,\n" + "charlie-[:INTERESTED_IN]->graphs,\n" + "charlie-[:INTERESTED_IN]->cars,\n" + "charlie-[:INTERESTED_IN]->medicine,\n" + "gordon-[:INTERESTED_IN]->graphs,\n" + "gordon-[:INTERESTED_IN]->art,\n" + "gordon-[:INTERESTED_IN]->music,\n" + "lucy-[:INTERESTED_IN]->art,\n" + "lucy-[:INTERESTED_IN]->drama,\n" + "lucy-[:INTERESTED_IN]->music,\n" + "emily-[:INTERESTED_IN]->art,\n" + "emily-[:INTERESTED_IN]->design,\n" + "sarah-[:INTERESTED_IN]->java,\n" + "sarah-[:INTERESTED_IN]->graphs,\n" + "sarah-[:INTERESTED_IN]->rest,\n" + "kate-[:INTERESTED_IN]->drama,\n" + "kate-[:INTERESTED_IN]->music,\n" + "arnold-[:WORKED_ON]->phoenix,\n" + "kate-[:WORKED_ON]->phoenix,\n" + "kate-[:WORKED_ON]->quantumLeap,\n" + "emily-[:WORKED_ON]->quantumLeap,\n" + "ben-[:WORKED_ON]->nextGenPlatform,\n" + "emily-[:WORKED_ON]->nextGenPlatform,\n" + "charlie-[:WORKED_ON]->nextGenPlatform,\n" + "sarah-[:WORKED_ON]->nextGenPlatform,\n" + "sarah-[:WORKED_ON]->quantumLeap"; return createFromCypher( "Social Network", cypher, IndexParam.indexParam( "User", "name" ), IndexParam.indexParam( "Topic", "name" ), IndexParam.indexParam( "Project", "name" ) ); } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/helpers/Db.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdatabases.queries.testing.IndexParams; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.index.AutoIndexer; import org.neo4j.test.AsciiDocGenerator; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.visualization.asciidoc.AsciidocHelper; public final class Db { private Db() { } public static GraphDatabaseService impermanentDb() { Map params = new HashMap(); params.put( "online_backup_enabled", "false" ); return new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder().setConfig( params ).newGraphDatabase(); } public static GraphDatabaseService tempDb() { return new GraphDatabaseFactory().newEmbeddedDatabase( createTempDatabaseDir().getAbsolutePath() ); } public static GraphDatabaseService createFromCypher( String name, String cypher, IndexParam... indexParams ) { GraphDatabaseService db = Db.impermanentDb(); return createFromCypher( db, name, cypher, indexParams ); } public static GraphDatabaseService createFromCypherWithAutoIndexing( String name, String cypher, IndexParam... indexParams ) { GraphDatabaseService db = Db.impermanentDb(); AutoIndexer nodeAutoIndexer = db.index().getNodeAutoIndexer(); for ( IndexParam indexParam : indexParams ) { nodeAutoIndexer.startAutoIndexingProperty( indexParam.propertyName() ); } nodeAutoIndexer.setEnabled( true ); return createFromCypher( db, name, cypher, indexParams ); } public static GraphDatabaseService createFromCypher( GraphDatabaseService db, String name, String cypher, IndexParam... indexParams ) { ExecutionEngine engine = new ExecutionEngine( db ); engine.execute( cypher ); new IndexParams( indexParams ).index( db); printGraph( name, db ); return db; } private static void printGraph( String name, GraphDatabaseService db ) { try { printFile( name, AsciidocHelper.createGraphVizDeletingReferenceNode( name, db, "graph" ) ); } catch ( NotFoundException e ) { printFile( name, AsciidocHelper.createGraphViz( name, db, "graph" ) ); } } private static void printFile( String fileName, String contents ) { PrintWriter writer = AsciiDocGenerator.getPrintWriter( "../examples/", fileName ); try { writer.println( contents ); } finally { writer.close(); } } private static File createTempDatabaseDir() { File d; try { d = File.createTempFile( "gdb-", "dir" ); System.out.println( String.format( "Created a new Neo4j database at [%s]", d.getAbsolutePath() ) ); } catch ( IOException e ) { throw new RuntimeException( e ); } if ( !d.delete() ) { throw new RuntimeException( "temp config directory pre-delete failed" ); } if ( !d.mkdirs() ) { throw new RuntimeException( "temp config directory not created" ); } d.deleteOnExit(); return d; } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/helpers/IndexNodeByOtherNodeIndexerTest.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import static org.junit.Assert.assertEquals; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import static org.neo4j.graphdb.DynamicRelationshipType.withName; import java.util.HashMap; import java.util.Map; import org.junit.Ignore; import org.junit.Test; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdb.*; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.kernel.Traversal; import org.neo4j.tooling.GlobalGraphOperations; public class IndexNodeByOtherNodeIndexerTest { @Test @Ignore("Doesn't work like that, the code used to index multiple children under one parent, not possible with the new indexes") public void shouldIndexNodesByOtherNodes() throws Exception { // given String cypher = "CREATE (a:Parent {name:'a'}), \n" + "(b:Child {name:'b'}), \n" + "(c:Child {name:'c'}), \n" + "(d:Child {name:'d'}), \n" + "(e:Child {name:'e'}), \n" + "(f:Child {name:'f'}),\n" + "(g:Child {name:'g'}),\n" + "(h:Child {name:'h'}),\n" + "a-[:CONNECTED_TO]->b,\n" + "a-[:CONNECTED_TO]->c,\n" + "a-[:CONNECTED_TO]->g,\n" + "d-[:CONNECTED_TO]->e,\n" + "d-[:CONNECTED_TO]->f,\n" + "d-[:CONNECTED_TO]->h"; final GraphDatabaseService db = createFromCypher( "Example", cypher, IndexParam.indexParam( "Parent", "name" ) ); ExecutionEngine executionEngine = new ExecutionEngine(db); IndexNodeByOtherNodeIndexer.GraphTraversal traversal1 = new IndexNodeByOtherNodeIndexer.GraphTraversal(){ @Override public Iterable execute( Node startNode ) { return GlobalGraphOperations.at(db).getAllNodesWithLabel(DynamicLabel.label("Parent")); } }; IndexNodeByOtherNodeIndexer.GraphTraversal traversal2 = new IndexNodeByOtherNodeIndexer.GraphTraversal(){ @Override public Iterable execute( Node startNode ) { TraversalDescription traversalDescription = db.traversalDescription().breadthFirst().relationships( withName( "CONNECTED_TO" ), Direction.OUTGOING ).evaluator( new Evaluator() { @Override public Evaluation evaluate( Path path ) { if ( path.endNode().equals( path.startNode() ) ) { return Evaluation.EXCLUDE_AND_CONTINUE; } return Evaluation.INCLUDE_AND_CONTINUE; } } ); return traversalDescription.traverse( startNode ).nodes(); } }; IndexNodeByOtherNodeIndexer indexer = new IndexNodeByOtherNodeIndexer( traversal1, traversal2, "Parent", "child", "name" ); // when indexer.execute( db, null, 2 ); // then Map indexValueToResult = new HashMap<>( ); indexValueToResult.put( "b", "a" ); indexValueToResult.put( "c", "a" ); indexValueToResult.put( "g", "a" ); indexValueToResult.put( "e", "d" ); indexValueToResult.put( "f", "d" ); indexValueToResult.put( "h", "d" ); for ( String indexValue : indexValueToResult.keySet() ) { String query = "MATCH (n:Parent {child:'"+indexValue+"'}) RETURN n.name AS parent"; ExecutionResult result = executionEngine.execute(query); assertEquals(indexValueToResult.get( indexValue ), result.iterator().next().get( "parent" )); } } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/helpers/PrintingExecutionEngineWrapper.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.io.PrintWriter; import java.util.Map; import org.junit.rules.TestName; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.test.AsciiDocGenerator; public class PrintingExecutionEngineWrapper implements ExecutionEngineWrapper { private final ExecutionEngine executionEngine; private final String useCase; private final TestName testName; public PrintingExecutionEngineWrapper( GraphDatabaseService db, String useCase, TestName testName ) { this.useCase = useCase; this.executionEngine = new ExecutionEngine( db ); this.testName = testName; } @Override public ExecutionResult execute( String query, Map params ) { return execute( query, params, 1 ); } @Override public ExecutionResult execute( String query, Map params, int index ) { printQuery( query, index ); ExecutionResult returnResult = executionEngine.execute( query, params ); // For CREATE queries, this may not return the same results ExecutionResult printResult = executionEngine.execute( query, params ); printResult( printResult, index ); return returnResult; } private void printResult( org.neo4j.cypher.javacompat.ExecutionResult results, int index ) { String output = "[queryresult]\n----\n" + results.dumpToString() + "\n----\n"; printFile( useCase + "-" + testName.getMethodName() + "-result-" + index, output ); } private void printQuery( String query, int index ) { String output = "----\n" + query + "\n----\n"; printFile( useCase + "-" + testName.getMethodName() + "-query-" + index, output ); } private static void printFile( String fileName, String contents ) { try (PrintWriter writer = AsciiDocGenerator.getPrintWriter("../examples", fileName)) { writer.println(contents); } } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/helpers/QueryUnionExecutionEngineTest.java ================================================ package org.neo4j.graphdatabases.queries.helpers; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import static org.neo4j.graphdatabases.queries.testing.IndexParam.indexParam; public class QueryUnionExecutionEngineTest { private static final GraphDatabaseService database = createDatabase(); private static ExecutionEngineWrapper executionEngine; private static Transaction transaction; @BeforeClass public static void init() { try { executionEngine = new ExecutionEngineWrapper() { private ExecutionEngine engine = new ExecutionEngine(database); @Override public ExecutionResult execute( String query, Map params ) { return engine.execute( query, params ); } @Override public ExecutionResult execute( String query, Map params, int index ) { return execute( query, params ); } }; } catch ( Exception e ) { System.out.println( e.getMessage() ); } transaction = database.beginTx(); } @AfterClass public static void end() { transaction.close(); database.shutdown(); } @Test public void shouldAllowIteratingSingleResult() throws Exception { // given String query = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when Iterator> results = queryUnionExecutionEngine.execute( query ).iterator(); // then assertEquals( "b", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "c", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldAllowIteratingMultipleResults() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:ENEMY]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when Iterator> results = queryUnionExecutionEngine.execute( query1, query2 ).iterator(); // then assertEquals( "b", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "c", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "d", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "e", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldReturnCorrectResultsWhenFirstQueryReturnsEmptyResults() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:COLLEAGUE]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:ENEMY]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when Iterator> results = queryUnionExecutionEngine.execute( query1, query2 ).iterator(); // then assertEquals( "d", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "e", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldReturnCorrectResultsWhenLastQueryReturnsEmptyResults() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:COLLEAGUE]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when Iterator> results = queryUnionExecutionEngine.execute( query1, query2 ).iterator(); // then assertEquals( "b", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "c", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldReturnCorrectResultsWhenMiddleQueryReturnsEmptyResults() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:COLLEAGUE]->(person)\n" + "RETURN person"; String query3 = "MATCH (a:User {name:'a'})-[:ENEMY]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when Iterator> results = queryUnionExecutionEngine.execute( query1, query2, query3 ).iterator(); // then assertEquals( "b", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "c", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "d", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "e", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldReturnCorrectResultsWhenAllQueriesReturnEmptyResults() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:COLLEAGUE]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:COLLEAGUE]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when Iterator> results = queryUnionExecutionEngine.execute( query1, query2 ).iterator(); // then assertFalse( results.hasNext() ); } @Test public void shouldAllowSameParametersToBePassedToAllQueries() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:ENEMY]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); Map params = new HashMap(); params.put( "name", "a" ); // when Iterator> results = queryUnionExecutionEngine.execute( params, query1, query2 ).iterator(); // then assertEquals( "b", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "c", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "d", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "e", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldAllowDifferentParametersToBePassedToDifferentQueries() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person"; String query2 = "MATCH (a:User {name:'a'})-[:ENEMY]->(person)\n" + "RETURN person"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); Map params = new HashMap(); params.put( "name", "a" ); params.put( "user", "a" ); // when Iterator> results = queryUnionExecutionEngine.execute( params, query1, query2 ).iterator(); // then assertEquals( "b", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "c", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "d", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertEquals( "e", ((Node) results.next().get( "person" )).getProperty( "name" ) ); assertFalse( results.hasNext() ); } @Test public void shouldThrowExceptionIfNoQueriesSupplied() throws Exception { // given QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // then try { queryUnionExecutionEngine.execute(); fail( "Expected IllegalArgumentException" ); } catch ( IllegalArgumentException ex ) { assertEquals( "Must supply one or more queries.", ex.getMessage() ); } } @Test public void shouldReturnAllResultsAsString() throws Exception { // given String query1 = "MATCH (a:User {name:'a'})-[:FRIEND]->(person)\n" + "RETURN person.name"; String query2 = "MATCH (a:User {name:'a'})-[:ENEMY]->(person)\n" + "RETURN person.name"; QueryUnionExecutionEngine queryUnionExecutionEngine = new QueryUnionExecutionEngine( executionEngine ); // when QueryUnionExecutionResult results = (QueryUnionExecutionResult) queryUnionExecutionEngine.execute( query1, query2 ); // then String resultsString = results.toString(); assertTrue( resultsString.startsWith( "+-------------+\n" + "| person.name |\n" + "+-------------+\n" + "| \"b\" |\n" + "| \"c\" |\n" + "+-------------+\n" + "2 rows\n" ) ); assertTrue( resultsString.contains( "+-------------+\n" + "| person.name |\n" + "+-------------+\n" + "| \"d\" |\n" + "| \"e\" |\n" + "+-------------+\n" + "2 rows\n" ) ); } private static GraphDatabaseService createDatabase() { String cypher = "CREATE\n" + "(a:User {name:'a'}),\n" + "(b:User {name:'b'}),\n" + "(c:User {name:'c'}),\n" + "(d:User {name:'d'}),\n" + "(e:User {name:'e'}),\n" + "(a)-[:FRIEND]->(b), (a)-[:FRIEND]->(c), (a)-[:ENEMY]->(d), (a)-[:ENEMY]->(e)"; return createFromCypher( "Union example", cypher, indexParam( "User", "name" ) ); } } ================================================ FILE: queries/src/test/java/org/neo4j/graphdatabases/queries/server/SimpleSocialNetworkExtensionTest.java ================================================ package org.neo4j.graphdatabases.queries.server; import static org.junit.Assert.assertEquals; import static org.neo4j.graphdatabases.queries.helpers.Db.createFromCypher; import java.io.IOException; import javax.ws.rs.core.MediaType; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.graphdatabases.queries.testing.IndexParam; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.server.CommunityNeoServer; import org.neo4j.server.helpers.CommunityServerBuilder; public class SimpleSocialNetworkExtensionTest { private static CommunityNeoServer server; private static GraphDatabaseService db; @BeforeClass public static void init() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.graphdatabases.queries.server", "/socnet" ) .build(); server.start(); db = server.getDatabase().getGraph(); populateDatabase( db ); } @AfterClass public static void teardown() { server.stop(); } @Test public void serverShouldReturnDistance() throws Exception { ClientConfig config = new DefaultClientConfig(); Client client = Client.create( config ); WebResource resource = client .resource( "http://localhost:7474/socnet/distance/Ben/Mike" ); ClientResponse response = resource .accept( MediaType.TEXT_PLAIN ) .get( ClientResponse.class ); assertEquals( 200, response.getStatus() ); assertEquals( "text/plain", response.getHeaders().get( "Content-Type" ).get( 0 ) ); assertEquals( "4", response.getEntity( String.class ) ); } @Test public void extensionShouldReturnDistance() throws Exception { // given SimpleSocialNetworkExtension extension = new SimpleSocialNetworkExtension( db ); // when String distance = extension.getDistance( "Ben", "Mike" ); // then assertEquals( "4", distance ); } private static GraphDatabaseService populateDatabase( GraphDatabaseService db ) { String cypher = "CREATE\n" + "(ben:User {name:'Ben'}),\n" + "(arnold:User {name:'Arnold'}),\n" + "(charlie:User {name:'Charlie'}),\n" + "(gordon:User {name:'Gordon'}),\n" + "(lucy:User {name:'Lucy'}),\n" + "(emily:User {name:'Emily'}),\n" + "(sarah:User {name:'Sarah'}),\n" + "(kate:User {name:'Kate'}),\n" + "(mike:User {name:'Mike'}),\n" + "(paula:User {name:'Paula'}),\n" + "ben-[:FRIEND]->charlie,\n" + "charlie-[:FRIEND]->lucy,\n" + "lucy-[:FRIEND]->sarah,\n" + "sarah-[:FRIEND]->mike,\n" + "arnold-[:FRIEND]->gordon,\n" + "gordon-[:FRIEND]->emily,\n" + "emily-[:FRIEND]->kate,\n" + "kate-[:FRIEND]->paula"; return createFromCypher( db, "Simple Social Network", cypher, IndexParam.indexParam( "User", "name" ) ); } }