1

Good morning, I am using SDN-RX which I am adding through spring-data-neo4j-rx-spring-boot-starter version 1.0.0-beta04 and my neo4j database is version 4.0.2 enterprise version. I have a ClassificationDomain node type and have defined a regionClassificationDomain of type ClassificationDomain to which i have attached a "Continent" node of type RegionType, the continent node is the root node of a hierarchical structure of more region types as follows:

"continent"->"country"->"state/province" etc.....

I am now defining actual region nodes which are also hierarchical and need to be attached to their corresponding region types as follows

"africa" -> "zimbabwe" -> "harare" etc

My problem is that when I save a new region node, say I want to attach a child region to "Harare", a lot of other relationships are getting detached on the regionType hierarchy as well as on the region hierarchy.

"Africa" detached from RegionType

What am I missing or doing wrong?

My Repositories are extending ReactiveNeo4jRepository.

The Region POJO


    /**
     * Parent relationship to be modeled by IS_PARENT_REGION_OF relationship
     */
    package registry.domain.geography;

    import framework.domain.BayPrincipal;
    import registry.domain.taxonomy.Classification;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    import org.neo4j.springframework.data.core.schema.Node;
    import org.neo4j.springframework.data.core.schema.Relationship;

    import java.util.ArrayList;
    import java.util.Set;

    /**
     * @author ltmutiwekuziwa
     *
     */
    @Getter
    @Setter
    @ToString
    @Node("Region")
    public class Region extends BayPrincipal {

        private int regionNumber;
        private String commonName;
        private String officialName;
        private char[] iso2Code = new char[2];
        private char[] iso3Code = new char[3];
        private ArrayList<String> boundingCoordinatess;
        private boolean isMetropolis = false;
        /**
         * Parent region maintained as set to allow for changes without losing the historical record. Only one relationship then
         * should have an open endDate for the relationship
         */
        @Relationship(type="PARENT_REGION", direction= Relationship.Direction.OUTGOING)
        private Set<Region> parentRegions;

        @Relationship(type = "REGION_CLASSIFICATION", direction = Relationship.Direction.INCOMING)
        private Set<Classification> regionClassifications;

        public Set<Region> getParentRegions() {
            return parentRegions;
        }

        public void setParentRegions(Set<Region> parentRegions) {
            this.parentRegions = parentRegions;
        }

        public Set<Classification> getRegionClassifications() {
            return regionClassifications;
        }

        public void setRegionClassifications(Set<Classification> regionClassifications) {
            this.regionClassifications = regionClassifications;
        }
    }

My Classification POJO


    package registry.domain.taxonomy;


    import framework.domain.BayModel;
    import lombok.*;
    import org.neo4j.springframework.data.core.schema.Node;
    import org.neo4j.springframework.data.core.schema.Relationship;

    import java.time.LocalDateTime;

    @Getter
    @Setter
    @ToString
    @EqualsAndHashCode
    @Node("Classification")
    public class Classification extends BayModel {
        private String classificationName;
        private String classificationCode;
        private String url;
        private LocalDateTime startDate;
        private LocalDateTime stopDate;

        @Relationship(type = "TAXONOMY_CHILD_CLASSIFICATION", direction=Relationship.Direction.OUTGOING)
        private Classification child;

    }

The Region controller, I have left out the imports.


    @RestController
    @RequestMapping(value="/api/v1/region")
    public class RegionRestApi extends BayRestApi<Region, RegionRepository, RegionService> {

        private final ArrayList<String> noneIds = new ArrayList<String>(Arrays.asList("none","null", "0", ""));

        @Autowired
        ClassificationService classificationService;

        @Autowired
        public RegionRestApi(RegionService service) {
            super(service);
        }

        @PostMapping("/attach/{parent}/{classification}")
        private Mono<Region> attach(@PathVariable("parent") String parentUuid,
                                    @PathVariable("classification")String  classificationUuid,
                                    @RequestBody Region region) {
            if(!this.noneIds.contains(parentUuid)) {
                region.setParentRegions(new HashSet<Region>(
                        Arrays.asList(this.service.getRepository().findById(parentUuid).block())));
            }
            Classification cls = this.classificationService.getRepository()
                    .findById(classificationUuid).block();
            if(cls != null){
                region.setRegionClassifications(new HashSet<Classification>(
                        Arrays.asList(cls)));
            }

            return this.service.getRepository().save(region);
        }
    }

Thanks in advance for your help.

1 Answers1

0

New answer

The region you are providing in the rest controller method seems not to contain the whole graph that should get persisted in the this.service.getRepository().save(region) call. As described in the old answer (and the documentation link), SDN-RX will update the whole graph reachable from the region you want to store. This includes also other regions that are connected via the parent region(s).

One solution would be to solve this adding via a custom Cypher statement (because you just want to add two (?) relationships. The other solution would be to reconstruct your idea to be more Domain Driven Design and you would work from a parent region without the "back references" from a region to its parent.

Old answer (may still help others with a similar problem):

It seems that you are not fetching the related nodes of the entity you want to persist upfront. The moment you persist the data, Spring Data Neo4j RX stores the Java model as it is in the graph. This means that it has to know about all relationships it needs to store for the given entity.

Since it is not clear from your example, I can only guess that your are loading a Continent or a Region only partial with custom Cypher. This leads to a sub-graph representation in your application. For SDN-RX this is the "truth" that needs to get persisted.

More details can be found here https://neo4j.github.io/sdn-rx/current/#save

meistermeier
  • 7,942
  • 2
  • 36
  • 45