////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////
:description: Apache Hop has extensive support for the Neo4j graph database, including a library of samples that explain how to load data to and read from your Neo4j graphs through Apache Hop.

= Working with Neo4j data
:toc:

Using examples, this article explains how to get started with loading data into Neo4j and then getting it back out if needed.

== Introduction

A graph is made up of nodes and relationships (also called edges).
Relationships link up Nodes and both can have any number of properties.
In that regard a graph is different from your typical 'relational database' which hints at the typical challenge when loading data into Neo4j and getting it back out: converting between relational and graph data.
In this document we'll sketch some possibilities to get started.

== Samples setup

To make the Neo4j examples work in the `neo4j/` folder of your `samples` project you need to configure a few variables:

* *NEO4J_HOSTNAME* : The Neo4j server hostname
* *NEO4J_PORT* : The neo4j database port (default is 7687)
* *NEO4J_USERNAME* : The username
* *NEO4J_PASSWORD* : The password

These variables can be easily set by creating a new environment which represents your situation.
Create a new environment configuration file and set the variables in there.

== Updating graphs

=== Graph Output : The beers Wikipedia example

The xref:pipeline/transforms/neo4j-graphoutput.adoc[Graph Output] transform is a useful transform which is capable of updating multiple nodes and relationships at the same time.
To get started with this transform you can take a look at workflow `neo4j/beers-wikipedia-graph.hwf`.

image::tech/neo4j/beers-wikipedia-graph-workflow.png[The beers wikipedia workflow,width=75%]

This workflow takes the following actions:

* *Ping Neo4j*: Perform a network ping to the Neo4j server to see if it's reachable.
* *Check Neo4j*: connect to the Neo4j server and returns a static value from it to see if it's working as expected
* *Create indexes for graph model Belgian Beers*: Create indexes to get great performance when updating nodes or relationships
* *Create constraints for graph model Belgian Beers*: Create constraints to guarantee uniqueness of primary key properties
* *Read and scrub the data*: Load the raw HTML from wikipedia containing a table with Belgian Beers information.
This information is scrubbed (cleaned up) and translated to English.
* *Load data into a graph*: Data is loaded into Neo4j using the `Belgian Beers` graph model and a field-to-graph mapping.

The way that data is mapped to a graph model is quite simple.
Take the Graph Output transform in the data loading pipeline:

image::tech/neo4j/data-loading-graph-output-transform.png[Data loading graph output transform,width=75%]

As you can see above this transform doesn't require any technical knowledge.
You can use the "Map fields" button to use a GUI to map the input fields on the left with the various node and relationship properties in your graph model.
You can also do this manually.

=== Neo4j Output : parallel data loading

The xref:pipeline/transforms/neo4j-output.adoc[Neo4j Output] transform is useful for simple data loading into one node, two nodes and/or a relationship.
It doesn't require any knowledge of Cypher to use.
Take a look at workflow `neo4j/neo4j-output-parallel-load.hpl`:

image::tech/neo4j/neo4j-output-parallel-load-pipeline.png[Neo4j Output parallel load,width=75%]

In this particular example we simply map the fields into the properties of a single node so we can use the Neo4j Output transform instead of Graph Output described above.
The advantage is in this case that we have a simpler mapping and can potentially load data faster.

image::tech/neo4j/neo4j-output-parallel-load-transform.png[Neo4j Output parallel load transform,width=75%]

There is also no need for a graph model so this transform is great to handle simple scenarios very quickly without any fuss.
What this sample also showcases is that you can load larger data sets in parallel.
In fact this pipeline has a parameter called `COPIES` set to 4 by default which will load into Neo4j in 4 different threads in parallel.

=== Cypher : a simple unwind example

The xref:pipeline/transforms/neo4j-cypher.adoc[Neo4j Cypher] transform is the Swiss army knife in our Neo4j toolbox.
It's capable of doing pretty much everything Neo4j related if you have knowledge of Cypher.
In example `neo4j/neo4j-cypher-unwind-simple.hpl`:

image::tech/neo4j/neo4j-cypher-unwind-simple.png[Neo4j Cypher : a simple unwind example,width=50%]

In this particular example we always use the same Cypher statement.
This allows us to group input data so we can use an https://neo4j.com/docs/cypher-manual/current/clauses/unwind/[UNWIND] command which will dramatically speed up the performance.
It's a step up from batching different commands together.

The first thing we need to do is collect all the input rows (mapped to parameter values) into a values map (on the Options tab):

image::tech/neo4j/neo4j-cypher-unwind-simple-collect-into-map.png[Collecting the input data into a values map,width=75%]

The parameters you want to pass to the Cypher statement are listed on the Parameters tab.
Finally, we can construct the Cypher statement itself:

[source]
----
UNWIND $events AS event
MERGE (y:Year { year: event.year })
MERGE (y)<-[:IN]-(e:Event { id: event.id })
RETURN e.id AS x
ORDER BY x
----

=== Removing data : The Cleanup example

Sometimes you might want to remove sets of data from Neo4j or indeed everything, for example during testing (see the Neo4j integration tests of the Hop project for examples).
The included example is called `neo4j/cleanup-remove-everything.hwf`

image::tech/neo4j/cleanup-remove-everything-workflow.png[Cleanup: remove everything workflow,width=75%]

The way indexes and constraints are removed is by calling `db.indexes()` or `db.constraints()` and by iterating over those values to remove all of them.
Nodes and relationships are deleted in batches to reduce the strain on the Neo4j database transaction handler.
That handler might have a hard time deleting very large amounts of nodes or relationships because Neo4j is an ACID compliant database.

== Retrieving data

=== Cypher : complex returns

As mentioned above, the xref:pipeline/transforms/neo4j-cypher.adoc[Neo4j Cypher] transform can not only be used to load data but also to retrieve it.
Example pipeline `neo4j/neo4j-cypher-complex-returns.hpl` shows how to return complex data types like List, Map and Node from a Neo4j graph:

image::tech/neo4j/neo4j-cypher-complex-returns-pipeline.png[Neo4j Cypher : complex returns,width=75%]

The way this happens is that Node, Relationship and Path values can be mapped to Hop Graph or String (in JSON format) output fields.
Neo4j List and Map data types are always converted to String fields in JSON format.
Those can then be processed further in the rest of the pipeline.

== Bulk loading

Sometimes we want to load years of historical data before we start updating our graph.
In this case we can use the tool https://neo4j.com/docs/operations-manual/current/tools/neo4j-admin-import/[neo4j-admin import].
This tool is designed to be very fast in bulk loading large amounts of data into a new graph database.
It works by loading CSV files which are expected to be in a particular format to describe the various nodes and relationships in a graph.

To help out with imports we have a number of tools at your disposal:

* The Graph data type: transforms xref:pipeline/transforms/neo4j-output.adoc[Neo4j Output] and xref:pipeline/transforms/neo4j-graphoutput.adoc[Neo4j Graph Output] have options to write their output to a field of type `Graph`.
These transforms are capable of flagging the primary key field of nodes which encapsulates enough information to generate CSV files with...
* xref:pipeline/transforms/neo4j-split-graph.adoc[Neo4j Split Graph] : With this transform we can split up the multiple nodes and relationships stored in a single Graph field produced by the Graph Output transform.
You can then filter out particular nodes or relationships to guarantee uniqueness or other post processing.
* xref:pipeline/transforms/neo4j-gencsv.adoc[Neo4j Generate CSVs] : this transform will help you with the creation of the CSV files for import.
* xref:pipeline/transforms/neo4j-import.adoc[Neo4j Import] : this transform will generate the `neo4j-admin import` statement for you and execute it.
It uses the list of CSV files and their types (node/relationship) to do this as well as the options you selected in the transform dialog.

