A Java DSL (Builder) for the Cypher Query Language
APACHE-2.0 License
Bot releases are visible (Hide)
equalsIgnoreCase
operatororg.neo4j.cypherdsl.codegen.sdn6
)neo4j-cypher-dsl-parser
).2021.3.1 is a pure bug fix release. API Guardian cannot be an optional dependency, otherwise compiling programs with -Werror
will fail, as the @API
-annotation has runtime and not class retention.
PatternComprehension
.2021.3 builds straight upon 2021.2.3, with few additions to the existing API, that didnβt quite fit with a patch release, but belong conceptually into this release, which brings a completely new module: The neo4j-cypher-dsl-parser.
Whatβs behind that name? A Cypher-Parser build on the official Neo4j 4.3 parser frontend and creating a Cypher-DSL-AST or single expressions usable in the context of the Cypher-DSL.
The module lives under the following coordinates org.neo4j:neo4j-cypher-dsl-parser and requires JDK 11+ (the same version like Neo4j does). We created a couple of examples, but we are sure you will have tons of more ideas and therefore a looking for your feedback, regardless if we missed out something in the API, bugs or just remarks.
Hereβs a sneak preview. It shows you can add a user supplied Cypher fragment to something you are building using the DSL.
var userProvidedCypher
= "MATCH (this)-[:LINK]-(o:Other) RETURN o as result";
var userStatement = CypherParser.parse(userProvidedCypher);
var node = Cypher.node("Node").named("node");
var result = Cypher.name("result");
var cypher = Cypher
.match(node)
.call(
userStatement,
node.as("this")
)
.returning(result.project("foo", "bar"))
.build()
.getCypher();
For this release a big thank you goes out to the Cypher-operations team at Neo4j, listening to our requests and ideas!
2021.2.3 is a rather big release as it contains many small improvements and API functionality required by our next major
release. Those are brought in now so that they can be benefial to others without bumping a major version.
ExposesReturning
.ExposesPatternLengthAccessors
for uniform access to relationships and chains thereof. [improvement]FunctionInvocation
directly. [improvement]MapProjection
and KeyValueMapEntry
as public API.set
and remove
labels operation as public API.set
and mutate
of expressions as public API.Hints
as public API.Node
pattern.Case
an interface and let it extend Expression
. [bug]with(Named⦠vars)
.Return
and With
internals.Thanks to @meistermeier and @aldrinm for their contributions.
Thanks to @meistermeier for the contribution of the API improvements in regard to collections.
Now with optional driver integration and executable results. Please have a look at the new section in the docs: http://neo4j-contrib.github.io/cypher-dsl/current/#driver-integration
returningRaw
for returning arbitrary (aliased) Cypher fragments (bot as part of a statement or as aRETURN xxx
clause without preceding query)$E
and named parameters in rawThanks to @Andy2003 for contributing to this release.
2021.2 doesn't bring any new features apart from being now a Java library supporting the Java module system not only with automatic module names but also with a correct module-info.java
when running on JDK 11+ on the module path.
The Cypher-DSL uses the technique of JEP 238: Multi-Release JAR Files to provide a module-info.java
for projects being on JDK 11+.
The MR-Jar allows us to compile for JDK 8 but also support JDK 11 (we choose 11 as it is the current LTS release as time of writing).
To use the Cypher-DSL in a modular application you would need to require the following modules:
module org.neo4j.cypherdsl.examples.core {
requires org.neo4j.cypherdsl.core;
}
This release comes with a small catch: We do support using some QueryDSL features. Query-DSL will have correct automatic module names in their 5.x release and we asked them to backport those to the 4.x line on which the Cypher-DSL optionally depends (See 2805).
Until then we statically require (that is "optional" in module speak) Query-DSL via the artifact name. This can cause errors when the artifact (querydsl-core.jar
) is renamed via the build process or similar. We are gonna improve that as soon as we can depend on fixed automatic module names.
Apart from this big change there is no change in any public API. This release should be a drop-in replacement for the prior.
A big thank you to @sormuras for his invaluable lessons about the Java module system.
This release comes with two notable things: It uses a couple of annotations on the API to guide developers using it correctly. IDEs like IDEA will now issue warnings if you don't use a returned builder, or a new instance of an object while wrongly assuming you mutated state.
In the light of that we discovered that the RelationshipChain
pattern was mutable and returning a mutated instance while it should have returned a fresh one.
Warning This might be a breaking change for users having code like this:
var pattern = Cypher.node("Start").named("s")
.relationshipTo(Cypher.anyNode())
.relationshipTo(Cypher.node("End").named("e"));
pattern.named("x");
Prior to 2021.1.2 this would give the pattern the name x
and modify it in place.
From 2021.1.2 onwards you must use the returned value:
pattern = pattern.named("x");
We think that this change is crucial and necessary as all other patterns are immutable as intended and in sum, they build
up truly immutable statements. One pattern that is mutable like the above invalides the whole guarantee about the statement.
named(SymbolicName s)
to RelationshipChain.RelationshipChain
.@Container
). [Only test related]This is a drop-in replacement for 2021.1.0. Introducing the interface for Property
broke the mutate
operation, for which no test was in place. This and the bug has been fixed.
2021.1.0 comes with a ton of new features and a handful of breaking changes. Fear not, the breaking changes are resolvable by recompiling your application. We turned Node
, Relationship
and Property
into interfaces and provide now NodeBase
and RelationshipBase
so that you can use them to build a static meta-model of your application. A PropertyBase
might follow.
Find out everything about the new possibility to define a static meta model in the manual. The manual also includes a major part about the two new modules we offer: org.neo4j:neo4j-cypher-dsl-codegen-core
and org.neo4j:neo4j-cypher-dsl-codegen-sdn6
. neo4j-cypher-dsl-codegen-core
provides the infrastructure necessary to build code generators for creating a domain model following our recommendation and neo4j-cypher-dsl-codegen-sdn6
is a first implementation of that: A Java annotation processor that can be added to any Spring Data Neo4j 6 project in version 6.0.6 or higher. It will find your annotated domain classes and turn them into a model you can use to build queries.
Last but not least: We added support for some expressions of the more generic QueryDSL. This will require com.querydsl:querydsl-core
on the class path but only if you decide to call Cypher#adapt(foreignExpression)
. This is a feature that is driven by Spring Data Neo4j 6.1 in which we build upon this to provide a QuerydslPredicateExecutor
. Find more in this section of the manual.
Cypher#anonParameter()
to define a parameter with a value but without a name. The name will be accessible on the statement after rendering).WARNING: This will already be the last release of the 2021.0 line. 2021.1 will be API compatible but not ABI compatible, as some classes have been changed into interfaces. That means it is not a drop in replacement, but your application needs to be recompiled.
WITH
.Thanks @Andy2003 for the improvements of the pretty printer.
The feature looks like this:
var c = node("Configuration").named("c");
var d = node("Cypher-DSL").named("d");
var mergeStatement = merge(c.relationshipTo(d, "CONFIGURES"))
.onCreate()
.set(
d.property("version").to(literalOf("2021.0.1")),
c.property("prettyPrint").to(literalTrue())
)
.onMatch().set(c.property("indentStyle").to(literalOf("TAB")))
.returning(d).build();
var renderer = Renderer.getRenderer(Configuration.prettyPrinting());
System.out.println(renderer.render(mergeStatement));
and gives you:
MERGE (c:Configuration)-[:CONFIGURES]->(d:`Cypher-DSL`)
ON CREATE SET d.version = '2021.0.1', c.prettyPrint = true
ON MATCH SET c.indentStyle = 'TAB'
RETURN d
2021.0.0 comes with a lot of new features.
Thanks to @Andy2003 for his contributions!
Andy is one of our first users outside Spring Data Neo4j 6 and he started to use the Cypher-DSL in Neo4j GraphQL Java. Neo4j GraphQL Java is a library to translate GraphQL based schemas and queries to Cypher and execute those statements with the Neo4j database.
It can be used from a wide variety of frameworks.
We are happy and proud to be part of this and even more so about the input and contribution we got back from Andy.
Of course thanks for your input in form of tickets and discussions go out to @utnaf, @aaramg, @K-Lovelace and @maximelovino as well!
Two things should be mentioned:
The bugfix for GH-121 might change behavior for some users: The changes prevents the forced rendering of an alias for objects when the original object - the one that has been aliased - is passed down to the DSL after an alias has been created.
The original intention for that behaviour was related to Map projection, in which the alias is actually rendered before the object.
So now the use of an aliased expression the first time triggers a AS b
respectively b: a
in a map projection. All further calls will just render b
. If the original object is used again, a
will be rendered. If that is not desired in your query and you rely on the alias, make sure you use the aliased expression returned from .as("someAlias")
.
The other thing are the combined features GH-135 and GH-146. The Statement
class has become a fully fledged accessor to the Cypher String and the parameters used and if provided, the values for those. The following shows a small example:
var person = Cypher.node("Person").named("p");
var statement = Cypher
.match(person)
.where(person.property("nickname").isEqualTo(Cypher.parameter("nickname")))
.set(
person.property("firstName").to(Cypher.parameter("firstName").withValue("Thomas")),
person.property("name").to(Cypher.parameter("name", "Anderson"))
)
.returning(person)
.build();
assertThat(statement.getCypher())
.isEqualTo("MATCH (p:`Person`) WHERE p.nickname = $nickname SET p.firstName = $firstName, p.name = $name RETURN p");
Collection<String> parameterNames = statement.getParameterNames();
assertThat(parameterNames).containsExactlyInAnyOrder("nickname", "firstName", "name");
Map<String, Object> parameters = statement.getParameters();
assertThat(parameters).hasSize(2);
assertThat(parameters).containsEntry("firstName", "Thomas");
assertThat(parameters).containsEntry("name", "Anderson");
We do publish the Project info now: Project info, including the Java API.
WITH
clause after YIELD
.Node
and Relationship
objects generate a symbolic name if required and not setproperties()
relationships()
startNode()
, endNode()
,[]
for accessing sub lists and indexes).call
RelationshipDetails
is now internal API.