0

I have two entities Person and Building. Both are related by two relationship entities "PERSON_HAS_ACCESS"(mutiple persons can access multiple buildings) and "PERSON_OWNS" (one Building can be owned by only one person, but a person can own multiple buildings). I am able to persist more than one relationship (2 to be specific) of each type and can see in the neo4j browser also. However, when I access the Person entity using the session API:

  session.loadAll(klazz, new Filter("uuid", uuid), 1).getAccessRelations.size()

always reurns back 1, not matter how many relationships it has.

I always get only one relationship back.

Here is my domain model. I added equals and hashcode, but issue still happens. Moreover, i tried having only one relationship, but again the issue is same. Its driving me crazy!!. Any help would be appreciated.

@NodeEntity
public class Building {
    @GraphId
    private Long id;

    private String uuid;

    @Relationship(type = "PERSON_OWNS", direction = Relationship.INCOMING)
    private OwnsBuilding ownsRelation;

    @Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.INCOMING)
    private Collection<CanAccess> accessRelations = new HashSet<CanAccess>();

    public Collection<CanAccess> getAccessRelations() {
    return accessRelations;
    }

    public Building setAccessRelations(Collection<CanAccess> accessRelations) {
    this.accessRelations = accessRelations;
    return this;
    }

    public String getUuid() {
    return uuid;
    }

    public Building setUuid(String uuid) {
    this.uuid = uuid;
    return this;
    }

    public OwnsBuilding getOwnsRelation() {
    return ownsRelation;
    }

    public Building setOwnsRelation(OwnsBuilding ownsRelation) {
    this.ownsRelation = ownsRelation;
    return this;
    }

    @Override
    public boolean equals(Object obj) {
    if (obj == null || !(obj instanceof Building)) return false;
    if(id == null) return false;
    return obj == this || this.id.equals(((Building) obj).id);
    }

    @Override
    public int hashCode() {
    return id != null ? id.hashCode() : super.hashCode();
    }
}


@NodeEntity
public class Person {

    private String uuid;

    @GraphId
    private Long id;

    @Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.OUTGOING)
    private Collection<CanAccess> accessRelations = new HashSet<CanAccess>();

    @Relationship(type = "PERSON_OWNS", direction = Relationship.OUTGOING)
    private Collection<OwnsBuilding> ownsRelations = new HashSet<OwnsBuilding>();

    public Collection<CanAccess> getAccessRelations() {
    return accessRelations;
    }

    public Person setAccessRelations(Collection<CanAccess> accessRelations) {
    this.accessRelations = accessRelations;
    return this;
    }

    public String getUuid() {
    return uuid;
    }

    public Person setUuid(String uuid) {
    this.uuid = uuid;
    return this;
    }

    public Collection<OwnsBuilding> getOwnsRelations() {
    return ownsRelations;
    }

    public Person setOwnsRelations(Collection<OwnsBuilding> ownsRelations) {
    this.ownsRelations = ownsRelations;
    return this;
    }

    @Override
    public boolean equals(Object obj) {
    if (obj == null || !(obj instanceof Person)) return false;
    if (id == null) return false;
    return obj == this || this.id.equals(((Person) obj).id);
    }

    @Override
    public int hashCode() {
    return id != null ? id.hashCode() : super.hashCode();
    }
}


@RelationshipEntity(type = "PERSON_HAS_ACCESS")
public class CanAccess {
    @GraphId
    private Long id;

    @StartNode
    private Person person;

    public CanAccess(){}

    public CanAccess(Person person, Building building) {
    this.person = person;
    this.building = building;
    }

    @EndNode
    private Building building;

    public Person getPerson() {
    return person;
    }

    public CanAccess setPerson(Person person) {
    this.person = person;
    return this;
    }

    public Building getBuilding() {
    return building;
    }

    public CanAccess setBuilding(Building building) {
    this.building = building;
    return this;
    }

    @Override
    public boolean equals(Object obj) {
    if (obj == null || !(obj instanceof CanAccess)) return false;
    if (id == null) return false;
    return obj == this || this.id.equals(((CanAccess) obj).id);
    }

    @Override
    public int hashCode() {
    return id != null ? id.hashCode() : super.hashCode();
    }
}


@RelationshipEntity(type = "PERSON_OWNS")
public class OwnsBuilding {
    @GraphId
    private Long id;

    public OwnsBuilding() {
    }

    public OwnsBuilding(Person owner, Building building) {
    this.owner = owner;
    this.building = building;
    }

    @StartNode

    private Person owner;

    @EndNode
    private Building building;

    public Person getOwner() {
    return owner;
    }

    public OwnsBuilding setOwner(Person owner) {
    this.owner = owner;
    return this;
    }

    public Building getBuilding() {
    return building;
    }

    public OwnsBuilding setBuilding(Building building) {
    this.building = building;
    return this;
    }

    @Override
    public boolean equals(Object obj) {
    if (obj == null || !(obj instanceof OwnsBuilding)) return false;
    if (id == null) return false;
    return obj == this || this.id.equals(((OwnsBuilding) obj).id);
    }

    @Override
    public int hashCode() {
    return id != null ? id.hashCode() : super.hashCode();
    }
}
Gaurav Abbi
  • 645
  • 9
  • 23

1 Answers1

2

Please change your Collection to a Set -

 @Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.INCOMING)
 private Collection<CanAccess> accessRelations = new HashSet<CanAccess>();

to

 @Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.INCOMING)
 private Set<CanAccess> accessRelations = new HashSet<CanAccess>();

The OGM can create arrays or Set,List,Vector.

Also, though unrelated to the issue, please make sure that when your relationship direction is INCOMING, you explicitly annotate any properties/accessors/mutators. In your case:

@Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.INCOMING)
private Set<CanAccess> accessRelations = new HashSet<CanAccess>();

@Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.INCOMING)
public Set<CanAccess> getAccessRelations() {...}

@Relationship(type = "PERSON_HAS_ACCESS", direction = Relationship.INCOMING)
public Building setAccessRelations(Set<CanAccess> accessRelations) {...}

Another is to make sure your object model is navigable especially in the case of relationship entities. When you create the relationship entity, remember to set it on participating nodes, e.g

public CanAccess(Person person, Building building) {
    this.person = person;
    this.building = building;
    this.person.getAccessRelations().add(this);
    this.building.getAccessRelations().add(this);
}
Luanne
  • 19,145
  • 1
  • 39
  • 51
  • Thanks Luanne, this seems to be working. I will do some more testing. Could you please explain the reason for changing the declaration to Set. Also does this apply to all the relationships that I can have in my domain model? – Gaurav Abbi Sep 24 '15 at 10:09
  • It is because the OGM will only create arrays or Sets or Lists or Vectors of relationships. Yes, applies to any collection of relationships in your domain model – Luanne Sep 24 '15 at 10:10
  • one more doubt i have, does it make sense to have equals and hashcode methods to be overridden for the relationship entities? – Gaurav Abbi Sep 24 '15 at 10:24
  • The OGM used by SDN does not rely on these, so you should implement whatever makes sense for your application. Each relationship entity is a different relationship in the underlying graph (with properties). If you have no properties on it, then you don't need a relationship entity, just a plain @Relationship – Luanne Sep 24 '15 at 10:30