About
The project itself is not an official Neo4j library. |
This project can be seen as a companion library for the official Neo4j Java Driver. It does not aim to replace or wrap functionality of the driver, but instead enriches already existing mechanisms.
The core idea (from today’s perspective) is to take some concepts and learnings of object-graph-mapping libraries like Neo4j-OGM or Spring Data Neo4j and re-assemble them into light-weight modules.
Namely, those parts are currently:
-
Read-only mapper
Converts Java driver’s records into Java objects.
-
Parameter renderer
Serializes a given Java object into a Java driver
Value
map.
Feel free to provide feedback and ideas or in general contributions at https://github.com/meistermeier/neo4j-java-toolbelt.
Dependencies
The mapper and renderer are currently part of the very same artifact: neo4j-java-toolbelt-mapper
.
This might change in the future or the combined artifact will get renamed.
<dependency>
<groupId>com.meistermeier.neo4j.toolbelt</groupId>
<artifactId>neo4j-java-toolbelt-mapper</artifactId>
<version>0.1.0</version>
</dependency>
Mapper
The mapper is meant for converting Neo4j Java driver’s Record
into Java objects.
It generates a typed Function<Record, T>
that can be provided to the driver’s Result#list
method.
The supported return patterns are:
-
Node(s)
-
Values
-
Map structure
Because the library wants to avoid any effect on the application code, there are no annotations or other kind meta-information. This leads to the question: What does get mapped in the end? The answer to this is pretty simple. The node’s properties, values names or keys in the map structure have to match the names of the defined parameter in the constructor of the target record/class.
Also, the mapper does not take care about post-initialization population of properties. There is only one mapping phase per instance and this is the instantiation via the best-matching constructor (type and name wise).
If you are working with classes, you need to compile your application with -parameters to preserve the constructor’s parameter names.
Otherwise, the mapper cannot link the right values.
|
There is also support to map related nodes.
To have an unambiguous definition, the result has to have the format RETURN n, [relatedNode] as relatedNodes
where relatedNodes
refers to the name of a List<RelatedNodeType> relatedNodes
defined field.
Example
To get started, a node returned from the driver, should get mapped into a record with two fields.
public record Person(String name, Integer yearBorn) {}
To retrieve the mapping function, the Mapper
class provides a createMapper
method.
Called with the matching type, it generates the right mapping function.
Function<Record, Person> personConverter = Mapper.INSTANCE.createMapperFor(Person.class);
This mapping function can be used in the Result#list
function to map the individual Records
.
This does, of course, also work for single results with a list size of 1.
List<Person> people = session.run("MATCH (p:Person) return p")
.list(personConverter);
Since this is a Java standard Function
, it can also get applied to the Result#single
returning Record
.
Record
Record singleNode = session.run("MATCH (p:Person) return p").single();
Person person = personConverter.apply(singleNode);
Parameter Renderer
With a Renderer
, it is possible to render a class into a map of driver values.
Those parameters can then be used within the driver’s Session#query
method.
record ParameterRecord(String a, int b) {}
A populated instance of this type can then be handed over to the Renderer
to get mapped into a driver Value
.
In detail, it will become a MapValue
in this case.
ParameterRecord parameterRecord = new ParameterRecord("test", 4711);
Value parameters = Renderer.INSTANCE.toParameters(parameterRecord);
Record result = session.run("RETURN $a, $b", parameters).single();
System.out.println(result); // Record<{$a: "test", $b: 4711}>
The Renderer
can also produce Cypher collections of rendered instances (for driver’s experts: ListValue
).
By default, those collections are named rows.
ParameterRecord parameterRecord1 = new ParameterRecord("test1", 4711);
ParameterRecord parameterRecord2 = new ParameterRecord("test2", 42);
Value parameters = Renderer.INSTANCE.toParameters(List.of(parameterRecord1, parameterRecord2));
session.run("UNWIND $rows as row RETURN row.a, row.b", parameters).forEachRemaining(
System.out::println
);
// Record<{row.a: "test1", row.b: 4711}>
// Record<{row.a: "test2", row.b: 42}>
In the example above the Renderer#toParameters(Collection)
method is a shortcut for Renderer#toParameters(Collection, String)
.
It is possible to use the overloaded method directly and name the collection that gets rendered explicitly.
ParameterRecord parameterRecord1 = new ParameterRecord("test1", 4711);
ParameterRecord parameterRecord2 = new ParameterRecord("test2", 42);
Value parameters = Renderer.INSTANCE.toParameters(List.of(parameterRecord1, parameterRecord2), "things");
session.run("UNWIND $things as thing RETURN thing.a, thing.b", parameters).forEachRemaining(
System.out::println
);
// Record<{thing.a: "test1", thing.b: 4711}>
// Record<{thing.a: "test2", thing.b: 42}>
Supported types
Although these are the supported types for the Mapper
,
the Renderer
might not support some of them yet.
Java | Cypher/Driver |
---|---|
Long |
Long |
Integer |
Long |
Double |
Double |
Float |
Double |
String |
String |
Boolean |
Boolean |
LocalDate |
LocalDate |
LocalDateTime |
LocalDateTime |
LocalTime |
LocalTime |
OffsetDateTime |
OffsetDateTime |
OffsetTime |
OffsetTime |
ZonedDateTime |
ZonedDateTime |
List<> of everything ^^ |
[] of everything ^^ |
List<RelatedNode> |
[] of nodes |