最新の安定バージョンについては、Spring Data Neo4j 7.4.4 を使用してください!

Spring Data Neo4j 拡張

Spring Data Neo4j リポジトリで利用可能な拡張機能

Spring Data Neo4j は、リポジトリに追加できる拡張機能または「ミックスイン」をいくつか提供します。ミックスインとは何ですか ? ウィキペディア [Wikipedia] (英語) によると、ミックスインはプログラマーがクラスにコードを挿入できるようにする言語概念です。ミックスインプログラミングはソフトウェア開発のスタイルであり、機能のユニットがクラス内に作成され、他のクラスと混合されます。

Java は言語レベルでその概念をサポートしていませんが、いくつかのインターフェースと、適切な実装とインターセプターを追加するランタイムを介してそれをエミュレートします。

デフォルトで追加されるミックスインはそれぞれ QueryByExampleExecutor と ReactiveQueryByExampleExecutor です。これらのインターフェースについては、"例示による問い合わせ" で詳しく説明されています。

提供される追加のミックスインは次のとおりです。

  • QuerydslPredicateExecutor

  • CypherdslConditionExecutor

  • CypherdslStatementExecutor

  • ReactiveQuerydslPredicateExecutor

  • ReactiveCypherdslConditionExecutor

  • ReactiveCypherdslStatementExecutor

生成されたクエリに動的条件を追加する

QuerydslPredicateExecutor と CypherdslConditionExecutor はどちらも同じ概念を提供します。つまり、SDN がクエリを生成し、追加される「述語」 (Query DSL) または「条件」 (Cypher DSL) を指定します。Cypher DSL は SDN がネイティブに使用するものであるため、Cypher DSL をお勧めします。静的メタモデルを生成するアノテーションプロセッサー (英語) の使用を検討することもできます。

それはどのように機能するのでしょうか ? 上で説明したようにリポジトリを宣言し、次のインターフェースのいずれかを追加します。

interface QueryDSLPersonRepository extends
        Neo4jRepository<Person, Long>, (1)
        QuerydslPredicateExecutor<Person> { (2)
}
1 標準リポジトリ宣言
2 クエリ DSL ミックスイン

OR

import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.support.CypherdslConditionExecutor;

    interface PersonRepository extends
            Neo4jRepository<Person, Long>, (1)
            CypherdslConditionExecutor<Person> { (2)
    }
1 標準リポジトリ宣言
2Cypher DSL ミックスイン

Cypher DSL 条件エグゼキュータを使用した使用例を示します。

Node person = Cypher.node("Person").named("person"); (1)
Property firstName = person.property("firstName"); (2)
Property lastName = person.property("lastName");

assertThat(
        repository.findAll(
                firstName.eq(Cypher.anonParameter("Helge"))
                        .or(lastName.eq(Cypher.parameter("someName", "B."))), (3)
                lastName.descending() (4)
        ))
        .extracting(Person::getFirstName)
        .containsExactly("Helge", "Bela");
1 クエリのルートをターゲットとする名前付き Node オブジェクトを定義します
2 そこからいくつかのプロパティを導き出す
3or 条件を作成します。名には匿名パラメーターが使用され、姓には名前付きパラメーターが使用されます。これは、これらのフラグメントでパラメーターを定義する方法であり、それができない Query-DSL ミックスインに比べて利点の 1 つです。リテラルは Cypher.literalOf で表現できます。
4 いずれかのプロパティから SortItem を定義します

このコードは、Query-DSL ミックスインの場合とよく似ています。Query-DSL ミックスインの理由としては、API が使い慣れていることと、他のストアでも動作することが考えられます。これに反対する理由は、クラスパス上に追加のライブラリが必要であること、リレーションシップのトラバースがサポートされていないこと、および述語内のパラメーターをサポートしていないこと (技術的にはサポートされていますが、API メソッドが存在しないこと) です。実際に実行中のクエリに渡します)。

エンティティと射影に対する (動的) Cypher-DSL ステートメントの使用

対応するミックスインを追加することは、条件エクゼキュータを使用することと変わりません。

interface PersonRepository extends
        Neo4jRepository<Person, Long>,
        CypherdslStatementExecutor<Person> {
}

ReactiveNeo4jRepository を延長する場合は ReactiveCypherdslStatementExecutor をご使用ください。

CypherdslStatementExecutor には、findOne および findAll 用のいくつかのオーバーロードが付属しています。これらはすべて、Cypher-DSL ステートメントをそれぞれ最初のパラメーターとして、その進行中の定義を受け取り、投影メソッドの場合は型を受け取ります。

クエリにパラメーターが必要な場合は、次のように、パラメーターは Cypher-DSL 自体を介して定義され、Cypher-DSL によって設定される必要があります。

static Statement whoHasFirstNameWithAddress(String name) { (1)
    Node p = Cypher.node("Person").named("p"); (2)
    Node a = Cypher.anyNode("a");
    Relationship r = p.relationshipTo(a, "LIVES_AT");
    return Cypher.match(r)
            .where(p.property("firstName").isEqualTo(Cypher.anonParameter(name))) (3)
            .returning(
                    p.getRequiredSymbolicName(),
                    Functions.collect(r),
                    Functions.collect(a)
            )
            .build();
}

@Test
void fineOneShouldWork(@Autowired PersonRepository repository) {

    Optional<Person> result = repository.findOne(whoHasFirstNameWithAddress("Helge"));  (4)

    assertThat(result).hasValueSatisfying(namesOnly -> {
        assertThat(namesOnly.getFirstName()).isEqualTo("Helge");
        assertThat(namesOnly.getLastName()).isEqualTo("Schneider");
        assertThat(namesOnly.getAddress()).extracting(Person.Address::getCity)
                .isEqualTo("Mülheim an der Ruhr");
    });
}

@Test
void fineOneProjectedShouldWork(@Autowired PersonRepository repository) {

    Optional<NamesOnly> result = repository.findOne(
            whoHasFirstNameWithAddress("Helge"),
            NamesOnly.class  (5)
    );

    assertThat(result).hasValueSatisfying(namesOnly -> {
        assertThat(namesOnly.getFirstName()).isEqualTo("Helge");
        assertThat(namesOnly.getLastName()).isEqualTo("Schneider");
        assertThat(namesOnly.getFullName()).isEqualTo("Helge Schneider");
    });
}
1 動的クエリはヘルパーメソッドで型 セーフな方法で構築されます
2 これについてはすでに here で確認しましたが、そこではモデルを保持するいくつかの変数も定義しました
3 メソッドに渡される name の実際の値が入る匿名パラメーターを定義します
4 ヘルパーメソッドから返されたステートメントは、エンティティを検索するために使用されます。
5 あるいは射影。

findAll メソッドも同様に機能します。命令型 Cypher-DSL ステートメントエグゼキューターは、ページングされた結果を返すオーバーロードも提供します。