@@ -480,9 +480,129 @@ public interface MyPersonRepository extends Neo4jRepository<Person, Long> {
480
480
Also, the `Pageable` should be unsorted and you should provide a stable order.
481
481
We won't use the sorting information from the pageable.
482
482
<.> This method returns a page. A page knows about the exact number of total pages.
483
- Therefore you must specify an additional count query.
483
+ Therefore, you must specify an additional count query.
484
484
All other restrictions from the second method apply.
485
485
486
+ [[faq.path-mapping]]
487
+ == Can I map named paths?
488
+
489
+ A series of connected nodes and relationships is called a "path" in Neo4j.
490
+ Cypher allows paths to be named using an identifer, as exemplified by:
491
+
492
+ [source,cypher]
493
+ ----
494
+ p = (a)-[*3..5]->(b)
495
+ ----
496
+
497
+ or as in the infamous Movie graph, that includes the following path (in that case, one of the shortest path between two actors):
498
+
499
+ [[bacon-distance]]
500
+ [source,cypher]
501
+ .The "Bacon" distance
502
+ ----
503
+ MATCH p=shortestPath((bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
504
+ RETURN p
505
+ ----
506
+
507
+ Which looks like this:
508
+
509
+ image::bacon-distance.png[]
510
+
511
+ We find 3 nodes labeled `Person` and 2 nodes labeled `Movie`. Both can be mapped with a custom queury.
512
+ Assume there's a node entity for both `Person` and `Movie` as well as `Actor` taking care of the relationship:
513
+
514
+
515
+ [source,java]
516
+ ."Standard" movie graph domain model
517
+ ----
518
+ @Node
519
+ public final class Person {
520
+
521
+ @Id @GeneratedValue
522
+ private final Long id;
523
+
524
+ private final String name;
525
+
526
+ private Integer born;
527
+
528
+ @Relationship("REVIEWED")
529
+ private List<Movie> reviewed = new ArrayList<>();
530
+ }
531
+
532
+ @RelationshipProperties
533
+ public final class Actor {
534
+
535
+ @Id @GeneratedValue
536
+ private final Long id;
537
+
538
+ @TargetNode
539
+ private final Person person;
540
+
541
+ private final List<String> roles;
542
+ }
543
+
544
+ @Node
545
+ public final class Movie {
546
+
547
+ @Id
548
+ private final String title;
549
+
550
+ @Property("tagline")
551
+ private final String description;
552
+
553
+ @Relationship(value = "ACTED_IN", direction = Direction.INCOMING)
554
+ private final List<Actor> actors;
555
+ }
556
+ ----
557
+
558
+ When using a query as shown in <<bacon-distance>> for a domain class of type `Person` like this
559
+
560
+ [source,java]
561
+ ----
562
+ interface PeopleRepository extends Neo4jRepository<Person, Long> {
563
+ @Query(""
564
+ + "MATCH p=shortestPath((bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
565
+ + "RETURN p"
566
+ )
567
+ List<Person> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
568
+ }
569
+ ----
570
+
571
+ it will retrieve all people from the path and map them.
572
+ If there are relationship types on the path like `REVIEWED` that are also present on the domain, these
573
+ will be filled accordingly from the path.
574
+
575
+ WARNING: Take special care when you use nodes hydrated from a path based query to save data.
576
+ If not all relationships are hydrated, data will be lost.
577
+
578
+ The other way round works as well. The same query can be used with the `Movie` entity.
579
+ It then will only populate movies.
580
+ The following listing shows how todo this as well as how the query can be enriched with additional data
581
+ not found on the path. That data is used to correctly populate the missing relationships (in that case, all the actors)
582
+
583
+ [source,java]
584
+ ----
585
+ interface MovieRepository extends Neo4jRepository<Movie, String> {
586
+
587
+ @Query(""
588
+ + "MATCH p=shortestPath(\n"
589
+ + "(bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
590
+ + "WITH p, [n IN nodes(p) WHERE n:Movie] AS x\n"
591
+ + "UNWIND x AS m\n"
592
+ + "MATCH (m) <-[r:DIRECTED]-(d:Person)\n"
593
+ + "RETURN p, collect(r), collect(d)"
594
+ )
595
+ List<Movie> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
596
+ }
597
+ ----
598
+
599
+ The query returns the path plus all relationships and related nodes collected so that the movie entities are fully hydrated.
600
+
601
+ The path mapping works for single paths as well for multiple records of paths (which are returned by the `allShortestPath` function.)
602
+
603
+ TIP: Named paths can be used efficiently to populate and return more than just a root node, see <<custom-query.paths>>.
604
+
605
+
486
606
[[faq.custom-queries-and-custom-mappings]]
487
607
== Is `@Query` the only way to use custom queries?
488
608
@@ -632,7 +752,7 @@ and one implementation. The implementation would than have had all three abstrac
632
752
All of this applies of course to reactive repositories as well.
633
753
They would work with the `ReactiveNeo4jTemplate` and `ReactiveNeo4jClient` and the reactive session provided by the driver.
634
754
635
- If you have recuring methods for all repositories, you could swap out the default repository implementation.
755
+ If you have recurring methods for all repositories, you could swap out the default repository implementation.
636
756
637
757
[[faq.custom-base-repositories]]
638
758
== How do I use custom Spring Data Neo4j base repositories?
@@ -666,6 +786,37 @@ Those are
666
786
* `org.springframework.data.annotation.LastModifiedBy`
667
787
* `org.springframework.data.annotation.LastModifiedDate`
668
788
789
+ <<auditing>> gives you a general view how to use auditing in the bigger context of Spring Data Commons.
790
+ The following listing presents every configuration option provided by Spring Data Neo4j:
791
+
792
+ [source,java,indent=0,tabsize=4]
793
+ .Enabling and configuring Neo4j auditing
794
+ ----
795
+ include::../../../../src/test/java/org/springframework/data/neo4j/integration/imperative/AuditingIT.java[tags=faq.entities.auditing]
796
+ ----
797
+ <.> Set to true if you want the modification data to be written during creating as well
798
+ <.> Use this attribute to specify the name of the bean that provides the auditor (i.e. a user name)
799
+ <.> Use this attribute to specify the name of a bean that provides the current date. In this case
800
+ a fixed date is used as the above configuration is part of our tests
801
+
802
+ The reactive version is basically the same apart from the fact the auditor aware bean is of type `ReactiveAuditorAware`,
803
+ so that the retrieval of an auditor is part of the reactive flow.
804
+
805
+ In addition to those auditing mechanism you can add as many beans implementing `BeforeBindCallback<T>` or `ReactiveBeforeBindCallback<T>`
806
+ to the context. These beans will be picked up by Spring Data Neo4j and called in order (in case they implement `Ordered` or
807
+ are annotated with `@Order`) just before an entity is persisted.
808
+
809
+ They can modify the entity or return a completely new one.
810
+ The following example adds one callback to the context that changes one attribute before the entity is persisted:
811
+
812
+ [source,java,indent=0,tabsize=4]
813
+ .Modifying entities before save
814
+ ----
815
+ include::../../../../src/test/java/org/springframework/data/neo4j/integration/imperative/CallbacksIT.java[tags=faq.entities.auditing.callbacks]
816
+ ----
817
+
818
+ No additional configuration is required.
819
+
669
820
[[faq.find-by-example]]
670
821
== How do I use "Find by example"?
671
822
@@ -693,125 +844,6 @@ movieExample = Example.of(
693
844
movies = this.movieRepository.findAll(movieExample);
694
845
----
695
846
696
- [[faq.path-mapping]]
697
- == Can I map named paths?
698
-
699
- A series of connected nodes and relationships is called a "path" in Neo4j.
700
- Cypher allows paths to be named using an identifer, as exemplified by:
701
-
702
- [source,cypher]
703
- ----
704
- p = (a)-[*3..5]->(b)
705
- ----
706
-
707
- or as in the infamous Movie graph, that includes the following path (in that case, one of the shortest path between two actors):
708
-
709
- [[bacon-distance]]
710
- [source,cypher]
711
- .The "Bacon" distance
712
- ----
713
- MATCH p=shortestPath((bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
714
- RETURN p
715
- ----
716
-
717
- Which looks like this:
718
-
719
- image::bacon-distance.png[]
720
-
721
- We find 3 nodes labeled `Person` and 2 nodes labeled `Movie`. Both can be mapped with a custom queury.
722
- Assume there's a node entity for both `Person` and `Movie` as well as `Actor` taking care of the relationship:
723
-
724
-
725
- [source,java]
726
- ."Standard" movie graph domain model
727
- ----
728
- @Node
729
- public final class Person {
730
-
731
- @Id @GeneratedValue
732
- private final Long id;
733
-
734
- private final String name;
735
-
736
- private Integer born;
737
-
738
- @Relationship("REVIEWED")
739
- private List<Movie> reviewed = new ArrayList<>();
740
- }
741
-
742
- @RelationshipProperties
743
- public final class Actor {
744
-
745
- @Id @GeneratedValue
746
- private final Long id;
747
-
748
- @TargetNode
749
- private final Person person;
750
-
751
- private final List<String> roles;
752
- }
753
-
754
- @Node
755
- public final class Movie {
756
-
757
- @Id
758
- private final String title;
759
-
760
- @Property("tagline")
761
- private final String description;
762
-
763
- @Relationship(value = "ACTED_IN", direction = Direction.INCOMING)
764
- private final List<Actor> actors;
765
- }
766
- ----
767
-
768
- When using a query as shown in <<bacon-distance>> for a domain class of type `Person` like this
769
-
770
- [source,java]
771
- ----
772
- interface PeopleRepository extends Neo4jRepository<Person, Long> {
773
- @Query(""
774
- + "MATCH p=shortestPath((bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
775
- + "RETURN p"
776
- )
777
- List<Person> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
778
- }
779
- ----
780
-
781
- it will retrieve all people from the path and map them.
782
- If there are relationship types on the path like `REVIEWED` that are also present on the domain, these
783
- will be filled accordingly from the path.
784
-
785
- WARNING: Take special care when you use nodes hydrated from a path based query to save data.
786
- If not all relationships are hydrated, data will be lost.
787
-
788
- The other way round works as well. The same query can be used with the `Movie` entity.
789
- It then will only populate movies.
790
- The following listing shows how todo this as well as how the query can be enriched with additional data
791
- not found on the path. That data is used to correctly populate the missing relationships (in that case, all the actors)
792
-
793
- [source,java]
794
- ----
795
- interface MovieRepository extends Neo4jRepository<Movie, String> {
796
-
797
- @Query(""
798
- + "MATCH p=shortestPath(\n"
799
- + "(bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
800
- + "WITH p, [n IN nodes(p) WHERE n:Movie] AS x\n"
801
- + "UNWIND x AS m\n"
802
- + "MATCH (m) <-[r:DIRECTED]-(d:Person)\n"
803
- + "RETURN p, collect(r), collect(d)"
804
- )
805
- List<Movie> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
806
- }
807
- ----
808
-
809
- The query returns the path plus all relationships and related nodes collected so that the movie entities are fully hydrated.
810
-
811
- The path mapping works for single paths as well for multiple records of paths (which are returned by the `allShortestPath` function.)
812
-
813
- TIP: Named paths can be used efficiently to populate and return more than just a root node, see <<custom-query.paths>>.
814
-
815
847
[[faq.spring-boot.sdn]]
816
848
== Do I need Spring Boot to use Spring Data Neo4j?
817
849
0 commit comments