├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── examples ├── config │ ├── articles.cypher │ ├── world-cup-db.json │ ├── movies-db.json │ ├── rbac-roles.json │ ├── reuters-db.json │ ├── role-analyst.json │ └── movies.cypher ├── aura │ ├── docker-compose.yml │ └── config │ │ └── db.json ├── 01-create-database.json ├── 02-create-role.json └── movies.cypher ├── src ├── test │ ├── resources │ │ ├── config │ │ │ ├── articles.cypher │ │ │ ├── world-cup-db.json │ │ │ ├── rbac-roles.json │ │ │ ├── reuters-db.json │ │ │ ├── role-analyst.json │ │ │ ├── movies-db.json │ │ │ └── rel-constraints.json │ │ └── application.properties │ └── java │ │ └── com │ │ └── graphaware │ │ └── neo4j │ │ └── config │ │ ├── unit │ │ ├── PropertyTypesTest.java │ │ └── RecordDeserializationTest.java │ │ ├── Neo4jIntegrationBase.java │ │ ├── CreateDatabaseTest.java │ │ ├── Neo4jAsserts.java │ │ ├── MultipleNeo4jVersionsTest.java │ │ └── Neo4jConfigurationTest.java └── main │ ├── java │ └── com │ │ └── graphaware │ │ └── neo4j │ │ └── config │ │ ├── model │ │ ├── rbac │ │ │ ├── RuleAccess.java │ │ │ ├── Rule.java │ │ │ ├── Privilege.java │ │ │ └── Role.java │ │ ├── Indexes.java │ │ ├── Database.java │ │ └── schema │ │ │ ├── IndexType.java │ │ │ ├── NodeIndex.java │ │ │ ├── NodeConstraint.java │ │ │ ├── RelationshipIndex.java │ │ │ ├── RelationshipConstraint.java │ │ │ ├── Constraints.java │ │ │ ├── ConstraintType.java │ │ │ └── PropertyType.java │ │ ├── service │ │ ├── DbmsComponent.java │ │ ├── CreateNodeConstraint.java │ │ ├── CreateRelationshipConstraint.java │ │ ├── CreateNodeIndex.java │ │ ├── CreateRelationshipIndex.java │ │ └── CreateDatabaseService.java │ │ ├── util │ │ ├── ConfigUtils.java │ │ ├── FilesToImport.java │ │ └── FileCollectionUtils.java │ │ ├── Neo4jConfigCliApplication.java │ │ ├── properties │ │ ├── ApplicationConfiguration.java │ │ └── ImportConfiguration.java │ │ ├── version │ │ └── Neo4jVersion.java │ │ ├── ImportProvider.java │ │ ├── Neo4jConfigRunner.java │ │ └── GraphDatabaseImport.java │ └── resources │ └── application.properties ├── Dockerfile ├── .gitignore ├── docker-compose.yml ├── CHANGELOG.md ├── pom.xml ├── mvnw.cmd ├── README.md ├── mvnw └── LICENSE.txt /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphaware/neo4j-config-cli/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /examples/config/articles.cypher: -------------------------------------------------------------------------------- 1 | CREATE (:Article {url: "https://graphware.com/blog.html", description: "GraphAware blog"}); -------------------------------------------------------------------------------- /src/test/resources/config/articles.cypher: -------------------------------------------------------------------------------- 1 | CREATE (:Article {url: "https://graphware.com/blog.html", description: "GraphAware blog"}); -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/rbac/RuleAccess.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.model.rbac; 2 | 3 | public enum RuleAccess { 4 | GRANTED, 5 | DENIED 6 | } 7 | -------------------------------------------------------------------------------- /examples/config/world-cup-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "world.cup", 4 | "dropIfExists": "false", 5 | "seedFromUri": "https://downloads.graphaware.com/neo4j-db-seeds/world-cup-2022-neo4j.backup" 6 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/rbac/Rule.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.model.rbac; 2 | 3 | public record Rule(String target, String labels, String action, String resource, RuleAccess access) { 4 | } 5 | -------------------------------------------------------------------------------- /src/test/resources/config/world-cup-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "world.cup", 4 | "dropIfExists": "false", 5 | "seedFromUri": "https://downloads.graphaware.com/neo4j-db-seeds/world-cup-2022-neo4j.backup" 6 | } -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eclipse-temurin:17.0.5_8-jre-ubi9-minimal 2 | 3 | RUN mkdir -p /opt 4 | COPY target/neo4j-config-cli-*.jar /opt/app.jar 5 | 6 | VOLUME /import 7 | 8 | USER 1001 9 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/opt/app.jar"] 10 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.neo4j.uri=${NEO4J_URI} 2 | spring.neo4j.authentication.username=${NEO4J_USER} 3 | spring.neo4j.authentication.password=${NEO4J_PASSWORD} 4 | NEO4J_USER=neo4j 5 | import.path=/config 6 | import.waiting.timeout=120000 7 | seed-only=false 8 | seed-db=neo4j -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/rbac/Privilege.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.model.rbac; 2 | 3 | import java.util.List; 4 | 5 | public record Privilege(String graph, boolean access, List rules) { 6 | 7 | public Privilege { 8 | rules = List.copyOf(rules); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | org.neo4j.driver.uri=${neo4j.uri} 2 | org.neo4j.driver.authentication.username=${neo4j.username} 3 | org.neo4j.driver.authentication.password=${neo4j.password} 4 | neo4j.uri=bolt://localhost:7687 5 | neo4j.username=neo4j 6 | neo4j.password=password 7 | import.path=/Users/christophewillemsen/dev/graphaware/cto/neo4j-config-cli/examples -------------------------------------------------------------------------------- /examples/aura/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | neo4j-config-cli: 4 | image: graphaware/neo4j-config-cli:1.2.0 5 | environment: 6 | - NEO4J_USER=neo4j 7 | - NEO4J_PASSWORD=r3plac3withpassw)rd 8 | - NEO4J_URI=neo4j+s://h666r91.databases.neo4j.io 9 | - IMPORT_PATH=/config 10 | volumes: 11 | - "./config:/config" -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/rbac/Role.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.model.rbac; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.util.List; 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public record Role(String name, boolean dropIfExists, List privileges) { 9 | 10 | public Role { 11 | privileges = List.copyOf(privileges); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/01-create-database.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "movies", 4 | "dropIfExists": "true", 5 | "indexes": { 6 | "fulltext" : [ 7 | { 8 | "labels": ["Person"], 9 | "properties": ["name"], 10 | "name": "Person" 11 | } 12 | ] 13 | }, 14 | "constraints": { 15 | "unique": [ 16 | { 17 | "label": "Movie", 18 | "property": "title" 19 | } 20 | ] 21 | }, 22 | "seeds": ["movies.cypher"] 23 | } -------------------------------------------------------------------------------- /examples/aura/config/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "neo4j", 4 | "dropIfExists": false, 5 | "skipCreate": true, 6 | "indexes": { 7 | "fulltext" : [ 8 | { 9 | "labels": ["Person"], 10 | "properties": ["name"], 11 | "name": "Person" 12 | } 13 | ] 14 | }, 15 | "constraints": { 16 | "unique": [ 17 | { 18 | "label": "Movie", 19 | "property": "title", 20 | "type": "UNIQUE" 21 | } 22 | ] 23 | }, 24 | "seeds": ["movies.cypher"] 25 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/unit/PropertyTypesTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.unit; 2 | 3 | import com.graphaware.neo4j.config.model.schema.PropertyType; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class PropertyTypesTest { 7 | 8 | @Test 9 | void test_lists() { 10 | var list_of_strings = PropertyType.LIST_OF_STRING; 11 | System.out.println(list_of_strings.getValue()); 12 | 13 | var from_string = PropertyType.valueOf("LIST_OF_STRING"); 14 | System.out.println(from_string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/Indexes.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.model; 2 | 3 | import com.graphaware.neo4j.config.model.schema.NodeIndex; 4 | import com.graphaware.neo4j.config.model.schema.RelationshipIndex; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public record Indexes(List nodes, List relationships) { 10 | 11 | public Indexes { 12 | nodes = List.copyOf(nodes); 13 | relationships = List.copyOf(Optional.ofNullable(relationships).orElse(List.of())); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/config/movies-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "movies", 4 | "dropIfExists": "true", 5 | "indexes": { 6 | "nodes" : [ 7 | { 8 | "labels": ["Person"], 9 | "properties": ["name"], 10 | "name": "Person", 11 | "type": "FULLTEXT" 12 | } 13 | ], 14 | }, 15 | "constraints": { 16 | "nodes": [ 17 | { 18 | "label": "Person", 19 | "property": "name", 20 | "type": "UNIQUE" 21 | } 22 | ] 23 | }, 24 | "seeds": ["movies.cypher"] 25 | } -------------------------------------------------------------------------------- /examples/02-create-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Role", 3 | "name": "ut1", 4 | "dropIfExists": true, 5 | "privileges": [ 6 | { 7 | "graph": "*", 8 | "access": true, 9 | "rules": [ 10 | { 11 | "target": "node", 12 | "labels": "*", 13 | "action": "match", 14 | "resource": "all_properties", 15 | "access": "GRANTED" 16 | }, 17 | { 18 | "target": "node", 19 | "labels": "Person", 20 | "action": "read", 21 | "resource": "born", 22 | "access": "DENIED" 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /examples/config/rbac-roles.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Role", 3 | "name": "ut1", 4 | "dropIfExists": true, 5 | "privileges": [ 6 | { 7 | "graph": "*", 8 | "access": true, 9 | "rules": [ 10 | { 11 | "target": "node", 12 | "labels": "*", 13 | "action": "match", 14 | "resource": "all_properties", 15 | "access": "GRANTED" 16 | }, 17 | { 18 | "target": "node", 19 | "labels": "Person", 20 | "action": "read", 21 | "resource": "born", 22 | "access": "DENIED" 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | neo4j: 4 | image: neo4j:5.6.0-enterprise 5 | ports: 6 | - "7474:7474" 7 | - "7687:7687" 8 | environment: 9 | - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes 10 | - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD:-password} 11 | neo4j-config-cli: 12 | image: graphaware/neo4j-config-cli:2.0.0 13 | environment: 14 | - NEO4J_PASSWORD=password 15 | - NEO4J_URI=bolt://neo4j:7687 16 | # - IMPORT_PATH=https://gist.githubusercontent.com/ikwattro/f99c1ed085673065fcb4e850526ccd49/raw/35f077b71e3f7a9fd95b0288cf6d622eea3d6501/db-demo.json 17 | - IMPORT_PATH=/import 18 | volumes: 19 | - "./examples/config:/import" -------------------------------------------------------------------------------- /src/test/resources/config/rbac-roles.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Role", 3 | "name": "ut1", 4 | "dropIfExists": true, 5 | "privileges": [ 6 | { 7 | "graph": "*", 8 | "access": true, 9 | "rules": [ 10 | { 11 | "target": "node", 12 | "labels": "*", 13 | "action": "match", 14 | "resource": "all_properties", 15 | "access": "GRANTED" 16 | }, 17 | { 18 | "target": "node", 19 | "labels": "Person", 20 | "action": "read", 21 | "resource": "born", 22 | "access": "DENIED" 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/Database.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.graphaware.neo4j.config.model.schema.Constraints; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public record Database(String name, boolean dropIfExists, boolean skipIfCreate, Indexes indexes, Constraints constraints, List seeds, String seedFromUri, boolean composite, List constituents) { 11 | 12 | public Database { 13 | seeds = List.copyOf(Optional.ofNullable(seeds).orElse(List.of())); 14 | constituents = List.copyOf(Optional.ofNullable(constituents).orElse(List.of())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/config/reuters-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "reuters", 4 | "dropIfExists": "true", 5 | "indexes": { 6 | "nodes" : [ 7 | { 8 | "labels": ["Article"], 9 | "properties": ["description", "text"], 10 | "name": "Article", 11 | "type": "FULLTEXT" 12 | }, 13 | { 14 | "labels": ["Author"], 15 | "properties": ["name"], 16 | "type": "FULLTEXT" 17 | } 18 | ] 19 | }, 20 | "constraints": { 21 | "nodes": [ 22 | { 23 | "labels": ["Author"], 24 | "property": "name", 25 | "type": "UNIQUE" 26 | }, 27 | { 28 | "labels": ["Article"], 29 | "property": "url", 30 | "type": "UNIQUE" 31 | } 32 | ] 33 | }, 34 | "seeds": ["articles.cypher"] 35 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/service/DbmsComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.graphaware.neo4j.config.service; 18 | 19 | import java.util.List; 20 | 21 | public record DbmsComponent(String name, List versions, String edition) {} 22 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/IndexType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | public enum IndexType { 21 | FULLTEXT, 22 | BTREE, 23 | RANGE, 24 | TEXT, 25 | POINT 26 | } 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.7.2 2 | 3 | - Hotfix : Handle Neo4j 2025.05.0 returning more than one row for `dbms.components()` 4 | 5 | ## 2.7.1 6 | 7 | - Hotfix : create database when in cluster 8 | 9 | ## 2.7.0 10 | 11 | - Upgrade driver to 5.27.0 12 | - Baseline 4.4.40 and 5.26.0 for tests 13 | - Support LIST<> property type constraints 14 | 15 | ## 2.5.0 16 | 17 | - Added support for composite database seeds 18 | - Composite databases are now created after normal databases 19 | - Drop database takes care of removing aliases before drop 20 | 21 | ## 2.4.0 22 | 23 | - Added support for property type constraints 24 | 25 | ## 2.3.0 26 | 27 | - Added support for composite database creation 28 | 29 | ## 2.2.0 30 | 31 | - Added support for Relationship Constraints 32 | 33 | ## 1.3.0 34 | 35 | - Seed only command 36 | 37 | ## 1.2.0 38 | 39 | - Added support for remote urls for `IMPORT_PATH` 40 | - Skip empty lines during import 41 | 42 | ## 1.1.0 Initial release -------------------------------------------------------------------------------- /src/test/resources/config/reuters-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "reuters", 4 | "dropIfExists": "true", 5 | "indexes": { 6 | "nodes" : [ 7 | { 8 | "labels": ["Article"], 9 | "properties": ["description", "text"], 10 | "name": "Article", 11 | "type": "FULLTEXT" 12 | }, 13 | { 14 | "labels": ["Author"], 15 | "properties": ["name"], 16 | "type": "FULLTEXT" 17 | } 18 | ], 19 | "relationships": [] 20 | }, 21 | "constraints": { 22 | "nodes": [ 23 | { 24 | "labels": ["Author"], 25 | "properties": ["name"], 26 | "type": "UNIQUE" 27 | }, 28 | { 29 | "labels": ["Article"], 30 | "properties": ["url"], 31 | "type": "UNIQUE" 32 | } 33 | ], 34 | "relationships": [] 35 | }, 36 | "seeds": ["articles.cypher"] 37 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/NodeIndex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | import java.util.List; 21 | 22 | public record NodeIndex(List labels, List properties, String name, IndexType type) { 23 | public NodeIndex { 24 | labels = List.copyOf(labels); 25 | properties = List.copyOf(properties); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/NodeConstraint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | import java.util.List; 21 | 22 | public record NodeConstraint(List labels, List properties, String name, ConstraintType type, PropertyType propertyType) { 23 | public NodeConstraint { 24 | labels = List.copyOf(labels); 25 | properties = List.copyOf(properties); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/RelationshipIndex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | import java.util.List; 21 | 22 | public record RelationshipIndex(List relationshipTypes, List properties, String name, IndexType type) { 23 | public RelationshipIndex { 24 | relationshipTypes = List.copyOf(relationshipTypes); 25 | properties = List.copyOf(properties); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/util/ConfigUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.util; 19 | 20 | import java.net.URL; 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.Scanner; 23 | 24 | public class ConfigUtils { 25 | 26 | public static String URLToString(String url) throws Exception { 27 | return new Scanner(new URL(url).openStream(), StandardCharsets.UTF_8).useDelimiter("\\A").next(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/RelationshipConstraint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | import java.util.List; 21 | 22 | public record RelationshipConstraint(List relationshipTypes, List properties, String name, ConstraintType type, PropertyType propertyType) { 23 | public RelationshipConstraint { 24 | relationshipTypes = List.copyOf(relationshipTypes); 25 | properties = List.copyOf(properties); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/Neo4jIntegrationBase.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.neo4j.driver.AuthTokens; 5 | import org.neo4j.driver.Driver; 6 | import org.neo4j.driver.GraphDatabase; 7 | import org.testcontainers.containers.Neo4jContainer; 8 | import org.testcontainers.junit.jupiter.Container; 9 | import org.testcontainers.junit.jupiter.Testcontainers; 10 | 11 | @Testcontainers 12 | public abstract class Neo4jIntegrationBase { 13 | 14 | protected Driver driver; 15 | 16 | protected Neo4jAsserts neo4jAsserts; 17 | 18 | @BeforeEach 19 | public void setUp() { 20 | driver = driver(); 21 | neo4jAsserts = new Neo4jAsserts(driver); 22 | } 23 | 24 | @Container 25 | protected static Neo4jContainer neo4jContainer = (Neo4jContainer) new Neo4jContainer("neo4j:4.4.40-enterprise") 26 | .withAdminPassword("password") 27 | .withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes"); 28 | 29 | private Driver driver() { 30 | return GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", "password")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/Constraints.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | import java.util.List; 21 | import java.util.Optional; 22 | 23 | 24 | public record Constraints(List nodes, List relationships) { 25 | 26 | public Constraints { 27 | nodes = List.copyOf(Optional.ofNullable(nodes).orElse(List.of())); 28 | relationships = List.copyOf(Optional.ofNullable(relationships).orElse(List.of())); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/Neo4jConfigCliApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config; 19 | 20 | import org.springframework.boot.SpringApplication; 21 | import org.springframework.boot.autoconfigure.SpringBootApplication; 22 | 23 | @SpringBootApplication(proxyBeanMethods = false) 24 | public class Neo4jConfigCliApplication { 25 | 26 | public static void main(String[] args) { 27 | 28 | System.exit( 29 | SpringApplication.exit( 30 | SpringApplication.run(Neo4jConfigCliApplication.class, args) 31 | ) 32 | ); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/ConstraintType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | public enum ConstraintType { 21 | UNIQUE("UNIQUE"), 22 | NOT_NULL("NOT NULL"), 23 | NODE_KEY("NODE KEY"), 24 | RELATIONSHIP_KEY("RELATIONSHIP KEY"), 25 | 26 | PROPERTY_TYPE("PROPERTY_TYPE"); 27 | 28 | private final String value; 29 | 30 | ConstraintType(String value) { 31 | this.value = value; 32 | } 33 | 34 | public String getValue() { 35 | return value; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/config/role-analyst.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Role", 3 | "name": "analyst", 4 | "dropIfExists": true, 5 | "privileges": [ 6 | { 7 | "graph": "cases", 8 | "access": true, 9 | "rules": [ 10 | { 11 | "target": "node", 12 | "labels": "Case", 13 | "action": "match", 14 | "resource": "all_properties", 15 | "access": "GRANTED" 16 | }, 17 | { 18 | "target": "node", 19 | "labels": "Case456", 20 | "action": "match", 21 | "resource": "all_properties", 22 | "access": "DENIED" 23 | }, 24 | { 25 | "target": "relationship", 26 | "labels": "*", 27 | "action": "read", 28 | "resource": "*", 29 | "access": "GRANTED" 30 | }, 31 | { 32 | "target": "relationship", 33 | "labels": "*", 34 | "action": "traverse", 35 | "resource": "*", 36 | "access": "GRANTED" 37 | } 38 | ] 39 | }, 40 | { 41 | "graph": "movies", 42 | "access": true, 43 | "rules": [ 44 | { 45 | "target": "node", 46 | "labels": "Movie", 47 | "action": "match", 48 | "resource": "all_properties", 49 | "access": "GRANTED" 50 | } 51 | ] 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /src/test/resources/config/role-analyst.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Role", 3 | "name": "analyst", 4 | "dropIfExists": true, 5 | "privileges": [ 6 | { 7 | "graph": "movies", 8 | "access": true, 9 | "rules": [ 10 | { 11 | "target": "node", 12 | "labels": "Case", 13 | "action": "match", 14 | "resource": "all_properties", 15 | "access": "GRANTED" 16 | }, 17 | { 18 | "target": "node", 19 | "labels": "Case456", 20 | "action": "match", 21 | "resource": "all_properties", 22 | "access": "DENIED" 23 | }, 24 | { 25 | "target": "relationship", 26 | "labels": "*", 27 | "action": "read", 28 | "resource": "*", 29 | "access": "GRANTED" 30 | }, 31 | { 32 | "target": "relationship", 33 | "labels": "*", 34 | "action": "traverse", 35 | "resource": "*", 36 | "access": "GRANTED" 37 | } 38 | ] 39 | }, 40 | { 41 | "graph": "movies", 42 | "access": true, 43 | "rules": [ 44 | { 45 | "target": "node", 46 | "labels": "Movie", 47 | "action": "match", 48 | "resource": "all_properties", 49 | "access": "GRANTED" 50 | } 51 | ] 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/util/FilesToImport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.util; 19 | 20 | import java.io.File; 21 | import java.util.Collection; 22 | 23 | public class FilesToImport { 24 | 25 | private final Collection configurationFiles; 26 | 27 | private final Collection cypherSeeds; 28 | 29 | public FilesToImport(Collection configurationFiles, Collection cypherSeeds) { 30 | this.configurationFiles = configurationFiles; 31 | this.cypherSeeds = cypherSeeds; 32 | } 33 | 34 | public Collection getConfigurationFiles() { 35 | return configurationFiles; 36 | } 37 | 38 | public Collection getCypherSeeds() { 39 | return cypherSeeds; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/properties/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.properties; 19 | 20 | import org.springframework.beans.factory.annotation.Value; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | @Configuration 25 | public class ApplicationConfiguration { 26 | 27 | @Value("${import.path}") 28 | private String importPath; 29 | 30 | @Value("${seed-only}") 31 | private boolean seedOnly; 32 | 33 | @Value("${seed-url:#{null}}") 34 | private String seedUrl; 35 | 36 | @Value("${seed-db}") 37 | private String seedDb; 38 | 39 | @Bean 40 | public ImportConfiguration importConfiguration() { 41 | return new ImportConfiguration(importPath, seedOnly, seedUrl, seedDb); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/version/Neo4jVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.version; 19 | 20 | public enum Neo4jVersion { 21 | UNDEFINED, 22 | /** 23 | * Constant for Neo4j 4.4 24 | */ 25 | V4_4, 26 | /** 27 | * Constant for Neo4j 5 28 | */ 29 | V5, 30 | CALENDAR_VERSION; 31 | 32 | Neo4jVersion() { 33 | } 34 | 35 | public static Neo4jVersion of(String version) { 36 | if (version == null) { 37 | return UNDEFINED; 38 | } else if (version.startsWith("4.4")) { 39 | return V4_4; 40 | } else if (version.startsWith("5.")) { 41 | return V5; 42 | } else if (version.startsWith("20")) { 43 | return CALENDAR_VERSION; 44 | } 45 | 46 | throw new IllegalArgumentException(String.format("Unsupported Neo4j version %s", version)); 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/properties/ImportConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.properties; 19 | 20 | public class ImportConfiguration { 21 | 22 | private final String importPath; 23 | private final boolean seedOnly; 24 | private final String seedUrl; 25 | private final String seedDb; 26 | 27 | public ImportConfiguration(String importPath, boolean seedOnly, String seedUrl, String seedDb) { 28 | this.importPath = importPath; 29 | this.seedOnly = seedOnly; 30 | this.seedUrl = seedUrl; 31 | this.seedDb = seedDb; 32 | } 33 | 34 | public String getImportPath() { 35 | return importPath; 36 | } 37 | 38 | public boolean isSeedOnly() { 39 | return seedOnly; 40 | } 41 | 42 | public String getSeedUrl() { 43 | return seedUrl; 44 | } 45 | 46 | public String getSeedDb() { 47 | return seedDb; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/unit/RecordDeserializationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.unit; 19 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import com.graphaware.neo4j.config.model.Database; 22 | import org.junit.jupiter.api.Test; 23 | 24 | public class RecordDeserializationTest { 25 | 26 | @Test 27 | void test_mapping_json_with_empty_values_to_record() throws Exception { 28 | String json = """ 29 | { 30 | "kind": "Database", 31 | "name": "world.cup", 32 | "dropIfExists": "false", 33 | "seedFromUri": "https://downloads.graphaware.com/neo4j-db-seeds/world-cup-2022-neo4j.backup", 34 | "indexes": { 35 | "nodes": [] 36 | } 37 | }"""; 38 | 39 | var om = new ObjectMapper(); 40 | var db = om.readValue(json, Database.class); 41 | System.out.println(db); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/CreateDatabaseTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config; 2 | 3 | import com.graphaware.neo4j.config.model.Database; 4 | import com.graphaware.neo4j.config.model.Indexes; 5 | import com.graphaware.neo4j.config.model.schema.Constraints; 6 | import com.graphaware.neo4j.config.service.CreateDatabaseService; 7 | import org.junit.jupiter.api.Test; 8 | import org.neo4j.driver.Session; 9 | import org.neo4j.driver.SessionConfig; 10 | 11 | import java.util.List; 12 | 13 | public class CreateDatabaseTest extends Neo4jIntegrationBase { 14 | 15 | private Database database(String name, boolean dropIfExists) { 16 | return new Database(name, dropIfExists, false, new Indexes(List.of(), List.of()), new Constraints(List.of(), List.of()), List.of(), null, false, List.of()); 17 | } 18 | 19 | @Test 20 | public void create_database() { 21 | CreateDatabaseService createDatabaseService = new CreateDatabaseService(driver); 22 | Database database = database("test", true); 23 | neo4jAsserts.databaseNotExists(database.name()); 24 | createDatabaseService.createDatabase(database); 25 | neo4jAsserts.databaseExists(database.name()); 26 | } 27 | 28 | @Test 29 | public void drop_before_create() { 30 | CreateDatabaseService createDatabaseService = new CreateDatabaseService(driver); 31 | Database database = database("droptest", true); 32 | createDatabaseService.createDatabase(database); 33 | insertSomeData(database.name()); 34 | neo4jAsserts.databaseNotEmpty(database.name()); 35 | createDatabaseService.createDatabase(database); 36 | neo4jAsserts.databaseEmpty(database.name()); 37 | } 38 | 39 | private void insertSomeData(String databaseName) { 40 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 41 | session.run("UNWIND range(1, 10) AS i CREATE (n:Node {id: i, data: timestamp()})"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/resources/config/movies-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "movies", 4 | "dropIfExists": "true", 5 | "indexes": { 6 | "nodes": [ 7 | { 8 | "labels": ["Person"], 9 | "properties": ["name"], 10 | "name": "Person", 11 | "type": "FULLTEXT" 12 | } 13 | ], 14 | "relationships": [ 15 | { 16 | "relationshipTypes": ["ACTED_IN"], 17 | "properties": ["roles"], 18 | "name": "rel_actedIn_roles" 19 | }, 20 | { 21 | "relationshipTypes": ["PRODUCED"], 22 | "properties": ["year"], 23 | "name": "rel_produced_year", 24 | "type": "BTREE" 25 | }, 26 | { 27 | "relationshipTypes": ["ACTED_IN"], 28 | "properties": ["roles"], 29 | "name": "ftx_actedIn_roles", 30 | "type": "FULLTEXT" 31 | }, 32 | { 33 | "relationshipTypes": ["RELTYPE_1", "RELTYPE_2"], 34 | "properties": ["id"], 35 | "name": "rel_idx_$relationshipType_id" 36 | } 37 | ] 38 | }, 39 | "constraints": { 40 | "nodes": [ 41 | { 42 | "labels": ["Person"], 43 | "properties": ["id"], 44 | "type": "UNIQUE", 45 | "name": "cr_person_id_unique" 46 | }, 47 | { 48 | "labels": ["Movie"], 49 | "properties": ["title"], 50 | "type": "NODE_KEY", 51 | "name": "cr_movie_title_nk" 52 | }, 53 | { 54 | "labels": ["Thing","AnotherThing"], 55 | "properties": ["name"], 56 | "type": "NOT_NULL", 57 | "name": "cr_$label_name_not_null" 58 | }, 59 | { 60 | "labels": ["Person"], 61 | "properties": ["name"], 62 | "type": "PROPERTY_TYPE", 63 | "propertyType": "STRING" 64 | } 65 | ], 66 | "relationships": [] 67 | }, 68 | "seeds": ["movies.cypher"] 69 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/model/schema/PropertyType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.model.schema; 19 | 20 | public enum PropertyType { 21 | 22 | BOOLEAN("BOOLEAN"), 23 | STRING("STRING"), 24 | INTEGER("INTEGER"), 25 | FLOAT("FLOAT"), 26 | DATE("DATE"), 27 | LOCAL_TIME("LOCAL TIME"), 28 | ZONED_TIME("ZONED TIME"), 29 | LOCAL_DATETIME("LOCAL DATETIME"), 30 | ZONED_DATETIME("ZONED DATETIME"), 31 | DURATION("DURATION"), 32 | POINT("POINT"), 33 | LIST_OF_BOOLEAN("LIST"), 34 | LIST_OF_STRING("LIST"), 35 | LIST_OF_INTEGER("LIST"), 36 | LIST_OF_FLOAT("LIST"), 37 | LIST_OF_DATETIME("LIST"), 38 | LIST_OF_LOCAL_TIME("LIST"), 39 | LIST_OF_ZONED_TIME("LIST"), 40 | LIST_OF_LOCAL_DATETIME("LIST"), 41 | LIST_OF_DATE("LIST"), 42 | LIST_OF_DURATION("LIST"), 43 | LIST_OF_POINT("LIST"), 44 | ; 45 | 46 | private final String value; 47 | 48 | PropertyType(String value) { 49 | this.value = value; 50 | } 51 | 52 | public String getValue() { 53 | return value; 54 | } 55 | 56 | // Optional: Override toString() to return the string value 57 | @Override 58 | public String toString() { 59 | return value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/util/FileCollectionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.util; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.io.File; 24 | import java.nio.file.Files; 25 | import java.nio.file.Paths; 26 | import java.util.Collection; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | import java.util.stream.Collectors; 30 | 31 | public class FileCollectionUtils { 32 | 33 | private static final Logger LOG = LoggerFactory.getLogger(FileCollectionUtils.class); 34 | 35 | public static FilesToImport getFilesToImport(Collection files) { 36 | var cypherSeeds = files 37 | .stream() 38 | .filter(f -> f.getName().endsWith(".cypher")) 39 | .collect(Collectors.toList()); 40 | var configFiles = files 41 | .stream() 42 | .filter(f -> !f.getName().endsWith(".cypher")) 43 | .toList(); 44 | 45 | return new FilesToImport(configFiles, cypherSeeds); 46 | } 47 | 48 | public static Map buildSeeds(Collection files) { 49 | Map seeds = new HashMap<>(); 50 | for (File file : files) { 51 | try { 52 | String s = new String(Files.readAllBytes(Paths.get(file.getPath()))); 53 | seeds.put(Paths.get(file.getPath()).getFileName().toString(), s); 54 | } catch (Exception e) { 55 | LOG.error("Could not read cypher seed {}", file.getPath()); 56 | } 57 | } 58 | 59 | return seeds; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/Neo4jAsserts.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config; 2 | 3 | import org.neo4j.driver.Driver; 4 | import org.neo4j.driver.Session; 5 | import org.neo4j.driver.SessionConfig; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | import static org.assertj.core.api.AssertionsForClassTypes.assertThat; 11 | 12 | public class Neo4jAsserts { 13 | 14 | private final Driver driver; 15 | 16 | public Neo4jAsserts(Driver driver) { 17 | this.driver = driver; 18 | } 19 | 20 | public void databaseExists(String databaseName) { 21 | assertThat(listDatabaseNames()).asList().contains(databaseName); 22 | } 23 | 24 | public void databaseNotExists(String databaseName) { 25 | assertThat(listDatabaseNames()).asList().doesNotContain(databaseName); 26 | } 27 | 28 | public void databaseEmpty(String databaseName) { 29 | assertThat(countNodes(databaseName)).isEqualTo(0); 30 | } 31 | 32 | public void databaseNotEmpty(String databaseName) { 33 | assertThat(countNodes(databaseName)).isGreaterThan(0); 34 | } 35 | 36 | public void indexExistForName(String name) { 37 | assertThat(listIndexNames()).asList().contains(name); 38 | } 39 | 40 | public void indexDoesNotExistForName(String name) { 41 | assertThat(listIndexNames()).asList().doesNotContain(name); 42 | } 43 | 44 | private long countNodes(String databaseName) { 45 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 46 | return session.run("MATCH (n) RETURN count(n) AS c").single().get("c").asLong(); 47 | } 48 | } 49 | 50 | private List listDatabaseNames() { 51 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 52 | return session.run("SHOW DATABASES").list().stream().map(record -> record.get("name").asString()).collect(Collectors.toList()); 53 | } 54 | } 55 | 56 | private List listIndexNames() { 57 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 58 | return session.run("SHOW INDEXES").list().stream().map(record -> record.get("name").asString()).collect(Collectors.toList()); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/MultipleNeo4jVersionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config; 19 | 20 | import com.graphaware.neo4j.config.version.Neo4jVersion; 21 | import org.junit.jupiter.api.TestInstance; 22 | import org.neo4j.driver.AuthTokens; 23 | import org.neo4j.driver.Driver; 24 | import org.neo4j.driver.GraphDatabase; 25 | import org.testcontainers.containers.Neo4jContainer; 26 | import org.testcontainers.junit.jupiter.Testcontainers; 27 | 28 | import java.util.stream.Stream; 29 | 30 | @Testcontainers(disabledWithoutDocker = true) 31 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 32 | public abstract class MultipleNeo4jVersionsTest { 33 | 34 | protected static String HEAP_SIZE = "256M"; 35 | 36 | public static Stream neo4jVersions() { 37 | return Stream.of("4.4.40", "5.26.7", "2025.05.0"); 38 | } 39 | 40 | protected static String heapSizeSetting(Neo4jVersion version) { 41 | return version.equals(Neo4jVersion.V4_4) 42 | ? "NEO4J_dbms_memory_heap_max__size" 43 | : "NEO4J_server_memory_heap_max__size" 44 | ; 45 | } 46 | 47 | protected Neo4jContainer getNeo4j(String version) { 48 | var imageName = String.format("neo4j:%s-enterprise", version); 49 | Neo4jVersion neo4jVersion = Neo4jVersion.of(version); 50 | Neo4jContainer container = new Neo4jContainer<>(imageName) 51 | .withoutAuthentication() 52 | .withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes") 53 | .withEnv(heapSizeSetting(neo4jVersion), HEAP_SIZE) 54 | .withReuse(true); 55 | if (version.startsWith("5") || version.startsWith("20")) { 56 | container.withEnv("NEO4J_dbms_databases_seed__from__uri__providers", "URLConnectionSeedProvider"); 57 | } 58 | container.start(); 59 | 60 | return container; 61 | } 62 | 63 | protected Driver getDriver(Neo4jContainer neo4j) { 64 | return GraphDatabase.driver(neo4j.getBoltUrl(), AuthTokens.none()); 65 | } 66 | } -------------------------------------------------------------------------------- /src/test/resources/config/rel-constraints.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Database", 3 | "name": "relationship.constraints", 4 | "dropIfExists": "true", 5 | "indexes": { 6 | "nodes": [ 7 | { 8 | "labels": ["Person"], 9 | "properties": ["name"], 10 | "name": "Person", 11 | "type": "FULLTEXT" 12 | } 13 | ], 14 | "relationships": [ 15 | { 16 | "relationshipTypes": ["ACTED_IN"], 17 | "properties": ["roles"], 18 | "name": "rel_actedIn_roles" 19 | }, 20 | { 21 | "relationshipTypes": ["PRODUCED"], 22 | "properties": ["year"], 23 | "name": "rel_produced_year", 24 | "type": "BTREE" 25 | }, 26 | { 27 | "relationshipTypes": ["ACTED_IN"], 28 | "properties": ["roles"], 29 | "name": "ftx_actedIn_roles", 30 | "type": "FULLTEXT" 31 | }, 32 | { 33 | "relationshipTypes": ["RELTYPE_1", "RELTYPE_2"], 34 | "properties": ["id"], 35 | "name": "rel_idx_$relationshipType_id" 36 | } 37 | ] 38 | }, 39 | "constraints": { 40 | "nodes": [ 41 | { 42 | "labels": ["Person"], 43 | "properties": ["id"], 44 | "type": "UNIQUE", 45 | "name": "cr_person_id_unique" 46 | }, 47 | { 48 | "labels": ["Movie"], 49 | "properties": ["title"], 50 | "type": "NODE_KEY", 51 | "name": "cr_movie_title_nk" 52 | }, 53 | { 54 | "labels": ["Thing","AnotherThing"], 55 | "properties": ["name"], 56 | "type": "NOT_NULL", 57 | "name": "cr_$label_name_not_null" 58 | }, 59 | { 60 | "labels": ["Person"], 61 | "properties": ["name"], 62 | "type": "PROPERTY_TYPE", 63 | "name": "person_property_name_string", 64 | "propertyType": "STRING" 65 | }, 66 | { 67 | "labels": ["Movie"], 68 | "properties": ["genres"], 69 | "type": "PROPERTY_TYPE", 70 | "name": "movie_property_genres_list_strings", 71 | "propertyType": "LIST_OF_STRING" 72 | } 73 | ], 74 | "relationships": [ 75 | { 76 | "relationshipTypes": ["RELTYPE_3"], 77 | "properties": ["id"], 78 | "name": "rel_uniq_$relationshipType_id", 79 | "type": "UNIQUE" 80 | }, 81 | { 82 | "relationshipTypes": ["RELTYPE_4"], 83 | "properties": ["id"], 84 | "name": "rel_rk_$relationshipType_id", 85 | "type": "RELATIONSHIP_KEY" 86 | }, 87 | { 88 | "relationshipTypes": ["RELTYPE_100"], 89 | "properties": ["since"], 90 | "name": "rel_ptc_since_$relationshipType", 91 | "type": "PROPERTY_TYPE", 92 | "propertyType": "ZONED_DATETIME" 93 | } 94 | ] 95 | }, 96 | "seeds": ["movies.cypher"] 97 | } -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/ImportProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config; 19 | 20 | import org.apache.commons.io.FileUtils; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.core.io.Resource; 24 | import org.springframework.core.io.ResourceLoader; 25 | import org.springframework.stereotype.Component; 26 | import org.springframework.util.ResourceUtils; 27 | 28 | import java.io.File; 29 | import java.net.URL; 30 | import java.util.*; 31 | import java.util.stream.Collectors; 32 | 33 | 34 | @Component 35 | public class ImportProvider { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(ImportProvider.class); 38 | 39 | private final ResourceLoader resourceLoader; 40 | 41 | public ImportProvider(ResourceLoader resourceLoader) { 42 | this.resourceLoader = resourceLoader; 43 | } 44 | 45 | public Collection getImportFiles(String path) throws Exception { 46 | 47 | if (path.startsWith("http")) { 48 | path = transformFromUrl(path); 49 | } 50 | 51 | if (!ResourceUtils.isUrl(path)) { 52 | path = "file:" + path; 53 | } 54 | 55 | Resource resource = resourceLoader.getResource(path); 56 | File f = resource.getFile(); 57 | Collection importResources = new ArrayList<>(); 58 | if (f.isDirectory()) { 59 | importResources.addAll(handleDirectory(resource)); 60 | } 61 | 62 | return importResources; 63 | } 64 | 65 | private Collection handleDirectory(Resource resource) throws Exception { 66 | File file = resource.getFile(); 67 | File[] files = file.listFiles(); 68 | 69 | return Optional.ofNullable(files).map(f -> Arrays.stream(f).filter(File::isFile).collect(Collectors.toList())) 70 | .orElse(Collections.emptyList()); 71 | } 72 | 73 | private String transformFromUrl(String url) throws Exception { 74 | URL u = new URL(url); 75 | String tempDir = System.getProperty("java.io.tmpdir") + "/" + "neo4jconfig"; 76 | String path = tempDir + "/" + System.currentTimeMillis() + ".json"; 77 | 78 | File f = new File(path); 79 | LOG.info("copying file from {} copied to {}", url, path); 80 | FileUtils.copyURLToFile(u, f); 81 | 82 | return tempDir; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/service/CreateNodeConstraint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.service; 19 | 20 | import com.graphaware.neo4j.config.model.schema.ConstraintType; 21 | import com.graphaware.neo4j.config.model.schema.NodeConstraint; 22 | import com.graphaware.neo4j.config.model.schema.PropertyType; 23 | import org.apache.commons.lang3.StringUtils; 24 | import org.neo4j.driver.Driver; 25 | import org.neo4j.driver.Session; 26 | import org.neo4j.driver.SessionConfig; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | public class CreateNodeConstraint { 34 | 35 | private static final Logger LOG = LoggerFactory.getLogger(CreateNodeConstraint.class); 36 | 37 | private final Driver driver; 38 | private final NodeConstraint nodeConstraint; 39 | 40 | public CreateNodeConstraint(Driver driver, NodeConstraint nodeConstraint) { 41 | this.driver = driver; 42 | this.nodeConstraint = nodeConstraint; 43 | } 44 | 45 | public void createConstraintOnDatabase(String databaseName) { 46 | nodeConstraint.labels().forEach(label -> { 47 | List properties = nodeConstraint.properties() 48 | .stream().map(p -> String.format("n.`%s`", p)) 49 | .toList(); 50 | String propsString = StringUtils.join(properties, ","); 51 | String constraintName = nodeConstraint.name() != null 52 | ? nodeConstraint.name().replace("$label", label) 53 | : String.format("unique_%s_%s", label, StringUtils.join(nodeConstraint.properties(), "_")).toLowerCase(); 54 | String query = String.format( 55 | "CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:`%s`) REQUIRE (%s) IS %s", 56 | constraintName, 57 | label, 58 | propsString, 59 | nodeConstraint.type().equals(ConstraintType.PROPERTY_TYPE) 60 | ? propertyTypeToCypher(nodeConstraint.propertyType()) 61 | : nodeConstraint.type().getValue() 62 | ); 63 | 64 | LOG.info("Creating unique constraint {}", query); 65 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 66 | session.run(query); 67 | } 68 | }); 69 | } 70 | 71 | private String propertyTypeToCypher(PropertyType p) { 72 | return ":: %s".formatted(p.getValue().replace("_", " ")); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/service/CreateRelationshipConstraint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.service; 19 | 20 | import com.graphaware.neo4j.config.model.schema.ConstraintType; 21 | import com.graphaware.neo4j.config.model.schema.PropertyType; 22 | import com.graphaware.neo4j.config.model.schema.RelationshipConstraint; 23 | import org.apache.commons.lang3.StringUtils; 24 | import org.neo4j.driver.Driver; 25 | import org.neo4j.driver.Session; 26 | import org.neo4j.driver.SessionConfig; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.util.List; 31 | 32 | public class CreateRelationshipConstraint { 33 | 34 | private final Logger LOG = LoggerFactory.getLogger(CreateRelationshipConstraint.class); 35 | 36 | private final Driver driver; 37 | 38 | private final RelationshipConstraint relationshipConstraint; 39 | 40 | public CreateRelationshipConstraint(Driver driver, RelationshipConstraint relationshipConstraint) { 41 | this.driver = driver; 42 | this.relationshipConstraint = relationshipConstraint; 43 | } 44 | 45 | public void createSchemaConstraintForRelationship(String databaseName) { 46 | relationshipConstraint.relationshipTypes().forEach(relType -> { 47 | var name = relationshipConstraint.name() != null ? relationshipConstraint.name().replace("$relationshipType", relType) : "rel_constraint_" + StringUtils.join(relationshipConstraint.properties(), "_").toLowerCase(); 48 | var type = relationshipConstraint.type(); 49 | List properties = relationshipConstraint.properties() 50 | .stream().map(p -> String.format("r.`%s`", p)) 51 | .toList(); 52 | String propsString = StringUtils.join(properties, ","); 53 | String query = "CREATE CONSTRAINT %s IF NOT EXISTS FOR ()-[r:`%s`]-() REQUIRE (%s) IS %s".formatted( 54 | name, 55 | relType, 56 | propsString, 57 | type.equals(ConstraintType.PROPERTY_TYPE) 58 | ? propertyTypeToCypher(relationshipConstraint.propertyType()) 59 | : type.name().replace("_", " ")); 60 | LOG.info("Creating relationship constraint {}", query); 61 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 62 | session.run(query); 63 | } 64 | }); 65 | } 66 | 67 | private String propertyTypeToCypher(PropertyType p) { 68 | return ":: %s".formatted(p.name().replace("_", " ")); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/service/CreateNodeIndex.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.service; 2 | 3 | import com.graphaware.neo4j.config.model.schema.IndexType; 4 | import com.graphaware.neo4j.config.model.schema.NodeIndex; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.neo4j.driver.Driver; 7 | import org.neo4j.driver.Session; 8 | import org.neo4j.driver.SessionConfig; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | import java.util.stream.Stream; 15 | 16 | public class CreateNodeIndex { 17 | 18 | private static final Logger LOG = LoggerFactory.getLogger(CreateNodeIndex.class); 19 | 20 | private final Driver driver; 21 | private final NodeIndex nodeIndex; 22 | 23 | public CreateNodeIndex(Driver driver, NodeIndex nodeIndex) { 24 | this.driver = driver; 25 | this.nodeIndex = nodeIndex; 26 | } 27 | 28 | public void createIndex(String databaseName) { 29 | if (nodeIndex.type().equals(IndexType.FULLTEXT)) { 30 | createFullTextIndexForNode(databaseName); 31 | } else { 32 | createSchemaIndexForNode(databaseName); 33 | } 34 | } 35 | 36 | private void createSchemaIndexForNode(String databaseName) { 37 | nodeIndex.labels().forEach(label -> { 38 | var name = nodeIndex.name() != null ? nodeIndex.name().replace("$label", label) : "node_idx_" + StringUtils.join(nodeIndex.properties(), "_").toLowerCase(); 39 | var type = getTypeDependingOnDatabase(); 40 | List properties = nodeIndex.properties() 41 | .stream().map(p -> String.format("n.`%s`", p)) 42 | .toList(); 43 | String propsString = StringUtils.join(properties, ","); 44 | String query = String.format("CREATE %s INDEX %s IF NOT EXISTS FOR (n:`%s`) ON (%s)", type.name(), name, label, propsString); 45 | LOG.info("Creating node index {}", query); 46 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 47 | session.run(query); 48 | } 49 | }); 50 | } 51 | 52 | private void createFullTextIndexForNode(String databaseName) { 53 | String labels = StringUtils.join(nodeIndex.labels(), "|"); 54 | List properties = nodeIndex.properties() 55 | .stream().map(p -> String.format("n.%s", p)) 56 | .toList(); 57 | String propsString = StringUtils.join(properties, ","); 58 | String indexName = nodeIndex.name() != null ? nodeIndex.name() : "fulltext_" + StringUtils.join(Stream.concat(nodeIndex.labels().stream(), nodeIndex.properties().stream()).collect(Collectors.toList()), "_").toLowerCase(); 59 | String q = String.format("CREATE FULLTEXT INDEX %s IF NOT EXISTS FOR (n:%s) ON EACH [%s]", indexName, labels, propsString); 60 | LOG.info("Creating fulltext index {}", q); 61 | 62 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 63 | session.run(q); 64 | } 65 | } 66 | 67 | private IndexType getTypeDependingOnDatabase() { 68 | if (nodeIndex.type() == null) { 69 | return isNeo4j5() ? IndexType.RANGE : IndexType.BTREE; 70 | } 71 | 72 | if (nodeIndex.type().equals(IndexType.BTREE) && isNeo4j5()) { 73 | return IndexType.RANGE; 74 | } 75 | 76 | return nodeIndex.type(); 77 | } 78 | 79 | private boolean isNeo4j5() { 80 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 81 | var version = session.run("CALL dbms.components() YIELD versions RETURN versions[0] AS version").single().get("version").asString(); 82 | 83 | return version.startsWith("5"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/Neo4jConfigRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config; 19 | 20 | import com.graphaware.neo4j.config.properties.ImportConfiguration; 21 | import com.graphaware.neo4j.config.util.ConfigUtils; 22 | import com.graphaware.neo4j.config.util.FileCollectionUtils; 23 | import com.graphaware.neo4j.config.util.FilesToImport; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import org.springframework.boot.CommandLineRunner; 27 | import org.springframework.boot.ExitCodeGenerator; 28 | import org.springframework.stereotype.Component; 29 | 30 | import java.io.File; 31 | import java.util.Collection; 32 | import java.util.Map; 33 | 34 | @Component 35 | public class Neo4jConfigRunner implements CommandLineRunner, ExitCodeGenerator { 36 | 37 | 38 | 39 | private static final int EXIT_CODE = 0; 40 | private static final Logger LOG = LoggerFactory.getLogger(Neo4jConfigRunner.class); 41 | 42 | private final ImportConfiguration configuration; 43 | 44 | private final ImportProvider importProvider; 45 | private final GraphDatabaseImport graphDatabaseImport; 46 | 47 | public Neo4jConfigRunner(ImportProvider importProvider, GraphDatabaseImport graphDatabaseImport, ImportConfiguration configuration) { 48 | this.configuration = configuration; 49 | this.importProvider = importProvider; 50 | this.graphDatabaseImport = graphDatabaseImport; 51 | } 52 | 53 | @Override 54 | public void run(String... args) throws Exception { 55 | applyConfiguration(); 56 | } 57 | 58 | public void applyConfiguration() throws Exception { 59 | LOG.info("Starting command line application"); 60 | if (configuration.isSeedOnly()) { 61 | LOG.info("seed-only mode detected, loading data from {}", configuration.getSeedUrl()); 62 | seedOnly(); 63 | } else { 64 | Collection importFiles = importProvider.getImportFiles(configuration.getImportPath()); 65 | FilesToImport filesToImport = FileCollectionUtils.getFilesToImport(importFiles); 66 | Map seeds = FileCollectionUtils.buildSeeds(filesToImport.getCypherSeeds()); 67 | graphDatabaseImport.waitUntilStarted(); 68 | graphDatabaseImport.importFiles(filesToImport.getConfigurationFiles(), seeds); 69 | // filesToImport.getConfigurationFiles() 70 | // .forEach(f -> { 71 | // LOG.info("Will import from file {}", f.getPath()); 72 | // try { 73 | // graphDatabaseImport.importFile(f, seeds); 74 | // } catch (Exception e) { 75 | // LOG.error(e.getMessage()); 76 | // } 77 | // }); 78 | } 79 | LOG.info("Ended command line application"); 80 | } 81 | 82 | private void seedOnly() throws Exception { 83 | Map seeds; 84 | if (null != configuration.getSeedUrl()) { 85 | seeds = Map.of(configuration.getSeedUrl(), ConfigUtils.URLToString(configuration.getSeedUrl())); 86 | } else { 87 | seeds = getSeedsFromConfigDirectory(); 88 | } 89 | 90 | graphDatabaseImport.seedDatabase(configuration.getSeedDb(), seeds); 91 | 92 | } 93 | 94 | private Map getSeedsFromConfigDirectory() throws Exception { 95 | Collection fileCollection = importProvider.getImportFiles(configuration.getImportPath()); 96 | Collection cypherSeeds = fileCollection 97 | .stream() 98 | .filter(f -> f.getName().endsWith(".cypher")) 99 | .toList(); 100 | return FileCollectionUtils.buildSeeds(fileCollection); 101 | } 102 | 103 | @Override 104 | public int getExitCode() { 105 | return EXIT_CODE; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.7.10 10 | 11 | 12 | 13 | com.graphaware 14 | neo4j-config-cli 15 | 2.7.4-SNAPSHOT 16 | 17 | 18 | Graph Aware Limited 19 | http://graphaware.com 20 | 21 | 22 | 23 | 24 | @ikwattro 25 | Christophe Willemsen 26 | 27 | 28 | 29 | neo4j-config-cli 30 | Neo4j Utility tool for ensuring the desired state of a Neo4j instance based on json/yml definitions 31 | 32 | 33 | 17 34 | 17 35 | 17 36 | 1.17.6 37 | 2.14.0 38 | 5.28.9 39 | 1.33 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter 46 | 47 | 48 | org.neo4j.driver 49 | neo4j-java-driver 50 | ${neo4j-java-driver.version} 51 | 52 | 53 | com.fasterxml.jackson.core 54 | jackson-databind 55 | ${jackson.version} 56 | 57 | 58 | com.fasterxml.jackson.dataformat 59 | jackson-dataformat-yaml 60 | ${jackson.version} 61 | 62 | 63 | org.apache.commons 64 | commons-lang3 65 | 3.12.0 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-devtools 70 | runtime 71 | true 72 | 73 | 74 | commons-io 75 | commons-io 76 | 2.11.0 77 | 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-starter-test 83 | test 84 | 85 | 86 | org.testcontainers 87 | junit-jupiter 88 | ${testcontainers.version} 89 | test 90 | 91 | 92 | org.testcontainers 93 | neo4j 94 | ${testcontainers.version} 95 | test 96 | 97 | 98 | org.assertj 99 | assertj-core 100 | 3.20.2 101 | test 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.yaml 109 | snakeyaml 110 | ${snakeyaml.version} 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | com.amashchenko.maven.plugin 119 | gitflow-maven-plugin 120 | 1.16.0 121 | 122 | 123 | develop 124 | main 125 | 126 | 127 | 128 | 129 | org.springframework.boot 130 | spring-boot-maven-plugin 131 | 132 | 133 | 134 | org.projectlombok 135 | lombok 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/service/CreateRelationshipIndex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config.service; 19 | 20 | import com.graphaware.neo4j.config.model.schema.IndexType; 21 | import com.graphaware.neo4j.config.model.schema.RelationshipIndex; 22 | import org.apache.commons.lang3.StringUtils; 23 | import org.neo4j.driver.Driver; 24 | import org.neo4j.driver.Session; 25 | import org.neo4j.driver.SessionConfig; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | import java.util.List; 30 | import java.util.stream.Collectors; 31 | import java.util.stream.Stream; 32 | 33 | public class CreateRelationshipIndex { 34 | 35 | private static final Logger LOG = LoggerFactory.getLogger(CreateRelationshipIndex.class); 36 | 37 | private final Driver driver; 38 | private final RelationshipIndex relationshipIndex; 39 | 40 | public CreateRelationshipIndex(Driver driver, RelationshipIndex relationshipIndex) { 41 | this.driver = driver; 42 | this.relationshipIndex = relationshipIndex; 43 | } 44 | 45 | public void createIndex(String databaseName) { 46 | if (relationshipIndex.type() != null && relationshipIndex.type().equals(IndexType.FULLTEXT)) { 47 | createFullTextRelationshipIndex(databaseName); 48 | } else { 49 | createSchemaIndexForRelationship(databaseName); 50 | } 51 | } 52 | 53 | private void createSchemaIndexForRelationship(String databaseName) { 54 | relationshipIndex.relationshipTypes().forEach(relType -> { 55 | var name = relationshipIndex.name() != null ? relationshipIndex.name().replace("$relationshipType", relType) : "rel_idx_" + StringUtils.join(relationshipIndex.properties(), "_").toLowerCase(); 56 | var type = getTypeDependingOnDatabase(); 57 | List properties = relationshipIndex.properties() 58 | .stream().map(p -> String.format("r.`%s`", p)) 59 | .toList(); 60 | String propsString = StringUtils.join(properties, ","); 61 | String query = String.format("CREATE %s INDEX %s IF NOT EXISTS FOR ()-[r:`%s`]-() ON (%s)", type.name(), name, relType, propsString); 62 | LOG.info("Creating relationship index {}", query); 63 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 64 | session.run(query); 65 | } 66 | }); 67 | } 68 | 69 | private void createFullTextRelationshipIndex(String databaseName) { 70 | String labels = StringUtils.join(relationshipIndex.relationshipTypes(), "|"); 71 | List properties = relationshipIndex.properties() 72 | .stream().map(p -> String.format("r.%s", p)) 73 | .toList(); 74 | String propsString = StringUtils.join(properties, ","); 75 | String indexName = relationshipIndex.name() != null ? relationshipIndex.name() : "fulltext_" + StringUtils.join(Stream.concat(relationshipIndex.relationshipTypes().stream(), relationshipIndex.properties().stream()).collect(Collectors.toList()), "_").toLowerCase(); 76 | String q = String.format("CREATE FULLTEXT INDEX %s IF NOT EXISTS FOR ()-[r:%s]-() ON EACH [%s]", indexName, labels, propsString); 77 | LOG.info("Creating fulltext index {}", q); 78 | 79 | try (Session session = driver.session(SessionConfig.forDatabase(databaseName))) { 80 | session.run(q); 81 | } 82 | } 83 | 84 | private IndexType getTypeDependingOnDatabase() { 85 | if (relationshipIndex.type() == null) { 86 | return isNeo4j5OrAbove() ? IndexType.RANGE : IndexType.BTREE; 87 | } 88 | 89 | if (relationshipIndex.type().equals(IndexType.BTREE) && isNeo4j5OrAbove()) { 90 | return IndexType.RANGE; 91 | } 92 | 93 | return relationshipIndex.type(); 94 | } 95 | 96 | private boolean isNeo4j5OrAbove() { 97 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 98 | var result = session.run("CALL dbms.components()"); 99 | var dbmsComponents = result.list().stream().map(r -> r.as(DbmsComponent.class)).toList(); 100 | var version = dbmsComponents.stream().filter(c -> c.name().equals("Neo4j Kernel")).findFirst().get().versions().get(0); 101 | 102 | return version.startsWith("5") || version.startsWith("20"); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/neo4j/config/Neo4jConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config; 19 | 20 | import com.graphaware.neo4j.config.properties.ImportConfiguration; 21 | import com.graphaware.neo4j.config.service.CreateDatabaseService; 22 | import com.graphaware.neo4j.config.version.Neo4jVersion; 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.params.ParameterizedTest; 25 | import org.junit.jupiter.params.provider.MethodSource; 26 | import org.neo4j.driver.Driver; 27 | import org.neo4j.driver.Session; 28 | import org.neo4j.driver.SessionConfig; 29 | import org.springframework.core.io.FileSystemResourceLoader; 30 | import org.springframework.core.io.ResourceLoader; 31 | import org.testcontainers.containers.Neo4jContainer; 32 | 33 | import java.io.File; 34 | import java.util.HashMap; 35 | import java.util.Map; 36 | 37 | import static org.assertj.core.api.AssertionsForClassTypes.assertThat; 38 | import static org.neo4j.driver.Values.ofString; 39 | 40 | public class Neo4jConfigurationTest extends MultipleNeo4jVersionsTest { 41 | 42 | @ParameterizedTest 43 | @MethodSource("neo4jVersions") 44 | void loading_config(String version) throws Exception { 45 | var configPath = new File("src/test/resources/config").getAbsolutePath(); 46 | ResourceLoader resourceLoader = new FileSystemResourceLoader(); 47 | ImportProvider importProvider = new ImportProvider(resourceLoader); 48 | var config = new ImportConfiguration(configPath, false, null, "neo4j"); 49 | Neo4jContainer neo4j = getNeo4j(version); 50 | Driver neo4jDriver = getDriver(neo4j); 51 | var serverVersion = Neo4jVersion.of(version); 52 | 53 | var service = new CreateDatabaseService(neo4jDriver); 54 | var importer = new GraphDatabaseImport(neo4jDriver, service); 55 | 56 | var runner = new Neo4jConfigRunner(importProvider, importer, config); 57 | runner.applyConfiguration(); 58 | 59 | try (Driver driver = getDriver(neo4j)) { 60 | try (Session session = driver.session(SessionConfig.forDatabase("movies"))) { 61 | // assert database exist 62 | session.run("CALL db.ping()").consume(); 63 | 64 | assertThat(session.run("MATCH (n:Movie) RETURN count(n) AS c").single().get("c").asLong()).isGreaterThan(0); 65 | } 66 | 67 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 68 | var roles = session.run("SHOW ROLES YIELD role RETURN collect(role) AS roles").single().get("roles").asList(ofString()); 69 | assertThat(roles).asList().contains("ut1"); 70 | } 71 | 72 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 73 | var dbs = session.run("SHOW DATABASES YIELD *"); 74 | dbs.forEachRemaining(r -> { 75 | System.out.println(r.asMap()); 76 | }); 77 | } 78 | 79 | try (Session session = driver.session(SessionConfig.forDatabase("world.cup"))) { 80 | session.run("CALL db.ping()").consume(); 81 | 82 | if (isNeo4j5OrAbove(serverVersion)) { 83 | var nodesCount = session.run("MATCH (n) RETURN count(n) AS c").single().get("c").asLong(); 84 | 85 | assertThat(nodesCount).isGreaterThan(0); 86 | } 87 | } 88 | 89 | if (isNeo4j5OrAbove(serverVersion)) { 90 | try (Session session = driver.session(SessionConfig.forDatabase("relationship.constraints"))) { 91 | final Map constraints = new HashMap<>(); 92 | final Map propertyTypeConstraints = new HashMap<>(); 93 | session.run("SHOW CONSTRAINTS").list().forEach(record -> { 94 | constraints.put(record.get("name").asString(), record.get("type").asString()); 95 | propertyTypeConstraints.put(record.get("name").asString(), record.get("propertyType").asString()); 96 | }); 97 | 98 | Assertions.assertEquals("RELATIONSHIP_UNIQUENESS", constraints.get("rel_uniq_RELTYPE_3_id")); 99 | Assertions.assertEquals("RELATIONSHIP_KEY", constraints.get("rel_rk_RELTYPE_4_id")); 100 | Assertions.assertEquals("ZONED DATETIME", propertyTypeConstraints.get("rel_ptc_since_RELTYPE_100")); 101 | Assertions.assertEquals("LIST", propertyTypeConstraints.get("movie_property_genres_list_strings")); 102 | Assertions.assertEquals("STRING", propertyTypeConstraints.get("person_property_name_string")); 103 | } 104 | 105 | try (Session session = driver.session(SessionConfig.forDatabase("movies"))) { 106 | final Map propertyTypeConstraints = new HashMap<>(); 107 | session.run("SHOW CONSTRAINTS").list().forEach(record -> { 108 | propertyTypeConstraints.put(record.get("name").asString(), record.get("propertyType").asString()); 109 | }); 110 | Assertions.assertEquals("STRING", propertyTypeConstraints.get("unique_person_name")); 111 | } 112 | } 113 | 114 | try (Session session = driver.session(SessionConfig.forDatabase("movies"))) { 115 | var constraints = session.run("SHOW CONSTRAINTS").list().stream().map(record -> record.get("name").asString()).toList(); 116 | Assertions.assertTrue(constraints.contains("cr_person_id_unique")); 117 | } 118 | } 119 | 120 | } 121 | 122 | private static boolean isNeo4j5OrAbove(Neo4jVersion version) { 123 | return version.equals(Neo4jVersion.V5) || version.equals(Neo4jVersion.CALENDAR_VERSION); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/service/CreateDatabaseService.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.neo4j.config.service; 2 | 3 | import com.graphaware.neo4j.config.model.Database; 4 | import com.graphaware.neo4j.config.model.schema.ConstraintType; 5 | import org.neo4j.driver.Driver; 6 | import org.neo4j.driver.Record; 7 | import org.neo4j.driver.Session; 8 | import org.neo4j.driver.SessionConfig; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.*; 14 | 15 | import static org.neo4j.driver.Values.ofString; 16 | 17 | @Service 18 | public class CreateDatabaseService { 19 | 20 | private static final Logger LOG = LoggerFactory.getLogger(CreateDatabaseService.class); 21 | 22 | private final Driver driver; 23 | 24 | public CreateDatabaseService(Driver driver) { 25 | this.driver = driver; 26 | } 27 | 28 | public void createDatabase(Database database) { 29 | if (database.composite()) { 30 | createCompositeDatabase(database); 31 | } else { 32 | createDatabase(database.name(), database.dropIfExists(), database.skipIfCreate(), database.seedFromUri()); 33 | createIndexes(database); 34 | createConstraints(database); 35 | } 36 | } 37 | 38 | private void createCompositeDatabase(Database database) { 39 | if (database.skipIfCreate()) { 40 | LOG.info("Skipping creation of composite database {}", database.name()); 41 | return; 42 | } 43 | 44 | LOG.info("Creating composite database {}", database.name()); 45 | String query = "CREATE COMPOSITE DATABASE `%s` IF NOT EXISTS ".formatted(database.name()); 46 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 47 | session.run(query); 48 | 49 | for (String db : database.constituents()) { 50 | LOG.info("Creating alias {} for composite database {}", db, database.name()); 51 | String aliasQuery = "CREATE ALIAS `%s`.`%s` IF NOT EXISTS FOR DATABASE %s".formatted(database.name(), db, db); 52 | session.run(aliasQuery); 53 | } 54 | } 55 | } 56 | 57 | public void createDatabase(String name, boolean dropIfExists, boolean skipCreate, String fromUri) { 58 | if (skipCreate) { 59 | LOG.info("Skipping creation of database {}", name); 60 | return; 61 | } 62 | 63 | if (dropIfExists) { 64 | dropDatabaseIfExists(name); 65 | } 66 | 67 | String query = "CREATE DATABASE `%s` IF NOT EXISTS ".formatted(name); 68 | 69 | if (fromUri != null && isNeo4j5OrAbove()) { 70 | LOG.info("seedFromUri detected, will seed database from {}", fromUri); 71 | query = "%s OPTIONS { existingData: \"use\", seedURI: \"%s\"} ".formatted(query, fromUri); 72 | } 73 | query = "%s WAIT".formatted(query); 74 | 75 | LOG.info("Creating database {} ", name); 76 | LOG.debug("Query : {}", query); 77 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 78 | var result = session.run(query).list(); 79 | if (!result.isEmpty()) { 80 | var record = result.get(0); 81 | System.out.println(record.asMap()); 82 | var success = record.get("success").asBoolean(); 83 | var message = record.get("message").asString(); 84 | 85 | if (!success) { 86 | LOG.error("Creating database %s with seedURI failed with the following message : %s".formatted(name, message)); 87 | LOG.info("Dropping database %s after failed seedURI".formatted(name)); 88 | return; 89 | } 90 | } 91 | // cypher25 doesn't return results 92 | } 93 | waitDatabaseIsOnline(name); 94 | } 95 | 96 | private boolean isNeo4j5OrAbove() { 97 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 98 | var result = session.run("CALL dbms.components()"); 99 | var dbmsComponents = result.list().stream().map(r -> r.as(DbmsComponent.class)).toList(); 100 | var version = dbmsComponents.stream().filter(c -> c.name().equals("Neo4j Kernel")).findFirst().get().versions().get(0); 101 | 102 | return version.startsWith("5") || version.startsWith("20"); 103 | } 104 | } 105 | 106 | private void createIndexes(Database database) { 107 | if (null != database.indexes()) { 108 | database.indexes().nodes().forEach(fullTextIndex -> { 109 | new CreateNodeIndex(driver, fullTextIndex).createIndex(database.name()); 110 | }); 111 | database.indexes().relationships().forEach(relationshipIndex -> { 112 | new CreateRelationshipIndex(driver, relationshipIndex).createIndex(database.name()); 113 | }); 114 | } 115 | } 116 | 117 | private void createConstraints(Database database) { 118 | if (null != database.constraints()) { 119 | database.constraints().nodes() 120 | .stream().filter(c -> { 121 | return !c.type().equals(ConstraintType.PROPERTY_TYPE) || isNeo4j5OrAbove(); 122 | }) 123 | .forEach(constraint -> { 124 | new CreateNodeConstraint(driver, constraint).createConstraintOnDatabase(database.name()); 125 | }); 126 | 127 | if (isNeo4j5OrAbove()) { 128 | database.constraints().relationships().forEach(relationshipConstraint -> { 129 | new CreateRelationshipConstraint(driver, relationshipConstraint).createSchemaConstraintForRelationship(database.name()); 130 | }); 131 | } 132 | } 133 | } 134 | 135 | private void dropDatabaseIfExists(String name) { 136 | dropDatabaseAliases(name); 137 | String query = String.format("DROP DATABASE %s IF EXISTS", name); 138 | LOG.info("Dropping database {}", name); 139 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 140 | session.run(query); 141 | } 142 | } 143 | 144 | private void dropDatabaseAliases(String name) { 145 | Set aliases = new HashSet<>(); 146 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 147 | session.run("SHOW DATABASES YIELD name, aliases RETURN *").forEachRemaining(record -> { 148 | if (record.get("name").asString().equals(name)) { 149 | aliases.addAll(record.get("aliases").asList(ofString())); 150 | } 151 | }); 152 | 153 | aliases.forEach(alias -> { 154 | LOG.info("Dropping database alias {}", alias); 155 | session.run("DROP ALIAS $alias IF EXISTS FOR DATABASE", Map.of("alias", alias)); 156 | }); 157 | } 158 | } 159 | 160 | private void waitDatabaseIsOnline(String databaseName) { 161 | long startTime = System.currentTimeMillis(); 162 | boolean online; 163 | boolean inTimeWindow; 164 | try (Session session = driver.session(SessionConfig.forDatabase("system"))) { 165 | do { 166 | List records = session.run(String.format("SHOW DATABASE `%s`", databaseName)).list(); 167 | online = records.size() > 0 168 | && records.stream().allMatch(r -> "online".equals(r.asMap().get("currentStatus"))) 169 | && records.stream().anyMatch(r -> "leader".equals(r.asMap().get("role")) || "primary".equals(r.asMap().get("role"))); 170 | inTimeWindow = System.currentTimeMillis() < (startTime + 10_000); 171 | } while (!online && inTimeWindow); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub license](https://img.shields.io/github/license/graphaware/neo4j-config-cli)](https://github.com/graphaware/neo4j-config-cli/blob/main/LICENSE.txt) 2 | [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/graphaware/neo4j-config-cli?logo=github&sort=semver)](https://github.com/graphaware/neo4j-config-cli/releases/latest) 3 | [![Docker Pulls](https://img.shields.io/docker/pulls/graphaware/neo4j-config-cli?logo=docker)](https://hub.docker.com/r/graphaware/neo4j-config-cli) 4 | 5 | # neo4j-config-cli 6 | 7 | `neo4j-config-cli` is a [Neo4j](https://neo4j.com) utility to ensure the desired configuration state of a Neo4j database 8 | based on a json file definition. 9 | 10 | Docker : https://hub.docker.com/repository/docker/graphaware/neo4j-config-cli 11 | 12 | The following `docker-compose.yml` : 13 | 14 | ```yaml 15 | version: '3' 16 | services: 17 | neo4j: 18 | image: neo4j:5.14.0-enterprise 19 | ports: 20 | - "7474:7474" 21 | - "7687:7687" 22 | environment: 23 | - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes 24 | - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD:-password} 25 | neo4j-config-cli: 26 | image: graphaware/neo4j-config-cli:2.5.0 27 | environment: 28 | - NEO4J_USER=neo4j 29 | - NEO4J_PASSWORD=password 30 | - NEO4J_URI=bolt://neo4j:7687 31 | - IMPORT_PATH=/config 32 | volumes: 33 | - "./config:/config" 34 | ``` 35 | 36 | with the following json definition in `config/simple-db-create.json` 37 | 38 | ```json 39 | { 40 | "kind": "Database", 41 | "name": "movies", 42 | "dropIfExists": true 43 | } 44 | ``` 45 | 46 | will wait Neo4j is started then drop the `movies` database and recreate it, once done the `neo4j-config-cli` container will stop : 47 | 48 | ```text 49 | $ docker-compose up 50 | Creating network "neo4j-config-demo_default" with the default driver 51 | Creating neo4j-config-demo_neo4j_1 ... done 52 | Creating neo4j-config-demo_neo4j-config-cli_1 ... done 53 | Attaching to neo4j-config-demo_neo4j-config-cli_1, neo4j-config-demo_neo4j_1 54 | neo4j_1 | Selecting JVM - Version:11.0.12, Name:OpenJDK 64-Bit Server VM, Vendor:Oracle Corporation 55 | neo4j-config-cli_1 | 56 | neo4j-config-cli_1 | . ____ _ __ _ _ 57 | neo4j-config-cli_1 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 58 | neo4j-config-cli_1 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 59 | neo4j-config-cli_1 | \\/ ___)| |_)| | | | | || (_| | ) ) ) ) 60 | neo4j-config-cli_1 | ' |____| .__|_| |_|_| |_\__, | / / / / 61 | neo4j-config-cli_1 | =========|_|==============|___/=/_/_/_/ 62 | neo4j-config-cli_1 | :: Spring Boot :: (v2.5.4) 63 | neo4j-config-cli_1 | 64 | neo4j-config-cli_1 | 2021-09-07 08:02:40.342 INFO 1 --- [main] c.g.n.config.Neo4jConfigCliApplication : Starting Neo4jConfigCliApplication v1.1.0-SNAPSHOT using Java 11.0.11 on 4daf472cb393 with PID 1 (/opt/app.jar started by appuser in /) 65 | neo4j-config-cli_1 | 2021-09-07 08:02:40.344 INFO 1 --- [main] c.g.n.config.Neo4jConfigCliApplication : No active profile set, falling back to default profiles: default 66 | neo4j-config-cli_1 | 2021-09-07 08:02:40.975 INFO 1 --- [main] org.neo4j.driver.Driver : Direct driver instance 841262455 created for server address neo4j:7687 67 | neo4j-config-cli_1 | 2021-09-07 08:02:41.160 INFO 1 --- [main] c.g.n.config.Neo4jConfigCliApplication : Started Neo4jConfigCliApplication in 1.29 seconds (JVM running for 1.677) 68 | neo4j-config-cli_1 | 2021-09-07 08:02:41.162 INFO 1 --- [main] c.g.neo4j.config.Neo4jConfigRunner : Starting command line application 69 | neo4j-config-cli_1 | 2021-09-07 08:02:41.168 INFO 1 --- [main] c.g.neo4j.config.GraphDatabaseImport : Detecting neo4j server availability 70 | neo4j-config-cli_1 | 2021-09-07 08:02:41.300 ERROR 1 --- [main] c.g.neo4j.config.GraphDatabaseImport : Neo4j server not yet available, waiting for 2 seconds... 71 | neo4j_1 | Changed password for user 'neo4j'. 72 | neo4j-config-cli_1 | 2021-09-07 08:02:43.309 ERROR 1 --- [main] c.g.neo4j.config.GraphDatabaseImport : Neo4j server not yet available, waiting for 2 seconds... 73 | neo4j_1 | 2021-09-07 08:02:44.228+0000 INFO Starting... 74 | neo4j-config-cli_1 | 2021-09-07 08:02:45.313 ERROR 1 --- [main] c.g.neo4j.config.GraphDatabaseImport : Neo4j server not yet available, waiting for 2 seconds... 75 | neo4j_1 | 2021-09-07 08:02:45.959+0000 INFO ======== Neo4j 4.3.3 ======== 76 | neo4j-config-cli_1 | 2021-09-07 08:02:47.303 ERROR 1 --- [main] c.g.neo4j.config.GraphDatabaseImport : Neo4j server not yet available, waiting for 2 seconds... 77 | neo4j_1 | 2021-09-07 08:02:47.731+0000 INFO Sending metrics to CSV file at /var/lib/neo4j/metrics 78 | neo4j_1 | 2021-09-07 08:02:48.012+0000 INFO Bolt enabled on 0.0.0.0:7687. 79 | neo4j_1 | 2021-09-07 08:02:48.917+0000 INFO Remote interface available at http://localhost:7474/ 80 | neo4j_1 | 2021-09-07 08:02:48.919+0000 INFO Started. 81 | neo4j-config-cli_1 | 2021-09-07 08:02:49.694 INFO 1 --- [main] c.g.neo4j.config.Neo4jConfigRunner : Will import from file /config/simple-db-create.json 82 | neo4j-config-cli_1 | 2021-09-07 08:02:49.781 INFO 1 --- [main] c.g.n.c.service.CreateDatabaseService : Dropping database movies 83 | neo4j-config-cli_1 | 2021-09-07 08:02:50.048 INFO 1 --- [main] c.g.n.c.service.CreateDatabaseService : Creating database movies 84 | neo4j-config-cli_1 | 2021-09-07 08:02:51.648 INFO 1 --- [main] c.g.neo4j.config.Neo4jConfigRunner : Ended command line application 85 | neo4j-config-cli_1 | 2021-09-07 08:02:51.654 INFO 1 --- [main] org.neo4j.driver.Driver : Closing driver instance 841262455 86 | neo4j-config-cli_1 | 2021-09-07 08:02:51.657 INFO 1 --- [main] org.neo4j.driver.ConnectionPool : Closing connection pool towards neo4j:7687 87 | neo4j-config-demo_neo4j-config-cli_1 exited with code 0 88 | ``` 89 | 90 | ## Config files 91 | 92 | Two `kind` of config files are supported today : 93 | 94 | - `Database` kind 95 | - `Role` kind 96 | 97 | ### Database Config File 98 | 99 | **Structure** 100 | 101 | ```json 102 | { 103 | "kind": "Database", 104 | "name": "movies", 105 | "dropIfExists": "true", 106 | "indexes": { 107 | "nodes" : [ 108 | { 109 | "labels": ["Person"], 110 | "properties": ["name"], 111 | "name": "Person", 112 | "type": "FULLTEXT" 113 | } 114 | ], 115 | }, 116 | "constraints": { 117 | "nodes": [ 118 | { 119 | "label": "Person", 120 | "property": "name", 121 | "type": "UNIQUE" 122 | }, 123 | { 124 | "labels": ["FacebookEvent"], 125 | "properties": ["date"], 126 | "type": "PROPERTY_TYPE", 127 | "propertyType": "DATE", 128 | "name": "facebook_event_date_type" 129 | } 130 | ] 131 | }, 132 | "seeds": ["movies.cypher"] 133 | } 134 | ``` 135 | 136 | #### Property type enum values 137 | 138 | ``` 139 | BOOLEAN("BOOLEAN"), 140 | STRING 141 | INTEGER 142 | FLOAT 143 | DATE 144 | LOCAL_TIME 145 | ZONED_TIME 146 | LOCAL_DATETIME 147 | ZONED_DATETIME 148 | DURATION 149 | POINT 150 | LIST_OF_BOOLEAN 151 | LIST_OF_STRING 152 | LIST_OF_INTEGER 153 | LIST_OF_FLOAT 154 | LIST_OF_DATETIME 155 | LIST_OF_LOCAL_TIME 156 | LIST_OF_ZONED_TIME 157 | LIST_OF_LOCAL_DATETIME 158 | LIST_OF_DATE 159 | LIST_OF_DURATION 160 | LIST_OF_POINT 161 | ``` 162 | 163 | ### Role Config File 164 | 165 | **Structure** 166 | 167 | ```json 168 | { 169 | "kind": "Role", 170 | "name": "ut1", 171 | "dropIfExists": true, 172 | "privileges": [ 173 | { 174 | "graph": "*", 175 | "access": true, 176 | "rules": [ 177 | { 178 | "target": "node", 179 | "labels": "*", 180 | "action": "match", 181 | "resource": "all_properties", 182 | "access": "GRANTED" 183 | }, 184 | { 185 | "target": "node", 186 | "labels": "Person", 187 | "action": "read", 188 | "resource": "born", 189 | "access": "DENIED" 190 | } 191 | ] 192 | } 193 | ] 194 | } 195 | ``` 196 | 197 | --- 198 | 199 | ## Neo4j Aura 200 | 201 | The command `CREATE DATABASE` is not available on Neo4j aura, you thus have to skip the step where this tool will 202 | execute that command by specifying `skipCreate = true` : 203 | 204 | ```json 205 | { 206 | "kind": "Database", 207 | "name": "neo4j", 208 | "skipCreate": true 209 | } 210 | ``` 211 | 212 | A full working example with Aura is available [here](./examples/aura) after inserting your Neo4j Aura credentials in 213 | the docker-compose file. 214 | 215 | --- 216 | 217 | ## Importing from remote files 218 | 219 | You can let your config files on the web, for eg as a `Github Gist` : https://gist.github.com/ikwattro/f99c1ed085673065fcb4e850526ccd49 220 | 221 | You will need to specify the raw versions of it, for eg : 222 | 223 | ```yaml 224 | version: '3.7' 225 | services: 226 | neo4j: 227 | image: neo4j:4.4.15-enterprise 228 | ports: 229 | - "7474:7474" 230 | - "7687:7687" 231 | environment: 232 | - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes 233 | - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD:-password} 234 | neo4j-config-cli: 235 | image: graphaware/neo4j-config-cli:1.2.0 236 | environment: 237 | - NEO4J_PASSWORD=password 238 | - NEO4J_URI=bolt://neo4j:7687 239 | - IMPORT_PATH=https://gist.githubusercontent.com/ikwattro/f99c1ed085673065fcb4e850526ccd49/raw/35f077b71e3f7a9fd95b0288cf6d622eea3d6501/db-demo.json 240 | ``` 241 | 242 | ## Seeding only an existing database 243 | 244 | ```shell 245 | docker run --rm -it \ 246 | -e NEO4J_URI=neo4j+s://18894d85.databases.neo4j.io \ 247 | -e NEO4J_PASSWORD=tT3h3ieK4sw-MATaEaimFFHY9YqkgYNo9WNYcEBqMZ4 \ 248 | -e seed-only=true \ 249 | -e seed-url=https://bit.ly/2XnJzFn \ 250 | graphaware/neo4j-config-cli:1.3.0 251 | ``` 252 | 253 | ## Seeding a database from a Neo4j 5 backup ( seedFromUri ) 254 | 255 | This feature requires that your Neo4j configuration allows seeding from uri's 256 | 257 | ```yaml 258 | - NEO4J_dbms_databases_seed__from__uri__providers=URLConnectionSeedProvider 259 | ``` 260 | 261 | ```json 262 | { 263 | "kind": "Database", 264 | "name": "world.cup", 265 | "dropIfExists": "false", 266 | "seedFromUri": "https://downloads.graphaware.com/neo4j-db-seeds/world-cup-2022-neo4j.backup" 267 | } 268 | ``` 269 | 270 | ## Licence 271 | 272 | Refer to the `LICENSE.txt` file shipped in this repository 273 | 274 | ## Support 275 | 276 | This repository is supported on best-effort basis. 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/neo4j/config/GraphDatabaseImport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 - 2021 GraphAware 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.graphaware.neo4j.config; 19 | 20 | import com.fasterxml.jackson.core.type.TypeReference; 21 | import com.fasterxml.jackson.databind.ObjectMapper; 22 | import com.graphaware.neo4j.config.model.Database; 23 | import com.graphaware.neo4j.config.model.rbac.Privilege; 24 | import com.graphaware.neo4j.config.model.rbac.Role; 25 | import com.graphaware.neo4j.config.model.rbac.Rule; 26 | import com.graphaware.neo4j.config.model.rbac.RuleAccess; 27 | import com.graphaware.neo4j.config.service.CreateDatabaseService; 28 | import com.graphaware.neo4j.config.util.ConfigUtils; 29 | import org.neo4j.driver.Driver; 30 | import org.neo4j.driver.Session; 31 | import org.neo4j.driver.SessionConfig; 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | import org.springframework.beans.factory.annotation.Value; 35 | import org.springframework.stereotype.Component; 36 | 37 | import java.io.File; 38 | import java.nio.file.Files; 39 | import java.nio.file.Paths; 40 | import java.util.ArrayList; 41 | import java.util.Arrays; 42 | import java.util.Collection; 43 | import java.util.HashMap; 44 | import java.util.List; 45 | import java.util.Map; 46 | 47 | @Component 48 | public class GraphDatabaseImport { 49 | 50 | @Value("${import.waiting.timeout}") 51 | private int importWaitingTimeout; 52 | 53 | private static final ObjectMapper MAPPER = new ObjectMapper(); 54 | private static final Logger LOG = LoggerFactory.getLogger(GraphDatabaseImport.class); 55 | 56 | private final Driver driver; 57 | private final CreateDatabaseService createDatabaseService; 58 | 59 | public GraphDatabaseImport(Driver driver, CreateDatabaseService createDatabaseService) { 60 | this.driver = driver; 61 | this.createDatabaseService = createDatabaseService; 62 | } 63 | 64 | private SessionConfig sessionConfig() { 65 | return SessionConfig.forDatabase("system"); 66 | } 67 | 68 | public void importFiles(Collection files, Map seeds) { 69 | List nonCompositeDatabasesToImport = new ArrayList<>(); 70 | List compositeDatabasesToImport = new ArrayList<>(); 71 | List rolesToImport = new ArrayList<>(); 72 | 73 | files.forEach(file -> { 74 | try { 75 | String s = new String(Files.readAllBytes(Paths.get(file.getPath()))); 76 | Map config = MAPPER.readValue(s, new TypeReference<>() {}); 77 | 78 | if (config.get("kind").equals("Database")) { 79 | Database database = MAPPER.convertValue(config, Database.class); 80 | if (database.composite()) { 81 | compositeDatabasesToImport.add(database); 82 | } else { 83 | nonCompositeDatabasesToImport.add(database); 84 | } 85 | } 86 | 87 | if (config.get("kind").equals("Role")) { 88 | Role role = MAPPER.convertValue(config, Role.class); 89 | rolesToImport.add(role); 90 | } 91 | } catch (Exception e) { 92 | LOG.error("Error when deserializing file %s - %s".formatted(file.toPath().toString(), e.getMessage())); 93 | } 94 | }); 95 | 96 | nonCompositeDatabasesToImport.forEach(database -> { 97 | importDatabase(database); 98 | if (!database.seeds().isEmpty()) { 99 | for (String se : database.seeds()) { 100 | importSeed(database.name(), se, seeds); 101 | } 102 | } 103 | }); 104 | compositeDatabasesToImport.forEach(database -> { 105 | importDatabase(database); 106 | if (!database.seeds().isEmpty()) { 107 | for (String se : database.seeds()) { 108 | importSeed(database.name(), se, seeds); 109 | } 110 | } 111 | }); 112 | rolesToImport.forEach(this::importPrivileges); 113 | } 114 | 115 | public void importFile(File file, Map seeds) throws Exception { 116 | String s = new String(Files.readAllBytes(Paths.get(file.getPath()))); 117 | Map config = MAPPER.readValue(s, new TypeReference<>() {}); 118 | List compositeDatabases = new ArrayList<>(); 119 | if (config.get("kind").equals("Database")) { 120 | LOG.info("deserializing file %s to Database object".formatted(file.getPath())); 121 | Database database = MAPPER.convertValue(config, Database.class); 122 | if (database.composite()) { 123 | compositeDatabases.add(database); 124 | } else { 125 | Map remoteSeeds = getRemoteSeeds(database); 126 | seeds.putAll(remoteSeeds); 127 | importDatabase(database); 128 | if (!database.seeds().isEmpty()) { 129 | for (String se : database.seeds()) { 130 | importSeed(database.name(), se, seeds); 131 | } 132 | } 133 | } 134 | } 135 | 136 | compositeDatabases.forEach(this::importDatabase); 137 | 138 | if (config.get("kind").equals("Role")) { 139 | importPrivileges(MAPPER.convertValue(config, Role.class)); 140 | } 141 | } 142 | 143 | private void importPrivileges(Role role) { 144 | importRole(role); 145 | } 146 | 147 | private void importDatabase(Database database) { 148 | createDatabaseService.createDatabase(database); 149 | } 150 | 151 | private void importRole(Role role) { 152 | List matchRead = List.of("match", "read"); 153 | try (Session session = driver.session(sessionConfig())) { 154 | 155 | if (role.dropIfExists()) { 156 | session.run(String.format("DROP ROLE %s IF EXISTS", role.name())); 157 | } 158 | 159 | String q = String.format( "CREATE ROLE %s IF NOT EXISTS", role.name()); 160 | session.run(q); 161 | 162 | for (Privilege privilege : role.privileges()) { 163 | 164 | if (privilege.access()) { 165 | String aq = String.format("GRANT ACCESS ON DATABASE %s TO %s", privilege.graph(), role.name()); 166 | session.run(aq); 167 | } 168 | 169 | for (Rule rule : privilege.rules()) { 170 | if (matchRead.contains(rule.action())) { 171 | String properties = rule.resource().equals("all_properties") 172 | ? "*" 173 | : rule.resource(); 174 | 175 | String op = rule.access().equals(RuleAccess.GRANTED) ? "GRANT" : "DENY"; 176 | String target = rule.target().equals("node") ? "NODES" : "RELATIONSHIPS"; 177 | String action = rule.action().toUpperCase(); 178 | 179 | String oq = String.format("%s %s {%s} ON GRAPH %s %s %s TO %s", 180 | op, 181 | action, 182 | properties, 183 | privilege.graph(), 184 | target, 185 | rule.labels(), 186 | role.name() 187 | ); 188 | session.run(oq); 189 | } else { 190 | String op = rule.access().equals(RuleAccess.GRANTED) ? "GRANT" : "DENY"; 191 | String target = rule.access().name().equals("node") ? "NODES" : "RELATIONSHIPS"; 192 | String action = rule.action().toUpperCase(); 193 | 194 | String oq = String.format("%s %s ON GRAPH %s %s %s TO %s", 195 | op, 196 | action, 197 | privilege.graph(), 198 | target, 199 | rule.labels(), 200 | role.name() 201 | ); 202 | session.run(oq); 203 | } 204 | } 205 | } 206 | } catch (Exception e) { 207 | LOG.error(e.getMessage()); 208 | } 209 | } 210 | 211 | private void importSeed(String dbname, String seed, Map seeds) { 212 | if (!seeds.containsKey(seed)) { 213 | LOG.error("Seed {} for database {} is not present", seed, dbname); 214 | return; 215 | } 216 | List lines = Arrays.asList(seeds.get(seed).split(";")); 217 | try (Session session = driver.session(SessionConfig.forDatabase(dbname))) { 218 | lines 219 | .stream() 220 | .filter(l -> !l.trim().equals("")) 221 | .forEach(session::run); 222 | } 223 | } 224 | 225 | private void importSeed(String dbname, Map seeds) { 226 | for (String s : seeds.values()) { 227 | List lines = Arrays.asList(s.split(";")); 228 | try (Session session = driver.session(SessionConfig.forDatabase(dbname))) { 229 | lines 230 | .stream() 231 | .filter(l -> !l.trim().equals("")) 232 | .forEach(session::run); 233 | } 234 | } 235 | } 236 | 237 | public void waitUntilStarted() throws Exception { 238 | LOG.info("Detecting neo4j server availability"); 239 | long begin = System.currentTimeMillis(); 240 | boolean available = false; 241 | do { 242 | try (Session session = driver.session(sessionConfig())) { 243 | session.run("CALL db.ping()"); 244 | available = true; 245 | } catch (Exception e) { 246 | LOG.error("Neo4j server not yet available, waiting for 2 seconds..."); 247 | Thread.sleep(2000); 248 | } 249 | } while (((System.currentTimeMillis() - begin) < importWaitingTimeout) && !available); 250 | } 251 | 252 | private Map getRemoteSeeds(Database database) { 253 | Map seeds = new HashMap<>(); 254 | database.seeds().forEach(s -> { 255 | if (s.startsWith("http")) { 256 | try { 257 | seeds.put(s, ConfigUtils.URLToString(s)); 258 | } catch (Exception e) { 259 | LOG.error(e.getMessage()); 260 | } 261 | } 262 | }); 263 | 264 | return seeds; 265 | } 266 | 267 | private static boolean isRemoteSeed(String s) { 268 | return s.startsWith("http") || s.startsWith("file://"); 269 | } 270 | 271 | public void seedDatabase(String database, Map seeds) { 272 | importSeed(database, seeds); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2013 - 2021 GraphAware 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /examples/movies.cypher: -------------------------------------------------------------------------------- 1 | CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'}) 2 | CREATE (Keanu:Person {name:'Keanu Reeves', born:1964}) 3 | CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967}) 4 | CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961}) 5 | CREATE (Hugo:Person {name:'Hugo Weaving', born:1960}) 6 | CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967}) 7 | CREATE (LanaW:Person {name:'Lana Wachowski', born:1965}) 8 | CREATE (JoelS:Person {name:'Joel Silver', born:1952}) 9 | CREATE 10 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix), 11 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix), 12 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix), 13 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix), 14 | (LillyW)-[:DIRECTED]->(TheMatrix), 15 | (LanaW)-[:DIRECTED]->(TheMatrix), 16 | (JoelS)-[:PRODUCED]->(TheMatrix) 17 | 18 | CREATE (Emil:Person {name:"Emil Eifrem", born:1978}) 19 | CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix) 20 | 21 | CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'}) 22 | CREATE 23 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded), 24 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded), 25 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded), 26 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded), 27 | (LillyW)-[:DIRECTED]->(TheMatrixReloaded), 28 | (LanaW)-[:DIRECTED]->(TheMatrixReloaded), 29 | (JoelS)-[:PRODUCED]->(TheMatrixReloaded) 30 | 31 | CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'}) 32 | CREATE 33 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions), 34 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions), 35 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions), 36 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions), 37 | (LillyW)-[:DIRECTED]->(TheMatrixRevolutions), 38 | (LanaW)-[:DIRECTED]->(TheMatrixRevolutions), 39 | (JoelS)-[:PRODUCED]->(TheMatrixRevolutions) 40 | 41 | CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'}) 42 | CREATE (Charlize:Person {name:'Charlize Theron', born:1975}) 43 | CREATE (Al:Person {name:'Al Pacino', born:1940}) 44 | CREATE (Taylor:Person {name:'Taylor Hackford', born:1944}) 45 | CREATE 46 | (Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate), 47 | (Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate), 48 | (Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate), 49 | (Taylor)-[:DIRECTED]->(TheDevilsAdvocate) 50 | 51 | CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."}) 52 | CREATE (TomC:Person {name:'Tom Cruise', born:1962}) 53 | CREATE (JackN:Person {name:'Jack Nicholson', born:1937}) 54 | CREATE (DemiM:Person {name:'Demi Moore', born:1962}) 55 | CREATE (KevinB:Person {name:'Kevin Bacon', born:1958}) 56 | CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966}) 57 | CREATE (NoahW:Person {name:'Noah Wyle', born:1971}) 58 | CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968}) 59 | CREATE (KevinP:Person {name:'Kevin Pollak', born:1957}) 60 | CREATE (JTW:Person {name:'J.T. Walsh', born:1943}) 61 | CREATE (JamesM:Person {name:'James Marshall', born:1967}) 62 | CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948}) 63 | CREATE (RobR:Person {name:'Rob Reiner', born:1947}) 64 | CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961}) 65 | CREATE 66 | (TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen), 67 | (JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen), 68 | (DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen), 69 | (KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen), 70 | (KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen), 71 | (NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen), 72 | (CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen), 73 | (KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen), 74 | (JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen), 75 | (JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen), 76 | (ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen), 77 | (AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen), 78 | (RobR)-[:DIRECTED]->(AFewGoodMen), 79 | (AaronS)-[:WROTE]->(AFewGoodMen) 80 | 81 | CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'}) 82 | CREATE (KellyM:Person {name:'Kelly McGillis', born:1957}) 83 | CREATE (ValK:Person {name:'Val Kilmer', born:1959}) 84 | CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962}) 85 | CREATE (TomS:Person {name:'Tom Skerritt', born:1933}) 86 | CREATE (MegR:Person {name:'Meg Ryan', born:1961}) 87 | CREATE (TonyS:Person {name:'Tony Scott', born:1944}) 88 | CREATE (JimC:Person {name:'Jim Cash', born:1941}) 89 | CREATE 90 | (TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun), 91 | (KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun), 92 | (ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun), 93 | (AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun), 94 | (TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun), 95 | (MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun), 96 | (TonyS)-[:DIRECTED]->(TopGun), 97 | (JimC)-[:WROTE]->(TopGun) 98 | 99 | CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'}) 100 | CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969}) 101 | CREATE (KellyP:Person {name:'Kelly Preston', born:1962}) 102 | CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974}) 103 | CREATE (JayM:Person {name:'Jay Mohr', born:1970}) 104 | CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961}) 105 | CREATE (ReginaK:Person {name:'Regina King', born:1971}) 106 | CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996}) 107 | CREATE (CameronC:Person {name:'Cameron Crowe', born:1957}) 108 | CREATE 109 | (TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire), 110 | (CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire), 111 | (ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire), 112 | (KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire), 113 | (JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire), 114 | (JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire), 115 | (BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire), 116 | (ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire), 117 | (JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire), 118 | (CameronC)-[:DIRECTED]->(JerryMaguire), 119 | (CameronC)-[:PRODUCED]->(JerryMaguire), 120 | (CameronC)-[:WROTE]->(JerryMaguire) 121 | 122 | CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."}) 123 | CREATE (RiverP:Person {name:'River Phoenix', born:1970}) 124 | CREATE (CoreyF:Person {name:'Corey Feldman', born:1971}) 125 | CREATE (WilW:Person {name:'Wil Wheaton', born:1972}) 126 | CREATE (JohnC:Person {name:'John Cusack', born:1966}) 127 | CREATE (MarshallB:Person {name:'Marshall Bell', born:1942}) 128 | CREATE 129 | (WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe), 130 | (RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe), 131 | (JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe), 132 | (CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe), 133 | (JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe), 134 | (KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe), 135 | (MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe), 136 | (RobR)-[:DIRECTED]->(StandByMe) 137 | 138 | CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'}) 139 | CREATE (HelenH:Person {name:'Helen Hunt', born:1963}) 140 | CREATE (GregK:Person {name:'Greg Kinnear', born:1963}) 141 | CREATE (JamesB:Person {name:'James L. Brooks', born:1940}) 142 | CREATE 143 | (JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets), 144 | (HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets), 145 | (GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets), 146 | (CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets), 147 | (JamesB)-[:DIRECTED]->(AsGoodAsItGets) 148 | 149 | CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'}) 150 | CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960}) 151 | CREATE (MaxS:Person {name:'Max von Sydow', born:1929}) 152 | CREATE (WernerH:Person {name:'Werner Herzog', born:1942}) 153 | CREATE (Robin:Person {name:'Robin Williams', born:1951}) 154 | CREATE (VincentW:Person {name:'Vincent Ward', born:1956}) 155 | CREATE 156 | (Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome), 157 | (CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome), 158 | (AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome), 159 | (MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome), 160 | (WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome), 161 | (VincentW)-[:DIRECTED]->(WhatDreamsMayCome) 162 | 163 | CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'}) 164 | CREATE (EthanH:Person {name:'Ethan Hawke', born:1970}) 165 | CREATE (RickY:Person {name:'Rick Yune', born:1971}) 166 | CREATE (JamesC:Person {name:'James Cromwell', born:1940}) 167 | CREATE (ScottH:Person {name:'Scott Hicks', born:1953}) 168 | CREATE 169 | (EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars), 170 | (RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars), 171 | (MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars), 172 | (JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars), 173 | (ScottH)-[:DIRECTED]->(SnowFallingonCedars) 174 | 175 | CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'}) 176 | CREATE (ParkerP:Person {name:'Parker Posey', born:1968}) 177 | CREATE (DaveC:Person {name:'Dave Chappelle', born:1973}) 178 | CREATE (SteveZ:Person {name:'Steve Zahn', born:1967}) 179 | CREATE (TomH:Person {name:'Tom Hanks', born:1956}) 180 | CREATE (NoraE:Person {name:'Nora Ephron', born:1941}) 181 | CREATE 182 | (TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail), 183 | (MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail), 184 | (GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail), 185 | (ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail), 186 | (DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail), 187 | (SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail), 188 | (NoraE)-[:DIRECTED]->(YouveGotMail) 189 | 190 | CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'}) 191 | CREATE (RitaW:Person {name:'Rita Wilson', born:1956}) 192 | CREATE (BillPull:Person {name:'Bill Pullman', born:1953}) 193 | CREATE (VictorG:Person {name:'Victor Garber', born:1949}) 194 | CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962}) 195 | CREATE 196 | (TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle), 197 | (MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle), 198 | (RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle), 199 | (BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle), 200 | (VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle), 201 | (RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle), 202 | (NoraE)-[:DIRECTED]->(SleeplessInSeattle) 203 | 204 | CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'}) 205 | CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950}) 206 | CREATE (Nathan:Person {name:'Nathan Lane', born:1956}) 207 | CREATE 208 | (TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano), 209 | (MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano), 210 | (Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano), 211 | (JohnS)-[:DIRECTED]->(JoeVersustheVolcano) 212 | 213 | CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'}) 214 | CREATE (BillyC:Person {name:'Billy Crystal', born:1948}) 215 | CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956}) 216 | CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949}) 217 | CREATE 218 | (BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally), 219 | (MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally), 220 | (CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally), 221 | (BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally), 222 | (RobR)-[:DIRECTED]->(WhenHarryMetSally), 223 | (RobR)-[:PRODUCED]->(WhenHarryMetSally), 224 | (NoraE)-[:PRODUCED]->(WhenHarryMetSally), 225 | (NoraE)-[:WROTE]->(WhenHarryMetSally) 226 | 227 | CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'}) 228 | CREATE (LivT:Person {name:'Liv Tyler', born:1977}) 229 | CREATE 230 | (TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo), 231 | (LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo), 232 | (Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo), 233 | (TomH)-[:DIRECTED]->(ThatThingYouDo) 234 | 235 | CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'}) 236 | CREATE (Brooke:Person {name:'Brooke Langton', born:1970}) 237 | CREATE (Gene:Person {name:'Gene Hackman', born:1930}) 238 | CREATE (Orlando:Person {name:'Orlando Jones', born:1968}) 239 | CREATE (Howard:Person {name:'Howard Deutch', born:1950}) 240 | CREATE 241 | (Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements), 242 | (Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements), 243 | (Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements), 244 | (Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements), 245 | (Howard)-[:DIRECTED]->(TheReplacements) 246 | 247 | CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"}) 248 | CREATE (ChristianB:Person {name:'Christian Bale', born:1974}) 249 | CREATE (ZachG:Person {name:'Zach Grenier', born:1954}) 250 | CREATE 251 | (MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn), 252 | (ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn), 253 | (ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn), 254 | (SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn), 255 | (WernerH)-[:DIRECTED]->(RescueDawn) 256 | 257 | CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'}) 258 | CREATE (MikeN:Person {name:'Mike Nichols', born:1931}) 259 | CREATE 260 | (Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage), 261 | (Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage), 262 | (Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage), 263 | (MikeN)-[:DIRECTED]->(TheBirdcage) 264 | 265 | CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"}) 266 | CREATE (RichardH:Person {name:'Richard Harris', born:1930}) 267 | CREATE (ClintE:Person {name:'Clint Eastwood', born:1930}) 268 | CREATE 269 | (RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven), 270 | (ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven), 271 | (Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven), 272 | (ClintE)-[:DIRECTED]->(Unforgiven) 273 | 274 | CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'}) 275 | CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947}) 276 | CREATE (Dina:Person {name:'Dina Meyer', born:1968}) 277 | CREATE (IceT:Person {name:'Ice-T', born:1958}) 278 | CREATE (RobertL:Person {name:'Robert Longo', born:1953}) 279 | CREATE 280 | (Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic), 281 | (Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic), 282 | (Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic), 283 | (IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic), 284 | (RobertL)-[:DIRECTED]->(JohnnyMnemonic) 285 | 286 | CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'}) 287 | CREATE (HalleB:Person {name:'Halle Berry', born:1966}) 288 | CREATE (JimB:Person {name:'Jim Broadbent', born:1949}) 289 | CREATE (TomT:Person {name:'Tom Tykwer', born:1965}) 290 | CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969}) 291 | CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961}) 292 | CREATE 293 | (TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas), 294 | (Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas), 295 | (HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas), 296 | (JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas), 297 | (TomT)-[:DIRECTED]->(CloudAtlas), 298 | (LillyW)-[:DIRECTED]->(CloudAtlas), 299 | (LanaW)-[:DIRECTED]->(CloudAtlas), 300 | (DavidMitchell)-[:WROTE]->(CloudAtlas), 301 | (StefanArndt)-[:PRODUCED]->(CloudAtlas) 302 | 303 | CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'}) 304 | CREATE (IanM:Person {name:'Ian McKellen', born:1939}) 305 | CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976}) 306 | CREATE (PaulB:Person {name:'Paul Bettany', born:1971}) 307 | CREATE (RonH:Person {name:'Ron Howard', born:1954}) 308 | CREATE 309 | (TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode), 310 | (IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode), 311 | (AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode), 312 | (PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode), 313 | (RonH)-[:DIRECTED]->(TheDaVinciCode) 314 | 315 | CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'}) 316 | CREATE (NatalieP:Person {name:'Natalie Portman', born:1981}) 317 | CREATE (StephenR:Person {name:'Stephen Rea', born:1946}) 318 | CREATE (JohnH:Person {name:'John Hurt', born:1940}) 319 | CREATE (BenM:Person {name: 'Ben Miles', born:1967}) 320 | CREATE 321 | (Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta), 322 | (NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta), 323 | (StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta), 324 | (JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta), 325 | (BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta), 326 | (JamesM)-[:DIRECTED]->(VforVendetta), 327 | (LillyW)-[:PRODUCED]->(VforVendetta), 328 | (LanaW)-[:PRODUCED]->(VforVendetta), 329 | (JoelS)-[:PRODUCED]->(VforVendetta), 330 | (LillyW)-[:WROTE]->(VforVendetta), 331 | (LanaW)-[:WROTE]->(VforVendetta) 332 | 333 | CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'}) 334 | CREATE (EmileH:Person {name:'Emile Hirsch', born:1985}) 335 | CREATE (JohnG:Person {name:'John Goodman', born:1960}) 336 | CREATE (SusanS:Person {name:'Susan Sarandon', born:1946}) 337 | CREATE (MatthewF:Person {name:'Matthew Fox', born:1966}) 338 | CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980}) 339 | CREATE (Rain:Person {name:'Rain', born:1982}) 340 | CREATE 341 | (EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer), 342 | (JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer), 343 | (SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer), 344 | (MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer), 345 | (ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer), 346 | (Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer), 347 | (BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer), 348 | (LillyW)-[:DIRECTED]->(SpeedRacer), 349 | (LanaW)-[:DIRECTED]->(SpeedRacer), 350 | (LillyW)-[:WROTE]->(SpeedRacer), 351 | (LanaW)-[:WROTE]->(SpeedRacer), 352 | (JoelS)-[:PRODUCED]->(SpeedRacer) 353 | 354 | CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'}) 355 | CREATE (NaomieH:Person {name:'Naomie Harris'}) 356 | CREATE 357 | (Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin), 358 | (NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin), 359 | (RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin), 360 | (BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin), 361 | (JamesM)-[:DIRECTED]->(NinjaAssassin), 362 | (LillyW)-[:PRODUCED]->(NinjaAssassin), 363 | (LanaW)-[:PRODUCED]->(NinjaAssassin), 364 | (JoelS)-[:PRODUCED]->(NinjaAssassin) 365 | 366 | CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."}) 367 | CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957}) 368 | CREATE (DavidM:Person {name:'David Morse', born:1953}) 369 | CREATE (SamR:Person {name:'Sam Rockwell', born:1968}) 370 | CREATE (GaryS:Person {name:'Gary Sinise', born:1955}) 371 | CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959}) 372 | CREATE (FrankD:Person {name:'Frank Darabont', born:1959}) 373 | CREATE 374 | (TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile), 375 | (MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile), 376 | (DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile), 377 | (BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile), 378 | (JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile), 379 | (SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile), 380 | (GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile), 381 | (PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile), 382 | (FrankD)-[:DIRECTED]->(TheGreenMile) 383 | 384 | CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'}) 385 | CREATE (FrankL:Person {name:'Frank Langella', born:1938}) 386 | CREATE (MichaelS:Person {name:'Michael Sheen', born:1969}) 387 | CREATE (OliverP:Person {name:'Oliver Platt', born:1960}) 388 | CREATE 389 | (FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon), 390 | (MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon), 391 | (KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon), 392 | (OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon), 393 | (SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon), 394 | (RonH)-[:DIRECTED]->(FrostNixon) 395 | 396 | CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."}) 397 | CREATE (DannyD:Person {name:'Danny DeVito', born:1944}) 398 | CREATE (JohnR:Person {name:'John C. Reilly', born:1965}) 399 | CREATE 400 | (JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa), 401 | (DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa), 402 | (JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa), 403 | (JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa), 404 | (DannyD)-[:DIRECTED]->(Hoffa) 405 | 406 | CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'}) 407 | CREATE (EdH:Person {name:'Ed Harris', born:1950}) 408 | CREATE (BillPax:Person {name:'Bill Paxton', born:1955}) 409 | CREATE 410 | (TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13), 411 | (KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13), 412 | (EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13), 413 | (BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13), 414 | (GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13), 415 | (RonH)-[:DIRECTED]->(Apollo13) 416 | 417 | CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."}) 418 | CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967}) 419 | CREATE (JanB:Person {name:'Jan de Bont', born:1943}) 420 | CREATE 421 | (BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister), 422 | (HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister), 423 | (ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister), 424 | (PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister), 425 | (JanB)-[:DIRECTED]->(Twister) 426 | 427 | CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'}) 428 | CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951}) 429 | CREATE 430 | (TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway), 431 | (HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway), 432 | (RobertZ)-[:DIRECTED]->(CastAway) 433 | 434 | CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"}) 435 | CREATE (MilosF:Person {name:'Milos Forman', born:1932}) 436 | CREATE 437 | (JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest), 438 | (DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest), 439 | (MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest) 440 | 441 | CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003}) 442 | CREATE (DianeK:Person {name:'Diane Keaton', born:1946}) 443 | CREATE (NancyM:Person {name:'Nancy Meyers', born:1949}) 444 | CREATE 445 | (JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive), 446 | (DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive), 447 | (Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive), 448 | (NancyM)-[:DIRECTED]->(SomethingsGottaGive), 449 | (NancyM)-[:PRODUCED]->(SomethingsGottaGive), 450 | (NancyM)-[:WROTE]->(SomethingsGottaGive) 451 | 452 | CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."}) 453 | CREATE (ChrisC:Person {name:'Chris Columbus', born:1958}) 454 | CREATE 455 | (Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan), 456 | (OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan), 457 | (ChrisC)-[:DIRECTED]->(BicentennialMan) 458 | 459 | CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."}) 460 | CREATE (JuliaR:Person {name:'Julia Roberts', born:1967}) 461 | CREATE 462 | (TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar), 463 | (JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar), 464 | (PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar), 465 | (MikeN)-[:DIRECTED]->(CharlieWilsonsWar) 466 | 467 | CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season... Believe'}) 468 | CREATE 469 | (TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress), 470 | (RobertZ)-[:DIRECTED]->(ThePolarExpress) 471 | 472 | CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'}) 473 | CREATE (Madonna:Person {name:'Madonna', born:1954}) 474 | CREATE (GeenaD:Person {name:'Geena Davis', born:1956}) 475 | CREATE (LoriP:Person {name:'Lori Petty', born:1963}) 476 | CREATE (PennyM:Person {name:'Penny Marshall', born:1943}) 477 | CREATE 478 | (TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn), 479 | (GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn), 480 | (LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn), 481 | (RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn), 482 | (Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn), 483 | (BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn), 484 | (PennyM)-[:DIRECTED]->(ALeagueofTheirOwn) 485 | 486 | CREATE (PaulBlythe:Person {name:'Paul Blythe'}) 487 | CREATE (AngelaScope:Person {name:'Angela Scope'}) 488 | CREATE (JessicaThompson:Person {name:'Jessica Thompson'}) 489 | CREATE (JamesThompson:Person {name:'James Thompson'}) 490 | 491 | CREATE 492 | (JamesThompson)-[:FOLLOWS]->(JessicaThompson), 493 | (AngelaScope)-[:FOLLOWS]->(JessicaThompson), 494 | (PaulBlythe)-[:FOLLOWS]->(AngelaScope) 495 | 496 | CREATE 497 | (JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas), 498 | (JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements), 499 | (JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements), 500 | (AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements), 501 | (JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven), 502 | (JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage), 503 | (JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode), 504 | (JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode), 505 | (JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire); -------------------------------------------------------------------------------- /examples/config/movies.cypher: -------------------------------------------------------------------------------- 1 | CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'}) 2 | CREATE (Keanu:Person {name:'Keanu Reeves', born:1964}) 3 | CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967}) 4 | CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961}) 5 | CREATE (Hugo:Person {name:'Hugo Weaving', born:1960}) 6 | CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967}) 7 | CREATE (LanaW:Person {name:'Lana Wachowski', born:1965}) 8 | CREATE (JoelS:Person {name:'Joel Silver', born:1952}) 9 | CREATE 10 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix), 11 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix), 12 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix), 13 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix), 14 | (LillyW)-[:DIRECTED]->(TheMatrix), 15 | (LanaW)-[:DIRECTED]->(TheMatrix), 16 | (JoelS)-[:PRODUCED]->(TheMatrix) 17 | 18 | CREATE (Emil:Person {name:"Emil Eifrem", born:1978}) 19 | CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix) 20 | 21 | CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'}) 22 | CREATE 23 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded), 24 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded), 25 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded), 26 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded), 27 | (LillyW)-[:DIRECTED]->(TheMatrixReloaded), 28 | (LanaW)-[:DIRECTED]->(TheMatrixReloaded), 29 | (JoelS)-[:PRODUCED]->(TheMatrixReloaded) 30 | 31 | CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'}) 32 | CREATE 33 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions), 34 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions), 35 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions), 36 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions), 37 | (LillyW)-[:DIRECTED]->(TheMatrixRevolutions), 38 | (LanaW)-[:DIRECTED]->(TheMatrixRevolutions), 39 | (JoelS)-[:PRODUCED]->(TheMatrixRevolutions) 40 | 41 | CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'}) 42 | CREATE (Charlize:Person {name:'Charlize Theron', born:1975}) 43 | CREATE (Al:Person {name:'Al Pacino', born:1940}) 44 | CREATE (Taylor:Person {name:'Taylor Hackford', born:1944}) 45 | CREATE 46 | (Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate), 47 | (Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate), 48 | (Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate), 49 | (Taylor)-[:DIRECTED]->(TheDevilsAdvocate) 50 | 51 | CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."}) 52 | CREATE (TomC:Person {name:'Tom Cruise', born:1962}) 53 | CREATE (JackN:Person {name:'Jack Nicholson', born:1937}) 54 | CREATE (DemiM:Person {name:'Demi Moore', born:1962}) 55 | CREATE (KevinB:Person {name:'Kevin Bacon', born:1958}) 56 | CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966}) 57 | CREATE (NoahW:Person {name:'Noah Wyle', born:1971}) 58 | CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968}) 59 | CREATE (KevinP:Person {name:'Kevin Pollak', born:1957}) 60 | CREATE (JTW:Person {name:'J.T. Walsh', born:1943}) 61 | CREATE (JamesM:Person {name:'James Marshall', born:1967}) 62 | CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948}) 63 | CREATE (RobR:Person {name:'Rob Reiner', born:1947}) 64 | CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961}) 65 | CREATE 66 | (TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen), 67 | (JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen), 68 | (DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen), 69 | (KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen), 70 | (KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen), 71 | (NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen), 72 | (CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen), 73 | (KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen), 74 | (JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen), 75 | (JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen), 76 | (ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen), 77 | (AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen), 78 | (RobR)-[:DIRECTED]->(AFewGoodMen), 79 | (AaronS)-[:WROTE]->(AFewGoodMen) 80 | 81 | CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'}) 82 | CREATE (KellyM:Person {name:'Kelly McGillis', born:1957}) 83 | CREATE (ValK:Person {name:'Val Kilmer', born:1959}) 84 | CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962}) 85 | CREATE (TomS:Person {name:'Tom Skerritt', born:1933}) 86 | CREATE (MegR:Person {name:'Meg Ryan', born:1961}) 87 | CREATE (TonyS:Person {name:'Tony Scott', born:1944}) 88 | CREATE (JimC:Person {name:'Jim Cash', born:1941}) 89 | CREATE 90 | (TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun), 91 | (KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun), 92 | (ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun), 93 | (AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun), 94 | (TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun), 95 | (MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun), 96 | (TonyS)-[:DIRECTED]->(TopGun), 97 | (JimC)-[:WROTE]->(TopGun) 98 | 99 | CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'}) 100 | CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969}) 101 | CREATE (KellyP:Person {name:'Kelly Preston', born:1962}) 102 | CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974}) 103 | CREATE (JayM:Person {name:'Jay Mohr', born:1970}) 104 | CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961}) 105 | CREATE (ReginaK:Person {name:'Regina King', born:1971}) 106 | CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996}) 107 | CREATE (CameronC:Person {name:'Cameron Crowe', born:1957}) 108 | CREATE 109 | (TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire), 110 | (CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire), 111 | (ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire), 112 | (KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire), 113 | (JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire), 114 | (JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire), 115 | (BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire), 116 | (ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire), 117 | (JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire), 118 | (CameronC)-[:DIRECTED]->(JerryMaguire), 119 | (CameronC)-[:PRODUCED]->(JerryMaguire), 120 | (CameronC)-[:WROTE]->(JerryMaguire) 121 | 122 | CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."}) 123 | CREATE (RiverP:Person {name:'River Phoenix', born:1970}) 124 | CREATE (CoreyF:Person {name:'Corey Feldman', born:1971}) 125 | CREATE (WilW:Person {name:'Wil Wheaton', born:1972}) 126 | CREATE (JohnC:Person {name:'John Cusack', born:1966}) 127 | CREATE (MarshallB:Person {name:'Marshall Bell', born:1942}) 128 | CREATE 129 | (WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe), 130 | (RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe), 131 | (JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe), 132 | (CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe), 133 | (JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe), 134 | (KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe), 135 | (MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe), 136 | (RobR)-[:DIRECTED]->(StandByMe) 137 | 138 | CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'}) 139 | CREATE (HelenH:Person {name:'Helen Hunt', born:1963}) 140 | CREATE (GregK:Person {name:'Greg Kinnear', born:1963}) 141 | CREATE (JamesB:Person {name:'James L. Brooks', born:1940}) 142 | CREATE 143 | (JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets), 144 | (HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets), 145 | (GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets), 146 | (CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets), 147 | (JamesB)-[:DIRECTED]->(AsGoodAsItGets) 148 | 149 | CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'}) 150 | CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960}) 151 | CREATE (MaxS:Person {name:'Max von Sydow', born:1929}) 152 | CREATE (WernerH:Person {name:'Werner Herzog', born:1942}) 153 | CREATE (Robin:Person {name:'Robin Williams', born:1951}) 154 | CREATE (VincentW:Person {name:'Vincent Ward', born:1956}) 155 | CREATE 156 | (Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome), 157 | (CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome), 158 | (AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome), 159 | (MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome), 160 | (WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome), 161 | (VincentW)-[:DIRECTED]->(WhatDreamsMayCome) 162 | 163 | CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'}) 164 | CREATE (EthanH:Person {name:'Ethan Hawke', born:1970}) 165 | CREATE (RickY:Person {name:'Rick Yune', born:1971}) 166 | CREATE (JamesC:Person {name:'James Cromwell', born:1940}) 167 | CREATE (ScottH:Person {name:'Scott Hicks', born:1953}) 168 | CREATE 169 | (EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars), 170 | (RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars), 171 | (MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars), 172 | (JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars), 173 | (ScottH)-[:DIRECTED]->(SnowFallingonCedars) 174 | 175 | CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'}) 176 | CREATE (ParkerP:Person {name:'Parker Posey', born:1968}) 177 | CREATE (DaveC:Person {name:'Dave Chappelle', born:1973}) 178 | CREATE (SteveZ:Person {name:'Steve Zahn', born:1967}) 179 | CREATE (TomH:Person {name:'Tom Hanks', born:1956}) 180 | CREATE (NoraE:Person {name:'Nora Ephron', born:1941}) 181 | CREATE 182 | (TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail), 183 | (MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail), 184 | (GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail), 185 | (ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail), 186 | (DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail), 187 | (SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail), 188 | (NoraE)-[:DIRECTED]->(YouveGotMail) 189 | 190 | CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'}) 191 | CREATE (RitaW:Person {name:'Rita Wilson', born:1956}) 192 | CREATE (BillPull:Person {name:'Bill Pullman', born:1953}) 193 | CREATE (VictorG:Person {name:'Victor Garber', born:1949}) 194 | CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962}) 195 | CREATE 196 | (TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle), 197 | (MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle), 198 | (RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle), 199 | (BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle), 200 | (VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle), 201 | (RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle), 202 | (NoraE)-[:DIRECTED]->(SleeplessInSeattle) 203 | 204 | CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'}) 205 | CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950}) 206 | CREATE (Nathan:Person {name:'Nathan Lane', born:1956}) 207 | CREATE 208 | (TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano), 209 | (MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano), 210 | (Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano), 211 | (JohnS)-[:DIRECTED]->(JoeVersustheVolcano) 212 | 213 | CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'}) 214 | CREATE (BillyC:Person {name:'Billy Crystal', born:1948}) 215 | CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956}) 216 | CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949}) 217 | CREATE 218 | (BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally), 219 | (MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally), 220 | (CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally), 221 | (BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally), 222 | (RobR)-[:DIRECTED]->(WhenHarryMetSally), 223 | (RobR)-[:PRODUCED]->(WhenHarryMetSally), 224 | (NoraE)-[:PRODUCED]->(WhenHarryMetSally), 225 | (NoraE)-[:WROTE]->(WhenHarryMetSally) 226 | 227 | CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'}) 228 | CREATE (LivT:Person {name:'Liv Tyler', born:1977}) 229 | CREATE 230 | (TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo), 231 | (LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo), 232 | (Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo), 233 | (TomH)-[:DIRECTED]->(ThatThingYouDo) 234 | 235 | CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'}) 236 | CREATE (Brooke:Person {name:'Brooke Langton', born:1970}) 237 | CREATE (Gene:Person {name:'Gene Hackman', born:1930}) 238 | CREATE (Orlando:Person {name:'Orlando Jones', born:1968}) 239 | CREATE (Howard:Person {name:'Howard Deutch', born:1950}) 240 | CREATE 241 | (Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements), 242 | (Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements), 243 | (Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements), 244 | (Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements), 245 | (Howard)-[:DIRECTED]->(TheReplacements) 246 | 247 | CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"}) 248 | CREATE (ChristianB:Person {name:'Christian Bale', born:1974}) 249 | CREATE (ZachG:Person {name:'Zach Grenier', born:1954}) 250 | CREATE 251 | (MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn), 252 | (ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn), 253 | (ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn), 254 | (SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn), 255 | (WernerH)-[:DIRECTED]->(RescueDawn) 256 | 257 | CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'}) 258 | CREATE (MikeN:Person {name:'Mike Nichols', born:1931}) 259 | CREATE 260 | (Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage), 261 | (Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage), 262 | (Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage), 263 | (MikeN)-[:DIRECTED]->(TheBirdcage) 264 | 265 | CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"}) 266 | CREATE (RichardH:Person {name:'Richard Harris', born:1930}) 267 | CREATE (ClintE:Person {name:'Clint Eastwood', born:1930}) 268 | CREATE 269 | (RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven), 270 | (ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven), 271 | (Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven), 272 | (ClintE)-[:DIRECTED]->(Unforgiven) 273 | 274 | CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'}) 275 | CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947}) 276 | CREATE (Dina:Person {name:'Dina Meyer', born:1968}) 277 | CREATE (IceT:Person {name:'Ice-T', born:1958}) 278 | CREATE (RobertL:Person {name:'Robert Longo', born:1953}) 279 | CREATE 280 | (Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic), 281 | (Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic), 282 | (Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic), 283 | (IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic), 284 | (RobertL)-[:DIRECTED]->(JohnnyMnemonic) 285 | 286 | CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'}) 287 | CREATE (HalleB:Person {name:'Halle Berry', born:1966}) 288 | CREATE (JimB:Person {name:'Jim Broadbent', born:1949}) 289 | CREATE (TomT:Person {name:'Tom Tykwer', born:1965}) 290 | CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969}) 291 | CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961}) 292 | CREATE 293 | (TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas), 294 | (Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas), 295 | (HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas), 296 | (JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas), 297 | (TomT)-[:DIRECTED]->(CloudAtlas), 298 | (LillyW)-[:DIRECTED]->(CloudAtlas), 299 | (LanaW)-[:DIRECTED]->(CloudAtlas), 300 | (DavidMitchell)-[:WROTE]->(CloudAtlas), 301 | (StefanArndt)-[:PRODUCED]->(CloudAtlas) 302 | 303 | CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'}) 304 | CREATE (IanM:Person {name:'Ian McKellen', born:1939}) 305 | CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976}) 306 | CREATE (PaulB:Person {name:'Paul Bettany', born:1971}) 307 | CREATE (RonH:Person {name:'Ron Howard', born:1954}) 308 | CREATE 309 | (TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode), 310 | (IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode), 311 | (AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode), 312 | (PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode), 313 | (RonH)-[:DIRECTED]->(TheDaVinciCode) 314 | 315 | CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'}) 316 | CREATE (NatalieP:Person {name:'Natalie Portman', born:1981}) 317 | CREATE (StephenR:Person {name:'Stephen Rea', born:1946}) 318 | CREATE (JohnH:Person {name:'John Hurt', born:1940}) 319 | CREATE (BenM:Person {name: 'Ben Miles', born:1967}) 320 | CREATE 321 | (Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta), 322 | (NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta), 323 | (StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta), 324 | (JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta), 325 | (BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta), 326 | (JamesM)-[:DIRECTED]->(VforVendetta), 327 | (LillyW)-[:PRODUCED]->(VforVendetta), 328 | (LanaW)-[:PRODUCED]->(VforVendetta), 329 | (JoelS)-[:PRODUCED]->(VforVendetta), 330 | (LillyW)-[:WROTE]->(VforVendetta), 331 | (LanaW)-[:WROTE]->(VforVendetta) 332 | 333 | CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'}) 334 | CREATE (EmileH:Person {name:'Emile Hirsch', born:1985}) 335 | CREATE (JohnG:Person {name:'John Goodman', born:1960}) 336 | CREATE (SusanS:Person {name:'Susan Sarandon', born:1946}) 337 | CREATE (MatthewF:Person {name:'Matthew Fox', born:1966}) 338 | CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980}) 339 | CREATE (Rain:Person {name:'Rain', born:1982}) 340 | CREATE 341 | (EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer), 342 | (JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer), 343 | (SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer), 344 | (MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer), 345 | (ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer), 346 | (Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer), 347 | (BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer), 348 | (LillyW)-[:DIRECTED]->(SpeedRacer), 349 | (LanaW)-[:DIRECTED]->(SpeedRacer), 350 | (LillyW)-[:WROTE]->(SpeedRacer), 351 | (LanaW)-[:WROTE]->(SpeedRacer), 352 | (JoelS)-[:PRODUCED]->(SpeedRacer) 353 | 354 | CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'}) 355 | CREATE (NaomieH:Person {name:'Naomie Harris'}) 356 | CREATE 357 | (Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin), 358 | (NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin), 359 | (RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin), 360 | (BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin), 361 | (JamesM)-[:DIRECTED]->(NinjaAssassin), 362 | (LillyW)-[:PRODUCED]->(NinjaAssassin), 363 | (LanaW)-[:PRODUCED]->(NinjaAssassin), 364 | (JoelS)-[:PRODUCED]->(NinjaAssassin) 365 | 366 | CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."}) 367 | CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957}) 368 | CREATE (DavidM:Person {name:'David Morse', born:1953}) 369 | CREATE (SamR:Person {name:'Sam Rockwell', born:1968}) 370 | CREATE (GaryS:Person {name:'Gary Sinise', born:1955}) 371 | CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959}) 372 | CREATE (FrankD:Person {name:'Frank Darabont', born:1959}) 373 | CREATE 374 | (TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile), 375 | (MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile), 376 | (DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile), 377 | (BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile), 378 | (JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile), 379 | (SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile), 380 | (GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile), 381 | (PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile), 382 | (FrankD)-[:DIRECTED]->(TheGreenMile) 383 | 384 | CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'}) 385 | CREATE (FrankL:Person {name:'Frank Langella', born:1938}) 386 | CREATE (MichaelS:Person {name:'Michael Sheen', born:1969}) 387 | CREATE (OliverP:Person {name:'Oliver Platt', born:1960}) 388 | CREATE 389 | (FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon), 390 | (MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon), 391 | (KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon), 392 | (OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon), 393 | (SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon), 394 | (RonH)-[:DIRECTED]->(FrostNixon) 395 | 396 | CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."}) 397 | CREATE (DannyD:Person {name:'Danny DeVito', born:1944}) 398 | CREATE (JohnR:Person {name:'John C. Reilly', born:1965}) 399 | CREATE 400 | (JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa), 401 | (DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa), 402 | (JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa), 403 | (JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa), 404 | (DannyD)-[:DIRECTED]->(Hoffa) 405 | 406 | CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'}) 407 | CREATE (EdH:Person {name:'Ed Harris', born:1950}) 408 | CREATE (BillPax:Person {name:'Bill Paxton', born:1955}) 409 | CREATE 410 | (TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13), 411 | (KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13), 412 | (EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13), 413 | (BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13), 414 | (GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13), 415 | (RonH)-[:DIRECTED]->(Apollo13) 416 | 417 | CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."}) 418 | CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967}) 419 | CREATE (JanB:Person {name:'Jan de Bont', born:1943}) 420 | CREATE 421 | (BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister), 422 | (HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister), 423 | (ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister), 424 | (PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister), 425 | (JanB)-[:DIRECTED]->(Twister) 426 | 427 | CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'}) 428 | CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951}) 429 | CREATE 430 | (TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway), 431 | (HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway), 432 | (RobertZ)-[:DIRECTED]->(CastAway) 433 | 434 | CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"}) 435 | CREATE (MilosF:Person {name:'Milos Forman', born:1932}) 436 | CREATE 437 | (JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest), 438 | (DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest), 439 | (MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest) 440 | 441 | CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003}) 442 | CREATE (DianeK:Person {name:'Diane Keaton', born:1946}) 443 | CREATE (NancyM:Person {name:'Nancy Meyers', born:1949}) 444 | CREATE 445 | (JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive), 446 | (DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive), 447 | (Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive), 448 | (NancyM)-[:DIRECTED]->(SomethingsGottaGive), 449 | (NancyM)-[:PRODUCED]->(SomethingsGottaGive), 450 | (NancyM)-[:WROTE]->(SomethingsGottaGive) 451 | 452 | CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."}) 453 | CREATE (ChrisC:Person {name:'Chris Columbus', born:1958}) 454 | CREATE 455 | (Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan), 456 | (OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan), 457 | (ChrisC)-[:DIRECTED]->(BicentennialMan) 458 | 459 | CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."}) 460 | CREATE (JuliaR:Person {name:'Julia Roberts', born:1967}) 461 | CREATE 462 | (TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar), 463 | (JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar), 464 | (PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar), 465 | (MikeN)-[:DIRECTED]->(CharlieWilsonsWar) 466 | 467 | CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season... Believe'}) 468 | CREATE 469 | (TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress), 470 | (RobertZ)-[:DIRECTED]->(ThePolarExpress) 471 | 472 | CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'}) 473 | CREATE (Madonna:Person {name:'Madonna', born:1954}) 474 | CREATE (GeenaD:Person {name:'Geena Davis', born:1956}) 475 | CREATE (LoriP:Person {name:'Lori Petty', born:1963}) 476 | CREATE (PennyM:Person {name:'Penny Marshall', born:1943}) 477 | CREATE 478 | (TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn), 479 | (GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn), 480 | (LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn), 481 | (RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn), 482 | (Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn), 483 | (BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn), 484 | (PennyM)-[:DIRECTED]->(ALeagueofTheirOwn) 485 | 486 | CREATE (PaulBlythe:Person {name:'Paul Blythe'}) 487 | CREATE (AngelaScope:Person {name:'Angela Scope'}) 488 | CREATE (JessicaThompson:Person {name:'Jessica Thompson'}) 489 | CREATE (JamesThompson:Person {name:'James Thompson'}) 490 | 491 | CREATE 492 | (JamesThompson)-[:FOLLOWS]->(JessicaThompson), 493 | (AngelaScope)-[:FOLLOWS]->(JessicaThompson), 494 | (PaulBlythe)-[:FOLLOWS]->(AngelaScope) 495 | 496 | CREATE 497 | (JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas), 498 | (JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements), 499 | (JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements), 500 | (AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements), 501 | (JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven), 502 | (JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage), 503 | (JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode), 504 | (JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode), 505 | (JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire); --------------------------------------------------------------------------------