Skip to content

Commit

Permalink
Integrate clauses based statements with executable statements, improv…
Browse files Browse the repository at this point in the history
…e documentation.
  • Loading branch information
michael-simons committed Jul 6, 2021
1 parent 8822ba2 commit 2870c46
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 13 deletions.
27 changes: 26 additions & 1 deletion docs/cypher-parser/cypher-parser.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ include::../../neo4j-cypher-dsl-examples/neo4j-cypher-dsl-examples-parser/src/te
<.> The callback from step one is passed as callback to the event `ON_RETURN_ITEM` and will be called for every item
<.> The final option instance will be applied to the parser. The statement will render to the same result as <<example-using-input,the first example>>.

== Preventing certain things
=== Preventing certain things

Callbacks can of course be used to prevent things.
Any exception thrown will halt the parsing.
Expand Down Expand Up @@ -208,3 +208,28 @@ include::../../neo4j-cypher-dsl-parser/src/test/java/org/neo4j/cypherdsl/parser/

Changing relationship types via a filter is possible as well, but as relationships might only have one type, the number of
usecases is smaller.


=== Combining the parser with SDN's `CypherdslConditionExecutor`

Spring Data Neo4j 6 provides `CypherdslConditionExecutor`. This is a fragment that adds the capability to execute
statements with added conditions to a `Neo4jRepository`.

Given the following repository:

[source,java,indent=0,tabsize=4]
----
include::../../neo4j-cypher-dsl-examples/neo4j-cypher-dsl-examples-sdn6/src/main/java/org/neo4j/cypherdsl/examples/sdn6/movies/PeopleRepository.java[tag=additional-fragments]
----
<.> Allows to just add conditions to our generated queries
<.> Provides an alternative to using @Query with strings

One possible use case is presented in this service:

[source,java,indent=0,tabsize=4]
----
include::../../neo4j-cypher-dsl-examples/neo4j-cypher-dsl-examples-sdn6/src/main/java/org/neo4j/cypherdsl/examples/sdn6/movies/PeopleService.java[tag=using-parser-with-spring]
----
<.> The condition that only people born later than 1980 is hard coded in the service.
An arbitrary String is than parsed into a condition and attached via `AND`.
Thus only valid cypher can go in there and with filters and callbacks, preconditions of that Cypher can be asserted.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@
<version>${project.version}</version>
</dependency>

<!-- Only needed when using the parser feature -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher-dsl-parser</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,10 @@ Iterable<Person> findPeopleBornInThe70tiesOr(@RequestParam(name = "name") Option

return peopleService.findPeopleBornInThe70tiesOr(optionalName);
}

@GetMapping("/findPeopleBornAfterThe70ties")
Iterable<Person> findPeopleBornAfterThe70ties(@RequestParam(name = "conditions") String additionalConditions) {

return peopleService.findPeopleBornAfterThe70tiesAnd(additionalConditions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.neo4j.cypherdsl.core.Cypher;
import org.neo4j.cypherdsl.core.Functions;
// end::using-person-repo[]
import org.neo4j.cypherdsl.parser.CypherParser;
import org.springframework.data.domain.Example;
// tag::using-person-repo[]
import org.springframework.stereotype.Service;
Expand All @@ -51,6 +52,17 @@ Optional<Person> findOne(Example<Person> example) {
return peopleRepository.findOne(example);
}


// tag::using-parser-with-spring[]
Iterable<Person> findPeopleBornAfterThe70tiesAnd(String additionalConditions) {

return peopleRepository.findAll(
PERSON.BORN.gte(Cypher.literalOf(1980))
.and(CypherParser.parseExpression(additionalConditions).asCondition()) // <.>
);
}
// end::using-parser-with-spring[]

// tag::using-person-repo[]
Iterable<Person> findPeopleBornInThe70tiesOr(Optional<String> optionalName) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,16 @@ void findPeopleBornInThe70tiesOrShouldWork2(@Autowired TestRestTemplate restTemp
var people = exchange.getBody();
assertThat(people).hasSize(18);
}

@Test
@DisplayName("Using conditions pt3.")
void findPeopleBornAfterThe70tiesShouldWork(@Autowired TestRestTemplate restTemplate) {

var exchange = restTemplate
.exchange("/api/people/findPeopleBornAfterThe70ties?conditions={conditions}", HttpMethod.GET, null, new ParameterizedTypeReference<List<Person>>() {
}, "n.name contains \"Ricci\" OR n.name ends with 'Hirsch'");
assertThat(exchange.getStatusCode()).isEqualTo(HttpStatus.OK);
var people = exchange.getBody();
assertThat(people).hasSize(2);
}
}
12 changes: 12 additions & 0 deletions neo4j-cypher-dsl-parser/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
<artifactId>neo4j-cypher-javacc-parser</artifactId>
</dependency>

<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId>
Expand All @@ -71,6 +77,12 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ public void accept(Visitor visitor) {
visitor.leave(this);
}

boolean doesReturnOrYield() {
@Override
public boolean doesReturnOrYield() {
if (this.clauses.isEmpty()) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,11 @@ interface RegularQuery extends Statement {
*/
interface SingleQuery extends RegularQuery {
}

/**
* @return True if this statement can be assured to return something.
*/
default boolean doesReturnOrYield() {
return this instanceof ResultStatement || this instanceof UnionQuery;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ static Subquery call(Statement statement, IdentifiableElement... imports) {
static Subquery call(Statement statement, boolean skipAssertions, IdentifiableElement... imports) {

if (!skipAssertions) {
boolean clausesBasedWithReturn = statement instanceof ClausesBasedStatement && ((ClausesBasedStatement) statement).doesReturnOrYield();
boolean validReturn = statement instanceof ResultStatement || statement instanceof UnionQuery || clausesBasedWithReturn;
boolean validReturn = statement.doesReturnOrYield();
if (!validReturn) {
throw new IllegalArgumentException("Only a statement that returns elements, either via RETURN or YIELD, can be used in a subquery.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
* @since 1.0
*/
@API(status = Status.EXPERIMENTAL, since = "1.0")
public class SymbolicName implements Expression, IdentifiableElement {
public final class SymbolicName implements Expression, IdentifiableElement {

private static final Map<String, SymbolicName> CACHE = Collections.synchronizedMap(new LRUCache<>(32));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import java.util.stream.Stream;

import org.apiguardian.api.API;
import org.neo4j.cypherdsl.core.ResultStatement;
import org.neo4j.cypherdsl.core.Statement;
import org.neo4j.driver.QueryRunner;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
Expand All @@ -42,7 +42,7 @@
@API(status = INTERNAL, since = "2021.2.1")
class DefaultExecutableResultStatement extends DefaultExecutableStatement implements ExecutableResultStatement {

DefaultExecutableResultStatement(ResultStatement delegate) {
DefaultExecutableResultStatement(Statement delegate) {
super(delegate);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import java.util.function.Function;

import org.apiguardian.api.API;
import org.neo4j.cypherdsl.core.ResultStatement;
import org.neo4j.cypherdsl.core.Statement;
import org.neo4j.driver.Record;
import org.neo4j.driver.reactive.RxQueryRunner;
import org.reactivestreams.Publisher;
Expand All @@ -38,7 +38,7 @@
@API(status = INTERNAL, since = "2021.2.1")
class DefaultReactiveExecutableResultStatement extends DefaultReactiveExecutableStatement implements ReactiveExecutableResultStatement {

DefaultReactiveExecutableResultStatement(ResultStatement delegate) {
DefaultReactiveExecutableResultStatement(Statement delegate) {
super(delegate);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public interface ExecutableStatement {
* @see #of(Statement)
*/
static ExecutableStatement makeExecutable(Statement statement) {
if (statement instanceof ResultStatement) {
return makeExecutable((ResultStatement) statement);
if (statement.doesReturnOrYield()) {
return new DefaultExecutableResultStatement(statement);
}
return new DefaultExecutableStatement(statement);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public interface ReactiveExecutableStatement extends ExecutableStatement {
* @see #of(Statement)
*/
static ReactiveExecutableStatement makeExecutable(Statement statement) {
if (statement instanceof ResultStatement) {
return makeExecutable((ResultStatement) statement);
if (statement.doesReturnOrYield()) {
return new DefaultReactiveExecutableResultStatement(statement);
}
return new DefaultReactiveExecutableStatement(statement);
}
Expand Down

0 comments on commit 2870c46

Please sign in to comment.