From 8d844dfd88b9094154c66bfa17f2a1c7868f1dfd Mon Sep 17 00:00:00 2001 From: Jennifer Reif Date: Wed, 21 May 2025 13:00:50 -0500 Subject: [PATCH] App-java course updates to support dependency upgrades + minor content tweaks --- asciidoc/courses/app-java/course.adoc | 20 +- .../lessons/0-setup/lesson.adoc | 104 ++++++ .../lesson.adoc | 8 +- .../lessons/1-setup/lesson.adoc | 118 ------- .../lessons/2-driver-lifecycle/lesson.adoc | 122 +++++++ .../2c-create-driver-instance/lesson.adoc | 15 + .../questions/1-instantiate-driver.adoc | 48 +++ .../modules/1-getting-started/module.adoc | 6 +- .../2-driver/lessons/1-about/lesson.adoc | 138 -------- .../questions/1-supported-languages.adoc | 23 -- .../1-about/questions/2-package-name.adoc | 20 -- .../lessons/2-connection-strings/lesson.adoc | 72 ----- .../questions/1-valid-schemes.adoc | 21 -- .../questions/2-aura-scheme.adoc | 20 -- .../2-driver/lessons/3-connecting/lesson.adoc | 99 ------ .../3-connecting/questions/1-test-number.adoc | 29 -- .../app-java/modules/2-driver/module.adoc | 17 - .../lessons/1-execute-query/lesson.adoc | 89 +++++ .../lessons/2c-your-first-query/lesson.adoc | 18 ++ .../questions/1-execute-query.adoc | 41 +++ .../questions/2-access-results.adoc | 63 ++++ .../lessons/3-handling-results/lesson.adoc | 262 +++++++++++++++ .../lessons/4-graph-results/lesson.adoc | 189 +++++++++++ .../lessons/5c-accessing-results/lesson.adoc | 13 + .../questions/1-node.adoc | 35 ++ .../questions/2-object-mapping.adoc | 47 +++ .../lessons/6-transactions/lesson.adoc | 186 +++++++++++ .../lessons/7c-read-transaction/lesson.adoc | 56 ++++ .../7c-read-transaction/questions/1-run.adoc | 54 ++++ .../2-interacting-with-neo4j/module.adoc | 11 + .../lessons/1-transactions/lesson.adoc | 210 ------------ .../questions/1-valid-methods.adoc | 23 -- .../questions/2-read-transaction.adoc | 31 -- .../questions/3-write-transaction.adoc | 31 -- .../10-user-favorites/questions/verify.adoc | 35 -- .../lessons/10-user-favorites/solution.cypher | 9 - .../lessons/10-user-favorites/verify.cypher | 2 - .../11-favorite-flag/questions/verify.adoc | 35 -- .../lessons/11-favorite-flag/solution.cypher | 11 - .../lessons/11-favorite-flag/verify.cypher | 2 - .../lessons/2-results/lesson.adoc | 126 -------- .../lessons/2-results/questions/1-method.adoc | 20 -- .../lessons/3-type-system/lesson.adoc | 305 ------------------ .../questions/1-node-property.adoc | 41 --- .../3-type-system/questions/2-temporal.adoc | 22 -- .../lessons/4-home-page/questions/verify.adoc | 29 -- .../5-registering/questions/verify.adoc | 38 --- .../lessons/5-registering/solution.cypher | 4 - .../lessons/5-registering/verify.cypher | 3 - .../7-unique-emails/questions/verify.adoc | 30 -- .../lessons/7-unique-emails/solution.cypher | 7 - .../lessons/7-unique-emails/verify.cypher | 4 - .../8-authenticating/questions/verify.adoc | 38 --- .../lessons/8-authenticating/solution.cypher | 2 - .../lessons/8-authenticating/verify.cypher | 2 - .../lessons/9-ratings/questions/verify.adoc | 38 --- .../lessons/9-ratings/solution.cypher | 10 - .../lessons/9-ratings/verify.cypher | 2 - .../modules/2-interacting/module.adoc | 11 - .../questions/1-movie-count.adoc | 24 -- .../questions/1-genre-movie-count.adoc | 23 -- .../lessons/1-neoflix-app/lesson.adoc | 58 ++++ .../10-user-favorites/images/movie-cards.png | Bin .../images/neoflix-person-tom-hanks.png | Bin .../lessons/10-user-favorites/lesson.adoc | 0 .../lessons/11-favorite-flag/lesson.adoc | 18 -- .../lessons/4-home-page/lesson.adoc | 9 +- .../lessons/5-registering/lesson.adoc | 4 - .../lessons/6-driver-errors/lesson.adoc | 0 .../questions/1-error-code.adoc | 0 .../questions/2-error-details.adoc | 0 .../lessons/7-unique-emails/lesson.adoc | 4 - .../lessons/8-authenticating/lesson.adoc | 9 +- .../lessons/9-ratings/images/rating-form.png | Bin .../lessons/9-ratings/lesson.adoc | 3 - .../modules/3-building-neoflix/module.adoc | 9 + .../lessons/1-browse-genres/lesson.adoc | 5 - .../lessons/2-find-genre-details/lesson.adoc | 5 - .../lessons/3-movie-lists/lesson.adoc | 0 .../questions/1-coppola-films.adoc | 0 .../lessons/4-movie-view/lesson.adoc | 0 .../questions/1-most-similar-movie.adoc | 0 .../lessons/5-listing-ratings/lesson.adoc | 0 .../questions/1-first-rating.adoc | 0 .../lessons/6-person-list/lesson.adoc | 0 .../questions/1-alphabetical-order.adoc | 0 .../lessons/6-person-list/reset.cypher | 0 .../lessons/7-person-view/lesson.adoc | 0 .../questions/1-similar-people.adoc | 0 .../{3-backlog => 4-backlog}/module.adoc | 2 +- 90 files changed, 1437 insertions(+), 1801 deletions(-) create mode 100644 asciidoc/courses/app-java/modules/1-getting-started/lessons/0-setup/lesson.adoc rename asciidoc/courses/app-java/modules/1-getting-started/lessons/{configure-connection => 1-configure-connection}/lesson.adoc (87%) delete mode 100644 asciidoc/courses/app-java/modules/1-getting-started/lessons/1-setup/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/1-getting-started/lessons/2-driver-lifecycle/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/questions/1-instantiate-driver.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/1-about/lesson.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/1-supported-languages.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/2-package-name.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/lesson.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/1-valid-schemes.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/2-aura-scheme.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/lesson.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/questions/1-test-number.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-driver/module.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/1-execute-query/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/1-execute-query.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/2-access-results.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/3-handling-results/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/4-graph-results/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/1-node.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/2-object-mapping.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/6-transactions/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/lesson.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/questions/1-run.adoc create mode 100644 asciidoc/courses/app-java/modules/2-interacting-with-neo4j/module.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/lesson.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/1-valid-methods.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/2-read-transaction.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/3-write-transaction.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/solution.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/verify.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/solution.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/verify.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/lesson.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/questions/1-method.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/lesson.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/1-node-property.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/2-temporal.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/solution.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/verify.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/solution.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/verify.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/solution.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/verify.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/questions/verify.adoc delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/solution.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/verify.cypher delete mode 100644 asciidoc/courses/app-java/modules/2-interacting/module.adoc delete mode 100644 asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/questions/1-movie-count.adoc delete mode 100644 asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/questions/1-genre-movie-count.adoc create mode 100644 asciidoc/courses/app-java/modules/3-building-neoflix/lessons/1-neoflix-app/lesson.adoc rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/10-user-favorites/images/movie-cards.png (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/10-user-favorites/images/neoflix-person-tom-hanks.png (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/10-user-favorites/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/11-favorite-flag/lesson.adoc (98%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/4-home-page/lesson.adoc (96%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/5-registering/lesson.adoc (98%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/6-driver-errors/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/6-driver-errors/questions/1-error-code.adoc (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/6-driver-errors/questions/2-error-details.adoc (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/7-unique-emails/lesson.adoc (98%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/8-authenticating/lesson.adoc (96%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/9-ratings/images/rating-form.png (100%) rename asciidoc/courses/app-java/modules/{2-interacting => 3-building-neoflix}/lessons/9-ratings/lesson.adoc (98%) create mode 100644 asciidoc/courses/app-java/modules/3-building-neoflix/module.adoc rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/1-browse-genres/lesson.adoc (95%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/2-find-genre-details/lesson.adoc (96%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/3-movie-lists/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/3-movie-lists/questions/1-coppola-films.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/4-movie-view/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/4-movie-view/questions/1-most-similar-movie.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/5-listing-ratings/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/5-listing-ratings/questions/1-first-rating.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/6-person-list/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/6-person-list/questions/1-alphabetical-order.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/6-person-list/reset.cypher (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/7-person-view/lesson.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/lessons/7-person-view/questions/1-similar-people.adoc (100%) rename asciidoc/courses/app-java/modules/{3-backlog => 4-backlog}/module.adoc (98%) diff --git a/asciidoc/courses/app-java/course.adoc b/asciidoc/courses/app-java/course.adoc index 924cb4477..203c994be 100644 --- a/asciidoc/courses/app-java/course.adoc +++ b/asciidoc/courses/app-java/course.adoc @@ -2,7 +2,7 @@ :categories: developer:2, java:1, software-development:12, intermediate:12, , development:3 :usecase: recommendations :status: active -:caption: Learn how to interact with Neo4j from Java using the Neo4j Java Driver +:caption: Learn how to build Java applications with Neo4j using the Neo4j Java Driver :key-points: Driver life cycle, installing and instantiation, read and write transactions, best practices // tag::config[] :repository: neo4j-graphacademy/app-java @@ -19,12 +19,10 @@ You will learn the skills you need to build applications with Java to read data === Prerequisites -Taking this course, you should have the following previous knowledge/skills: +By taking this course, we assume that you have a working knowledge of Java and how to build applications. +We also assume that you have at least a basic knowledge of Neo4j. -1. Be able to write Java programs. -2. Have completed the link:/courses/neo4j-fundamentals/[Neo4j Fundamentals] and link:/courses/cypher-fundamentals/[Cypher Fundamentals] courses. - -include::{shared}/courses/gitpod/overview.adoc[leveloffset=+1] +If you haven't already done so, we recommend that you also take the link:/courses/neo4j-fundamentals/[Neo4j Fundamentals] course in order to gain a basic understanding of Neo4j, the link:/courses/cypher-fundamentals/[Cypher Fundamentals] to understand how to query Neo4j using Cypher, and the link:/courses/drivers-java/[Using Neo4j with Java] course to understand how to integrate Neo4j into your Java projects. === Duration @@ -32,7 +30,7 @@ include::{shared}/courses/gitpod/overview.adoc[leveloffset=+1] === What you will learn -* How to use the Neo4j with Java +* How to use Neo4j with Java * How to map a graph data model to a Java application domain model * How to convert between Neo4j and Java data type systems * How to read data from and write data to Neo4j @@ -44,7 +42,7 @@ include::{shared}/courses/gitpod/overview.adoc[leveloffset=+1] [.includes] == This course includes -//TODO: Update this!! -* [lessons]#8 lessons# -* [challenges]#15 short hands-on challenges# -* [quizes]#20 simple quizzes to support your learning# + +* [lessons]#Lessons for understanding concepts and syntax# +* [challenges]#Hands-on challenges for practical application# +* [quizes]#Quizzes to support your learning# diff --git a/asciidoc/courses/app-java/modules/1-getting-started/lessons/0-setup/lesson.adoc b/asciidoc/courses/app-java/modules/1-getting-started/lessons/0-setup/lesson.adoc new file mode 100644 index 000000000..9df6a6e7b --- /dev/null +++ b/asciidoc/courses/app-java/modules/1-getting-started/lessons/0-setup/lesson.adoc @@ -0,0 +1,104 @@ += Setup +:type: lesson +:disable-cache: true +:slides: true +:order: 1 +:minutes: 10 +:branch: main + +To experiment with Neo4j and Java during this course you will need to setup a development environment. + +include::../../../../../../shared/courses/codespace/get-started.adoc[] + +The repository contains an example Maven project with the required dependencies and a `pom.xml` file. + +[%collapsible] +.Develop on your local machine +==== +You will need link:https://www.java.com[Java] 17 or higher installed. +You can check your version by running `java -version`. +To download Java, you can choose from many link:https://neo4j.com/docs/operations-manual/current/installation/requirements/#deployment-requirements-software[supported vendor options^]. For example, link:https://www.azul.com/downloads/?package=jdk#zulu[Azul's JDK^] or hlink:ttps://openjdk.org/install/[OpenJDK^]. + +Clone the link:{repository-link}[github.com/neo4j-graphacademy/app-java] repository: + +[source,bash] +---- +git clone https://github.com/neo4j-graphacademy/app-java +---- + +[TIP] +.Fork the repository +You can link:https://github.com/neo4j-graphacademy/app-java/fork[fork the repository] and have a copy for future reference. + +Install the project and dependencies using Maven: + +[source,bash] +---- +cd app-java +./mvnw clean install -U -DskipTests +---- + +You do not need to create a Neo4j database as you will use the provided sandbox instance. +==== + +== Driver installation + +The Neo4j driver is included in the `pom.xml` file, so you do not need to install it separately. + +[source, xml] +---- +include::{repository-raw}/{branch}/pom.xml[tag=neo4j-driver] +---- + +[%collapsible] +.Click to view the complete pom.xml file +==== +[source, xml] +---- +include::{repository-raw}/{branch}/pom.xml[tag=**] +---- +==== + +== Setup the environment + +Create a new `/src/main/resources/application.properties` file and copy the contents of the `example.properties` file into it. + +Fill in the required values: + +[source] +.Create a resources/application.properties file +---- +include::{repository-raw}/{branch}/src/main/resources/example.properties[] +---- + +Update the Neo4j sandbox connection details: + +NEO4J_URI:: [copy]#bolt://{sandbox-ip}:{sandbox-boltPort}# +NEO4J_USERNAME:: [copy]#{sandbox-username}# +NEO4J_PASSWORD:: [copy]#{sandbox-password}# + +== Test your setup + +You can test your setup by running the test - `src/test/java/neoflix/_01_ConnectToNeo4jTest.java`. + +The test will attempt to load the values in the `application.properties` file and connect to the Neo4j sandbox. + +[source,bash] +---- +./mvnw test +---- + +You will see `Tests run: 2, Failures: 0, Errors: 0, Skipped: 0` and a `BUILD SUCCESS` message if you have set up your environment correctly. + +If any tests fail, check the contents of the `application.properties` file. + +== Continue + +When you are ready, you can move on to the next task. + +read::Success - let's get started![] + +[.summary] +== Summary + +You have setup your environment and are ready to start this module. diff --git a/asciidoc/courses/app-java/modules/1-getting-started/lessons/configure-connection/lesson.adoc b/asciidoc/courses/app-java/modules/1-getting-started/lessons/1-configure-connection/lesson.adoc similarity index 87% rename from asciidoc/courses/app-java/modules/1-getting-started/lessons/configure-connection/lesson.adoc rename to asciidoc/courses/app-java/modules/1-getting-started/lessons/1-configure-connection/lesson.adoc index c142e3f55..cd895b354 100644 --- a/asciidoc/courses/app-java/modules/1-getting-started/lessons/configure-connection/lesson.adoc +++ b/asciidoc/courses/app-java/modules/1-getting-started/lessons/1-configure-connection/lesson.adoc @@ -2,10 +2,6 @@ :order: 2 :type: lesson :sandbox: true -//FIX below properties! -:lab: {repository-blob}/src/{lab-file} -:lab-file: test/java/com/example/appjava -:lab-filename: AppJavaApplicationTests.java :disable-cache: true To connect to the Neo4j database, you will need to set a few configuration properties. @@ -51,10 +47,10 @@ read::Success[] [.summary] == Lesson Summary -//TODO: Fix this when content is complete! + In this lesson, you added connection details to your application and tested the connection to the Neo4j database. This lesson completes Module 1 of the course. -In the next module, you will learn about mapping the graph data model to the application domain model. +In the next module, you will refresh concepts for interacting with Neo4j using the Neo4j Java driver and then apply these concepts in your application. === Further Reading diff --git a/asciidoc/courses/app-java/modules/1-getting-started/lessons/1-setup/lesson.adoc b/asciidoc/courses/app-java/modules/1-getting-started/lessons/1-setup/lesson.adoc deleted file mode 100644 index cb4022163..000000000 --- a/asciidoc/courses/app-java/modules/1-getting-started/lessons/1-setup/lesson.adoc +++ /dev/null @@ -1,118 +0,0 @@ -= Setting up the Project -:order: 1 -:type: lesson -:lab: {repository-link} -:disable-cache: true -:java-version: 17 - -In order to build an application with Java and connect to a Neo4j database, you will need a few foundational skills. Then you will set up the tools needed. - -== Prerequisites - -1. Be able to write Java programs. While you can use Neo4j with other JVM languages, this course will use Java. -2. Complete the link:/courses/neo4j-fundamentals/[Neo4j Fundamentals] and link:/courses/cypher-fundamentals/[Cypher Fundamentals] courses. You should be familiar with what a graph is and how to use the Cypher query language to read and write data to Neo4j. - -== Tools - -We have created a link:{repository-link}[repository^] for this course. -It contains the starter code and resources you need. - -This course defaults to Maven for dependency management, but the application could migrate to use Gradle instead. Users have valid preferences for either dependency management framework, but for simplicity's sake, we chose to only pick one for this course. - -A blank Neo4j Sandbox instance has also been created for you to use during this course. - -You can open a Neo4j Browser window throughout this course by clicking the link:#[Toggle Sandbox,role=classroom-sandbox-toggle] button in the bottom right-hand corner of the screen. - -== Get the code - -You can use link:https://gitpod.io[Gitpod^] as an online IDE and workspace for this workshop. -It will automatically clone the workshop repository and set up your environment. - -lab::Open `Gitpod workspace`[] - -[NOTE] -You will need to login with a Github, Gitlab, or Bitbucket account. - -Alternatively, you can clone the repository and set up the environment yourself. - -[%collapsible] -.Develop on your local machine -==== -1. Verify your Java version. You will need Java 17 or higher. You can check your version by running the following command in your terminal window: + -`java -version`. To download Java, you can choose from many link:https://neo4j.com/docs/operations-manual/current/installation/requirements/#deployment-requirements-software[supported vendor options^]. For example, link:https://www.azul.com/downloads/?package=jdk#zulu[Azul's JDK^] or hlink:ttps://openjdk.org/install/[OpenJDK^]. - -2. Pick an IDE of your choice. If you don't yet have a preference, you can download link:https://www.jetbrains.com/idea/download[IntelliJ IDEA Community Edition^] or link:https://code.visualstudio.com/download[Visual Studio Code^], are both excellent choices. -==== - -You do not need to create a Neo4j database as you will use the provided sandbox instance. - -read::Continue[] - -[.summary] -== Lesson Summary - -In this lesson, you reviewed any prerequisites needed to complete this course and set up your development environment to work with Java and Neo4j. - -//TODO: Update this!! -// In the next lesson, you will learn about ???. - -//// -[NOTE] -.Failing tests -==== -You will notice that some tests fail when you run `mvn verify`. During the course you will complete the project and resolve the issues. -==== - -[WARNING] -.Errors while installing dependencies? -==== -This project has been written using Java version **{java-version}**. -If you are using the wrong version, you may experience errors when trying to install the dependencies. -==== - -== Application Configuration - -This project uses System properties to manage configuration variables for this project. -When the link:{repository-blob}/main/src/main/java/neoflix/AppUtils.java[`AppUtils.loadProperties()`^] method is called, the `application.properties` file in the `src/main/resources` of the project is parsed and all settings made accessible from `System.getProperty`. - -The project contains an example file at `{repository-blob}/main/src/main/resources[example.properties^]`. -You can run the following command from the root folder in your terminal window to copy the example file to `application.properties`. - -[source,sh] -cp src/main/resources/example.properties src/main/resources/application.properties - -== Start the Project - -To start the project, run the following command: - -.Start the project using Maven -[source,sh] -mvn compile exec:java - -You should see an output similar to the following confirming that the server has successfully started: - -.Console Output -[source,console,role=nocopy] -Started server on http://localhost:3000/ - -== A Brief Tour of the Project - -If you open up the listening address in your browser, you will see a Single Page Application (SPA) that communicates with the API served at http://localhost:3000/api/movies[http://localhost:3000/api/movies^]. -Currently, the responses are hardcoded, but as you progress through the course, you will learn how to query Neo4j to find this information. - -Here are some of the important directories in the project: - -* `src/main/java/example/` - Example code for driver instantiation. -* `src/main/java/neoflix` - The application code: -** `src/main/java/neoflix/routes/` - Route handlers that are registered on the server. You shouldn't need to edit these files. -** `src/main/java/neoflix/services/` - Services that you will need to update to interact with Neo4j. -* `src/test/java/neoflix` - Test files that will you will need to run in order to pass the test. You will run these using the `mvn test` or individually with the + -`mvn test -Dtest=neoflix._0x_XxxTest#methodName` command. -* `src/main/resources/public/` - Minified build files for the Web application. *Do not edit these files*. - -== Done! - -Once you have the project up and running, click the button below to complete this lesson. - -read::The project is running![] -//// diff --git a/asciidoc/courses/app-java/modules/1-getting-started/lessons/2-driver-lifecycle/lesson.adoc b/asciidoc/courses/app-java/modules/1-getting-started/lessons/2-driver-lifecycle/lesson.adoc new file mode 100644 index 000000000..dd288be81 --- /dev/null +++ b/asciidoc/courses/app-java/modules/1-getting-started/lessons/2-driver-lifecycle/lesson.adoc @@ -0,0 +1,122 @@ += Using the driver +:type: lesson +:slides: true +:order: 3 +:minutes: 10 + +[.slide.discrete] +== Introduction +In the link:/courses/cypher-fundamentals/[Cypher Fundamentals^] course, you learned how to query Neo4j using Cypher. + +To run Cypher statements in a Java application, you'll need the link:https://neo4j.com/developer/Java[Neo4j Java Driver^]. +The driver acts as a bridge between your Java code and Neo4j, handling connections to the database and the execution of Cypher queries. + +[.slide] +== Creating a Driver Instance + +Open the `src/main/java/neoflix/NeoflixApp.java` file. + +Import the driver: + +[source,java] +---- +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.AuthTokens; +---- + +[.slide.discrete.col-2] +== Creating a Driver Instance + +[.col] +==== +Create a `driver` instance in `main()`: + +[source,Java] +---- +public class App { + public static void main(String[] args) { + AppUtils.loadProperties(); + + // Create a new Neo4j driver instance + var driver = GraphDatabase.driver( + System.getProperty("NEO4J_URI"), // <1> + AuthTokens.basic( + System.getProperty("NEO4J_USERNAME"), // <2> + System.getProperty("NEO4J_PASSWORD")) + ); + // Verify connectivity + driver.verifyConnectivity(); // <3> + } +} +---- +==== + +[.col] +==== +<1> The connection string for your Neo4j database +<2> Your Neo4j username and password +<3> Verify the connection (exception raised if connection fails) + + +[TIP] +.Best Practice +===== +Create **one** Driver instance and share it across your entire application. +===== +==== + +[.slide.col-2] +== Running Your First Query + +[.col] +==== +The `executableQuery()` method executes a Cypher query and returns the results. + +[source,Java] +---- +// <1> +var result = driver.executableQuery( + "RETURN COUNT {()} AS count" + ).execute(); + +// Get the first record +var records = result.records(); // <2> +var first = records.get(0); + +// Print the count entry +System.out.println(first.get("count")); // <3> +---- +==== + +[.col] +==== +<1> `executableQuery()` runs a Cypher query to get the count of all nodes in the database +<2> `records()` returns a list of the records returned +<3> Keys from the `RETURN` clause are accessed using the `get` method +==== + +[.slide.discrete] +== Run the application + +You can run the application to see the output: + +[source, bash] +---- +mvn compile exec:java +---- + +[TIP] +You can also run the application using the _play_ button in your IDE. + + +[.next.discrete] +== Check your understanding + +link:../2c-create-driver-instance/[Take challenge,role=btn] + +[.summary] +== Lesson Summary + +In this lesson you learned how to install the Neo4j Java Driver, create a Driver instance, verify connectivity to your database, and execute your first Cypher statement. + +In the next lesson, you will take a quiz to test your knowledge of installing and creating a driver instance. \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/lesson.adoc b/asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/lesson.adoc new file mode 100644 index 000000000..d3790b427 --- /dev/null +++ b/asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/lesson.adoc @@ -0,0 +1,15 @@ += Create a Driver Instance +:type: quiz +:order: 4 + +How do you create a new driver instance? + +include::questions/1-instantiate-driver.adoc[leveloffset=+1] + + +[.summary] +== Summary + +You can now install the `neo4j` library and connect to Neo4j by creating a new driver instance. + +In the next lesson, you will learn how to execute your first Cypher query. diff --git a/asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/questions/1-instantiate-driver.adoc b/asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/questions/1-instantiate-driver.adoc new file mode 100644 index 000000000..d5cfa9e6c --- /dev/null +++ b/asciidoc/courses/app-java/modules/1-getting-started/lessons/2c-create-driver-instance/questions/1-instantiate-driver.adoc @@ -0,0 +1,48 @@ +[.question.select-in-source] += Create a new driver instance + +Complete the code below to create a new driver instance. + +[source,Java,role=nocopy noplay] +---- +final String NEO4J_URI = "neo4j://localhost:7687" +final String NEO4J_USERNAME = "neo4j" +final String NEO4J_PASSWORD = "letmein" + +var driver = GraphDatabase./*select:driver(*/( + +NEO4J_URI, + AuthTokens.basic( + NEO4J_USERNAME, + NEO4J_PASSWORD) +); + +---- + +- [ ] `connect` +- [x] `driver` +- [ ] `GraphDatabase` +- [ ] `new_driver` + +[TIP,role=hint] +.Hint +==== +What is the name of the object used to connect a Neo4j instance? +==== + +[TIP,role=solution] +.Solution +==== +The method to create a new connection is `GraphDatabase.driver()`. + +[source,Java,role=nocopy noplay] +---- +var driver = GraphDatabase.driver( + NEO4J_URI, + AuthTokens.basic( + NEO4J_USERNAME, + NEO4J_PASSWORD) +); + +---- +==== diff --git a/asciidoc/courses/app-java/modules/1-getting-started/module.adoc b/asciidoc/courses/app-java/modules/1-getting-started/module.adoc index 0e8360f03..82a6024e1 100644 --- a/asciidoc/courses/app-java/modules/1-getting-started/module.adoc +++ b/asciidoc/courses/app-java/modules/1-getting-started/module.adoc @@ -5,9 +5,7 @@ == Module Overview In this module, you will learn: -//TODO: Update this!! -* This course's prerequisites and how to set up your development environment for the course. -* How to create a Java application with link:https://javalin.io/[Javalin^]. +* How to set up your development environment for the course. * How to connect your application to a Neo4j database and test the connection. -link:./1-setup/[Ready? Let's go →, role=btn] +link:./0-setup/[Ready? Let's go →, role=btn] diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/lesson.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/lesson.adoc deleted file mode 100644 index 37f4b3ca8..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/lesson.adoc +++ /dev/null @@ -1,138 +0,0 @@ -= About the Driver -:type: lesson -:order: 1 - -In the link:/courses/cypher-fundamentals/[Cypher Fundamentals^] course, we cover how to query Neo4j using a language called Cypher. -To execute a Cypher statement against a Neo4j database you will use an object called a *Driver*. - -// tag::driver[] -> The **Driver** object is a thread-safe, application-wide fixture from which all Neo4j interaction derives. -> -> The Driver API is **topology independent**, so you can run the same code against a *Neo4j cluster* or a *single DBMS*. - -// end::driver[] - -To connect to and query Neo4j from within a Java application, you use the link:https://neo4j.com/developer/java[Neo4j Java Driver^]. - -The Neo4j Java Driver is one of five officially supported drivers, the others are JavaScript, .NET, Python, and Go. -There are also a wide range of Community Drivers available for other languages including PHP and Ruby. - -You should create a **single instance** of the Driver in your application per Neo4j cluster or DBMS, which can then be shared across your application. - -== Installing the Driver - -The Neo4j Java Driver is available from Maven Central, so you can use it with Maven, Gradle and other build tools. - -In our project we use Maven, so you can add the driver with the following dependency to the `` section of your `pom.xml` file: - -[source,xml,indent=0] ----- -include::{repository-raw}/main/pom.xml[tag=driver] ----- - -== Creating a Driver Instance - -Each driver instance will connect to one DBMS, or Neo4j cluster, depending on the value provided in the connection string. - -After importing the `org.neo4j.driver.*` package, you can instantiate a `Driver` instance from the `GraphDatabase.driver()` factory method call. -The `driver()` method requires two arguments: - -1. **A connection string** for the Neo4j cluster or DBMS - for example `neo4j://localhost:7687` or `neo4j+s://_dbhash_.databases.neo4j.io:7687` -2. **An authentication token** - Neo4j supports basic username and password authentication, kerberos tokens or custom authentication. You can create an authentication token by calling one of the static methods provided by `AuthTokens`. - - -Here is an example for how to create a driver instance: - -.Creating a Driver Instance -[source,java,indent=0,role=nocopy,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=import] -include::{repository-raw}/main/src/main/java/example/Index.java[tag=credentials,indent=0] -include::{repository-raw}/main/src/main/java/example/Index.java[tag=driver,indent=0] ----- - -The above example creates an _unencrypted_ connection to the Neo4j server at `localhost` on the default port number of `7687`. -The driver then attemps to authenticate against the server using a basic authentication with the username `neo4j` and password `letmein!`. - - -=== Verifying Connectivity - -You can verify that the connection details used during driver instantiation are correct by calling the `verifyConnectivity()` function. -This function returns the Driver instance if the connection details are correct, or fails with a `Neo.ClientError.Security.Unauthorized` Exception if a connection could not be made. - -.Verify Connectivity -[source,java,indent=0,role=nocopy,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=verifyConnectivity,indent=0] ----- - - -== Check Your Understanding - -// To do: need to add some hints for these - -include::./questions/1-supported-languages.adoc[leveloffset=+1] - -include::./questions/2-package-name.adoc[leveloffset=+1] - - -[.summary] -== Lesson Summary - -In this lesson, you learned about the Neo4j Java Driver and how it can be used to connect to Neo4j from within a Java application. - -In the next lesson, we will take a closer look at the first argument in the `GraphDatabase.driver()` method, the connection string. - - -//// -SDN course content: - -== Verifying Connectivity - -Running the application as-is will only result in an error if general syntax is incorrect. However, it will not test whether you can actually connect to the instance or not. - -To take it one step further and fully test the connection, open the `src/main/test/java/neoflix/_01_ConnectToNeo4jTest` file and add the code shown below. - -1. Create a new test method named `createDriverAndConnectToServer()`. -+ -[source,java] ----- -//TODO: Add code to code folder in this lesson!! -include::code/_01_ConnectToNeo4jTest.java[tag=method] ----- -2. Load the database credentials from the `AppUtils` class. -+ -[source,java] ----- -include::code/_01_ConnectToNeo4jTest.java[tag=load-properties] ----- -3. Instantiate the driver object and check that it isn't null. -+ -[source,java] ----- -include::code/_01_ConnectToNeo4jTest.java[tag=instantiate-driver] ----- -4. Call the `verifyConnectivity()` method and assert it doesn't throw an exception. -+ -[source,java] ----- -include::code/_01_ConnectToNeo4jTest.java[tag=verify-connectivity] ----- - -Completed code is available below to cross-check. - -[%collapsible] -.Click to reveal completed `AppSpringDataApplicationTests` code -==== -[source,java] ----- -//TODO: Add code to code folder in this lesson!! -include::code/_01_ConnectToNeo4jTestCompleted.java[indent=0] ----- -==== - -Run the test class in the IDE to verify that the test passes by clicking on the `Test Results` tab at the bottom of the IDE. If the test doesn't pass, then the database credentials or connection details are incorrect. - -image::images/java-verifyConnectivity-testPassed.png[VerifyConnectivity Test Passed,width=600,align=center] - -//// \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/1-supported-languages.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/1-supported-languages.adoc deleted file mode 100644 index 401bd3450..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/1-supported-languages.adoc +++ /dev/null @@ -1,23 +0,0 @@ -[.question] -= 1. Which of the following programming languages have officially supported drivers? - -- [*] .NET -- [*] Go -- [*] Java -- [*] JavaScript -- [*] Python -- [ ] PHP -- [ ] Ruby - - -[TIP,role=hint] -.Hint -==== -Five languages are officially supported by Neo4j. -==== - -[TIP,role=solution] -.Solution -==== -The five supported languages are link:https://neo4j.com/docs/dotnet-manual/current/[.NET^], link:https://neo4j.com/docs/go-manual/current/[Go^], link:https://neo4j.com/docs/java-manual/current/[Java^], link:https://neo4j.com/docs/javascript-manual/current/[JavaScript^] and link:https://neo4j.com/docs/python-manual/current/[Python^]. -==== diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/2-package-name.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/2-package-name.adoc deleted file mode 100644 index c06e043b2..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/1-about/questions/2-package-name.adoc +++ /dev/null @@ -1,20 +0,0 @@ -[.question] -= 2. What name is the Neo4j Java Driver registered under on Maven? - -- [ ] `@neo4j/driver` -- [ ] `neo4j` -- [ ] `neo4j-driver` -- [*] `org.neo4j.driver:neo4j-java-driver` - - -[TIP,role=hint] -.Hint -==== -The package is registered on maven as link:https://mvnrepository.com/artifact/org.neo4j.driver/neo4j-java-driver[`org.neo4j.driver:neo4j-java-driver`^]. -==== - -[TIP,role=solution] -.Solution -==== -The package is registered on maven as link:https://mvnrepository.com/artifact/org.neo4j.driver/neo4j-java-driver[`org.neo4j.driver:neo4j-java-driver`^]. -==== diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/lesson.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/lesson.adoc deleted file mode 100644 index 4d12cf99e..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/lesson.adoc +++ /dev/null @@ -1,72 +0,0 @@ -= Connection Strings and Authentication -:type: lesson -:order: 2 - -In the previous lesson, you saw some example code for creating a new driver instance. -Let's take a closer look at the `driver()` method and how it is used to create a driver instance. - -.Creating a Driver Instance -[source,java,indent=0,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=pseudo] ----- - -The `GraphDatabase.driver()` function accepts the following arguments: - -1. A connection string -2. An authentication token -3. Optionally, an object containing additional driver configuration - -Let's take a look at these arguments in more detail. - - -// == 1. Connection String -include::{shared}/courses/apps/connection-strings.adoc[tags="connectionstring"] - - -== `2` An Authentication Token - -In most cases, you will connect to the DBMS using basic authentication consisting of a username and password. -You can create a basic authentication token by calling the `basic()` method on the `AuthTokens` object. - -.Creating a Basic Authentication Token -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=auth] ----- - -// Neo4j also supports kerberos authentication tokens and can be extended to support custom authentication. -// These authentication methods require additional configuration and are outside of the scope of this course. - -For more information on additional authentication methods, see the link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/[Authentication and authorization^] section of the link:https://neo4j.com/docs/operations-manual/current/[Neo4j Operations Manual^]. - - -== `3` Additional Driver Configuration (Optional) - -The `driver()` method also accepts a third additional configuration object. -This object allows you to provide advanced configuration options, for example setting the connection pool size or changing timeout limits. - -.Example Additional Configuration -[source,java,role=nocopy,indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=configuration] ----- - -For more information or a full list of configuration options, please link:https://neo4j.com/docs/java-manual/current/client-applications/#java-driver-configuration[visit the Neo4j Java Driver manual^]. - - -// == What happens next? -include::{shared}/courses/apps/connection-strings.adoc[tag="next"] - - -== Check Your Understanding - -// To do: need to add some hints for these - -include::./questions/1-valid-schemes.adoc[leveloffset=+1] - -include::./questions/2-aura-scheme.adoc[leveloffset=+1] - - -// == Summary -include::{shared}/courses/apps/connection-strings.adoc[tag="summary"] \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/1-valid-schemes.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/1-valid-schemes.adoc deleted file mode 100644 index c865fbef7..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/1-valid-schemes.adoc +++ /dev/null @@ -1,21 +0,0 @@ -[.question] -= 1. Which of the following options are valid schemes to use in the connection string? - -- [ ] `aura://` -- [*] `bolt://` -- [ ] `graphdb://` -- [*] `neo4j://` -- [*] `neo4j+s://` - - -[TIP,role=hint] -.Hint -==== -There are three valid options above. -==== - -[TIP,role=solution] -.Solution -==== -From the options above, both `neo4j://` and `neo4j+s://` are schemes used for either connecting to a single instance or cluster without needing to change configuration, while `bolt://` for creating a direct connection to a specific Neo4j server=. -==== diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/2-aura-scheme.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/2-aura-scheme.adoc deleted file mode 100644 index 5b4128b54..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/2-connection-strings/questions/2-aura-scheme.adoc +++ /dev/null @@ -1,20 +0,0 @@ -[.question] -= 2. Which scheme should you use when connecting to a Neo4j Aura Database? - -- [ ] `aura://` -- [ ] `bolt://` -- [ ] `neo4j://` -- [*] `neo4j+s://` - - -[TIP,role=hint] -.Hint -==== -A _secure_ connection is required to a Neo4j Aura database. -==== - -[TIP,role=solution] -.Solution -==== -The correct answer is `neo4j+s://`. -==== diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/lesson.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/lesson.adoc deleted file mode 100644 index 95946600a..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/lesson.adoc +++ /dev/null @@ -1,99 +0,0 @@ -= Adding the Driver -:type: challenge -:order: 3 -:branch: 01-connect-to-neo4j -:test-filename: _01_ConnectToNeo4jTest -:test-method: createDriverAndConnectToServer -// - -This is your first challenge of this course. -Your challenge here is to modify the code to create a new instance of the Driver that can be used across the application. - -As we discussed in the link:../1-about/[About the Driver lesson], it is best practice to create a single instance of the driver in our application per Neo4j cluster or DBMS. - -Inside `AppUtils.java`, you will see an `initDriver()` function. - -.AppUtils.java -[source,java,indent=0,role=nocopy] ----- -include::{repository-raw}/main/src/main/java/neoflix/AppUtils.java[tag=initDriver] ----- - -This function should use the `getNeo4jUri()`, `getNeo4jUsername()` and `getNeo4jPassword()` methods from `AppUtils` to create an instance of the Neo4j Java Driver, verify connectivity and return it. - -== Challenge: Implement the initDriver function. - -Your first challenge is to modify the `initDriver()` method in `src/AppUtils.java` to create an instance of the driver and return it. - -To do this, we will need to: - -1. Add the `org.neo4j.driver:neo4j-java-driver` dependency to your `pom.xml`. -2. Import the package into `AppUtil.java`. -3. Create the driver instance using the credentials from `getNeo4jUri()`, `getNeo4jUsername()` and `getNeo4jPassword()` and then use the `driver.verifyConnectivity()` to assert that the credentials are correct. - - -=== 1. Add the neo4j-java-driver Dependency - -First, you will need to add the `neo4j-java-driver` dependency to your `pom.xml`. - -[source,xml,indent=0] ----- -include::{repository-raw}/main/pom.xml[tag=driver] ----- - - -[TIP] -.Verifying the Dependency -You can use the `mvn verify` to download the driver library and its dependencies. - - -=== 2. Importing the Dependency - -To include the Driver dependency in our module, you will add an `import` directive to the top of the file. -Copy and paste the following code into the top of link:{repository-blob}/main/src/main/java/neoflix/AppUtils.java[`src/main/java/neoflix/AppUtils.java`^]. - -[source,java,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=import] ----- - - -=== 3. Creating the Driver Instance - -Create the driver instance by calling the `GraphDatabase.driver()` method. - -The first argument will be the `uri` from `getNeo4jUri()`. - -The second argument will be an authentication token which can be created using the `AuthTokens.basic()` function, this takes two arguments; the username returned by the `getNeo4jUsername()` method and the password returned by the `getNeo4jPassword()` method. - - -Replace the `initDriver` placeholder function with the following code: - -.AppUtils.java -[source,java,indent=0] ----- -include::{repository-raw}/{branch}/src/main/java/neoflix/AppUtils.java[tag=initDriver] ----- - -In the last line of the function above, the `verifyConnectivity()` method will verify that the connection details are correct. - -If the connection cannot be made for any reason, call will fail with an Exception. -If this occurs, your application will be unable to communicate with Neo4j. -Manual investigation will be required to diagnose the issue. - - -If the connection has been successfully verified, it will return an instance of the driver. - -// == Testing Connectivity -include::../../../../includes/test.adoc[] - -== Check Your Understanding - -include::./questions/1-test-number.adoc[leveloffset=+1] - -[.summary] -== Lesson Summary - -You have implemented the code to create a new driver instance with environment variables read from the `application.properties` file which verifies that the driver can connect to the DBMS before the application starts. - -In the next module we will look at how to use the driver to query the DBMS. diff --git a/asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/questions/1-test-number.adoc b/asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/questions/1-test-number.adoc deleted file mode 100644 index 4add77044..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/lessons/3-connecting/questions/1-test-number.adoc +++ /dev/null @@ -1,29 +0,0 @@ -[.question] -= 1. How many tests are run in the test you ran? - -* [*] 1 -* [ ] 2 -* [ ] 3 -* [ ] 4 - - - -[TIP,role=hint] -.Hint -==== -You can run the link:{repository-blob}/main/src/test/java/neoflix/{test-filename}.java[`src/test/java/neoflix/{test-filename}.java`^] test suite by running the following command: - -[source,sh,subs="attributes+"] ----- -ifdef::test-method[] -mvn test -Dtest=neoflix.{test-filename}#{test-method} ----- - -==== - - -[TIP,role=solution] -.Solution -==== -There is **1** test in the test suite. -==== diff --git a/asciidoc/courses/app-java/modules/2-driver/module.adoc b/asciidoc/courses/app-java/modules/2-driver/module.adoc deleted file mode 100644 index 804ad2714..000000000 --- a/asciidoc/courses/app-java/modules/2-driver/module.adoc +++ /dev/null @@ -1,17 +0,0 @@ -= The Neo4j Java Driver -:order: 1 - -In this module you will learn about the lifecycle of the Neo4j Driver and how it should be used within your application. -By the end of this module, you will have learned to how to: - -* Install the Neo4j Java Driver. -* Build a Connection String. -* Create an instance of the Driver. -* Verify that the Driver has successfully connected to Neo4j. - -// * Create a session, and run an example query -// * Learn how to write to Neo4j by registering the User -// * Learn how to read from Neo4j by implementing a Read Transaction - -// Throughout the course you will be asked to run one of a set of tests to verify that you have completed the steps. - diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/1-execute-query/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/1-execute-query/lesson.adoc new file mode 100644 index 000000000..ce8eace16 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/1-execute-query/lesson.adoc @@ -0,0 +1,89 @@ += Query Neo4j +:type: lesson +:order: 1 + +This lesson will cover how to query Neo4j using the Java driver. For those who completed the https://graphacademy.neo4j.com/courses/drivers-java/[Using Neo4j with Java^] course, this lesson will be a refresher on the concepts. + +[.slide] +== Executing Cypher Statements + +There are two main ways to execute Cypher statements using the Java driver. + +1. `.executableQuery()` method - the simplest and most common approach optimized for one-off queries because it executes and returns all results. For longer running queries or larger datasets, waiting for all results can consume a lot of memory and time. +2. Create a session and use `.executeRead()`/`.executeWrite()` methods - this approach provides finer control of database transactions and ability to run multiple queries in a single transaction (helpful in a production environment). + +For this course, we will continue to focus on the first approach, using the `executableQuery()` method. However, more information on the second approach can be found in the https://neo4j.com/docs/java-manual/current/transactions/[Neo4j Java Driver documentation^]. + +Here is an example using `executableQuery()`: + +[source,java] +---- +var result = driver.executableQuery(""" + MATCH (p:Person)-[r:ACTED_IN]->(:Movie {title: "The Matrix"}) + RETURN p.name AS name, r.role AS role + """) + .execute(); +---- + +[.slide] +== Query Parameters + +When executing a Cypher statement, you can pass parameters to the query using the `withParameters()` method. This is useful for dynamic queries where you want to pass in values at runtime. + +For example using the query above, if you want to want to pass the `Movie` `title` of "The Matrix" as a query parameter, you can do it like this: + +[source,java] +---- +var result = driver.executableQuery(""" + MATCH (p:Person)-[r:ACTED_IN]->(:Movie {title: $title}) + RETURN p.name AS name, r.role AS role + """) + .withParameters(Map.of("title", "The Matrix")) + .execute(); +---- + +[.slide] +== Handling the Result + +The `execute()` method returns an link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/EagerResult.html[`EagerResult`^] object that contains: + +. A list of link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/Record.html[`Record`^] objects +. link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/summary/ResultSummary.html[`ResultSummary`^] of the query execution +. A list of keys specified in the `RETURN` clause + +[source,java] +---- +var records = result.records(); // <1> +var summary = result.summary(); // <2> +var keys = result.keys(); // <3> + +System.out.println(records); +System.out.println(summary); +System.out.println(keys); +---- + +[.slide] +== Accessing results + +Each row returned by the query is a `Record` object. The `Record` object provides access to the data returned by the query. + +You can access any item in the `RETURN` clause using the `get` method. + +[source,java] +---- +var records = result.records(); +records.forEach(r -> { + System.out.println(r.get("name")); + System.out.println(r.get("role")); +}); +---- + +[.next.discrete] +== Check your understanding + +link:../4c-executing-queries/[Take challenge,role=btn] + +[.summary] +== Lesson Summary + +In this lesson, you learned how to execute a Cypher statement and access the results. diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/lesson.adoc new file mode 100644 index 000000000..a42af88b8 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/lesson.adoc @@ -0,0 +1,18 @@ += Executing queries +:type: quiz +:sequential: true +:minutes: 5 +:order: 2 + +How do you execute a Cypher statement and access the results? + +include::questions/1-execute-query.adoc[leveloffset=+1] + +include::questions/2-access-results.adoc[leveloffset=+1] + +[.summary] +== Lesson summary + +Nice work! You learned how to execute a Cypher statement and access the results. + +In the next lesson, you will review how to transform the results. diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/1-execute-query.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/1-execute-query.adoc new file mode 100644 index 000000000..e60f9add4 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/1-execute-query.adoc @@ -0,0 +1,41 @@ +[.question.select-in-source] += Execute the Cypher statement + +Select the correct method to create an executable Cypher statement. + +[source,java,role=nocopy noplay] +---- +final String cypher = """ + MATCH (m:Movie {title: $title})<-[:ACTED_IN]-(p) + RETURN p.name AS actor + """; +final String title = "Toy Story"; + +var result = driver./*select:executableQuery(*/ + cypher + ) + .withParameters(Map.of("name", name)) + .execute(); +---- + +- [ ] cypher( +- [x] executableQuery( +- [ ] execute( +- [ ] verifyConnectivity( + +[TIP,role=hint] +.Hint +==== +The method to execute a Cypher query with the driver is the same one you used in the previous lesson - it starts with `executable`. +==== + +[TIP,role=solution] +.Solution +==== +The correct answer is `executableQuery()`. This method creates an executable Cypher query which can be executed. + +[source,Java,role=nocopy noplay] +---- +var result = driver.executableQuery(cypher).execute(); +---- +==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/2-access-results.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/2-access-results.adoc new file mode 100644 index 000000000..4637fff04 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/2c-your-first-query/questions/2-access-results.adoc @@ -0,0 +1,63 @@ +[.question.select-in-source] += Print the actor names + +Select the correct method to print the name of each actor. + +[source,java,role=nocopy noplay] +---- +final String cypher = """ + MATCH (m:Movie {title: $title})<-[:ACTED_IN]-(p) + RETURN p.name AS actor + """; +final String title = "Toy Story"; + +var result = driver.executableQuery( + cypher + ) + .withParameters(Map.of("title", title)) + .execute(); + +var records = result.records(); +records.forEach(r -> { + System.out.println( + /*select:r.get("actor")*/ + ); + }); +---- + +- [ ] records.get("p.name") +- [ ] records.get("actor") +- [ ] r.get("p.name") +- [x] r.get("actor") + + +[TIP,role=hint] +.Hint +==== +Review the `RETURN` clause in the Cypher statement. + +[source,Java] +---- +cypher = """ +MATCH (m:Movie {title: $title})<-[:ACTED_IN]-(p) +RETURN p.name AS actor +""" +---- +==== + +[TIP,role=solution] +.Solution +==== +The `name` property of the `p` node is aliased as `actor` in the `RETURN` clause. + +The `get` method of the `Record` object takes the alias as an argument. + +[source,Java,role=nocopy noplay] +---- +records.forEach(r -> { + System.out.println( + r.get("actor") + ); +}); +---- +==== diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/3-handling-results/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/3-handling-results/lesson.adoc new file mode 100644 index 000000000..4977cec40 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/3-handling-results/lesson.adoc @@ -0,0 +1,262 @@ += Handling Query Results +:type: lesson +:order: 3 + +[.slide.discrete.col-60-40] +== Introduction + +[.col] +==== +Let's take a look at the types of data returned by a Cypher query. + +The majority of the https://neo4j.com/docs/java-reference/5/extending-neo4j/values-and-types/[data types returned by a Cypher query map directly to Java types^], but there are a few types that need special handling. + +* Primitive types (select few) - `Integer`, `Float`, and temporal types +* Spatial types - Points and distances +* Graph types - Nodes, Relationships and Paths + +[TIP] +.Types in Neo4j Browser +===== +When graph types are returned by a query, they are visualized in a graph layout. +===== +==== + +=== Primitive Data Types + +[.col] +==== + +[cols="1,1"] +.Core data type mapping +|=== +| Java Type | Neo4j Cypher Type + +| `Long` +| `Integer` + +| `Double` +| `Float` + +| `java.time.LocalDate` +| `Date` + +| `java.time.OffsetTime` +| `Time` + +| `java.time.LocalTime` +| `LocalTime` + +| `java.time.ZonedDateTime` +| `DateTime` + +| `java.time.LocalDateTime` +| `LocalDateTime` + +| `java.time.temporal.TemporalAmount` +| `Duration` +|=== + +==== + +[.slide.discrete] +=== Temporal types + +Temporal types in Neo4j are a combination of date, time, and timezone elements. + +.Temporal Types +[cols="2,3,1,1,1"] +|=== +|Type |Description |Date? |Time? |Timezone? + +|`Date` |A tuple of Year, Month and Day |Y | | +|`Time` |The time of the day with a UTC offset |Y |Y | +|`LocalTime` |A time without a timezone | |Y | +|`DateTime` |A combination of Date and Time |Y |Y |Y +|`LocalDateTime` |A combination of Date and Time without a timezone |Y |Y | +|=== + +[.slide.col-60-40] +==== Writing temporal types + +[.col] +==== + +[source,java] +---- +import java.time.ZonedDateTime; +import java.time.ZoneId; + +String dtstring="2024-05-15T14:30:00+02:00"; +var datetime = ZonedDateTime.of(2024, 05, 15, 14, 30, 00, 0, ZoneId.of("+02:00")); + +var result = driver.executableQuery(""" + CREATE (e:Event { + startsAt: $datetime, // <1> + createdAt: datetime($dtstring), // <2> + }) + RETURN e.startsAt AS startsAt, e.createdAt AS createdAt; + """) + .withParameters( + Map.of("datetime", datetime, "dtstring", dtstring)) // <3> + .execute(); +---- +==== + +[.col] +==== +This example demonstrates how to: + +<1> Use a `DateTime` object as a parameter to the query (`<4>`) +<2> Cast an link:https://www.iso.org/iso-8601-date-and-time-format.html[ISO 8601 format string^] within a Cypher statement +<3> Get the current date and time using the `datetime()` function. + +==== + +[.slide.col-2] +==== Reading temporal types + +[.col] +==== +When reading temporal types from the database, you will receive an instance of the corresponding Java type. +==== + +[.col] +==== +[source,java] +---- +var result = driver.executableQuery(""" + RETURN datetime() as datetime, + toString(datetime()) as asString; + """) + .execute(); + +var records = result.records(); +records.forEach(r -> { + System.out.println(r.get("datetime")); // neo4j.time.DateTime + System.out.println(r.get("asString")); // String +}); +---- +==== + +[.slide.col-60-40] +==== Working with Durations + +[.col] +==== + +[source,java] +---- +import java.time.LocalDateTime; +import java.time.Duration; + +var startsAt = LocalDateTime.now(); +var eventLength = Duration.ofHours(1).plusMinutes(30); +var endsAt = startsAt.plus(eventLength); + +var result = driver.executableQuery(""" + CREATE (e:Event { startsAt: $startsAt, endsAt: $endsAt, + duration: $eventLength, // <1> + interval: duration("PT1H30M") // <2> + }) + RETURN e + """) + .withParameters(Map.of( + "startsAt", startsAt, "endsAt", endsAt, "eventLength", eventLength + )) + .execute(); +---- + +==== + +[.col] +==== +Durations represent a period of time and can be used for date arithmetic in both Java and Cypher. These types can also be created in Java or cast within a Cypher statement. + +<1> Pass an instance of Java `Duration` to the query +<2> Use the `duration()` Cypher function to create a `Duration` object from an ISO 8601 format string + +[.transcript-only] +===== + +You can cast the results of the query by getting the properties from the node: + +[source,java] +---- +// Output results +var event = result.records().get(0).get("e").asNode(); +System.out.println(event.get("startsAt")); // current datetime +System.out.println(event.get("endsAt")); // current datetime + 1h 30m +System.out.println(event.get("interval")); // P0M0DT5400S (1h 30m in seconds) +---- +===== + +[TIP] +.Calculating durations +===== +You can use the `duration.between` method to calculate the duration between two date or time objects. +===== + +==== + +[.slide.col-2] +==== Spatial types + +[.col] +==== +Neo4j has built-in support for two-dimensional and three-dimensional spatial data types (**point**) that may represent geographic coordinates (longitude, latitude) or Cartesian coordinates (x, y). + +In Java, points are represented by the `org.neo4j.driver.types.Point` type, which is wrapped by the `org.neo4j.driver.Values` class to expose as a generic `Value` object. + +The `Point` type provides methods to access the coordinates and SRID (unique id of type of coordinate system) of the point, allowing for easy manipulation and retrieval of spatial data. +==== + +[.col] +==== +|=== +| Cypher Type | Java Type | SRID | 3D SRID +| Point (Cartesian) | `org.neo4j.driver.types.Point` | `7203` | `9157` +| Point (WGS-84) | `org.neo4j.driver.types.Point` | `4326` | `4979` +|=== +==== + +[.slide.col-2] +===== Spatial Distance + +[.col] +==== +The `point.distance` function can be used to calculate the distance between two points with the same SRID, resulting in a `float` of the straight-line distance. + +[WARNING] +.SRIDs must be compatible +===== +If the SRID values are different, the function will return `None`. +===== + +==== + +[.col] +==== +[source,java] +---- +var point1 = Values.point(7203, 1.23, 4.56); +var point2 = Values.point(7203, 2.34, 5.67); + +var result = driver.executableQuery(""" + RETURN point.distance($p1, $p2) AS distance + """) + .withParameters( + Map.of("p1", point1, "p2", point2)) + .execute(); + +var distance = result.records().get(0).get("distance").asDouble(); +System.out.println(distance); +---- +==== + +[.summary] +== Lesson Summary + +You now have information required to send Cypher queries to Neo4j and consume the results with primitive, temporal, and spatial types with Java. + +Next, we will look at results with graph types and some of the considerations that you need to make when working with these types in your Java application. diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/4-graph-results/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/4-graph-results/lesson.adoc new file mode 100644 index 000000000..86e359e92 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/4-graph-results/lesson.adoc @@ -0,0 +1,189 @@ += Handling Graph Results +:type: lesson +:order: 4 + +[.slide] +== Graph types + +The following code snippet finds all movies with the specified title and returns `person`, `acted_in` and `movie`. The next few examples will show how to work with the returned nodes and relationships. + +.Return Nodes and Relationships +[source,java] +---- +final String cypher = """ + MATCH path = (person:Person)-[actedIn:ACTED_IN]->(movie:Movie {title: $title}) + RETURN path, person, actedIn, movie + """; +final String title = "Toy Story"; + +var result = driver.executableQuery(cypher) + .withParameters(Map.of("title", title)) + .execute(); +---- + +[.slide.discrete.col-2] +=== Nodes + +Nodes are returned as a link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/types/Node.html[`Node`^] object. + +[.col] +==== +.Working with Node Objects +[source,java,role=ncopy] +---- +import org.neo4j.driver.types.Node; + +var records = result.records(); +records.forEach(r -> { + Node node = r.get("person").asNode(); + + System.out.println(node.get("name")); // <1> +}); +---- +==== + +[.col] +==== +You can also access other metadata of the node with the `.elementId()`, `.labels()`, and `.values()` methods. + +1. A single property can be retrieved using the `get()` method. +==== + +[.slide.col-2] +=== Relationships + +[.col] +==== +Relationships are returned as a link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/types/Relationship.html[`Relationship`^] object. + +[source,java] +---- +import org.neo4j.driver.types.Relationship; + +records.forEach(r -> { + Relationship actedIn = r.get("actedIn").asRelationship(); + + System.out.println(actedIn.get("role")); // <1> +}); +---- + +==== + +[.col] +==== +Similar to nodes, you can also access other relationship metadata with the `.elementId()`, `.type()`, `.values()`, `.startNodeElementId()`, and `.endNodeElementId()` methods. + +1. Access properties using the `get()` method +==== + +[.slide.col-2] +=== Paths + +[.col] +==== + +A path is a sequence of nodes and relationships and is returned as a `Path` object. + +[source,java] +---- +import org.neo4j.driver.types.Path; + +records.forEach(r -> { + Path path = r.get("path").asPath(); + + System.out.println(path.nodes()); // <1> + System.out.println(path.relationships()); // <2> + System.out.println(path.length()); // <3> +}); +---- + +==== + +[.col] +==== +You can also access other metadata of the path such as the `.start()` and `.end()` methods. + +1. `nodes()` - An iterable of `Node` objects in the path +2. `relationships()` - An iterable of `Relationship` objects in the path +3. `length()` - The number of relationships within the path + +==== + +[.slide.col-2] +=== Object mapping + +[.col] +==== +You can also map the results to a domain class using the `as()` method. + +For a `Person` class such as this one: + +==== + +[.col] +==== +[source,java] +---- +public class Person { + private String imdbId; + private String name; + + public Person(String imdbId, String name) { + this.imdbId = imdbId; + this.name = name; + } + + public String getImdbId() { + return imdbId; + } + public void setImdbId(String imdbId) { + this.imdbId = imdbId; + } + + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } +} +---- +==== + +[.slide.col-2] +=== Object mapping + +[.col] +==== +You can return the `Person` node and map it to the `Person.class` from the query using the `as()` method. + +[source,java] +---- +var people = driver.executableQuery(""" + MATCH (p:Person) + RETURN p AS person + LIMIT 10; + """) + .execute() + .records() + .stream() + .map(record -> record.get("person").as(Person.class)) + .toList(); +for (var person : people) { + System.out.println(person); +} +---- +==== + +[.next.discrete] +== Check your understanding + +link:../7c-accessing-results/[Take challenge,role=btn] + +[.summary] +== Lesson Summary + +You now have all the information required to send Cypher queries to Neo4j and consume the results. + +Next, we will look at the Cypher Type System and some of the considerations that you need to make when working with values coming from Neo4j in your Java application. diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/lesson.adoc new file mode 100644 index 000000000..74a296e8d --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/lesson.adoc @@ -0,0 +1,13 @@ += Accessing Results +:type: quiz +:order: 5 +:sequential: true + +include::questions/1-node.adoc[leveloffset=+1] + +include::questions/2-object-mapping.adoc[leveloffset=+1] + +[.summary] +== Lesson Summary + +In this lesson, you correctly identified how to access properties from nodes and the type of relationships, as well as how to map query results back to Java domain classes. diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/1-node.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/1-node.adoc new file mode 100644 index 000000000..111114261 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/1-node.adoc @@ -0,0 +1,35 @@ +[.question.select-in-source] += Accessing Nodes + +Select the correct method to get the `Node` from a record: + +[source,java,role=nocopy noplay] +---- +var records = result.records(); +records.forEach(r -> { + Node node = r./*select:get("person").asNode()*/; +}); +---- + +- [ ] get("person") +- [ ] asNode("person") +- [x] get("person").asNode() +- [ ] node.get("person") + +[TIP,role=hint] +.Hint +==== +The `get()` method allows you to retrieve a node from a record, and the `asNode()` method converts it to a `Node` object. +==== + +[TIP,role=solution] +.Solution +==== +The correct answer is `get("person").asNode();`. This retrieves the `Node` object from the record and converts it to a `Node` type. + +[source,java,role=nocopy noplay] +---- +Node node = record.get("node").asNode(); +---- + +==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/2-object-mapping.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/2-object-mapping.adoc new file mode 100644 index 000000000..8b9f0c113 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/5c-accessing-results/questions/2-object-mapping.adoc @@ -0,0 +1,47 @@ +[.question.select-in-source] += Object Mapping + +Select the correct method to map the `Movie` node back to a `Movie.class`. + +[source,java,role=nocopy noplay] +---- +var movies = driver.executableQuery(""" + MATCH (movie:Movie) + RETURN movie + LIMIT 10; + """) + .execute() + .records() + .stream() + .map(record -> record.get("movie")./*select:as()*/) + .toList(); +---- + +- [ ] mapTo(Movie.class) +- [ ] toObject(Movie.class) +- [x] as(Movie.class) +- [ ] asEntity(Movie.class) + +[TIP,role=hint] +.Hint +==== +The object (e.g., `Movie`) is mapped as the domain class `Movie`. +==== + +[TIP,role=solution] +.Solution +==== +The correct answer is `as(Movie.class)`. This method maps the returning object as the domain class `Movie`. + +[source,java,role=nocopy noplay] +---- +var movies = driver.executableQuery("MATCH (m:Movie) RETURN movie LIMIT 10;") + .execute() + .records() + .stream() + .map(record -> record.get("movie").as(Movie.class)) + .toList(); +---- + +The `as()` method is the way to map objects to domain classes in Neo4j's Java driver. +==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/6-transactions/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/6-transactions/lesson.adoc new file mode 100644 index 000000000..0f665b35c --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/6-transactions/lesson.adoc @@ -0,0 +1,186 @@ += Transaction management +:type: lesson +:minutes: 10 +:slides: true +:order: 1 + +[.slide.discrete] +== Introduction + +You have learned how to execute one-off Cypher statements using the `executableQuery()` method. + +The drawback of this method is that the entire record set is only available once the final result is returned. +For longer running queries or larger datasets, this can consume a lot of memory and a long wait for the final result. + +In a production application, you may also need finer control of database transactions or to run multiple related queries as part of a single transaction. + +Transaction methods allow you to run multiple queries in a single transaction while accessing results immediately. + +[TIP] +.Understanding Transactions +==== +Neo4j is an ACID-compliant transactional database, which means queries are executed as part of a single atomic transaction. This ensures your data operations are consistent and reliable. +==== + +[.slide] +== Sessions + +To execute transactions, you need to open a session. The session object manages the underlying database connections and provides methods for executing transactions. For async applications, link:https://neo4j.com/docs/java-manual/5/async/[use the `AsyncSession`]. + +[source,Java] +---- +try (var session = driver.session()) { + // Call transaction functions here +} +---- + +Consuming a session within a `try-with-resources` will automatically close the session and release any underlying connections when the block is exited. + +[.transcript-only] +==== +[TIP] +.Specifying a database +===== +In a multi-database instance, you can specify the database to use when creating a session using `SessionConfig`. + +[source,Java] +---- +import org.neo4j.driver.SessionConfig; + +try (var session = driver.session( + SessionConfig.builder().withDatabase("databaseName").build() + )) { + // Call transaction functions here +} +---- +===== +==== + +[.slide] +== Transaction functions + +The link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/Session.html[`Session`^] object provides two methods for managing transactions: + +* `Session.executeRead()` +* `Session.executeWrite()` + +If the entire function runs successfully, the transaction is committed automatically. If any errors occur, the entire transaction is rolled back. + +[TIP] +.Transient errors +==== +These functions will also retry if the transaction fails due to a transient error, for example, a network issue. +==== + +[.slide.col-60-40] +== Unit of work patterns + +[.col] +==== +A unit of work groups operations into a single method, which is executed using the `Session`: + +[source,Java] +---- +// Unit of work +public static int createPerson(TransactionContext tx, String name, int age) { // <1> + var result = tx.run(""" + CREATE (p:Person {name: $name, age: $age}) RETURN p + """, Map.of("name", name, "age", age)); // <2> + return result.list().size(); +} +// Execute the unit of work +try (var session = driver.session()) { // <3> + var count = session.executeWrite(tx -> createPerson(tx, name, age)); +} +---- +==== + +[.col] +==== +1. The first argument to the transaction function is always a `TransactionContext` object. Any additional arguments are passed from the call to `Session.executeRead` / `Session.executeWrite`. +2. The `run()` method on the `TransactionContext` object is called to execute a Cypher statement. +3. The `executeWrite()` method is called on the session object to execute the transaction function. The result of the transaction function is returned to the caller. +==== + +[.slide] +== Multiple Queries in One Transaction + +You can execute multiple queries within the same transaction function to ensure that all operations are completed or fail as a single unit. + +[source,Java] +---- +public static void transferFunds(TransactionContext tx, String fromAccount, String toAccount, double amount) { + tx.run( + "MATCH (a:Account {id: $from_}) SET a.balance = a.balance - $amount", + Map.of("from_", fromAccount, "amount", amount) + ); + tx.run( + "MATCH (a:Account {id: $to_}) SET a.balance = a.balance + $amount", + Map.of("to_", toAccount, "amount", amount) + ); +} +---- + +[.transcript-only] +==== +[WARNING] +.Transaction state +===== +Transaction state is maintained in the DBMS's memory, so be mindful of running too many operations in a single transaction. Break up very large operations into smaller transactions when possible. +===== +==== + +[.slide.col-40-60] +== Handling outputs + +[.col] +==== +The `TransactionContext.run()` method returns a link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/Result.html[`Result`^] object. + +The records contained within the result will be iterated over as soon as they are available. + +The result must be consumed within the transaction function. + +The `consume()` method discards any remaining records and returns a link:https://neo4j.com/docs/api/java-driver/5.28/org.neo4j.driver/org/neo4j/driver/summary/ResultSummary.html[`ResultSummary`^] object that can be used to access metadata about the Cypher statement. + +The `Session.executeRead` / `Session.executeWrite` method will return the result of the transaction function upon successful execution. +==== + +[.col] +==== + +[source,Java] +.Consuming results +---- +public static ResultSummary getAnswer(TransactionContext tx, String answer) { + var result = tx.run("RETURN $answer AS answer", Map.of("answer", answer)); + return result.consume(); + } + +String result = "Hello, World!"; +try (var session = driver.session()) { + ResultSummary summary = session.executeWrite(tx -> getAnswer(tx, result)); + System.out.println( + String.format( + "Results available after %d ms and consumed after %d ms", + summary.resultAvailableAfter(TimeUnit.MILLISECONDS), + summary.resultConsumedAfter(TimeUnit.MILLISECONDS) + ) + ); +} +---- +==== + +[.next.discrete] +== Check your understanding + +link:../7c-read-transaction/[Advance to the next lesson,role=btn] + +[.summary] +== Lesson Summary + +In this lesson, you learned how to use transaction functions for read and write operations, implement the unit of work pattern, and execute multiple queries within a single transaction. + +You should use transaction functions for read and write operations when you to start consuming results as soon as they are available. + +In the next lesson, you will take a quiz to test your knowledge of using transactions. \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/lesson.adoc new file mode 100644 index 000000000..56a974964 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/lesson.adoc @@ -0,0 +1,56 @@ += Transaction functions +:type: quiz +:minutes: 10 +:order: 7 + +You have built an application that streams the results of a long-running Cypher statement to the client. + +You have written a transaction function called `getActorsFrom` to execute a **read** query within a transaction. + +[%collapsible] +.View the `getActorsFrom` function +==== +[source,Java] +---- +public static List getActorsFrom( + TransactionContext tx, + String birthDate, + String deathDate, + String location) { + + var result = tx.run(""" + MATCH (person:Person)-[:ACTED_IN]->(m:Movie), + (person)-[:FROM]->(city:City)-[:LOCATED_IN]->(country:Country) + WHERE person.birthDate >= $birthDate AND person.deathDate <= $deathDate + AND country.name = $location + RETURN person.name AS actor, person.birthDate AS birthDate, + person.deathDate AS deathDate, + city.name AS city, country.name AS country, + collect(m.title) AS movieList + """, Map.of("birthDate", birthDate, "deathDate", deathDate, "location", location)); + + return result.list(); +} +---- +==== + +include::./questions/1-run.adoc[leveloffset=+1] + +[.summary] +== Summary + +In this lesson, you demonstrated how to execute a read transaction using the `executeRead()` method. +The method handles transaction management automatically and allows for streaming results as they become available. + +The `executeRead()` method is the recommended way to run read transactions as it: + +* Automatically handles transaction management +* Enables streaming of results as they become available +* Ensures proper transaction lifecycle and resource cleanup + +[TIP] +.Consuming results +==== +Remember that the results must be consumed within the transaction function. +Once the transaction is completed, the results are no longer available. +==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/questions/1-run.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/questions/1-run.adoc new file mode 100644 index 000000000..fd1c2fe87 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/lessons/7c-read-transaction/questions/1-run.adoc @@ -0,0 +1,54 @@ +[.question.select-in-source] += Read transactions + +Select the correct function to execute the Cypher statement in a read transaction and stream the results to the client as soon as they are available. + +[source,Java,role=nocopy noplay] +---- + +try (var session = driver.session()) { + var result = session./*select:executeRead(*/ + tx -> getActorsFrom( + tx, + "1900-01-01", + "1950-12-31", + "Italy" + ) + ); + var records = result.list(); +} +---- + +- [ ] cypher( +- [x] executeRead( +- [ ] executeWrite( +- [ ] execute( + +[TIP,role=hint] +.Hint +==== +When reading data from Neo4j, use `executeRead()` to run a read transaction. This ensures that the transaction is properly managed and allows for streaming results back to the client. +==== + +[TIP,role=solution] +.Solution +==== +The correct answer is `executeRead()`. + +`executeRead()` is the recommended method for running read transactions in Neo4j. +The function handles transaction management automatically and allows for streaming results as they become available. + +[source,Java,role=nocopy] +---- +try (var session = driver.session()) { + var result = session.executeRead( + tx -> getActorsFrom( + tx, + "1900-01-01", + "1950-12-31", + "Italy" + ) + ); +} +---- +==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/module.adoc b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/module.adoc new file mode 100644 index 000000000..b1a6b78d5 --- /dev/null +++ b/asciidoc/courses/app-java/modules/2-interacting-with-neo4j/module.adoc @@ -0,0 +1,11 @@ += Interacting with Neo4j +:order: 2 + +== Module Overview + +In this module, you will learn: +* How to run a Cypher query and access the results. +* How to work with different data types and results. +* How to map the results of a Cypher query to Java objects. + +link:./1-execute-query/[Ready? Let's go →, role=btn] \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/lesson.adoc deleted file mode 100644 index 96f4a1ba3..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/lesson.adoc +++ /dev/null @@ -1,210 +0,0 @@ -= Sessions and Transactions -:type: lesson -:order: 1 - -== Sessions - -Through the Driver, we open **Sessions**. - -> A session is a container for a sequence of transactions. -> Sessions borrow connections from a pool as required and are considered lightweight and disposable. - -It is important to remember that sessions are not the same as database connections. -When the Driver connects to the database, it opens up multiple TCP connections that can be borrowed by the session. -A query may be sent over multiple connections, and results may be received by the driver over multiple connections. - -Instead, sessions should be considered a client-side abstraction for grouping units of work, which also handle the underlying connections. -The connections themselves are managed internally by the driver and are not directly exposed to the application. - -To open a new session, call the `session()` method on the driver. - -.Open a new Session -[source,java,role=nocopy,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag="driver.session",indent=0] ----- - -The `Session` needs to be closed again, fortunately it's an auto-closeable, so that a try-with-resources construct works well for us, where the session is automatically closed when the scope of the `try` ends. - -.Use a session within a try-with-resources -[source,java,role=nocopy,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag="session",indent=0] ----- - -This `session` method takes an optional configuration argument, which can be used to set the database to run any queries against in a multi-database setup, and the default access mode for any queries run within the transaction (either `READ` or `WRITE`). - -.Open a new Session with additional arguments -[source,java,role=nocopy,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=sessionWithArgs,indent=0] ----- - -If no database is supplied, the default database will be used. This is configured in the `dbms.default_database` in `neo4j.conf`, the default value is `neo4j`. -You cannot create multiple databases in Neo4j Aura or in Neo4j Community Edition. - -The default access mode is set to `WRITE`, but this can be overwritten by explicitly calling the `executeRead()` or `executeWrite()` methods. - - -== Transactions - -Through a Session, we can run one or more **Transactions**. - -> A transaction comprises a unit of work performed against a database. -> It is treated in a coherent and reliable way, independent of other transactions. - - -[TIP] -.ACID Transactions -==== -// Neo4j uses an **ACID** consistency model to ensure that data is safely and consistently stored. -A transaction, by definition, must be - -* atomic, -* consistent, -* isolated, and -* durable. - -Many developers are familiar with ACID transactions from their work with relational databases, and as such the ACID consistency model has been the norm for some time. - -// The ACID acronym stands for: - -// * **Atomic** - All operations in a transaction succeed or every operation is rolled back. -// * **Consistent** - On the completion of a transaction, the database is structurally sound. -// * **Isolated** - Transactions do not contend with one another. Contentious access to data is moderated by the database so that transactions appear to run sequentially. -// * **Durable** - The results of applying a transaction are permanent, even in the presence of failures. -==== - - -There are three types of transaction exposed by the driver: - -* Auto-commit Transactions -* Read Transactions -* Write Transactions - -=== Auto-commit Transactions - -Auto-commit transactions are a single unit of work that are immediately executed against the DBMS and acknowledged immediately. -You can run an auto-commit transaction by calling the `run()` method on the session object, passing in a Cypher statement as a string and optionally an object containing a set of parameters. - -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=session.run] ----- - - -[WARNING] -.For one-off queries only -In the event that there are any transient errors when running a query, the driver will not attempt to retry a query when using `session.run()`. -For this reason, these should only be used for one-off queries and shouldn't be used in production with Neo4j clusters. - - -=== Read Transactions - -When you intend to read data from Neo4j, you should execute a **Read** Transaction. -In a clustered environment (including Neo4j AuraDB), read queries are distributed across the database cluster. - -The session provides an `executeRead()` method, which expects a single parameter, a callback function that represents the unit of work. -The function will accept a single parameter, a Transaction object, on which you can call the `tx.run()` method with two arguments: the Cypher statement as a string and an optional set of query parameters. - -.Running a Read Transaction -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=session.readTransaction] ----- - -[TIP] -.Parameterized Queries -In the query above, the `$` prefix of `$title` (1) indicates that this value relates to the parameter defined in the second argument (2) of the `run()` method call. - - -You do not need to explicitly commit a read transaction. -If anything goes wrong within of the unit of work or there is a problem on Neo4j's side, the transaction will be automatically rolled back and the database will remain in its previous state. -If the unit of work succeeds, the transaction will be automatically committed. - -Additionally, unlike `session.run()`, if a _transient_ error is received by the driver, for example a connectivity issue with the DBMS, the driver will automatically retry the unit of work. - - -=== Write Transactions - -If you intend to write data to the database, you should execute a **Write** Transaction. - -If anything goes wrong within of the unit of work or there is a problem on Neo4j's side, the transaction will be automatically rolled back and the database will remain in its previous state. -If the unit of work succeeds, the transaction will be automatically committed and the changes applied and synchronized. - -In clustered environments, write queries are sent exclusively to the _leader_ of the cluster. -The leader of the cluster is then responsible for processing the query and synchronising the transaction across a write-quorum of the _followers_ and eventually _read-replica_ servers in the cluster. - -.Running a Write Transaction -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=session.writeTransaction,indent=0] ----- - - -== Manually Creating Transactions - -It is also possible to explicitly create a transaction object by calling the `beginTransaction()` method on the session. - -.Creating an Manual Transaction -[source,java,role=nocopy,subs="attributes+"] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=session.beginTransaction,indent=0] ----- - -This returns a Transaction object identical to the one passed in to the unit of work function when calling `executeRead()` or `executeWrite()`. - -This method differs from the `executeRead` and `executeWrite()` methods, in that the transaction will have to be manually committed or rolled back depending on the outcome of the unit of work. - -You can commit a transaction by calling the `tx.commit()` method, or roll back the transaction by calling `tx.rollback()`. - - -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=session.beginTransaction.Try] ----- - - -== Closing the Session - -Usually the session is auto-closed by the try-with-resources setup - -Only if you manage/pass around the session manually, you need to close it explicitly by calling the `close()` method to release any resources held by that session. - -.Closing a Session -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=session.close] ----- - - - -== A Working Example - -[%collapsible] -.Click to reveal a complete working example -==== -The following code defines a method that accepts a name parameter, then executes a write transaction to create a `:Person` node in the `people` database. - -.Create a Person node in the customers database -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Index.java[tag=createPerson] ----- -==== - - -== Check your understanding - -include::./questions/1-valid-methods.adoc[leveloffset=+1] - -include::./questions/2-read-transaction.adoc[leveloffset=+1] - -include::./questions/3-write-transaction.adoc[leveloffset=+1] - -[.summary] -== Lesson Summary - -In this lesson, you have learned about the process of creating sessions and running Cypher queries within transaction functions. - -In the next lesson we will look at how we process the results of a query. diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/1-valid-methods.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/1-valid-methods.adoc deleted file mode 100644 index 7d82a6425..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/1-valid-methods.adoc +++ /dev/null @@ -1,23 +0,0 @@ -[.question] -= 1. Valid Query Methods - -Which of the following options are valid methods for running a read query through the driver? - -* [*] `session.run()` -* [ ] `session.query()` -* [ ] `session.read()` -* [*] `session.executeRead()` - - -[TIP,role=hint] -.Hint -==== -You can either run a Cypher statement within an auto-commit transaction or execute a Cypher statement within a managed transaction. -==== - - -[TIP,role=solution] -.Solution -==== -The answers are `session.run()` and `session.executeRead()`. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/2-read-transaction.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/2-read-transaction.adoc deleted file mode 100644 index 2ad723b8a..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/2-read-transaction.adoc +++ /dev/null @@ -1,31 +0,0 @@ -[.question.select-in-source] -= 2. Reading from the Database - -Say we want to create a new transaction that reads. We want any queries from this method to be distributed across the cluster. - -Use the dropdown in the code block below to select the correct method. - -[source,java,role=nocopy] ----- -var res = session./*select:readMethod*/(tx -> { - // Use tx.run to read from the database -}) ----- - -- [ ] read -- [ ] readQuery -- [*] executeRead -- [ ] readTransaction - - -[TIP,role=hint] -.Hint -==== -You are looking to _execute_ a _read_ query against the database. -==== - -[TIP,role=solution] -.Solution -==== -The answer is `executeRead` -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/3-write-transaction.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/3-write-transaction.adoc deleted file mode 100644 index c41db56ad..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/1-transactions/questions/3-write-transaction.adoc +++ /dev/null @@ -1,31 +0,0 @@ -[.question.select-in-source] -= 3. Writing to the Database - -Now we want to create a new node in the database. - -Use the dropdown in the code block below to select the correct method. - -[source,java,role=nocopy] ----- -var res = session./*select:writeMethod*/(tx -> { - // Use tx.run to write to the database -}) ----- - -- [ ] insert -- [ ] write -- [ ] writeQuery -- [*] executeWrite - - -[TIP,role=hint] -.Hint -==== -You are looking to _execute_ a _write_ query against the database. -==== - -[TIP,role=solution] -.Solution -==== -The answer is `executeWrite` -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/questions/verify.adoc deleted file mode 100644 index e851524f5..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/questions/verify.adoc +++ /dev/null @@ -1,35 +0,0 @@ -:id: _challenge - -[.verify] -= Verifying the Test - -If the test has run successfully, a user with the email address `graphacademy.favorite@neo4j.com` will have added the movie Toy Story to their list of favorites. - - -verify::[] - -//appears when user clicks the Hint button -[TIP,role=hint] -.Hint -==== -You can run the following query to check for the user within the database. -If the `shouldVerify` value returns true, the verification should be successful. - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/2-interacting/9-user-favorites/verify-relationship-exists.cypher[] ----- - -==== - -[TIP,role=solution] -.Solution -==== -The following statement will mimic the behaviour of the test, merging a new `:User` node with the email address `graphacademy.favorite@neo4j.com` and ensuring that a node exists for the movie Toy Story. -The test then merges a `:HAS_FAVORITE` relationship between the user and movie nodes. -[source,cypher] ----- -include::../solution.cypher[] ----- -Once you have run this statement, click **Try again...*** to complete the challenge. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/solution.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/solution.cypher deleted file mode 100644 index 6c1e758aa..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/solution.cypher +++ /dev/null @@ -1,9 +0,0 @@ -MERGE (u:User {userId: '9f965bf6-7e32-4afb-893f-756f502b2c2a'}) -SET u.email = 'graphacademy.favorite@neo4j.com' - -MERGE (m:Movie {tmdbId: '862'}) -SET m.title = 'Toy Story' - -MERGE (u)-[r:HAS_FAVORITE]->(m) - -RETURN * diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/verify.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/verify.cypher deleted file mode 100644 index 45c56e4d3..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/verify.cypher +++ /dev/null @@ -1,2 +0,0 @@ -MATCH (u:User {email: "graphacademy.favorite@neo4j.com"})-[:HAS_FAVORITE]->(:Movie {title: 'Toy Story'}) -RETURN true AS outcome \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/questions/verify.adoc deleted file mode 100644 index bfeea1884..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/questions/verify.adoc +++ /dev/null @@ -1,35 +0,0 @@ -:id: _challenge - -[.verify] -= Verifying the Test - -If the test has run successfully, a user with the email address `graphacademy.flag@neo4j.com` will have added Band of Brothers, the most popular movie in the dataset to their list of favorites. - - -verify::[] - -//appears when user clicks the Hint button -[TIP,role=hint] -.Hint -==== -You can run the following query to check for the user within the database. -If the `shouldVerify` value returns true, the verification should be successful. - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/2-interacting/10-favorite-flag/verify-relationship-exists.cypher[] ----- - -==== - -[TIP,role=solution] -.Solution -==== -The following statement will mimic the behaviour of the test by first finding the movie with the highest `.imdbId` rating and merging a new `:User` node into the graph with the email address `graphacademy.flag@neo4j.com`. -The test then merges a `:HAS_FAVORITE` relationship between the user and movie. -[source,cypher] ----- -include::../solution.cypher[] ----- -Once you have run this statement, click **Try again...*** to complete the challenge. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/solution.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/solution.cypher deleted file mode 100644 index de307f34f..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/solution.cypher +++ /dev/null @@ -1,11 +0,0 @@ -MATCH (m:Movie) WITH m -WHERE m.imdbRating IS NOT NULL -WITH m -ORDER BY m.imdbRating DESC LIMIT 1 - -MERGE (u:User {userId: '9f965bf6-7e32-4afb-893f-756f502b2c2a'}) -SET u.email = 'graphacademy.favorite@neo4j.com' - -MERGE (u)-[r:HAS_FAVORITE]->(m) - -RETURN * diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/verify.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/verify.cypher deleted file mode 100644 index 35ba65ae1..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/verify.cypher +++ /dev/null @@ -1,2 +0,0 @@ -MATCH (u:User {email: "graphacademy.flag@neo4j.com"})-[:HAS_FAVORITE]->(:Movie {title: 'Band of Brothers'}) -RETURN true AS outcome \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/lesson.adoc deleted file mode 100644 index b542d1d61..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/lesson.adoc +++ /dev/null @@ -1,126 +0,0 @@ -= Processing Results -:type: lesson -:order: 2 - -The Neo4j Java Driver provides you with three APIs for consuming results: - -* **Synchronous** API -* **Async** API -* **Reactive** API - - -== The Three APIs - -The most common and straightforward method of consuming results is with the synchronous API. - -When using `session.run()`, `tx.run()`, or one of the two transaction functions, the query will return a `Result` object that you can process incrementally and then return the results of that processing. - -For the asynchronous and reactive APIs you need to use different entry-points and API methods and helpers like a reactive framework. -In return you get more efficient resource usage in the database, middleware and client by using the non-synchronous APIs. - -[.tab] -.Synchronous API -==== -[source,java,role=nocopy,subs="attributes+",indent=0] ----- - -include::{repository-raw}/main/src/main/java/example/AsyncApi.java[tag=sync] ----- - -==== - -[.tab] -.Async API -==== -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/AsyncApi.java[tag=async] ----- - -==== - -[.tab] -.Reactive API -==== -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/AsyncApi.java[tag=reactive] ----- - -==== - - -== The Result - -The `Result` object, contains the records received by the Driver along with a set of additional meta data about the query execution and results. - -An individual row of results is referred to as a `Record`, and can be accessed from the result various ways, as `Iterator` and via the `stream()`, the `list()` or `single()` methods. - -A `Record` refers to the keyed set of values specified in the `RETURN` portion of the statement. - -If no `RETURN` values are specified, the query will not return any results, and record results will be empty or throw an error in the case of `single()`. - -Additional meta data about the result and query is accessible from `Result` too (see below). - -=== Records - -You can access the records returned by the query through several means. -A `Result` is an `Iterator` there are `stream()` and `list()` accessors for streaming and materialization/conversion. - -.Iterating over Records -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=records] ----- - -[TIP] -.Key or Index -You can either access a value within the record by using the alias as specified in the `RETURN` portion of the Cypher statement or by specifying the column index (not recommended). -The available keys can be accessed through `res.keys()`. - -.Accessing record column values -[source,java,role=nocopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=record] ----- - -=== Result Summary - -The meta data `ResultSummary` accessed from `Result.consume()` include - -* statistics on how many nodes and relationships were created, updated, or deleted as a result of the query, -* the query type -* database and server info -* query plan with and without profile -* notifications - -You can find more detail in the https://neo4j.com/docs/api/java-driver/current/org/neo4j/driver/summary/ResultSummary.html[API docs for ResultSummary^] - -For example, to get information about how long the query took to complete, you can use the following property: - -.Using the Result Summary -[source,java,role=nocopy,indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=summary] ----- - -Another interesting part of the summary is the `SummaryCounters` available via `counters()`, which has update counts about a write-statement's execution. -You can check via `counters.containsUpdates()` if there were any updates. - -.Result Counters -[source,java,role=nocopy,indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=summary:counters] ----- - -== Check Your Understanding - -include::./questions/1-method.adoc[leveloffset=+1] - - -[.summary] -== Lesson Summary - -You now have all the information required to send Cypher queries to Neo4j and consume the results. - -Next, we will look at the Cypher Type System and some of the considerations that you need to make when working with values coming from Neo4j in your Java application. diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/questions/1-method.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/questions/1-method.adoc deleted file mode 100644 index d3167071f..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/2-results/questions/1-method.adoc +++ /dev/null @@ -1,20 +0,0 @@ -[.question] -= 1. What is the drawback of using the synchronous API to consume results? - -* [ ] The synchronous API is only available to Enterprise customers. -* [*] Results are only available once the Driver has received the final record. -* [ ] You can only use the synchronous API within a Read Transactions. - - -[TIP,role=hint] -.Hint -==== -If you are not subscribing to the record stream, you will only be able to access the first record once the entire stream has finished. -==== - -[TIP,role=solution] -.Solution -==== -Results are only available once the Driver has received the final record. -This can provide a negative experience for users waiting for the results of a long running query. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/lesson.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/lesson.adoc deleted file mode 100644 index e0252315b..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/lesson.adoc +++ /dev/null @@ -1,305 +0,0 @@ -= The Neo4j Type System -:type: lesson -:order: 3 - -At this point, we should take a look at the Cypher type system. -Despite Neo4j being written in Java (the _j_ in Neo4**j** stands for Java after all), there are some discrepancies between the types available in Cypher and native Java types. - -Some values like strings, numbers, booleans, dates, and nulls map directly to Java types but more complex types like nodes, relationship, points, durations need special handling. - -//Adam: are the Cypher types here supposed to be the types in the db (according to apoc.meta.nodeTypeProperty?? -[%collapsible] -.Java Types to Neo4j/Cypher Types -==== -[cols="1,1,1"] -|=== -| Java Type | Neo4j Cypher Type | Notes - -| `null`, -| `null` -| - -| `List` -| `List`,`Array` -| Neo4j can only store a flat array containing strings, booleans or numbers. - -| `Map` -| `Map` -| - -| `Boolean` -| `Boolean` -| - -| `Long` -| `Integer` -| - -| `Double` -| `Float` -| - -| `String` -| `String` -| - -| `byte[]` -| `byte[]` -| - -| `LocalDate` -| `LocalDate` -| See <> - -| `Time` -| `Time` -| See <> - -| `LocalTime` -| `LocalTime` -| See <> - -| `DateTime` -| `DateTime` -| See <> - -| `LocalDateTime` -| `LocalDateTime` -| See <> - -| `IsoDuration` -| `Duration` -| - -| `Point` -| `Point` -| - -| `Node` -| `Node` -| See <> - -| `Relationship` -| `Relationship` -| See <> - -| `Path` -| `Path` -| See <> - -|=== -==== - - -You can use the `as\{Type}()` method on any `Value` type or nested structure to cast underlying values to expected types. -See the https://neo4j.com/docs/api/java-driver/current/org/neo4j/driver/Value.html[API docs for `Value`^] for more information. - -For example, in the code block below, the `year` value is cast as a `Number`. - -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=values] ----- - -Let's take a look at some of these types in more detail. - -== Numbers - -For numeric values the main confusion comes from the naming, while Neo4j itself can store all kinds of Java primitive values for a unified surface, Cypher only exposes one floating point type called `Float` (equivalent to 64-bit double) and one integer type called `Integer` (equivalent to 64-bit long). - -Cypher itself has functions like `toFloat()` and `toInteger()` respectively and driver parameters of other types are automatically coerced. - -The driver's `Value` type can return most Java numeric types via `as\{Type}()` methods. -Only `BigDecimal/BigInteger` for arbitrary precision math are not supported. - -== Temporal Types - -The Temporal types used in the Cypher type system mirror the `java.time.*` types, so there are few surprises. -The only difference is `IsoDuration` for which the driver provides a custom type. - -.Temporal Types -[cols="1,3,1,1"] -|=== -| Type | Description | Example | Access Function - -| `Date` -| Represents an instant capturing the date, but not the time, nor the timezone. -| `2020-01-02` -| `asDate` - -| `DateTime` -| Represents an instant capturing the date, the time and the timezone identifier. -| `2020-01-02T01:02:03+04:00` -| `asDateTime` - - -| `LocalDateTime` -| Represents an instant capturing the date and the time, but not the timezone. -| `2020-01-02T01:02:03` -| `asLocalDateTime` - -| `LocalTime` -| Represents an instant capturing the time of day, but not the date, nor the timezone. -| `12:34:56` -| `asLocalTime` - -| `OffsetTime` -| Represents an instant capturing the time of day, and the timezone offset in seconds, but not the date. -| `12:34:56+04:00` -| `asOffsetTime` - -| `IsoDuration` -| Represents a duration between two dates or timestamps (different resolutions). Has individual accessors for the parts. -| `P1M12D` -| `asIsoDuration` - -|=== - -.Working with Temporal types -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=temporal] ----- - - -== Nodes & Relationships - -Nodes and Relationships are both returned as similar types with a common superclass `Entity`. - -As an example, let's take the following code snippet: - -.Return Nodes and Relationships -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=run] ----- - -=== Nodes - -We can retrieve the `person` value using the `.get()` method on the row and then turn it into a `Node` via `asNode()`. - -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=get] ----- - -The value assigned to the `person` variable will be the instance of a `Node`. -`Node` is a type provided by the Neo4j Java Driver to represent the information held in Neo4j for a node. - -An instance of a `Node` has three parts: - -.Working with Node Objects -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=node] ----- - -1. `id` - representing the internal ID for the node. -2. `labels` - an Iterable of String values, eg. `['Person', 'Actor']` -3. `properties` - A Java Map containing all the properties for the node. + - eg. `{"name": "Tom Hanks", "tmdbId": "31" }` - -Properties can also be retrieved from `Entity` instances with the `get(name)` method which then returns a `Value` that has to be converted further. - -[TIP] -.Internal IDs -Internal IDs should be treated as opaque values and just sent back to the database as you get them. -These ids can be re-used, a best practice is to always look up a node by its business key and label rather than relying on an internal ID. - -=== Relationships - -`Relationship` objects are also `Entity` instances, they also include an `id`, a type and properties. - - -.Working with Relationships -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=rel] ----- - - -1. `identity` - the internal ID for the relationship. -2. `type` - the type of the relationship, eg - `ACTED_IN` -3. `properties` - A Java Map containing all the properties for the relationship. + - eg. `{"role": "Woody" }` -4. `startNodeId` - representing the internal ID for the node at the start of the relationship -5. `endNodeId` - representing the internal ID for the node at the end of the relationship - - -=== Paths - -If you return a path of nodes and relationships, they will be returned as an instance of a `Path`. - -.Working with Path Objects -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=path] ----- - -1. `start` - the node starting the path -2. `end` - the node ending the path -3. `length` - A count of the number of _segments_ within the path -4. `segments` - A path is an Iterable of `Path.Segment` -5. `nodes` - An Iterable of Nodes of the Path -6. `relationships` - An Iterable of Relationships of the Path - -==== Path Segments - -A path is split into segments representing each relationship in the path. -For example, say we have a path of `+(p:Person)-[:ACTED_IN]->(m:Movie)-[:IN_GENRE]->(g:Genre)+`, there would be two segments. - -1. `+(p:Person)-[:ACTED_IN]->(m:Movie)+` -2. `+(m:Movie)-[:IN_GENRE]->(g:Genre)+` - -.Iterating over Segments -[source,java,role=ncopy,subs="attributes+",indent=0] ----- -include::{repository-raw}/main/src/main/java/example/Results.java[tag=segments] ----- - -The `PathSegment` object has three properties: - -* `relationship` - A `Relationship` object representing that part of the path. -* `start` - start node for this path segment `*` -* `end` - end node for this path segment `*` - -[TIP] -.`*` Start and End nodes within the Path Segment object -The start and end nodes on the `PathSegment` may differ from the start and end nodes of the relationship itself if the relationship was traversed in reverse direction. - - -== Converting these values en masse - -There may be times when you need to convert many Neo4j types back into native Java types. -For example, when retrieving a set of properties. - -For this the `Value` and `Record` type has three functions - -* `asObject()` recursively converts a `Value` into the appropriate Java objects -* `asMap()` converts a Value into a Java Map, it can take a callback function to customize conversion of individual keys and values -* `list()` converts a Value into a Java List, it can take a callback function to customize conversion of individual values - -The function are recursive, and will handle nested objects and arrays. - -== Additional helper functions - -`Value` has additional helper functions - -* `isTrue` and `isFalse` for boolean values -* `isNull` for null checks -* `isEmpty` for lists and maps - - -== Check Your Understanding - -include::questions/1-node-property.adoc[leveloffset=+1] - -include::questions/2-temporal.adoc[leveloffset=+1] - -[.summary] -== Lesson Summary - -In this lesson you have learned how to handle some of the more complex objects returned by a Cypher statement. -As we progress through this module, you will use the knowledge gained so far to read data from, and write data back to the database. - -In the next Challenge, you will modify code to read from the database. \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/1-node-property.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/1-node-property.adoc deleted file mode 100644 index 5386eb72b..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/1-node-property.adoc +++ /dev/null @@ -1,41 +0,0 @@ -[.question.select-in-source] -= 1. Accessing Node Properties - -Which property would you access to retrieve the "name" property for each person? - -Select the correct option in the code block below. - - -[source,java,role=nocopy] ----- -var res = session.executeRead(tx -> - tx.run(""" - MATCH (p:Person)-[:ACTED_IN]->(:Movie {title: $title}) - RETURN p - LIMIT 10 - """, - Values.parameters("title", "Toy Story")) -) - -var names = res.stream().map(row -> { - return row.get('p')./*select:properties.name*/ -}) ----- - -- [ ] name -- [ ] property['name'] -- [*] get("name") -- [ ] properties[0] - - -[TIP,role=hint] -.Hint -==== -There is a dedicated method for retrieving properties. -==== - -[TIP,role=solution] -.Solution -==== -Properties can be accessed on nodes and relationships using the `.get()` method - for example `node.get("name")`. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/2-temporal.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/2-temporal.adoc deleted file mode 100644 index fe42a51a3..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/3-type-system/questions/2-temporal.adoc +++ /dev/null @@ -1,22 +0,0 @@ -[.question] -= 2. Temporal Accessors - -Which of the following functions does the Neo4j Value not support for temporal types? - -- [ ] `asOffsetTime()` -- [ ] `asIsoDuration()` -- [*] `asTimestamp()` -- [ ] `asLocalDateTime()` - - -[TIP,role=hint] -.Hint -==== -Neo4j supports five temporal types. -==== - -[TIP,role=solution] -.Solution -==== -The only unsupported method above is `asTimestamp()` -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/questions/verify.adoc deleted file mode 100644 index 198334871..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/questions/verify.adoc +++ /dev/null @@ -1,29 +0,0 @@ -[.question.freetext] -= Highest Rated Movie - -The final test in the suite logs out the name of the highest rated movie according to the `imdbRating` property on each movie. -Enter the title of the highest rated movie. - -input::answer[] - -* [x] Band of Brothers - -[TIP,role=hint] -.Hint -==== -You can also find the answer by running the following Cypher statement: - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/2-interacting/4-home-page/highest-rated-movie.cypher[] ----- - -Copy the answer without any quotes or whitespace. -==== - - -[TIP,role=solution] -.Solution -==== -The answer is **Band of Brothers**. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/questions/verify.adoc deleted file mode 100644 index e3e770988..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/questions/verify.adoc +++ /dev/null @@ -1,38 +0,0 @@ -:id: _challenge - -[.verify] -= Verifying the Test - -Here is where things get interesting. - -If you have completed the course to this point, you should have a project that connects to the Neo4j Sandbox instance. - -If the test above has succeeded, there should be a `:User` node in the sandbox with the email address `graphacademy.register@neo4j.com`, name `Graph Academy`, and an encrypted password. - - -verify::[] - -//appears when user clicks the Hint button -[TIP,role=hint] -.Hint -==== -You can run the following query to check for the user within the database. -If the `shouldVerify` value returns true, the verification should be successful. - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/2-interacting/5-registering/user-registered.cypher[] ----- - -==== - -[TIP,role=solution] -.Solution -==== -The following statement will mimic the behaviour of the test, merging a new `:User` node with the email address `graphacademy@neo4j.com` and assigning a random UUID value to the `.userId` property. -[source,cypher] ----- -include::../solution.cypher[] ----- -Once you have run this statement, click **Try again...*** to complete the challenge. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/solution.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/solution.cypher deleted file mode 100644 index 2a7dc8cee..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/solution.cypher +++ /dev/null @@ -1,4 +0,0 @@ -MERGE (u:User {email: "graphacademy@neo4j.com"}) -SET u.userId = randomUuid(), - u.createdAt = datetime(), - u.authenticatedAt = datetime() diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/verify.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/verify.cypher deleted file mode 100644 index 1248db077..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/verify.cypher +++ /dev/null @@ -1,3 +0,0 @@ -MATCH (u:User {email: 'graphacademy.register@neo4j.com'}) -RETURN - (u.email = 'graphacademy.register@neo4j.com' AND u.name = 'Graph Academy' AND u.password <> 'letmein') AS outcome \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/questions/verify.adoc deleted file mode 100644 index b60385c31..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/questions/verify.adoc +++ /dev/null @@ -1,30 +0,0 @@ -:id: _challenge - -[.verify] -= Verifying the Test - -If you have completed the steps in this challenge, a Unique Constraint will have been added to the database. - -Click the **Check Database** button below to verify the constraint has been correctly created. - -verify::[] - -//appears when user clicks the Hint button -[TIP,role=hint] -.Hint -==== -Try running the Cypher statement at <> and then click **Check Database** again. - -==== - -[TIP,role=solution] -.Solution -==== -If you haven't already done so, run the following statement to create the constraint: -[source,cypher] ----- -include::../solution.cypher[tag=constraint] ----- -The unit test then attempts to create a user twice with a random email address, with the test passing if the `ValidationException` error is thrown by the `AuthService`. -Once you have run this statement, click **Try again...*** to complete the challenge. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/solution.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/solution.cypher deleted file mode 100644 index fab9ebe56..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/solution.cypher +++ /dev/null @@ -1,7 +0,0 @@ -// tag::constraint[] -CREATE CONSTRAINT UserEmailUnique -IF NOT EXISTS -FOR (user:User) -REQUIRE user.email IS UNIQUE -// end::constraint[] -; diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/verify.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/verify.cypher deleted file mode 100644 index 7555bfc67..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/verify.cypher +++ /dev/null @@ -1,4 +0,0 @@ -SHOW CONSTRAINTS -YIELD labelsOrTypes, properties -WHERE labelsOrTypes = ['User'] AND properties = ['email'] -RETURN count(*) = 1 AS outcome diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/questions/verify.adoc deleted file mode 100644 index 9e8421cf5..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/questions/verify.adoc +++ /dev/null @@ -1,38 +0,0 @@ -:id: _challenge - -[.verify] -= Verifying the Test - -This test creates a new `:User` node in the database with an email address of `authenticated@neo4j.com` using the `register()` method on the `AuthService` and then attempts to verify the user using the `authenticate` method. - -Hit the **Check Database** button below to verify that the test has been successfully run. - -verify::[] - -//appears when user clicks the Hint button -[TIP,role=hint] -.Hint -==== -At the end of the test, a piece of code finds the User node and sets the `authenticatedAt` property to the current date and time using the Cypher `datetime()` function. -As long as this value is within the past 24 hours, the test should pass. - -You can run the following query to check for the user within the database. -If the `shouldVerify` value returns true, the verification should be successful. - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/2-interacting/7-authenticating/user-created.cypher[] ----- - -==== - -[TIP,role=solution] -.Solution -==== -The following statement will mimic the behaviour of the test, merging a new `:User` node with the email address `authenticated@neo4j.com`, assigning a random UUID value to the `.userId` property and setting an `.authenticatedAt` property to the current date and time. -[source,cypher] ----- -include::../solution.cypher[] ----- -Once you have run this statement, click **Try again...*** to complete the challenge. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/solution.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/solution.cypher deleted file mode 100644 index c9f878eed..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/solution.cypher +++ /dev/null @@ -1,2 +0,0 @@ -MERGE (u:User {email: "authenticated@neo4j.com"}) -SET u.authenticatedAt = datetime() diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/verify.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/verify.cypher deleted file mode 100644 index 55d09196b..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/verify.cypher +++ /dev/null @@ -1,2 +0,0 @@ -MATCH (u:User {email: 'authenticated@neo4j.com'}) -RETURN u.authenticatedAt >= datetime() - duration('PT24H') AS outcome \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/questions/verify.adoc b/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/questions/verify.adoc deleted file mode 100644 index 32bd5ca34..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/questions/verify.adoc +++ /dev/null @@ -1,38 +0,0 @@ -:id: _challenge - -[.verify] -= Verifying the Test - -If the test has run successfully, a user with the email address `graphacademy.reviewer@neo4j.com` will have given the movie `Pulp Fiction` a rating of `5`. - -That number should have a type of `INTEGER` - - - -verify::[] - -//appears when user clicks the Hint button -[TIP,role=hint] -.Hint -==== -You can run the following query to check for the user within the database. -If the `shouldVerify` value returns true, the verification should be successful. - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/2-interacting/8-ratings/user-rated-goodfellas.cypher[] ----- - -==== - -[TIP,role=solution] -.Solution -==== -The following statement will mimic the behaviour of the test, merging a new `:User` node with the email address `graphacademy.reviewer@neo4j.com` and a `:Movie` node with a `.tmdbId` property of `'769'`. -The test then merges a relationship between the user and movie nodes in the graph, giving the relationship a `.rating` property. -[source,cypher] ----- -include::../solution.cypher[] ----- -Once you have run this statement, click **Try again...*** to complete the challenge. -==== diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/solution.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/solution.cypher deleted file mode 100644 index e19acb813..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/solution.cypher +++ /dev/null @@ -1,10 +0,0 @@ -MERGE (u:User {userId: '1185150b-9e81-46a2-a1d3-eb649544b9c4'}) -SET u.email = 'graphacademy.reviewer@neo4j.com' -MERGE (m:Movie {tmdbId: '769'}) -MERGE (u)-[r:RATED]->(m) -SET r.rating = 5, - r.timestamp = timestamp() -RETURN m { - .*, - rating: r.rating -} AS movie diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/verify.cypher b/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/verify.cypher deleted file mode 100644 index 770e5885f..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/verify.cypher +++ /dev/null @@ -1,2 +0,0 @@ -MATCH (u:User {email: "graphacademy.reviewer@neo4j.com"})-[r:RATED]->(m:Movie {title: 'Pulp Fiction'}) -RETURN r.rating = 5 AS outcome \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/module.adoc b/asciidoc/courses/app-java/modules/2-interacting/module.adoc deleted file mode 100644 index 9987bc912..000000000 --- a/asciidoc/courses/app-java/modules/2-interacting/module.adoc +++ /dev/null @@ -1,11 +0,0 @@ -= Interacting with Neo4j -:order: 2 - -In this module, you will learn how to use the Java driver within the Neoflix project. - -You will learn how to: - -* Open a Session and execute a Unit of Work within a Transaction. -* Execute Read and Write queries through the Driver. -* Consume the results returned from Neo4j. -* Handle potential errors thrown by the Driver. diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/questions/1-movie-count.adoc b/asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/questions/1-movie-count.adoc deleted file mode 100644 index 5438d6030..000000000 --- a/asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/questions/1-movie-count.adoc +++ /dev/null @@ -1,24 +0,0 @@ -[.question.freetext] -= Which genre has the highest movie count? - -After the test has succeeded, the test suite will log the name of the genre with the greatest number of movies. - -Enter the name of the genre below and click **Check Answer**. - -input::answer[] - -* [x] Drama - -[TIP,role=hint] -.Hint -==== - -You can also find the answer by running the following Cypher statement: - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/3-backlog/1-browse-genres/genre-with-most-movies.cypher[] ----- - -Copy the answer without any quotes or whitespace. -==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/questions/1-genre-movie-count.adoc b/asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/questions/1-genre-movie-count.adoc deleted file mode 100644 index 8e599f18c..000000000 --- a/asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/questions/1-genre-movie-count.adoc +++ /dev/null @@ -1,23 +0,0 @@ -[.question.freetext] -= How many movies are in the Action genre? - -After the test has succeeded, the test suite will log the count of movies in the _Action_ genre to the console. - -Enter the number of movies below and click **Check Answer**. - -input::answer[] - -* [x] 1545 - -[TIP,role=hint] -.Hint -==== - -You can also find the answer by running the following Cypher statement: - -[source,cypher] ----- -include::{cypher-repository-raw}/main/cypher/3-backlog/2-find-genre-details/genre-movie-count.cypher[] ----- - -==== \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/1-neoflix-app/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/1-neoflix-app/lesson.adoc new file mode 100644 index 000000000..884994601 --- /dev/null +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/1-neoflix-app/lesson.adoc @@ -0,0 +1,58 @@ += About the Neoflix App +:type: lesson +:order: 1 + +[.slide] +== The Neoflix App + +The Neoflix app is a movie recommendation application that allows users to search for movies and view their details. Users will also be able to rate movies they have seen. + +The application is built using the link:https://javalin.io/[Javalin Java framework] and uses the Neo4j database to store and query movie data. In this module, you will build upon existing starter code to implement some functionality in the application. + +[NOTE] +.Failing tests +==== +You will notice that some tests fail when you run `mvn verify`. During the course you will complete the project and resolve the issues. +==== + +[WARNING] +.Errors while installing dependencies? +==== +This project has been written using Java version **{java-version}**. +If you are using the wrong version, you may experience errors when trying to install the dependencies. +==== + +== Start the Project + +To start the project, run the following command: + +.Start the project using Maven +[source,sh] +mvn compile exec:java + +You should see an output similar to the following confirming that the server has successfully started: + +.Console Output +[source,console,role=nocopy] +Started server on http://localhost:3000/ + +== A Brief Tour of the Project + +If you open up the listening address in your browser, you will see a Single Page Application (SPA) that communicates with the API served at http://localhost:3000/api/movies[http://localhost:3000/api/movies^]. +Currently, the responses are hardcoded, but as you progress through the course, you will learn how to query Neo4j to find this information. + +Here are some of the important directories in the project: + +* `src/main/java/example/` - Example code for initiating the driver. +* `src/main/java/neoflix` - The application code: +** `src/main/java/neoflix/routes/` - Route handlers that are registered on the server. You will not need to edit these files. +** `src/main/java/neoflix/services/` - Services that you will need to update to interact with Neo4j. +* `src/test/java/neoflix` - Test files that you will need to run in order to check functionality. You will run these using the `mvn test` or individually with the + +`mvn test -Dtest=neoflix._0x_XxxTest#methodName` command. +* `src/main/resources/public/` - Minified build files for the Web application. *Do not edit these files*. + +== Done! + +Once you have the project up and running, click the button below to complete this lesson. + +read::The project is running![] \ No newline at end of file diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/images/movie-cards.png b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/10-user-favorites/images/movie-cards.png similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/images/movie-cards.png rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/10-user-favorites/images/movie-cards.png diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/images/neoflix-person-tom-hanks.png b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/10-user-favorites/images/neoflix-person-tom-hanks.png similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/images/neoflix-person-tom-hanks.png rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/10-user-favorites/images/neoflix-person-tom-hanks.png diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/10-user-favorites/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/10-user-favorites/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/10-user-favorites/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/11-favorite-flag/lesson.adoc similarity index 98% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/11-favorite-flag/lesson.adoc index 7643bdce3..c6cb96cf2 100644 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/11-favorite-flag/lesson.adoc +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/11-favorite-flag/lesson.adoc @@ -21,7 +21,6 @@ So far, you have only used the `tx` Transaction within the Unit of Work to run a This will be fine for the majority of cases, but there may also be scenarios where more than one Query may be required. - === User Favorites One way that we could find the user's favorites would be to run a separate `MATCH` clause within the same query. @@ -32,10 +31,8 @@ Instead, it would be cleaner to execute two separate queries within the same tra Fortunately, with only a few minor tweaks to the code, we can create a method that can be used to populate the `favorite` flag in the every other method in the `MovieService`. - == Creating a Reusable Method - At the bottom of the `MovieService`, a placeholder `getUserFavorites()` method exists which is currently hardcoded to return an empty array. [source,java,indent=0] @@ -47,7 +44,6 @@ The purpose of this method is to run a Cypher statement against the `Transaction Your challenge is to modify this method to retrieve that list of Movie ID's and then call this function from the Read Transaction in the `all()` method. - === Finding Favorites Modify the `getUserFavorites()` method to run the following query against the `tx` parameter. @@ -64,7 +60,6 @@ include::{cypher-repository-raw}/main/cypher/2-interacting/10-favorite-flag/user ---- ==== - ==== Working Solution [%collapsible] .Click here to reveal the completed `getUserFavorites()` method @@ -89,42 +84,29 @@ Instead of returning the results directly, you'll need to add a call to `getUser If we take a look at the two versions of the `all()` method, not much has changed. The `favorites` array has been passed through as a parameter to the query, and the query now uses the Cypher `IN` clause to check if the ID is included in the array. - [.tab] .Updated Method ==== - .neoflix/services/MovieService.java [source,java,indent=0] ---- include::{repository-raw}/{branch}/src/main/java/neoflix/services/MovieService.java[tag="allcypher"] ---- - ==== - - [.tab] .Previous Version ==== - .neoflix/services/MovieService.java [source,java,indent=0] ---- include::{repository-raw}/{previous-branch}/src/main/java/neoflix/services/MovieService.java[tag="allcypher"] ---- - ==== - - - include::../../../../includes/test.adoc[] -include::./questions/verify.adoc[leveloffset=+1] - - [.summary] == Module Summary diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/4-home-page/lesson.adoc similarity index 96% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/4-home-page/lesson.adoc index 2a02e87ca..e0b0f2d7e 100644 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/4-home-page/lesson.adoc +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/4-home-page/lesson.adoc @@ -9,7 +9,6 @@ Now for another challenge. In this challenge, you will use the knowledge gained so far in this course to add new functionality to the API. You will modify the `all()` method of the link:{repository-blob}/main/src/main/java/neoflix/services/MovieService.java[`MovieService`^] to do the following: - 1. <> 2. <> 3. <> @@ -20,7 +19,6 @@ If the test runs correctly, the title of the highest rated movie will be logged. You will need this value to verify that the test has run correctly. - == Exploring the Code Before you start, let's take a look at the code. @@ -66,7 +64,7 @@ You will need to replace these `TODO` comments with working code to complete the == Implementing Read Transactions -As you learned in the Sessions and Transactions lesson, you will complete code to open a new session and run the query within a Read Transaction. +As you learned in the transaction management lesson, you will complete code to open a new session and run the query within a Read Transaction. Then, finally you add code to extract and return the results. @@ -143,11 +141,6 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/MovieService.j include::../../../../includes/test.adoc[] -== Verifying the Test - -include::./questions/verify.adoc[leveloffset=+1] - - [.summary] == Lesson Summary diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/5-registering/lesson.adoc similarity index 98% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/5-registering/lesson.adoc index 0783d01b4..00a1fa822 100644 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/5-registering/lesson.adoc +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/5-registering/lesson.adoc @@ -114,10 +114,6 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/AuthService.ja include::../../../../includes/test.adoc[] - -include::./questions/verify.adoc[leveloffset=+1] - - [.summary] == Lesson Summary diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/6-driver-errors/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/6-driver-errors/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/6-driver-errors/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/6-driver-errors/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/6-driver-errors/questions/1-error-code.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/6-driver-errors/questions/1-error-code.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/6-driver-errors/questions/1-error-code.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/6-driver-errors/questions/1-error-code.adoc diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/6-driver-errors/questions/2-error-details.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/6-driver-errors/questions/2-error-details.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/6-driver-errors/questions/2-error-details.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/6-driver-errors/questions/2-error-details.adoc diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/7-unique-emails/lesson.adoc similarity index 98% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/7-unique-emails/lesson.adoc index 64054e479..5c93d5aa5 100644 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/7-unique-emails/lesson.adoc +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/7-unique-emails/lesson.adoc @@ -115,10 +115,6 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/AuthService.ja include::../../../../includes/test.adoc[] - -include::./questions/verify.adoc[leveloffset=+1] - - [.summary] == Lesson Summary diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/8-authenticating/lesson.adoc similarity index 96% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/8-authenticating/lesson.adoc index e75ee4212..b81816478 100644 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/8-authenticating/lesson.adoc +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/8-authenticating/lesson.adoc @@ -45,8 +45,6 @@ Once a user is authorized a JWT token is generated (using the Auth0 JWT library) For authenticated users the application uses a pre-processor `before()` the routes to verify the authentication for each request, which is delegated to: `AppUtils.handleAuthAndSetUser`. - - There the `Authorization` header is extracted and validated using the Auth0 JWT library. If that is successful the userId is set as a request attribute which can be accessed by the route implementation. @@ -138,12 +136,9 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/AuthService.ja include::../../../../includes/test.adoc[] - -include::./questions/verify.adoc[leveloffset=+1] - [.summary] == Lesson Summary -In this Challenge, you have updated the `AuthService` to authenticate a User using the data held in the Sandbox database. +In this challenge, you have updated the `AuthService` to authenticate a User using the data held in the Sandbox database. -In the next Challenge, you will save the current user's movie ratings to the database. +In the next challenge, you will save the current user's movie ratings to the database. diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/images/rating-form.png b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/9-ratings/images/rating-form.png similarity index 100% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/images/rating-form.png rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/9-ratings/images/rating-form.png diff --git a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/lesson.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/9-ratings/lesson.adoc similarity index 98% rename from asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/lesson.adoc rename to asciidoc/courses/app-java/modules/3-building-neoflix/lessons/9-ratings/lesson.adoc index bca89b893..6e53195e6 100644 --- a/asciidoc/courses/app-java/modules/2-interacting/lessons/9-ratings/lesson.adoc +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/lessons/9-ratings/lesson.adoc @@ -96,9 +96,6 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/RatingService. include::../../../../includes/test.adoc[] -include::./questions/verify.adoc[leveloffset=+1] - - [.summary] == Lesson Summary diff --git a/asciidoc/courses/app-java/modules/3-building-neoflix/module.adoc b/asciidoc/courses/app-java/modules/3-building-neoflix/module.adoc new file mode 100644 index 000000000..e22c43cbf --- /dev/null +++ b/asciidoc/courses/app-java/modules/3-building-neoflix/module.adoc @@ -0,0 +1,9 @@ += Building Neoflix with the Java Driver +:order: 3 + +In this module, you will learn how to use the Java driver within the Neoflix project. + +You will learn how to: +* Build functionality for a real application. +* Execute queries to read data and populate information in the application. +* Write data to Neo4j from the application. diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/1-browse-genres/lesson.adoc similarity index 95% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/1-browse-genres/lesson.adoc index 5384e3ffe..793420777 100644 --- a/asciidoc/courses/app-java/modules/3-backlog/lessons/1-browse-genres/lesson.adoc +++ b/asciidoc/courses/app-java/modules/4-backlog/lessons/1-browse-genres/lesson.adoc @@ -49,11 +49,6 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/GenreService.j include::../../../../includes/test.adoc[] -== Verifying the Test - -include::./questions/1-movie-count.adoc[leveloffset=+1] - - [.summary] == Lesson Summary diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/2-find-genre-details/lesson.adoc similarity index 96% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/2-find-genre-details/lesson.adoc index 50be4ee4f..a21f33679 100644 --- a/asciidoc/courses/app-java/modules/3-backlog/lessons/2-find-genre-details/lesson.adoc +++ b/asciidoc/courses/app-java/modules/4-backlog/lessons/2-find-genre-details/lesson.adoc @@ -57,11 +57,6 @@ include::{repository-raw}/{branch}/src/main/java/neoflix/services/GenreService.j // Testing include::../../../../includes/test.adoc[] - -== Verifying the Test - -include::./questions/1-genre-movie-count.adoc[leveloffset=+1] - [.summary] == Lesson Summary diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/3-movie-lists/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/3-movie-lists/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/3-movie-lists/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/3-movie-lists/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/3-movie-lists/questions/1-coppola-films.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/3-movie-lists/questions/1-coppola-films.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/3-movie-lists/questions/1-coppola-films.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/3-movie-lists/questions/1-coppola-films.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/4-movie-view/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/4-movie-view/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/4-movie-view/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/4-movie-view/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/4-movie-view/questions/1-most-similar-movie.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/4-movie-view/questions/1-most-similar-movie.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/4-movie-view/questions/1-most-similar-movie.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/4-movie-view/questions/1-most-similar-movie.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/5-listing-ratings/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/5-listing-ratings/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/5-listing-ratings/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/5-listing-ratings/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/5-listing-ratings/questions/1-first-rating.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/5-listing-ratings/questions/1-first-rating.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/5-listing-ratings/questions/1-first-rating.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/5-listing-ratings/questions/1-first-rating.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/6-person-list/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/6-person-list/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/6-person-list/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/6-person-list/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/6-person-list/questions/1-alphabetical-order.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/6-person-list/questions/1-alphabetical-order.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/6-person-list/questions/1-alphabetical-order.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/6-person-list/questions/1-alphabetical-order.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/6-person-list/reset.cypher b/asciidoc/courses/app-java/modules/4-backlog/lessons/6-person-list/reset.cypher similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/6-person-list/reset.cypher rename to asciidoc/courses/app-java/modules/4-backlog/lessons/6-person-list/reset.cypher diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/7-person-view/lesson.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/7-person-view/lesson.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/7-person-view/lesson.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/7-person-view/lesson.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/lessons/7-person-view/questions/1-similar-people.adoc b/asciidoc/courses/app-java/modules/4-backlog/lessons/7-person-view/questions/1-similar-people.adoc similarity index 100% rename from asciidoc/courses/app-java/modules/3-backlog/lessons/7-person-view/questions/1-similar-people.adoc rename to asciidoc/courses/app-java/modules/4-backlog/lessons/7-person-view/questions/1-similar-people.adoc diff --git a/asciidoc/courses/app-java/modules/3-backlog/module.adoc b/asciidoc/courses/app-java/modules/4-backlog/module.adoc similarity index 98% rename from asciidoc/courses/app-java/modules/3-backlog/module.adoc rename to asciidoc/courses/app-java/modules/4-backlog/module.adoc index 9f4e8a0a5..f750a4e3b 100644 --- a/asciidoc/courses/app-java/modules/3-backlog/module.adoc +++ b/asciidoc/courses/app-java/modules/4-backlog/module.adoc @@ -1,5 +1,5 @@ = Project Backlog - +:order: 4 Now that we have covered everything that we need to know, you can practise your skills by implementing the remainder of the backlog. There is still functionality to be added to the genre listings, movie listings and people directory.