├── .gitignore ├── HELP.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── graph-model.png ├── init-db.png ├── run.sh ├── settings.gradle.kts ├── simpsons.png └── src ├── howto.md ├── main ├── docker │ ├── backend-api │ │ └── Dockerfile │ ├── docker-compose.yml │ └── neo4j │ │ ├── dockerfile │ │ ├── graphaware-server-community-all-3.5.4.53.jar │ │ ├── graphaware-uuid-3.5.4.53.17.jar │ │ └── neo4j.conf ├── kotlin │ └── com │ │ └── cisse │ │ └── demo │ │ ├── Application.kt │ │ ├── api │ │ ├── config │ │ │ └── security │ │ │ │ └── WebSecurityConfiguration.kt │ │ ├── dto │ │ │ ├── PersonDTO.kt │ │ │ └── RelationshipDTO.kt │ │ ├── endpoints │ │ │ └── graphql │ │ │ │ ├── AppSchema.kt │ │ │ │ └── GraphQL.kt │ │ └── service │ │ │ └── person │ │ │ └── GetPersonServiceImpl.kt │ │ ├── core │ │ └── domain │ │ │ ├── entities │ │ │ ├── ChildToFather.kt │ │ │ ├── ChildToMother.kt │ │ │ ├── Entity.kt │ │ │ ├── Friendship.kt │ │ │ ├── Person.kt │ │ │ ├── Relationship.kt │ │ │ ├── Sibling.kt │ │ │ └── Spouse.kt │ │ │ └── usecases │ │ │ └── person │ │ │ └── GetPerson.kt │ │ ├── data │ │ └── neo4j │ │ │ ├── config │ │ │ └── DateConverter.kt │ │ │ └── repositories │ │ │ └── PersonRepository.kt │ │ └── exception │ │ └── NotFoundException.kt └── resources │ ├── banner.txt │ ├── config │ ├── application-docker.yml │ └── application.yml │ └── neo4j │ └── migrations │ └── V000__init_database.cypher └── test └── kotlin └── com └── cisse └── demo └── ApplicationTests.kt /.gitignore: -------------------------------------------------------------------------------- 1 | /confluence/target 2 | /dependencies/repo 3 | /dist 4 | /local 5 | /gh-pages 6 | /ideaSDK 7 | /clionSDK 8 | /android-studio/sdk 9 | out/ 10 | /tmp 11 | workspace.xml 12 | *.versionsBackup 13 | /jps-plugin/testData/kannotator 14 | /js/js.translator/testData/out/ 15 | /js/js.translator/testData/out-min/ 16 | .gradle/ 17 | 18 | build/ 19 | !**/src/**/build 20 | !**/test/**/build 21 | *.iml 22 | !**/testData/**/*.iml 23 | .idea/** 24 | kotlin-ultimate/ 25 | node_modules/ 26 | .rpt2_cache/ 27 | libraries/tools/kotlin-test-nodejs-runner/lib/ 28 | local.properties 29 | 30 | # mac os 31 | **/.DS_Store 32 | 33 | # IntelliJ 34 | .idea/** 35 | auto-import. 36 | *.iml 37 | *.ipr -------------------------------------------------------------------------------- /HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Gradle documentation](https://docs.gradle.org) 7 | 8 | ### Guides 9 | The following guides illustrate how to use some features concretely: 10 | 11 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 12 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 13 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) 14 | * [Accessing Data with Neo4j](https://spring.io/guides/gs/accessing-data-neo4j/) 15 | * [Building a RESTful Web Service with Spring Boot Actuator](https://spring.io/guides/gs/actuator-service/) 16 | * [Securing a Web Application](https://spring.io/guides/gs/securing-web/) 17 | * [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) 18 | * [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) 19 | 20 | ### Additional Links 21 | These additional references should also help you: 22 | 23 | * [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Abdoul Karim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Family tree sample with Spring Boot, Kotlin, GraphQL and Neo4j 2 | 3 | This is a simple app showing how to create a [GraphQL](https://graphql.org/) api in [Kotlin](https://kotlinlang.org/) backed by [Neo4J](https://neo4j.com/developer/?ref=home-2) and powered by [Spring Boot](https://spring.io/projects/spring-boot) 4 | 5 | ![The simpsons](simpsons.png) 6 | 7 | ## Dependencies 8 | 9 | This demo api uses the following dependencies 10 | 11 | [Spring Boot](https://spring.io/projects/spring-boot#overview) 12 | 13 | [GraphQL Server - KGraphQL](https://github.com/aPureBase/KGraphQL): A Pure Kotlin GraphQL implementation 14 | 15 | [Neo4J database](https://neo4j.com/developer/?ref=home-2): A NoSQL Graph database 16 | 17 | [Neo4J uuid](https://github.com/graphaware/neo4j-uuid): GraphAware Runtime Module that assigns a UUID to all nodes (and relationships) in the graph transparently 18 | 19 | [Neo4j Migrations](https://github.com/michael-simons/neo4j-migrations): Manages database changes. 20 | 21 | ## Graph Model 22 | 23 | The graph model is quite simple 24 | 25 | We have one type of node: a `Person` and 3 types of edges corresponding to the relationships (`HAS_FATHER`, `HAS_MOTHER` and `SPOUSE_OF`): 26 | 27 | ``` 28 | (Person)-[:HAS_MOTHER]->(Person) 29 | (Person)-[:HAS_FATHER]->(Person) 30 | (Person)-[:SPOUSE_OF]-(Person) // undirected since it is bidirectionnal 31 | ``` 32 | The siblings are calculated using the `HAS_FATHER` and `HAS_MOTHER` relations : two person with the same mother or father are siblings 33 | 34 | ![Graph model](graph-model.png) 35 | 36 | You can find below an example of corresponding cypher queries to create our model 37 | 38 | ```cql 39 | CREATE (Abraham:Person {firstname:'Abraham', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 40 | CREATE (Mona:Person {firstname:'Mona', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 41 | CREATE (Clancy:Person {firstname:'Clancy', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 42 | CREATE (Jacqueline:Person {firstname:'Jacqueline', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 43 | CREATE (Herber:Person {firstname:'Herber', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 44 | CREATE (Homer:Person {firstname:'Homer', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 45 | CREATE (Marge:Person {firstname:'Marge', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 46 | CREATE (Patty:Person {firstname:'Patty', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 47 | CREATE (Selma:Person {firstname:'Selma', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 48 | CREATE (Bart:Person {firstname: 'Bart', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 49 | CREATE (Lisa:Person {firstname:'Lisa', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 50 | CREATE (Maggie:Person {firstname: 'Maggie', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 51 | CREATE (Ling:Person {firstname: 'Ling', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 52 | ; 53 | 54 | MATCH (f:Person {firstname:"Homer"}) 55 | MATCH (m:Person {firstname:"Marge"}) 56 | MATCH (c:Person) 57 | WHERE (c.firstname in ['Bart', 'Lisa', 'Maggie']) 58 | MERGE (c)-[:HAS_FATHER]->(f) 59 | MERGE (c)-[:HAS_MOTHER]->(m) 60 | MERGE (f)-[:SPOUSE_OF]-(m) 61 | ; 62 | 63 | MATCH (f:Person {firstname:"Abraham"}) 64 | MATCH(m:Person {firstname:"Mona"}) 65 | MATCH (c:Person) 66 | WHERE c.firstname in ['Homer', 'Herber'] 67 | MERGE (c)-[:HAS_FATHER]->(f) 68 | MERGE (c)-[:HAS_MOTHER]->(m) 69 | MERGE (f)-[:SPOUSE_OF]-(m) 70 | ; 71 | 72 | MATCH (f:Person {firstname:"Clancy"}) 73 | MATCH (m:Person {firstname:"Jacqueline"}) 74 | MATCH (c:Person) 75 | WHERE c.firstname in ['Marge', 'Patty', 'Selma'] 76 | MERGE (c)-[:HAS_FATHER]->(f) 77 | MERGE (c)-[:HAS_MOTHER]->(m) 78 | MERGE (f)-[:SPOUSE_OF]-(m) 79 | ; 80 | 81 | MATCH (c:Person {firstname:"Ling"}) 82 | MATCH (m:Person {firstname:"Selma"}) 83 | MERGE (c)-[:HAS_MOTHER]->(m) 84 | ; 85 | ``` 86 | 87 | ## Running the app 88 | 89 | Simpy execute the script run.sh` 90 | 91 | ```bash 92 | $ ./run.sh` 93 | ``` 94 | 95 | ## Querying the api 96 | 97 | Example 98 | 99 | `POST localhost:8080/graphql` 100 | 101 | ```graphql 102 | { 103 | person(firstname: "Homer") { 104 | uuid, 105 | firstname, 106 | lastname, 107 | birthdate, 108 | spouse { 109 | firstname, 110 | siblings { 111 | firstname 112 | } 113 | } 114 | mother { 115 | firstname 116 | }, 117 | father { 118 | firstname 119 | } 120 | siblings { 121 | firstname 122 | } 123 | } 124 | } 125 | ``` 126 | 127 | Here is the corresponding curl command 128 | ``` 129 | curl -X POST -i http://localhost:8080/graphql --data '{ 130 | person(firstname: "Homer") { 131 | uuid, 132 | firstname, 133 | lastname, 134 | birthdate, 135 | spouse { 136 | firstname, 137 | siblings { 138 | firstname 139 | } 140 | } 141 | mother { 142 | firstname 143 | }, 144 | father { 145 | firstname 146 | } 147 | siblings { 148 | firstname 149 | } 150 | } 151 | }' 152 | ``` 153 | 154 | Response 155 | 156 | ```json 157 | { 158 | "data" : { 159 | "person" : { 160 | "uuid" : "58a98c71-90f3-11e9-aa6c-0242ac140002", 161 | "firstname" : "Homer", 162 | "lastname" : "Simpson", 163 | "birthdate" : "2019-06-17", 164 | "spouse" : { 165 | "firstname" : "Marge", 166 | "siblings" : [ { 167 | "firstname" : "Patty" 168 | }, { 169 | "firstname" : "Selma" 170 | } ] 171 | }, 172 | "mother" : { 173 | "firstname" : "Mona" 174 | }, 175 | "father" : { 176 | "firstname" : "Abraham" 177 | }, 178 | "siblings" : [ { 179 | "firstname" : "Herber" 180 | } ] 181 | } 182 | } 183 | } 184 | ``` 185 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.springframework.boot") version "2.3.4.RELEASE" 5 | id("io.spring.dependency-management") version "1.0.10.RELEASE" 6 | kotlin("jvm") version "1.4.10" 7 | kotlin("plugin.spring") version "1.4.10" 8 | } 9 | 10 | val kgraphql_version = "0.15.0" 11 | val junit_version = "5.4.2" 12 | val gson_version = "2.8.5" 13 | val spring_retry_version= "1.3.0" 14 | val spring_aspects_version= "5.2.9.RELEASE" 15 | 16 | group = "com.cisse" 17 | version = "0.0.1-SNAPSHOT" 18 | java.sourceCompatibility = JavaVersion.VERSION_1_8 19 | java.targetCompatibility = JavaVersion.VERSION_1_8 20 | 21 | 22 | repositories { 23 | mavenCentral() 24 | maven("https://jcenter.bintray.com/") 25 | } 26 | 27 | dependencies { 28 | 29 | // Spring boot 30 | implementation("org.springframework.boot:spring-boot-starter-actuator") 31 | implementation("org.springframework.boot:spring-boot-starter-security") 32 | implementation("org.springframework.boot:spring-boot-starter-web") 33 | implementation("org.springframework.boot:spring-boot-devtools") 34 | 35 | // Neo4j 36 | implementation("org.springframework.boot:spring-boot-starter-data-neo4j") 37 | 38 | implementation("eu.michael-simons.neo4j:neo4j-migrations-spring-boot-starter:0.0.13") 39 | 40 | // Jackson 41 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 42 | 43 | // Kotlin 44 | implementation("org.jetbrains.kotlin:kotlin-reflect") 45 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 46 | 47 | // GraphQL 48 | implementation("com.apurebase:kgraphql-ktor:$kgraphql_version") 49 | 50 | // Json 51 | implementation("com.google.code.gson:gson:$gson_version") 52 | 53 | // retry 54 | implementation("org.springframework.retry:spring-retry:$spring_retry_version") 55 | implementation("org.springframework:spring-aspects:$spring_aspects_version") 56 | 57 | // Testing 58 | testImplementation("org.springframework.boot:spring-boot-starter-test") 59 | testImplementation("org.springframework.security:spring-security-test") 60 | testImplementation("org.junit.jupiter:junit-jupiter:$junit_version") 61 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit_version") 62 | } 63 | 64 | 65 | tasks.withType { 66 | useJUnitPlatform() 67 | testLogging { 68 | events("passed", "skipped", "failed") 69 | } 70 | } 71 | 72 | tasks.withType { 73 | gradleVersion = "5.4.1" 74 | } 75 | 76 | tasks.withType { 77 | kotlinOptions { 78 | freeCompilerArgs = listOf("-Xjsr305=strict") 79 | jvmTarget = "1.8" 80 | } 81 | } 82 | 83 | springBoot { 84 | mainClassName = "com.cisse.demo.ApplicationKt" 85 | } 86 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rim-k/family-tree-kotlin-springboot-neo4j-graphql/952b01828cec5beeb41c2da1d1aad02967605d40/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Oct 02 16:34:24 GMT 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /graph-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rim-k/family-tree-kotlin-springboot-neo4j-graphql/952b01828cec5beeb41c2da1d1aad02967605d40/graph-model.png -------------------------------------------------------------------------------- /init-db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rim-k/family-tree-kotlin-springboot-neo4j-graphql/952b01828cec5beeb41c2da1d1aad02967605d40/init-db.png -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./gradlew bootJar 4 | 5 | cp -rf src/main/docker build/ 6 | 7 | cp -f build/libs/demo-*.jar build/docker/backend-api/ 8 | 9 | docker-compose -f build/docker/docker-compose.yml up -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | rootProject.name = "demo" 7 | -------------------------------------------------------------------------------- /simpsons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rim-k/family-tree-kotlin-springboot-neo4j-graphql/952b01828cec5beeb41c2da1d1aad02967605d40/simpsons.png -------------------------------------------------------------------------------- /src/howto.md: -------------------------------------------------------------------------------- 1 | # GraphAware Neo4j UUID 2 | 3 | https://github.com/graphaware/neo4j-uuid 4 | 5 | https://products.graphaware.com/ -------------------------------------------------------------------------------- /src/main/docker/backend-api/Dockerfile: -------------------------------------------------------------------------------- 1 | #see https://hub.docker.com/_/openjdk/ 2 | FROM openjdk:8-jdk-alpine 3 | 4 | ARG JAR_FILE=demo-*.jar 5 | 6 | ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS 7 | ENV SLEEP_TIME=0 8 | #ENV JAVA_OPTS="-Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8" 9 | 10 | ENV DB_USERNAME="" 11 | ENV DB_PASSWORD="" 12 | ENV DB_ENDPOINT="" 13 | # Add a user to run our application so that it doesn't need to run as root 14 | RUN adduser -D -s /bin/sh demo 15 | 16 | WORKDIR /home/demo 17 | 18 | USER demo 19 | 20 | COPY ${JAR_FILE} app.jar 21 | 22 | ENTRYPOINT ["java", "-jar", "app.jar"] 23 | 24 | EXPOSE 8080 -------------------------------------------------------------------------------- /src/main/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | database: 4 | build: ./neo4j 5 | ports: 6 | - "7474:7474" 7 | - "7687:7687" 8 | volumes: 9 | - $HOME/neo4j-ce/data:/data 10 | environment: 11 | - NEO4J_AUTH=neo4j/secret 12 | 13 | backend-api: 14 | build: ./backend-api 15 | ports: 16 | - "8080:8080" 17 | environment: 18 | - DB_ENDPOINT=bolt://database:7687 19 | - DB_USERNAME=neo4j 20 | - DB_PASSWORD=secret 21 | - SPRING_PROFILES_ACTIVE=docker -------------------------------------------------------------------------------- /src/main/docker/neo4j/dockerfile: -------------------------------------------------------------------------------- 1 | FROM neo4j:3.5.5 2 | 3 | COPY *.jar /var/lib/neo4j/plugins/ 4 | 5 | COPY neo4j.conf /var/lib/neo4j/conf/ -------------------------------------------------------------------------------- /src/main/docker/neo4j/graphaware-server-community-all-3.5.4.53.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rim-k/family-tree-kotlin-springboot-neo4j-graphql/952b01828cec5beeb41c2da1d1aad02967605d40/src/main/docker/neo4j/graphaware-server-community-all-3.5.4.53.jar -------------------------------------------------------------------------------- /src/main/docker/neo4j/graphaware-uuid-3.5.4.53.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rim-k/family-tree-kotlin-springboot-neo4j-graphql/952b01828cec5beeb41c2da1d1aad02967605d40/src/main/docker/neo4j/graphaware-uuid-3.5.4.53.17.jar -------------------------------------------------------------------------------- /src/main/docker/neo4j/neo4j.conf: -------------------------------------------------------------------------------- 1 | #***************************************************************** 2 | # Neo4j configuration 3 | # 4 | # For more details and a complete list of settings, please see 5 | # https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/ 6 | #***************************************************************** 7 | 8 | # The name of the database to mount. Note that this is *not* to be confused with 9 | # the causal_clustering.database setting, used to specify a logical database 10 | # name when creating a multi-clustering deployment. 11 | #dbms.active_database=graph.db 12 | 13 | # Paths of directories in the installation. 14 | #dbms.directories.data=data 15 | #dbms.directories.plugins=plugins 16 | #dbms.directories.certificates=certificates 17 | #dbms.directories.logs=logs 18 | #dbms.directories.lib=lib 19 | #dbms.directories.run=run 20 | #dbms.directories.metrics=metrics 21 | 22 | # This setting constrains all `LOAD CSV` import files to be under the `import` directory. Remove or comment it out to 23 | # allow files to be loaded from anywhere in the filesystem; this introduces possible security problems. See the 24 | # `LOAD CSV` section of the manual for details. 25 | dbms.directories.import=import 26 | 27 | # Whether requests to Neo4j are authenticated. 28 | # To disable authentication, uncomment this line 29 | #dbms.security.auth_enabled=false 30 | 31 | # Enable this to be able to upgrade a store from an older version. 32 | #dbms.allow_upgrade=true 33 | 34 | # Java Heap Size: by default the Java heap size is dynamically 35 | # calculated based on available system resources. 36 | # Uncomment these lines to set specific initial and maximum 37 | # heap size. 38 | #dbms.memory.heap.initial_size=512m 39 | #dbms.memory.heap.max_size=512m 40 | 41 | # The amount of memory to use for mapping the store files, in bytes (or 42 | # kilobytes with the 'k' suffix, megabytes with 'm' and gigabytes with 'g'). 43 | # If Neo4j is running on a dedicated server, then it is generally recommended 44 | # to leave about 2-4 gigabytes for the operating system, give the JVM enough 45 | # heap to hold all your transaction state and QUERY_GET_PERSON_WITH_RELATIONS context, and then leave the 46 | # rest for the page cache. 47 | # The default page cache memory assumes the machine is dedicated to running 48 | # Neo4j, and is heuristically set to 50% of RAM minus the max Java heap size. 49 | #dbms.memory.pagecache.size=10g 50 | 51 | # Enable online backups to be taken from this database. 52 | #dbms.backup.enabled=true 53 | 54 | # By default the backup service will only listen on localhost. 55 | # To enable remote backups you will have to bind to an external 56 | # network interface (e.g. 0.0.0.0 for all interfaces). 57 | # The protocol running varies depending on deployment. In a Causal Clustering environment this is the 58 | # same protocol that runs on causal_clustering.transaction_listen_address. 59 | #dbms.backup.address=0.0.0.0:6362 60 | 61 | # Enable encryption on the backup service for CC instances (does not work for single-instance or HA clusters) 62 | #dbms.backup.ssl_policy=backup 63 | 64 | #***************************************************************** 65 | # Network connector configuration 66 | #***************************************************************** 67 | 68 | # With default configuration Neo4j only accepts local connections. 69 | # To accept non-local connections, uncomment this line: 70 | #dbms.connectors.default_listen_address=0.0.0.0 71 | 72 | # You can also choose a specific network interface, and configure a non-default 73 | # port for each connector, by setting their individual listen_address. 74 | 75 | # The address at which this server can be reached by its clients. This may be the server's IP address or DNS name, or 76 | # it may be the address of a reverse proxy which sits in front of the server. This setting may be overridden for 77 | # individual connectors below. 78 | #dbms.connectors.default_advertised_address=localhost 79 | 80 | # You can also choose a specific advertised hostname or IP address, and 81 | # configure an advertised port for each connector, by setting their 82 | # individual advertised_address. 83 | 84 | # Bolt connector 85 | dbms.connector.bolt.enabled=true 86 | #dbms.connector.bolt.tls_level=OPTIONAL 87 | #dbms.connector.bolt.listen_address=:7687 88 | 89 | # HTTP Connector. There can be zero or one HTTP connectors. 90 | dbms.connector.http.enabled=true 91 | #dbms.connector.http.listen_address=:7474 92 | 93 | # HTTPS Connector. There can be zero or one HTTPS connectors. 94 | dbms.connector.https.enabled=true 95 | #dbms.connector.https.listen_address=:7473 96 | 97 | # Number of Neo4j worker threads. 98 | #dbms.threads.worker_count= 99 | 100 | #***************************************************************** 101 | # SSL system configuration 102 | #***************************************************************** 103 | 104 | # Names of the SSL policies to be used for the respective components. 105 | 106 | # The legacy policy is a special policy which is not defined in 107 | # the policy configuration section, but rather derives from 108 | # dbms.directories.certificates and associated files 109 | # (by default: neo4j.key and neo4j.cert). Its use will be deprecated. 110 | 111 | # The policies to be used for connectors. 112 | # 113 | # N.B: Note that a connector must be configured to support/require 114 | # SSL/TLS for the policy to actually be utilized. 115 | # 116 | # see: dbms.connector.*.tls_level 117 | 118 | #bolt.ssl_policy=legacy 119 | #https.ssl_policy=legacy 120 | 121 | # For a causal cluster the configuring of a policy mandates its use. 122 | 123 | #causal_clustering.ssl_policy= 124 | 125 | #***************************************************************** 126 | # SSL policy configuration 127 | #***************************************************************** 128 | 129 | # Each policy is configured under a separate namespace, e.g. 130 | # dbms.ssl.policy..* 131 | # 132 | # The example settings below are for a new policy named 'default'. 133 | 134 | # The base directory for cryptographic objects. Each policy will by 135 | # default look for its associated objects (keys, certificates, ...) 136 | # under the base directory. 137 | # 138 | # Every such setting can be overridden using a full path to 139 | # the respective object, but every policy will by default look 140 | # for cryptographic objects in its base location. 141 | # 142 | # Mandatory setting 143 | 144 | #dbms.ssl.policy.default.base_directory=certificates/default 145 | 146 | # Allows the generation of a fresh private key and a self-signed 147 | # certificate if none are found in the expected locations. It is 148 | # recommended to turn this off again after keys have been generated. 149 | # 150 | # Keys should in general be generated and distributed offline 151 | # by a trusted certificate authority (CA) and not by utilizing 152 | # this mode. 153 | 154 | #dbms.ssl.policy.default.allow_key_generation=false 155 | 156 | # Enabling this makes it so that this policy ignores the contents 157 | # of the trusted_dir and simply resorts to trusting everything. 158 | # 159 | # Use of this mode is discouraged. It would offer encryption but no security. 160 | 161 | #dbms.ssl.policy.default.trust_all=false 162 | 163 | # The private key for the default SSL policy. By default a file 164 | # named private.key is expected under the base directory of the policy. 165 | # It is mandatory that a key can be found or generated. 166 | 167 | #dbms.ssl.policy.default.private_key= 168 | 169 | # The private key for the default SSL policy. By default a file 170 | # named public.crt is expected under the base directory of the policy. 171 | # It is mandatory that a certificate can be found or generated. 172 | 173 | #dbms.ssl.policy.default.public_certificate= 174 | 175 | # The certificates of trusted parties. By default a directory named 176 | # 'trusted' is expected under the base directory of the policy. It is 177 | # mandatory to create the directory so that it exists, because it cannot 178 | # be auto-created (for security purposes). 179 | # 180 | # To enforce client authentication client_auth must be set to 'require'! 181 | 182 | #dbms.ssl.policy.default.trusted_dir= 183 | 184 | # Certificate Revocation Lists (CRLs). By default a directory named 185 | # 'revoked' is expected under the base directory of the policy. It is 186 | # mandatory to create the directory so that it exists, because it cannot 187 | # be auto-created (for security purposes). 188 | 189 | #dbms.ssl.policy.default.revoked_dir= 190 | 191 | # Client authentication setting. Values: none, optional, require 192 | # The default is to require client authentication. 193 | # 194 | # Servers are always authenticated unless explicitly overridden 195 | # using the trust_all setting. In a mutual authentication setup this 196 | # should be kept at the default of require and trusted certificates 197 | # must be installed in the trusted_dir. 198 | 199 | #dbms.ssl.policy.default.client_auth=require 200 | 201 | # It is possible to verify the hostname that the client uses 202 | # to connect to the remote server. In order for this to work, the server public 203 | # certificate must have a valid CN and/or matching Subject Alternative Names. 204 | 205 | # Note that this is irrelevant on host side connections (sockets receiving 206 | # connections). 207 | 208 | # To enable hostname verification client side on nodes, set this to true. 209 | 210 | #dbms.ssl.policy.default.verify_hostname=false 211 | 212 | # A comma-separated list of allowed TLS versions. 213 | # By default only TLSv1.2 is allowed. 214 | 215 | #dbms.ssl.policy.default.tls_versions= 216 | 217 | # A comma-separated list of allowed ciphers. 218 | # The default ciphers are the defaults of the JVM platform. 219 | 220 | #dbms.ssl.policy.default.ciphers= 221 | 222 | #***************************************************************** 223 | # Logging configuration 224 | #***************************************************************** 225 | 226 | # To enable HTTP logging, uncomment this line 227 | #dbms.logs.http.enabled=true 228 | 229 | # Number of HTTP logs to keep. 230 | #dbms.logs.http.rotation.keep_number=5 231 | 232 | # Size of each HTTP log that is kept. 233 | #dbms.logs.http.rotation.size=20m 234 | 235 | # To enable GC Logging, uncomment this line 236 | #dbms.logs.gc.enabled=true 237 | 238 | # GC Logging Options 239 | # see http://docs.oracle.com/cd/E19957-01/819-0084-10/pt_tuningjava.html#wp57013 for more information. 240 | #dbms.logs.gc.options=-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintPromotionFailure -XX:+PrintTenuringDistribution 241 | 242 | # For Java 9 and newer GC Logging Options 243 | # see https://docs.oracle.com/javase/10/tools/java.htm#JSWOR-GUID-BE93ABDC-999C-4CB5-A88B-1994AAAC74D5 244 | #dbms.logs.gc.options=-Xlog:gc*,safepoint,age*=trace 245 | 246 | # Number of GC logs to keep. 247 | #dbms.logs.gc.rotation.keep_number=5 248 | 249 | # Size of each GC log that is kept. 250 | #dbms.logs.gc.rotation.size=20m 251 | 252 | # Log level for the debug log. One of DEBUG, INFO, WARN and ERROR. Be aware that logging at DEBUG level can be very verbose. 253 | #dbms.logs.debug.level=INFO 254 | 255 | # Size threshold for rotation of the debug log. If set to zero then no rotation will occur. Accepts a binary suffix "k", 256 | # "m" or "g". 257 | #dbms.logs.debug.rotation.size=20m 258 | 259 | # Maximum number of history files for the internal log. 260 | #dbms.logs.debug.rotation.keep_number=7 261 | 262 | # Log executed queries that takes longer than the configured threshold. Enable by uncommenting this line. 263 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.enabled=true 264 | 265 | # If the execution of QUERY_GET_PERSON_WITH_RELATIONS takes more time than this threshold, the QUERY_GET_PERSON_WITH_RELATIONS is logged. If set to zero then all queries 266 | # are logged. 267 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.threshold=0 268 | 269 | # The file size in bytes at which the QUERY_GET_PERSON_WITH_RELATIONS log will auto-rotate. If set to zero then no rotation will occur. Accepts a 270 | # binary suffix "k", "m" or "g". 271 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.rotation.size=20m 272 | 273 | # Maximum number of history files for the QUERY_GET_PERSON_WITH_RELATIONS log. 274 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.rotation.keep_number=7 275 | 276 | # Include parameters for the executed queries being logged (this is enabled by default). 277 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.parameter_logging_enabled=true 278 | 279 | # Uncomment this line to include detailed time information for the executed queries being logged: 280 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.time_logging_enabled=true 281 | 282 | # Uncomment this line to include bytes allocated by the executed queries being logged: 283 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.allocation_logging_enabled=true 284 | 285 | # Uncomment this line to include page hits and page faults information for the executed queries being logged: 286 | #dbms.logs.QUERY_GET_PERSON_WITH_RELATIONS.page_logging_enabled=true 287 | 288 | # The security log is always enabled when `dbms.security.auth_enabled=true`, and resides in `logs/security.log`. 289 | 290 | # Log level for the security log. One of DEBUG, INFO, WARN and ERROR. 291 | #dbms.logs.security.level=INFO 292 | 293 | # Threshold for rotation of the security log. 294 | #dbms.logs.security.rotation.size=20m 295 | 296 | # Minimum time interval after last rotation of the security log before it may be rotated again. 297 | #dbms.logs.security.rotation.delay=300s 298 | 299 | # Maximum number of history files for the security log. 300 | #dbms.logs.security.rotation.keep_number=7 301 | 302 | #***************************************************************** 303 | # Causal Clustering Configuration 304 | #***************************************************************** 305 | 306 | # Uncomment and specify these lines for running Neo4j in Causal Clustering mode. 307 | # See the Causal Clustering documentation at https://neo4j.com/docs/ for details. 308 | 309 | # Database mode 310 | # Allowed values: 311 | # CORE - Core member of the cluster, part of the consensus quorum. 312 | # READ_REPLICA - Read replica in the cluster, an eventually-consistent read-only instance of the database. 313 | # To operate this Neo4j instance in Causal Clustering mode as a core member, uncomment this line: 314 | #dbms.mode=CORE 315 | 316 | # Expected number of Core servers in the cluster at formation 317 | #causal_clustering.minimum_core_cluster_size_at_formation=3 318 | 319 | # Minimum expected number of Core servers in the cluster at runtime. 320 | #causal_clustering.minimum_core_cluster_size_at_runtime=3 321 | 322 | # A comma-separated list of the address and port for which to reach all other members of the cluster. It must be in the 323 | # host:port format. For each machine in the cluster, the address will usually be the public ip address of that machine. 324 | # The port will be the value used in the setting "causal_clustering.discovery_listen_address". 325 | #causal_clustering.initial_discovery_members=localhost:5000,localhost:5001,localhost:5002 326 | 327 | # Host and port to bind the cluster member discovery management communication. 328 | # This is the setting to add to the collection of address in causal_clustering.initial_core_cluster_members. 329 | # Use 0.0.0.0 to bind to any network interface on the machine. If you want to only use a specific interface 330 | # (such as a private ip address on AWS, for example) then use that ip address instead. 331 | # If you don't know what value to use here, use this machines ip address. 332 | #causal_clustering.discovery_listen_address=:5000 333 | 334 | # Network interface and port for the transaction shipping server to listen on. 335 | # Please note that it is also possible to run the backup client against this port so always limit access to it via the 336 | # firewall and configure an ssl policy. If you want to allow for messages to be read from 337 | # any network on this machine, us 0.0.0.0. If you want to constrain communication to a specific network address 338 | # (such as a private ip on AWS, for example) then use that ip address instead. 339 | # If you don't know what value to use here, use this machines ip address. 340 | #causal_clustering.transaction_listen_address=:6000 341 | 342 | # Network interface and port for the RAFT server to listen on. If you want to allow for messages to be read from 343 | # any network on this machine, us 0.0.0.0. If you want to constrain communication to a specific network address 344 | # (such as a private ip on AWS, for example) then use that ip address instead. 345 | # If you don't know what value to use here, use this machines ip address. 346 | #causal_clustering.raft_listen_address=:7000 347 | 348 | # List a set of names for groups to which this server should belong. This 349 | # is a comma-separated list and names should only use alphanumericals 350 | # and underscore. This can be used to identify groups of servers in the 351 | # configuration for load balancing and replication policies. 352 | # 353 | # The main intention for this is to group servers, but it is possible to specify 354 | # a unique identifier here as well which might be useful for troubleshooting 355 | # or other special purposes. 356 | #causal_clustering.server_groups= 357 | 358 | #***************************************************************** 359 | # Causal Clustering Load Balancing 360 | #***************************************************************** 361 | 362 | # N.B: Read the online documentation for a thorough explanation! 363 | 364 | # Selects the load balancing plugin that shall be enabled. 365 | #causal_clustering.load_balancing.plugin=server_policies 366 | 367 | ####### Examples for "server_policies" plugin ####### 368 | 369 | # Will select all available servers as the default policy, which is the 370 | # policy used when the client does not specify a policy preference. The 371 | # default configuration for the default policy is all(). 372 | #causal_clustering.load_balancing.config.server_policies.default=all() 373 | 374 | # Will select servers in groups 'group1' or 'group2' under the default policy. 375 | #causal_clustering.load_balancing.config.server_policies.default=groups(group1,group2) 376 | 377 | # Slightly more advanced example: 378 | # Will select servers in 'group1', 'group2' or 'group3', but only if there are at least 2. 379 | # This policy will be exposed under the name of 'mypolicy'. 380 | #causal_clustering.load_balancing.config.server_policies.mypolicy=groups(group1,group2,group3) -> min(2) 381 | 382 | # Below will create an even more advanced policy named 'regionA' consisting of several rules 383 | # yielding the following behaviour: 384 | # 385 | # select servers in regionA, if at least 2 are available 386 | # otherwise: select servers in regionA and regionB, if at least 2 are available 387 | # otherwise: select all servers 388 | # 389 | # The intention is to create a policy for a particular region which prefers 390 | # a certain set of local servers, but which will fallback to other regions 391 | # or all available servers as required. 392 | # 393 | # N.B: The following configuration uses the line-continuation character \ 394 | # which allows you to construct an easily readable rule set spanning 395 | # several lines. 396 | # 397 | #causal_clustering.load_balancing.config.server_policies.policyA=\ 398 | #groups(regionA) -> min(2);\ 399 | #groups(regionA,regionB) -> min(2); 400 | 401 | # Note that implicitly the last fallback is to always consider all() servers, 402 | # but this can be prevented by specifying a halt() as the last rule. 403 | # 404 | #causal_clustering.load_balancing.config.server_policies.regionA_only=\ 405 | #groups(regionA);\ 406 | #halt(); 407 | 408 | #***************************************************************** 409 | # Causal Clustering Additional Configuration Options 410 | #***************************************************************** 411 | # The following settings are used less frequently. 412 | # If you don't know what these are, you don't need to change these from their default values. 413 | 414 | # The name of the database being hosted by this server instance. This 415 | # configuration setting may be safely ignored unless deploying a multicluster. 416 | # Instances may be allocated to constituent clusters by assigning them 417 | # distinct database names using this setting. For instance if you had 6 418 | # instances you could form 2 clusters by assigning half the database name 419 | # "foo", half the name "bar". The setting value must match exactly between 420 | # members of the same cluster. This setting is a one-off: once an instance 421 | # is configured with a database name it may not be changed in future without 422 | # using `neo4j-admin unbind`. 423 | #causal_clustering.database=default 424 | 425 | # Address and port that this machine advertises that it's RAFT server is listening at. Should be a 426 | # specific network address. If you are unsure about what value to use here, use this machine's ip address. 427 | #causal_clustering.raft_advertised_address=:7000 428 | 429 | # Address and port that this machine advertises that it's transaction shipping server is listening at. Should be a 430 | # specific network address. If you are unsure about what value to use here, use this machine's ip address. 431 | #causal_clustering.transaction_advertised_address=:6000 432 | 433 | # The time limit within which a new leader election will occur if no messages from the current leader are received. 434 | # Larger values allow for more stable leaders at the expense of longer unavailability times in case of leader 435 | # failures. 436 | #causal_clustering.leader_election_timeout=7s 437 | 438 | # The time limit allowed for a new member to attempt to update its data to match the rest of the cluster. 439 | #causal_clustering.join_catch_up_timeout=10m 440 | 441 | # The size of the batch for streaming entries to other machines while trying to catch up another machine. 442 | #causal_clustering.catchup_batch_size=64 443 | 444 | # When to pause sending entries to other machines and allow them to catch up. 445 | #causal_clustering.log_shipping_max_lag=256 446 | 447 | # Raft log pruning frequncy. 448 | #causal_clustering.raft_log_pruning_frequency=10m 449 | 450 | # The size to allow the raft log to grow before rotating. 451 | #causal_clustering.raft_log_rotation_size=250M 452 | 453 | ### The following setting is relevant for Edge servers only. 454 | # The interval of pulling updates from Core servers. 455 | #causal_clustering.pull_interval=1s 456 | 457 | # For how long should drivers cache the discovery data from 458 | # the dbms.cluster.routing.getServers() procedure. Defaults to 300s. 459 | #causal_clustering.cluster_routing_ttl=300s 460 | 461 | #***************************************************************** 462 | # HA configuration 463 | #***************************************************************** 464 | 465 | # Uncomment and specify these lines for running Neo4j in High Availability mode. 466 | # See the High Availability documentation at https://neo4j.com/docs/ for details. 467 | 468 | # Database mode 469 | # Allowed values: 470 | # HA - High Availability 471 | # SINGLE - Single mode, default. 472 | # To run in High Availability mode uncomment this line: 473 | #dbms.mode=HA 474 | 475 | # ha.server_id is the number of each instance in the HA cluster. It should be 476 | # an integer (e.g. 1), and should be unique for each cluster instance. 477 | #ha.server_id= 478 | 479 | # ha.initial_hosts is a comma-separated list (without spaces) of the host:port 480 | # where the ha.host.coordination of all instances will be listening. Typically 481 | # this will be the same for all cluster instances. 482 | #ha.initial_hosts=127.0.0.1:5001,127.0.0.1:5002,127.0.0.1:5003 483 | 484 | # IP and port for this instance to listen on, for communicating cluster status 485 | # information with other instances (also see ha.initial_hosts). The IP 486 | # must be the configured IP address for one of the local interfaces. 487 | #ha.host.coordination=127.0.0.1:5001 488 | 489 | # IP and port for this instance to listen on, for communicating transaction 490 | # data with other instances (also see ha.initial_hosts). The IP 491 | # must be the configured IP address for one of the local interfaces. 492 | #ha.host.data=127.0.0.1:6001 493 | 494 | # The interval, in seconds, at which slaves will pull updates from the master. You must comment out 495 | # the option to disable periodic pulling of updates. 496 | #ha.pull_interval=10 497 | 498 | # Amount of slaves the master will try to push a transaction to upon commit 499 | # (default is 1). The master will optimistically continue and not fail the 500 | # transaction even if it fails to reach the push factor. Setting this to 0 will 501 | # increase write performance when writing through master but could potentially 502 | # lead to branched data (or loss of transaction) if the master goes down. 503 | #ha.tx_push_factor=1 504 | 505 | # Strategy the master will use when pushing data to slaves (if the push factor 506 | # is greater than 0). There are three options available "fixed_ascending" (default), 507 | # "fixed_descending" or "round_robin". Fixed strategies will start by pushing to 508 | # slaves ordered by server uuid (accordingly with qualifier) and are useful when 509 | # planning for a stable fail-over based on ids. 510 | #ha.tx_push_strategy=fixed_ascending 511 | 512 | # Policy for how to handle branched data. 513 | #ha.branched_data_policy=keep_all 514 | 515 | # How often heartbeat messages should be sent. Defaults to ha.default_timeout. 516 | #ha.heartbeat_interval=5s 517 | 518 | # How long to wait for heartbeats from other instances before marking them as suspects for failure. 519 | # This value reflects considerations of network latency, expected duration of garbage collection pauses 520 | # and other factors that can delay message sending and processing. Larger values will result in more 521 | # stable masters but also will result in longer waits before a failover in case of master failure. 522 | # This value should not be set to less than twice the ha.heartbeat_interval value otherwise there is a high 523 | # risk of frequent master switches and possibly branched data occurrence. 524 | #ha.heartbeat_timeout=40s 525 | 526 | # If you are using a load-balancer that doesn't support HTTP Auth, you may need to turn off authentication for the 527 | # HA HTTP status endpoint by uncommenting the following line. 528 | #dbms.security.ha_status_auth_enabled=false 529 | 530 | # Whether this instance should only participate as slave in cluster. If set to 531 | # true, it will never be elected as master. 532 | #ha.slave_only=false 533 | 534 | #******************************************************************** 535 | # Security Configuration 536 | #******************************************************************** 537 | 538 | # The authentication and authorization provider that contains both users and roles. 539 | # This can be one of the built-in `native` or `ldap` auth providers, 540 | # or it can be an externally provided plugin, with a custom name prefixed by `plugin`, 541 | # i.e. `plugin-`. 542 | #dbms.security.auth_provider=native 543 | 544 | # The time to live (TTL) for cached authentication and authorization info when using 545 | # external auth providers (LDAP or plugin). Setting the TTL to 0 will 546 | # disable auth caching. 547 | #dbms.security.auth_cache_ttl=10m 548 | 549 | # The maximum capacity for authentication and authorization caches (respectively). 550 | #dbms.security.auth_cache_max_capacity=10000 551 | 552 | # Set to log successful authentication events to the security log. 553 | # If this is set to `false` only failed authentication events will be logged, which 554 | # could be useful if you find that the successful events spam the logs too much, 555 | # and you do not require full auditing capability. 556 | #dbms.security.log_successful_authentication=true 557 | 558 | #================================================ 559 | # LDAP Auth Provider Configuration 560 | #================================================ 561 | 562 | # URL of LDAP server to use for authentication and authorization. 563 | # The format of the setting is `://:`, where hostname is the only required field. 564 | # The supported values for protocol are `ldap` (default) and `ldaps`. 565 | # The default port for `ldap` is 389 and for `ldaps` 636. 566 | # For example: `ldaps://ldap.example.com:10389`. 567 | # 568 | # NOTE: You may want to consider using STARTTLS (`dbms.security.ldap.use_starttls`) instead of LDAPS 569 | # for secure connections, in which case the correct protocol is `ldap`. 570 | #dbms.security.ldap.host=localhost 571 | 572 | # Use secure communication with the LDAP server using opportunistic TLS. 573 | # First an initial insecure connection will be made with the LDAP server, and then a STARTTLS command 574 | # will be issued to negotiate an upgrade of the connection to TLS before initiating authentication. 575 | #dbms.security.ldap.use_starttls=false 576 | 577 | # The LDAP referral behavior when creating a connection. This is one of `follow`, `ignore` or `throw`. 578 | # `follow` automatically follows any referrals 579 | # `ignore` ignores any referrals 580 | # `throw` throws an exception, which will lead to authentication failure 581 | #dbms.security.ldap.referral=follow 582 | 583 | # The timeout for establishing an LDAP connection. If a connection with the LDAP server cannot be 584 | # established within the given time the attempt is aborted. 585 | # A value of 0 means to use the network protocol's (i.e., TCP's) timeout value. 586 | #dbms.security.ldap.connection_timeout=30s 587 | 588 | # The timeout for an LDAP read request (i.e. search). If the LDAP server does not respond within 589 | # the given time the request will be aborted. A value of 0 means wait for a response indefinitely. 590 | #dbms.security.ldap.read_timeout=30s 591 | 592 | #---------------------------------- 593 | # LDAP Authentication Configuration 594 | #---------------------------------- 595 | 596 | # LDAP authentication mechanism. This is one of `simple` or a SASL mechanism supported by JNDI, 597 | # for example `DIGEST-MD5`. `simple` is basic username 598 | # and password authentication and SASL is used for more advanced mechanisms. See RFC 2251 LDAPv3 599 | # documentation for more details. 600 | #dbms.security.ldap.authentication.mechanism=simple 601 | 602 | # LDAP user DN template. An LDAP object is referenced by its distinguished name (DN), and a user DN is 603 | # an LDAP fully-qualified unique user identifier. This setting is used to generate an LDAP DN that 604 | # conforms with the LDAP directory's schema from the user principal that is submitted with the 605 | # authentication token when logging in. 606 | # The special token {0} is a placeholder where the user principal will be substituted into the DN string. 607 | #dbms.security.ldap.authentication.user_dn_template=uid={0},ou=users,dc=example,dc=com 608 | 609 | # Determines if the result of authentication via the LDAP server should be cached or not. 610 | # Caching is used to limit the number of LDAP requests that have to be made over the network 611 | # for users that have already been authenticated successfully. A user can be authenticated against 612 | # an existing cache entry (instead of via an LDAP server) as long as it is alive 613 | # (see `dbms.security.auth_cache_ttl`). 614 | # An important consequence of setting this to `true` is that 615 | # Neo4j then needs to cache a hashed version of the credentials in order to perform credentials 616 | # matching. This hashing is done using a cryptographic hash function together with a random salt. 617 | # Preferably a conscious decision should be made if this method is considered acceptable by 618 | # the security standards of the organization in which this Neo4j instance is deployed. 619 | #dbms.security.ldap.authentication.cache_enabled=true 620 | 621 | #---------------------------------- 622 | # LDAP Authorization Configuration 623 | #---------------------------------- 624 | # Authorization is performed by searching the directory for the groups that 625 | # the user is a member of, and then map those groups to Neo4j roles. 626 | 627 | # Perform LDAP search for authorization info using a system account instead of the user's own account. 628 | # 629 | # If this is set to `false` (default), the search for group membership will be performed 630 | # directly after authentication using the LDAP context bound with the user's own account. 631 | # The mapped roles will be cached for the duration of `dbms.security.auth_cache_ttl`, 632 | # and then expire, requiring re-authentication. To avoid frequently having to re-authenticate 633 | # sessions you may want to set a relatively long auth cache expiration time together with this option. 634 | # NOTE: This option will only work if the users are permitted to search for their 635 | # own group membership attributes in the directory. 636 | # 637 | # If this is set to `true`, the search will be performed using a special system account user 638 | # with read access to all the users in the directory. 639 | # You need to specify the username and password using the settings 640 | # `dbms.security.ldap.authorization.system_username` and 641 | # `dbms.security.ldap.authorization.system_password` with this option. 642 | # Note that this account only needs read access to the relevant parts of the LDAP directory 643 | # and does not need to have access rights to Neo4j, or any other systems. 644 | #dbms.security.ldap.authorization.use_system_account=false 645 | 646 | # An LDAP system account username to use for authorization searches when 647 | # `dbms.security.ldap.authorization.use_system_account` is `true`. 648 | # Note that the `dbms.security.ldap.authentication.user_dn_template` will not be applied to this username, 649 | # so you may have to specify a full DN. 650 | #dbms.security.ldap.authorization.system_username= 651 | 652 | # An LDAP system account password to use for authorization searches when 653 | # `dbms.security.ldap.authorization.use_system_account` is `true`. 654 | #dbms.security.ldap.authorization.system_password= 655 | 656 | # The name of the base object or named context to search for user objects when LDAP authorization is enabled. 657 | # A common case is that this matches the last part of `dbms.security.ldap.authentication.user_dn_template`. 658 | #dbms.security.ldap.authorization.user_search_base=ou=users,dc=example,dc=com 659 | 660 | # The LDAP search filter to search for a user principal when LDAP authorization is 661 | # enabled. The filter should contain the placeholder token {0} which will be substituted for the 662 | # user principal. 663 | #dbms.security.ldap.authorization.user_search_filter=(&(objectClass=*)(uid={0})) 664 | 665 | # A list of attribute names on a user object that contains groups to be used for mapping to roles 666 | # when LDAP authorization is enabled. 667 | #dbms.security.ldap.authorization.group_membership_attributes=memberOf 668 | 669 | # An authorization mapping from LDAP group names to Neo4j role names. 670 | # The map should be formatted as a semicolon separated list of key-value pairs, where the 671 | # key is the LDAP group name and the value is a comma separated list of corresponding role names. 672 | # For example: group1=role1;group2=role2;group3=role3,role4,role5 673 | # 674 | # You could also use whitespaces and quotes around group names to make this mapping more readable, 675 | # for example: dbms.security.ldap.authorization.group_to_role_mapping=\ 676 | # "cn=Neo4j Read Only,cn=users,dc=example,dc=com" = reader; \ 677 | # "cn=Neo4j Read-Write,cn=users,dc=example,dc=com" = publisher; \ 678 | # "cn=Neo4j Schema Manager,cn=users,dc=example,dc=com" = architect; \ 679 | # "cn=Neo4j Administrator,cn=users,dc=example,dc=com" = admin 680 | #dbms.security.ldap.authorization.group_to_role_mapping= 681 | 682 | 683 | #***************************************************************** 684 | # Miscellaneous configuration 685 | #***************************************************************** 686 | 687 | # Enable this to specify a parser other than the default one. 688 | #cypher.default_language_version=3.0 689 | 690 | # Determines if Cypher will allow using file URLs when loading data using 691 | # `LOAD CSV`. Setting this value to `false` will cause Neo4j to fail `LOAD CSV` 692 | # clauses that load data from the file system. 693 | #dbms.security.allow_csv_import_from_file_urls=true 694 | 695 | # Retention policy for transaction logs needed to perform recovery and backups. 696 | #dbms.tx_log.rotation.retention_policy=7 days 697 | 698 | # Limit the number of IOs the background checkpoint process will consume per second. 699 | # This setting is advisory, is ignored in Neo4j Community Edition, and is followed to 700 | # best effort in Enterprise Edition. 701 | # An IO is in this case a 8 KiB (mostly sequential) write. Limiting the write IO in 702 | # this way will leave more bandwidth in the IO subsystem to service random-read IOs, 703 | # which is important for the response time of queries when the database cannot fit 704 | # entirely in memory. The only drawback of this setting is that longer checkpoint times 705 | # may lead to slightly longer recovery times in case of a database or system crash. 706 | # A lower number means lower IO pressure, and consequently longer checkpoint times. 707 | # The configuration can also be commented out to remove the limitation entirely, and 708 | # let the checkpointer flush data as fast as the hardware will go. 709 | # Set this to -1 to disable the IOPS limit. 710 | # dbms.checkpoint.iops.limit=300 711 | 712 | # Only allow read operations from this Neo4j instance. This mode still requires 713 | # write access to the directory for lock purposes. 714 | #dbms.read_only=false 715 | 716 | # Comma separated list of JAX-RS packages containing JAX-RS resources, one 717 | # package name for each mountpoint. The listed package names will be loaded 718 | # under the mountpoints specified. Uncomment this line to mount the 719 | # org.neo4j.examples.server.unmanaged.HelloWorldResource.java from 720 | # neo4j-server-examples under /examples/unmanaged, resulting in a final URL of 721 | # http://localhost:7474/examples/unmanaged/helloworld/{nodeId} 722 | #dbms.unmanaged_extension_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged 723 | 724 | # A comma separated list of procedures and user defined functions that are allowed 725 | # full access to the database through unsupported/insecure internal APIs. 726 | #dbms.security.procedures.unrestricted=my.extensions.example,my.procedures.* 727 | 728 | # A comma separated list of procedures to be loaded by default. 729 | # Leaving this unconfigured will load all procedures found. 730 | #dbms.security.procedures.whitelist=apoc.coll.*,apoc.load.* 731 | 732 | # Specified comma separated list of uuid types (like node or relationship) that should be reused. 733 | # When some type is specified database will try to reuse corresponding ids as soon as it will be safe to do so. 734 | # Currently only 'node' and 'relationship' types are supported. 735 | # This settings is ignored in Neo4j Community Edition. 736 | #dbms.ids.reuse.types.override=node,relationship 737 | 738 | #******************************************************************** 739 | # JVM Parameters 740 | #******************************************************************** 741 | 742 | # G1GC generally strikes a good balance between throughput and tail 743 | # latency, without too much tuning. 744 | dbms.jvm.additional=-XX:+UseG1GC 745 | 746 | # Have common exceptions keep producing stack traces, so they can be 747 | # debugged regardless of how often logs are rotated. 748 | dbms.jvm.additional=-XX:-OmitStackTraceInFastThrow 749 | 750 | # Make sure that `initmemory` is not only allocated, but committed to 751 | # the process, before starting the database. This reduces memory 752 | # fragmentation, increasing the effectiveness of transparent huge 753 | # pages. It also reduces the possibility of seeing performance drop 754 | # due to heap-growing GC events, where a decrease in available page 755 | # cache leads to an increase in mean IO response time. 756 | # Try reducing the heap memory, if this flag degrades performance. 757 | dbms.jvm.additional=-XX:+AlwaysPreTouch 758 | 759 | # Trust that non-static final fields are really final. 760 | # This allows more optimizations and improves overall performance. 761 | # NOTE: Disable this if you use embedded mode, or have extensions or dependencies that may use reflection or 762 | # serialization to change the value of final fields! 763 | dbms.jvm.additional=-XX:+UnlockExperimentalVMOptions 764 | dbms.jvm.additional=-XX:+TrustFinalNonStaticFields 765 | 766 | # Disable explicit garbage collection, which is occasionally invoked by the JDK itself. 767 | dbms.jvm.additional=-XX:+DisableExplicitGC 768 | 769 | # Remote JMX monitoring, uncomment and adjust the following lines as needed. Absolute paths to jmx.access and 770 | # jmx.password files are required. 771 | # Also make sure to update the jmx.access and jmx.password files with appropriate permission roles and passwords, 772 | # the shipped configuration contains only a read only role called 'monitor' with password 'Neo4j'. 773 | # For more details, see: http://download.oracle.com/javase/8/docs/technotes/guides/management/agent.html 774 | # On Unix based systems the jmx.password file needs to be owned by the user that will run the server, 775 | # and have permissions set to 0600. 776 | # For details on setting these file permissions on Windows see: 777 | # http://docs.oracle.com/javase/8/docs/technotes/guides/management/security-windows.html 778 | #dbms.jvm.additional=-Dcom.sun.management.jmxremote.port=3637 779 | #dbms.jvm.additional=-Dcom.sun.management.jmxremote.authenticate=true 780 | #dbms.jvm.additional=-Dcom.sun.management.jmxremote.ssl=false 781 | #dbms.jvm.additional=-Dcom.sun.management.jmxremote.password.file=/absolute/path/to/conf/jmx.password 782 | #dbms.jvm.additional=-Dcom.sun.management.jmxremote.access.file=/absolute/path/to/conf/jmx.access 783 | 784 | # Some systems cannot discover host name automatically, and need this line configured: 785 | #dbms.jvm.additional=-Djava.rmi.server.hostname=$THE_NEO4J_SERVER_HOSTNAME 786 | 787 | # Expand Diffie Hellman (DH) key size from default 1024 to 2048 for DH-RSA cipher suites used in server TLS handshakes. 788 | # This is to protect the server from any potential passive eavesdropping. 789 | dbms.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048 790 | 791 | # This mitigates a DDoS vector. 792 | dbms.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true 793 | 794 | #******************************************************************** 795 | # Wrapper Windows NT/2000/XP Service Properties 796 | #******************************************************************** 797 | # WARNING - Do not modify any of these properties when an application 798 | # using this configuration file has been installed as a service. 799 | # Please uninstall the service before modifying this section. The 800 | # service can then be reinstalled. 801 | 802 | # Name of the service 803 | dbms.windows_service_name=neo4j 804 | 805 | #******************************************************************** 806 | # Other Neo4j system properties 807 | #******************************************************************** 808 | dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball 809 | wrapper.java.additional=-Dneo4j.ext.udc.source=docker 810 | ha.host.data=69330ed2c412:6001 811 | ha.host.coordination=69330ed2c412:5001 812 | dbms.tx_log.rotation.retention_policy=100M size 813 | dbms.memory.pagecache.size=512M 814 | dbms.memory.heap.max_size=512M 815 | dbms.memory.heap.initial_size=512M 816 | dbms.directories.logs=/logs 817 | dbms.connectors.default_listen_address=0.0.0.0 818 | dbms.connector.https.listen_address=0.0.0.0:7473 819 | dbms.connector.http.listen_address=0.0.0.0:7474 820 | dbms.connector.bolt.listen_address=0.0.0.0:7687 821 | causal_clustering.transaction_listen_address=0.0.0.0:6000 822 | causal_clustering.transaction_advertised_address=69330ed2c412:6000 823 | causal_clustering.raft_listen_address=0.0.0.0:7000 824 | causal_clustering.raft_advertised_address=69330ed2c412:7000 825 | causal_clustering.discovery_listen_address=0.0.0.0:5000 826 | causal_clustering.discovery_advertised_address=69330ed2c412:5000 827 | HOME=/var/lib/neo4j 828 | EDITION=enterprise 829 | ACCEPT.LICENSE.AGREEMENT=yes 830 | 831 | #******************************************************************** 832 | # GraphAware Neo4j UUID 833 | #******************************************************************** 834 | com.graphaware.runtime.enabled=true 835 | #UIDM becomes the module ID: 836 | com.graphaware.module.UIDM.1=com.graphaware.module.uuid.UuidBootstrapper 837 | #optional, default is uuid: 838 | #com.graphaware.module.UIDM.uuidProperty=uuid 839 | #optional, default is false: 840 | #com.graphaware.module.UIDM.stripHyphens=false 841 | #optional, default is all nodes: 842 | #com.graphaware.module.UIDM.node=hasLabel('Label1') || hasLabel('Label2') 843 | #optional, default is no relationships: 844 | com.graphaware.module.UIDM.relationship=all 845 | #optional, default is uuidIndex 846 | #com.graphaware.module.UIDM.uuidIndex=uuidIndex 847 | #optional, default is uuidRelIndex 848 | #com.graphaware.module.UIDM.uuidRelationshipIndex=uuidRelIndex -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/Application.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo 2 | 3 | import ac.simons.neo4j.migrations.core.Migrations 4 | import ac.simons.neo4j.migrations.core.MigrationsConfig 5 | import org.neo4j.driver.AuthTokens 6 | import org.neo4j.driver.GraphDatabase 7 | import org.neo4j.ogm.config.Configuration 8 | import org.neo4j.ogm.config.UsernamePasswordCredentials 9 | import org.springframework.boot.CommandLineRunner 10 | import org.springframework.boot.autoconfigure.SpringBootApplication 11 | import org.springframework.boot.runApplication 12 | import org.springframework.context.annotation.Bean 13 | import org.springframework.retry.RetryCallback 14 | import org.springframework.retry.support.RetryTemplate 15 | import java.io.IOException 16 | 17 | @SpringBootApplication 18 | class Application { 19 | 20 | @Bean 21 | fun init(neo4jConfiguration: Configuration) = CommandLineRunner { 22 | 23 | // don't do that in real life, put the config values in the properties file :-) 24 | RetryTemplate.builder() 25 | .maxAttempts(3) 26 | .exponentialBackoff(20000, 2.0, 30000) 27 | .retryOn(IOException::class.java) 28 | .traversingCauses() 29 | .build() 30 | .execute(RetryCallback { migrations(neo4jConfiguration).apply() }) 31 | } 32 | 33 | private fun migrations(neo4jConfiguration: Configuration): Migrations { 34 | 35 | val usernameAndPassword = checkNotNull(neo4jConfiguration.credentials) as UsernamePasswordCredentials 36 | 37 | return Migrations( 38 | MigrationsConfig.builder().build(), 39 | GraphDatabase.driver( 40 | neo4jConfiguration.uri, 41 | AuthTokens.basic(usernameAndPassword.username, usernameAndPassword.password) 42 | ) 43 | ) 44 | } 45 | 46 | } 47 | 48 | fun main(args: Array) { 49 | runApplication(*args) 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/api/config/security/WebSecurityConfiguration.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.api.config.security 2 | 3 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 4 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity 5 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 6 | 7 | @EnableWebSecurity 8 | class WebSecurityConfiguration : WebSecurityConfigurerAdapter() { 9 | 10 | override fun configure(http: HttpSecurity) { 11 | http.csrf().disable() 12 | http.authorizeRequests() 13 | .antMatchers("/**").permitAll() 14 | .anyRequest().permitAll() 15 | .and() 16 | .formLogin().permitAll() 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/api/dto/PersonDTO.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.api.dto 2 | 3 | import java.time.LocalDate 4 | 5 | data class PersonDTO( 6 | val uuid: String, 7 | val firstname: String, 8 | val lastname: String, 9 | val birthName: String?=null, 10 | val birthdate: LocalDate, 11 | val gender: Gender, 12 | var mother: PersonDTO? = null, 13 | var father: PersonDTO? = null, 14 | var spouse: PersonDTO? = null, 15 | val siblings: MutableSet = HashSet(), 16 | val friends: MutableSet = HashSet() 17 | ) 18 | 19 | enum class Gender { 20 | MALE, 21 | FEMALE 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/api/dto/RelationshipDTO.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.api.dto 2 | 3 | import org.springframework.data.neo4j.annotation.QueryResult 4 | 5 | @QueryResult 6 | data class RelationshipDTO( 7 | private val uuid: String?, 8 | private val from: PersonDTO, 9 | private val to: PersonDTO, 10 | private val relationshipType: RelationshipType 11 | ) 12 | 13 | enum class RelationshipType { 14 | HAS_MOTHER, 15 | HAS_FATHER, 16 | SPOUSE_OF, 17 | SIBLING_OF, 18 | FRIEND_OF 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/api/endpoints/graphql/AppSchema.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.api.endpoints.graphql 2 | 3 | import com.cisse.demo.api.dto.Gender 4 | import com.cisse.demo.api.dto.PersonDTO 5 | import com.cisse.demo.core.domain.usecases.person.GetPerson 6 | import com.apurebase.kgraphql.KGraphQL 7 | import org.springframework.stereotype.Component 8 | import java.time.LocalDate 9 | 10 | @Component 11 | class AppSchema(private val getPerson: GetPerson) { 12 | 13 | 14 | val schema = KGraphQL.schema { 15 | 16 | configure { 17 | useDefaultPrettyPrinter = true 18 | } 19 | 20 | stringScalar { 21 | serialize = { date -> date.toString() } 22 | deserialize = { dateString -> LocalDate.parse(dateString) } 23 | } 24 | 25 | query("people") { 26 | description = "Returns a subset of the people" 27 | 28 | resolver { size: Int? -> getPerson.all(size) }.withArgs { 29 | arg { name = "size"; defaultValue = 10; description = "The number of people to return" } 30 | } 31 | } 32 | 33 | query("person") { 34 | description = "Returns a single person based on the firstname" 35 | 36 | resolver { 37 | firstname: String -> getPerson.byFirstname(firstname).first() 38 | } 39 | } 40 | 41 | type{ 42 | 43 | property("mother") { 44 | resolver { 45 | person -> getPerson.mother(person.uuid) 46 | } 47 | } 48 | 49 | property("father") { 50 | resolver { 51 | person -> getPerson.father(person.uuid) 52 | } 53 | } 54 | 55 | property("spouse") { 56 | resolver { 57 | person -> getPerson.spouse(person.uuid) 58 | } 59 | } 60 | 61 | property>("siblings") { 62 | resolver { 63 | person -> getPerson.siblings(person.uuid) 64 | } 65 | } 66 | 67 | property>("friends") { 68 | resolver { 69 | person -> getPerson.friends(person.uuid) 70 | } 71 | } 72 | } 73 | 74 | enum{} 75 | } 76 | 77 | 78 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/api/endpoints/graphql/GraphQL.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.api.endpoints.graphql 2 | 3 | import com.apurebase.kgraphql.schema.Schema 4 | import org.slf4j.Logger 5 | import org.slf4j.LoggerFactory 6 | import org.springframework.web.bind.annotation.PostMapping 7 | import org.springframework.web.bind.annotation.RequestBody 8 | import org.springframework.web.bind.annotation.RequestMapping 9 | import org.springframework.web.bind.annotation.RestController 10 | 11 | @RestController 12 | @RequestMapping("/graphql") 13 | class GraphQL (appSchema: AppSchema) { 14 | 15 | private val schema: Schema = appSchema.schema 16 | 17 | val log: Logger = LoggerFactory.getLogger(this.javaClass) 18 | 19 | @PostMapping 20 | fun graphql(@RequestBody query: String): String { 21 | log.info("the graphql FIND_BY_FIRSTNAME_WITH_SIBLINGS: $query") 22 | return schema.executeBlocking(query); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/api/service/person/GetPersonServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.api.service.person 2 | 3 | import com.cisse.demo.api.dto.PersonDTO 4 | import com.cisse.demo.core.domain.usecases.person.GetPerson 5 | import com.cisse.demo.data.neo4j.repositories.PersonRepository 6 | import org.springframework.stereotype.Service 7 | import org.springframework.transaction.annotation.Transactional 8 | 9 | @Service 10 | @Transactional 11 | class GetPersonImpl(val personRepository: PersonRepository) : GetPerson { 12 | 13 | override fun mother(uuid: String) = personRepository.findMotherOf(uuid)?.toPersonDTO() 14 | 15 | override fun father(uuid: String) = personRepository.findFatherOf(uuid)?.toPersonDTO() 16 | 17 | override fun spouse(uuid: String) = personRepository.findSpouseOf(uuid)?.toPersonDTO() 18 | 19 | override fun siblings(uuid: String) = personRepository.findSiblingsOf(uuid).map { it.toPersonDTO() } 20 | 21 | override fun friends(uuid: String) = personRepository.findFriendsOf(uuid).map { it.toPersonDTO() } 22 | 23 | override fun byUuid(uuid: String) = personRepository.findByUuid(uuid)?.toPersonDTO() 24 | 25 | override fun byFirstname(firstname: String) = personRepository.findByFirstname(firstname).map { it.toPersonDTO() } 26 | 27 | override fun byLastname(lastname: String) = personRepository.findByLastname(lastname).map { it.toPersonDTO() } 28 | 29 | override fun all(size: Int?) = personRepository.findAll().map { it.toPersonDTO() } 30 | 31 | 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/ChildToFather.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import com.cisse.demo.api.dto.RelationshipType 4 | import org.neo4j.ogm.annotation.RelationshipEntity 5 | 6 | @RelationshipEntity("HAS_FATHER") 7 | class ChildToFather(uuid: String, 8 | from: Person, 9 | to: Person, 10 | relationshipType: RelationshipType) : Relationship(uuid, from, to, relationshipType) -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/ChildToMother.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import com.cisse.demo.api.dto.RelationshipType 4 | import org.neo4j.ogm.annotation.RelationshipEntity 5 | 6 | @RelationshipEntity("HAS_MOTHER") 7 | class ChildToMother(uuid: String, 8 | from: Person, 9 | to: Person, 10 | relationshipType: RelationshipType) : Relationship(uuid, from, to, relationshipType) -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/Entity.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import org.neo4j.ogm.annotation.GeneratedValue 4 | import org.neo4j.ogm.annotation.Id 5 | 6 | abstract class Entity (@Id @GeneratedValue val uuid: String) -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/Friendship.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import com.cisse.demo.api.dto.RelationshipType 4 | import org.neo4j.ogm.annotation.RelationshipEntity 5 | 6 | @RelationshipEntity("FRIEND_OF") 7 | class Friendship(uuid: String, 8 | from: Person, 9 | to: Person, 10 | relationshipType: RelationshipType) : Relationship(uuid, from, to, relationshipType) -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/Person.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import com.cisse.demo.api.dto.Gender 4 | import com.cisse.demo.api.dto.PersonDTO 5 | import com.cisse.demo.data.neo4j.config.DateConverter 6 | import org.neo4j.ogm.annotation.* 7 | import org.neo4j.ogm.annotation.Relationship 8 | import org.neo4j.ogm.annotation.typeconversion.Convert 9 | import java.time.LocalDate 10 | 11 | @NodeEntity 12 | class Person( 13 | @Index(unique=true, primary = true) @Property(name = "uuid") var uuid: String, 14 | val firstname: String, 15 | val lastname: String, 16 | val birthName: String? = null, 17 | @Convert(DateConverter::class) val birthdate: LocalDate, 18 | val gender: Gender, 19 | @Relationship("SPOUSE_OF") var spouse: Person? = null, 20 | @Relationship("HAS_MOTHER") var mother: Person? = null, 21 | @Relationship("HAS_FATHER") var father: Person? = null, 22 | @Relationship("SIBLING_OF") var siblings: MutableSet = HashSet(), 23 | @Relationship("FRIEND_OF") var friends: MutableSet = HashSet() 24 | 25 | ){ 26 | 27 | /** 28 | * Title is our primary index and merge key, therefore equals contract is based on it. 29 | */ 30 | override fun equals(other: Any?): Boolean = when(other) { 31 | null -> false 32 | this -> true 33 | is Person -> uuid == other.uuid 34 | else -> false 35 | } 36 | 37 | /** 38 | * Title is our primary index and merge key, therefore hashCode contract is based on it. 39 | */ 40 | override fun hashCode(): Int { 41 | return uuid.hashCode() 42 | } 43 | 44 | fun toPersonDTO() = PersonDTO( 45 | uuid = uuid, 46 | firstname = firstname, 47 | lastname = lastname, 48 | birthName = birthName, 49 | birthdate = birthdate, 50 | gender = gender, 51 | spouse = spouse?.let { 52 | PersonDTO(it.uuid, it.firstname, it.lastname, it.birthName, it.birthdate, it.gender) 53 | }, 54 | mother = mother?.let { 55 | PersonDTO(it.uuid, it.firstname, it.lastname, it.birthName, it.birthdate, it.gender) 56 | }, 57 | father = father?.let { 58 | PersonDTO(it.uuid, it.firstname, it.lastname, it.birthName, it.birthdate, it.gender) 59 | }, 60 | siblings = siblings.map { PersonDTO(it.uuid, it.firstname, it.lastname, it.birthName, it.birthdate, it.gender) }.toMutableSet(), 61 | friends = friends.map { PersonDTO(it.uuid, it.firstname, it.lastname, it.birthName, it.birthdate, it.gender) }.toMutableSet() 62 | ) 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/Relationship.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | import com.cisse.demo.api.dto.RelationshipDTO 3 | import com.cisse.demo.api.dto.RelationshipType 4 | import org.neo4j.ogm.annotation.EndNode 5 | import org.neo4j.ogm.annotation.RelationshipEntity 6 | import org.neo4j.ogm.annotation.StartNode 7 | 8 | abstract class Relationship( 9 | uuid: String, 10 | @StartNode val from: Person, 11 | @EndNode val to: Person, 12 | val relationshipType: RelationshipType 13 | ) : Entity(uuid) { 14 | 15 | override fun equals(other: Any?): Boolean = when(other) { 16 | null -> false 17 | this -> true 18 | is Relationship -> uuid == other.uuid 19 | else -> false 20 | } 21 | 22 | override fun hashCode(): Int { 23 | return uuid.hashCode() 24 | } 25 | 26 | fun toRelationship() = RelationshipDTO( 27 | uuid = uuid, 28 | from = from.toPersonDTO(), 29 | to = to.toPersonDTO(), 30 | relationshipType = relationshipType 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/Sibling.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import com.cisse.demo.api.dto.RelationshipType 4 | import org.neo4j.ogm.annotation.RelationshipEntity 5 | 6 | @RelationshipEntity("SIBLING_OF") 7 | class Sibling(uuid: String, 8 | from: Person, 9 | to: Person, 10 | relationshipType: RelationshipType) : Relationship(uuid, from, to, relationshipType) -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/entities/Spouse.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.entities 2 | 3 | import com.cisse.demo.api.dto.RelationshipType 4 | import org.neo4j.ogm.annotation.RelationshipEntity 5 | 6 | @RelationshipEntity("SPOUSE_OF") 7 | class Spouse(uuid: String, 8 | from: Person, 9 | to: Person, 10 | relationshipType: RelationshipType) : Relationship(uuid, from, to, relationshipType) -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/core/domain/usecases/person/GetPerson.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.core.domain.usecases.person 2 | 3 | import com.cisse.demo.api.dto.PersonDTO 4 | 5 | interface GetPerson { 6 | fun byUuid(uuid: String): PersonDTO? 7 | fun byFirstname(firstname: String): Collection 8 | fun byLastname(lastname: String): Collection 9 | fun all(size: Int?): Collection 10 | fun siblings(uuid: String): Collection 11 | fun friends(uuid: String): Collection 12 | fun mother(uuid: String): PersonDTO? 13 | fun father(uuid: String): PersonDTO? 14 | fun spouse(uuid: String): PersonDTO? 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/data/neo4j/config/DateConverter.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.data.neo4j.config 2 | 3 | import org.neo4j.ogm.typeconversion.AttributeConverter 4 | import java.time.LocalDate 5 | 6 | 7 | class DateConverter : AttributeConverter { 8 | override fun toGraphProperty(value: LocalDate) = value 9 | override fun toEntityAttribute(value: LocalDate) = value 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/data/neo4j/repositories/PersonRepository.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.data.neo4j.repositories 2 | 3 | import com.cisse.demo.core.domain.entities.Person 4 | import org.springframework.data.neo4j.annotation.Query 5 | import org.springframework.data.neo4j.repository.Neo4jRepository 6 | import org.springframework.data.repository.query.Param 7 | import org.springframework.stereotype.Repository 8 | 9 | @Repository 10 | interface PersonRepository : Neo4jRepository { 11 | 12 | @Query("MATCH (person:Person {firstname: {firstname}}) RETURN person") 13 | fun findByFirstname(@Param("firstname") firstname: String): Collection 14 | 15 | @Query("MATCH (person:Person {lastname: {lastname}}) RETURN person") 16 | fun findByLastname(@Param("lastname") lastname: String): Collection 17 | 18 | @Query("MATCH (person:Person {uuid: {uuid}}) RETURN person") 19 | fun findByUuid(@Param("uuid") uuid: String): Person? 20 | 21 | @Query(FIND_SIBLINGS) 22 | fun findSiblingsOf(@Param("uuid") uuid: String): MutableSet 23 | 24 | @Query(FIND_SPOUSE) 25 | fun findSpouseOf(@Param("uuid") uuid: String): Person? 26 | 27 | @Query(FIND_MOTHER) 28 | fun findMotherOf(@Param("uuid") uuid: String): Person? 29 | 30 | @Query(FIND_FATHER) 31 | fun findFatherOf(@Param("uuid") uuid: String): Person? 32 | 33 | @Query(FIND_FRIENDS) 34 | fun findFriendsOf(@Param("uuid") uuid: String): MutableSet 35 | 36 | companion object { 37 | const val FIND_BY_FIRSTNAME_WITH_SIBLINGS = """MATCH (person:Person {firstname: {firstname}}) 38 | WITH person 39 | OPTIONAL MATCH (person)-[:HAS_MOTHER]->(mother) 40 | OPTIONAL MATCH (person)-[:HAS_FATHER]->(father) 41 | OPTIONAL MATCH (person)-[:SPOUSE_OF]-(spouse) 42 | OPTIONAL MATCH (person)-[:HAS_MOTHER|HAS_FATHER]->(Parent)<-[:HAS_MOTHER|HAS_FATHER]-(siblings) 43 | RETURN person, spouse, mother, father, collect(distinct siblings) as siblings""" 44 | 45 | const val FIND_SIBLINGS = """MATCH (person:Person {uuid: {0}}) 46 | WITH person 47 | MATCH (person)-[:HAS_MOTHER|HAS_FATHER]->(Parent)<-[:HAS_MOTHER|HAS_FATHER]-(siblings) 48 | RETURN distinct siblings 49 | """ 50 | 51 | const val FIND_SPOUSE = """MATCH (:Person {uuid: {0}})-[:SPOUSE_OF]-(spouse) 52 | RETURN spouse 53 | """ 54 | 55 | const val FIND_MOTHER = """MATCH (:Person {uuid: {0}})-[:HAS_MOTHER]->(mother) 56 | RETURN mother 57 | """ 58 | 59 | const val FIND_FATHER = """MATCH (:Person {uuid: {0}})-[:HAS_FATHER]->(father) 60 | RETURN father 61 | """ 62 | 63 | const val FIND_FRIENDS = """MATCH (:Person {uuid: {0}})-[:FRIEND_OF]-(friends) 64 | RETURN friends 65 | """ 66 | } 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/main/kotlin/com/cisse/demo/exception/NotFoundException.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo.exception 2 | 3 | class NotFoundException(message : String) : RuntimeException(message) -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ,-----. ,--. ,---. 2 | ' .--./ ,---. ,-| | ,---. ' .-' ,---. ,--.,--. ,--.--. ,---. ,---. 3 | | | | .-. | ' .-. | | .-. : `. `-. | .-. | | || | | .--' | .--' | .-. : 4 | ' '--'\ ' '-' ' \ `-' | \ --. .-' | ' '-' ' ' '' ' | | \ `--. \ --. 5 | `-----' `---' `---' `----' `-----' `---' `----' `--' `---' `----' 6 | 7 | ,--. ,--. 8 | ,--. ,--. ,--. / / / / ,--. ,--. 9 | | ,---. ,-' '-. ,-' '-. ,---. ,---. .--. / / / / ,---. `--' ,---. ,---. ,---. `--' ,---. 10 | | .-. | '-. .-' '-. .-' | .-. | ( .-' '--' / / / / | .--' ,--. ( .-' ( .-' | .-. : ,--. | .-. | 11 | | | | | | | | | | '-' ' .-' `) .--. / / / / \ `--. | | .-' `) .-' `) \ --. .--. | | ' '-' ' 12 | `--' `--' `--' `--' | |-' `----' '--' / / / / `---' `--' `----' `----' `----' '--' `--' `---' 13 | `--' `--' `--' -------------------------------------------------------------------------------- /src/main/resources/config/application-docker.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | neo4j: 4 | username: ${DB_USERNAME} 5 | password: ${DB_PASSWORD} 6 | uri: ${DB_ENDPOINT} -------------------------------------------------------------------------------- /src/main/resources/config/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | #debug: true 5 | 6 | spring: 7 | data: 8 | neo4j: 9 | username: neo4j 10 | password: secret 11 | 12 | #logging: 13 | # level: 14 | # org.neo4j.ogm.drivers.bolt.request.BoltRequest: DEBUG -------------------------------------------------------------------------------- /src/main/resources/neo4j/migrations/V000__init_database.cypher: -------------------------------------------------------------------------------- 1 | CREATE (Abraham:Person {firstname:'Abraham', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 2 | CREATE (Mona:Person {firstname:'Mona', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 3 | CREATE (Clancy:Person {firstname:'Clancy', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 4 | CREATE (Jacqueline:Person {firstname:'Jacqueline', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 5 | CREATE (Herber:Person {firstname:'Herber', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 6 | CREATE (Homer:Person {firstname:'Homer', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 7 | CREATE (Marge:Person {firstname:'Marge', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 8 | CREATE (Patty:Person {firstname:'Patty', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 9 | CREATE (Selma:Person {firstname:'Selma', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 10 | CREATE (Bart:Person {firstname: 'Bart', lastname: 'Simpson', birthdate: date(), gender: 'MALE'}) 11 | CREATE (Lisa:Person {firstname:'Lisa', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 12 | CREATE (Maggie:Person {firstname: 'Maggie', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 13 | CREATE (Ling:Person {firstname: 'Ling', lastname: 'Simpson', birthdate: date(), gender: 'FEMALE'}) 14 | ; 15 | 16 | MATCH (f:Person {firstname:"Homer"}) 17 | MATCH (m:Person {firstname:"Marge"}) 18 | MATCH (c:Person) 19 | WHERE (c.firstname in ['Bart', 'Lisa', 'Maggie']) 20 | MERGE (c)-[:HAS_FATHER]->(f) 21 | MERGE (c)-[:HAS_MOTHER]->(m) 22 | MERGE (f)-[:SPOUSE_OF]-(m) 23 | ; 24 | 25 | MATCH (f:Person {firstname:"Abraham"}) 26 | MATCH(m:Person {firstname:"Mona"}) 27 | MATCH (c:Person) 28 | WHERE c.firstname in ['Homer', 'Herber'] 29 | MERGE (c)-[:HAS_FATHER]->(f) 30 | MERGE (c)-[:HAS_MOTHER]->(m) 31 | MERGE (f)-[:SPOUSE_OF]-(m) 32 | ; 33 | 34 | MATCH (f:Person {firstname:"Clancy"}) 35 | MATCH (m:Person {firstname:"Jacqueline"}) 36 | MATCH (c:Person) 37 | WHERE c.firstname in ['Marge', 'Patty', 'Selma'] 38 | MERGE (c)-[:HAS_FATHER]->(f) 39 | MERGE (c)-[:HAS_MOTHER]->(m) 40 | MERGE (f)-[:SPOUSE_OF]-(m) 41 | ; 42 | 43 | MATCH (c:Person {firstname:"Ling"}) 44 | MATCH (m:Person {firstname:"Selma"}) 45 | MERGE (c)-[:HAS_MOTHER]->(m) -------------------------------------------------------------------------------- /src/test/kotlin/com/cisse/demo/ApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package com.cisse.demo 2 | 3 | import org.junit.Test 4 | import org.junit.jupiter.api.extension.ExtendWith 5 | import org.junit.runner.RunWith 6 | import org.springframework.boot.test.context.SpringBootTest 7 | import org.springframework.test.context.junit.jupiter.SpringExtension 8 | import org.springframework.test.context.junit4.SpringRunner 9 | 10 | @ExtendWith(SpringExtension::class) 11 | @SpringBootTest 12 | class ApplicationTests { 13 | 14 | @Test 15 | fun contextLoads() { 16 | } 17 | 18 | } 19 | --------------------------------------------------------------------------------