@@ -553,6 +553,126 @@ public interface MyPersonRepository extends Neo4jRepository<Person, Long> {
553
553
Therefore, you must specify an additional count query.
554
554
All other restrictions from the second method apply.
555
555
556
+ [[faq.path-mapping]]
557
+ == Can I map named paths?
558
+
559
+ A series of connected nodes and relationships is called a "path" in Neo4j.
560
+ Cypher allows paths to be named using an identifer, as exemplified by:
561
+
562
+ [source,cypher]
563
+ ----
564
+ p = (a)-[*3..5]->(b)
565
+ ----
566
+
567
+ or as in the infamous Movie graph, that includes the following path (in that case, one of the shortest path between two actors):
568
+
569
+ [[bacon-distance]]
570
+ [source,cypher]
571
+ .The "Bacon" distance
572
+ ----
573
+ MATCH p=shortestPath((bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
574
+ RETURN p
575
+ ----
576
+
577
+ Which looks like this:
578
+
579
+ image::bacon-distance.png[]
580
+
581
+ We find 3 nodes labeled `Person` and 2 nodes labeled `Movie`. Both can be mapped with a custom queury.
582
+ Assume there's a node entity for both `Person` and `Movie` as well as `Actor` taking care of the relationship:
583
+
584
+
585
+ [source,java]
586
+ ."Standard" movie graph domain model
587
+ ----
588
+ @Node
589
+ public final class Person {
590
+
591
+ @Id @GeneratedValue
592
+ private final Long id;
593
+
594
+ private final String name;
595
+
596
+ private Integer born;
597
+
598
+ @Relationship("REVIEWED")
599
+ private List<Movie> reviewed = new ArrayList<>();
600
+ }
601
+
602
+ @RelationshipProperties
603
+ public final class Actor {
604
+
605
+ @Id @GeneratedValue
606
+ private final Long id;
607
+
608
+ @TargetNode
609
+ private final Person person;
610
+
611
+ private final List<String> roles;
612
+ }
613
+
614
+ @Node
615
+ public final class Movie {
616
+
617
+ @Id
618
+ private final String title;
619
+
620
+ @Property("tagline")
621
+ private final String description;
622
+
623
+ @Relationship(value = "ACTED_IN", direction = Direction.INCOMING)
624
+ private final List<Actor> actors;
625
+ }
626
+ ----
627
+
628
+ When using a query as shown in <<bacon-distance>> for a domain class of type `Person` like this
629
+
630
+ [source,java]
631
+ ----
632
+ interface PeopleRepository extends Neo4jRepository<Person, Long> {
633
+ @Query(""
634
+ + "MATCH p=shortestPath((bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
635
+ + "RETURN p"
636
+ )
637
+ List<Person> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
638
+ }
639
+ ----
640
+
641
+ it will retrieve all people from the path and map them.
642
+ If there are relationship types on the path like `REVIEWED` that are also present on the domain, these
643
+ will be filled accordingly from the path.
644
+
645
+ WARNING: Take special care when you use nodes hydrated from a path based query to save data.
646
+ If not all relationships are hydrated, data will be lost.
647
+
648
+ The other way round works as well. The same query can be used with the `Movie` entity.
649
+ It then will only populate movies.
650
+ The following listing shows how todo this as well as how the query can be enriched with additional data
651
+ not found on the path. That data is used to correctly populate the missing relationships (in that case, all the actors)
652
+
653
+ [source,java]
654
+ ----
655
+ interface MovieRepository extends Neo4jRepository<Movie, String> {
656
+
657
+ @Query(""
658
+ + "MATCH p=shortestPath(\n"
659
+ + "(bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
660
+ + "WITH p, [n IN nodes(p) WHERE n:Movie] AS x\n"
661
+ + "UNWIND x AS m\n"
662
+ + "MATCH (m) <-[r:DIRECTED]-(d:Person)\n"
663
+ + "RETURN p, collect(r), collect(d)"
664
+ )
665
+ List<Movie> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
666
+ }
667
+ ----
668
+
669
+ The query returns the path plus all relationships and related nodes collected so that the movie entities are fully hydrated.
670
+
671
+ The path mapping works for single paths as well for multiple records of paths (which are returned by the `allShortestPath` function.)
672
+
673
+ TIP: Named paths can be used efficiently to populate and return more than just a root node, see <<custom-query.paths>>.
674
+
675
+
556
676
[[faq.custom-queries-and-custom-mappings]]
557
677
== Is `@Query` the only way to use custom queries?
558
678
@@ -718,7 +838,7 @@ and one implementation. The implementation would than have had all three abstrac
718
838
All of this applies of course to reactive repositories as well.
719
839
They would work with the `ReactiveNeo4jTemplate` and `ReactiveNeo4jClient` and the reactive session provided by the driver.
720
840
721
- If you have recuring methods for all repositories, you could swap out the default repository implementation.
841
+ If you have recurring methods for all repositories, you could swap out the default repository implementation.
722
842
723
843
[[faq.custom-base-repositories]]
724
844
== How do I use custom Spring Data Neo4j base repositories?
@@ -752,6 +872,37 @@ Those are
752
872
* `org.springframework.data.annotation.LastModifiedBy`
753
873
* `org.springframework.data.annotation.LastModifiedDate`
754
874
875
+ <<auditing>> gives you a general view how to use auditing in the bigger context of Spring Data Commons.
876
+ The following listing presents every configuration option provided by Spring Data Neo4j:
877
+
878
+ [source,java,indent=0,tabsize=4]
879
+ .Enabling and configuring Neo4j auditing
880
+ ----
881
+ include::../../../../src/test/java/org/springframework/data/neo4j/integration/imperative/AuditingIT.java[tags=faq.entities.auditing]
882
+ ----
883
+ <.> Set to true if you want the modification data to be written during creating as well
884
+ <.> Use this attribute to specify the name of the bean that provides the auditor (i.e. a user name)
885
+ <.> Use this attribute to specify the name of a bean that provides the current date. In this case
886
+ a fixed date is used as the above configuration is part of our tests
887
+
888
+ The reactive version is basically the same apart from the fact the auditor aware bean is of type `ReactiveAuditorAware`,
889
+ so that the retrieval of an auditor is part of the reactive flow.
890
+
891
+ In addition to those auditing mechanism you can add as many beans implementing `BeforeBindCallback<T>` or `ReactiveBeforeBindCallback<T>`
892
+ to the context. These beans will be picked up by Spring Data Neo4j and called in order (in case they implement `Ordered` or
893
+ are annotated with `@Order`) just before an entity is persisted.
894
+
895
+ They can modify the entity or return a completely new one.
896
+ The following example adds one callback to the context that changes one attribute before the entity is persisted:
897
+
898
+ [source,java,indent=0,tabsize=4]
899
+ .Modifying entities before save
900
+ ----
901
+ include::../../../../src/test/java/org/springframework/data/neo4j/integration/imperative/CallbacksIT.java[tags=faq.entities.auditing.callbacks]
902
+ ----
903
+
904
+ No additional configuration is required.
905
+
755
906
[[faq.find-by-example]]
756
907
== How do I use "Find by example"?
757
908
@@ -779,125 +930,6 @@ movieExample = Example.of(
779
930
movies = this.movieRepository.findAll(movieExample);
780
931
----
781
932
782
- [[faq.path-mapping]]
783
- == Can I map named paths?
784
-
785
- A series of connected nodes and relationships is called a "path" in Neo4j.
786
- Cypher allows paths to be named using an identifer, as exemplified by:
787
-
788
- [source,cypher]
789
- ----
790
- p = (a)-[*3..5]->(b)
791
- ----
792
-
793
- or as in the infamous Movie graph, that includes the following path (in that case, one of the shortest path between two actors):
794
-
795
- [[bacon-distance]]
796
- [source,cypher]
797
- .The "Bacon" distance
798
- ----
799
- MATCH p=shortestPath((bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
800
- RETURN p
801
- ----
802
-
803
- Which looks like this:
804
-
805
- image::bacon-distance.png[]
806
-
807
- We find 3 nodes labeled `Person` and 2 nodes labeled `Movie`. Both can be mapped with a custom queury.
808
- Assume there's a node entity for both `Person` and `Movie` as well as `Actor` taking care of the relationship:
809
-
810
-
811
- [source,java]
812
- ."Standard" movie graph domain model
813
- ----
814
- @Node
815
- public final class Person {
816
-
817
- @Id @GeneratedValue
818
- private final Long id;
819
-
820
- private final String name;
821
-
822
- private Integer born;
823
-
824
- @Relationship("REVIEWED")
825
- private List<Movie> reviewed = new ArrayList<>();
826
- }
827
-
828
- @RelationshipProperties
829
- public final class Actor {
830
-
831
- @Id @GeneratedValue
832
- private final Long id;
833
-
834
- @TargetNode
835
- private final Person person;
836
-
837
- private final List<String> roles;
838
- }
839
-
840
- @Node
841
- public final class Movie {
842
-
843
- @Id
844
- private final String title;
845
-
846
- @Property("tagline")
847
- private final String description;
848
-
849
- @Relationship(value = "ACTED_IN", direction = Direction.INCOMING)
850
- private final List<Actor> actors;
851
- }
852
- ----
853
-
854
- When using a query as shown in <<bacon-distance>> for a domain class of type `Person` like this
855
-
856
- [source,java]
857
- ----
858
- interface PeopleRepository extends Neo4jRepository<Person, Long> {
859
- @Query(""
860
- + "MATCH p=shortestPath((bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
861
- + "RETURN p"
862
- )
863
- List<Person> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
864
- }
865
- ----
866
-
867
- it will retrieve all people from the path and map them.
868
- If there are relationship types on the path like `REVIEWED` that are also present on the domain, these
869
- will be filled accordingly from the path.
870
-
871
- WARNING: Take special care when you use nodes hydrated from a path based query to save data.
872
- If not all relationships are hydrated, data will be lost.
873
-
874
- The other way round works as well. The same query can be used with the `Movie` entity.
875
- It then will only populate movies.
876
- The following listing shows how todo this as well as how the query can be enriched with additional data
877
- not found on the path. That data is used to correctly populate the missing relationships (in that case, all the actors)
878
-
879
- [source,java]
880
- ----
881
- interface MovieRepository extends Neo4jRepository<Movie, String> {
882
-
883
- @Query(""
884
- + "MATCH p=shortestPath(\n"
885
- + "(bacon:Person {name: $person1})-[*]-(meg:Person {name: $person2}))\n"
886
- + "WITH p, [n IN nodes(p) WHERE n:Movie] AS x\n"
887
- + "UNWIND x AS m\n"
888
- + "MATCH (m) <-[r:DIRECTED]-(d:Person)\n"
889
- + "RETURN p, collect(r), collect(d)"
890
- )
891
- List<Movie> findAllOnShortestPathBetween(@Param("person1") String person1, @Param("person2") String person2);
892
- }
893
- ----
894
-
895
- The query returns the path plus all relationships and related nodes collected so that the movie entities are fully hydrated.
896
-
897
- The path mapping works for single paths as well for multiple records of paths (which are returned by the `allShortestPath` function.)
898
-
899
- TIP: Named paths can be used efficiently to populate and return more than just a root node, see <<custom-query.paths>>.
900
-
901
933
[[faq.spring-boot.sdn]]
902
934
== Do I need Spring Boot to use Spring Data Neo4j?
903
935
0 commit comments