简体   繁体   中英

Prevent Multiple Relationships between two nodes in Neo4J

I am building an application using Spring-Data-Neo4J (4.0.0.RELEASE). The application has two entities, a Person entity, and a Group entity. There is also a relationship entity, Member_Of, which says that a person is a member of a group. The entities look like below

@NodeEntity(label="Person")
public class Person {
   protected String uuid;
   protected String fullName;
   protected String email;

   @Relationship(type = RelationshipNames.MEMBER_OF, direction = Relationship.OUTGOING)
   protected List<GroupMembership> groupMemberships = new ArrayList<GroupMembership>() ;
}

@NodeEntity(label="Group")
public class Group implements Serializable{
   protected static final long serialVersionUID = 1L;

   @GraphId
   protected Long id;
   protected String uuid;
   protected String name;

   @Relationship(type = RelationshipNames.MEMBER_OF, direction = Relationship.INCOMING)
   protected List<GroupMembership> groupMemberships = new ArrayList<GroupMembership>() ;
}

@RelationshipEntity(type = RelationshipNames.MEMBER_OF)
public class GroupMembership implements Serializable{
   private static final long serialVersionUID = 1L;

   @GraphId Long id;
   @Property String uuid; 
   @StartNode Person member;
   @EndNode Group group;
   @DateLong
   Date date;
}

There is a method which adds members to a group. The method is annotated with @Transactional. The sample code is given below.

@Override
@Transactional(propagation=Propagation.REQUIRED)
public ResponseEntity<GroupVO> addMembersToGroup(String groupUuid, String personUuid, List<String> personsToAdd){
   Group group = groupRepository.findGroupByUuid(groupUuid);
   List<Person> personsToAdd = IterableUtils.toList(personRepository.findPersonsByUuid(personUuids));

    personsToAdd.forEach(personToAdd -> {
        GroupMembership existingGroupMembership = getActiveMembership(group.getUuid(), personToAdd.getUuid());
        if(existingGroupMembership==null){
            GroupMembership newGroupMembership = new GroupMembership(personToAdd, group, GroupRoleNames.MEMBER, member.getUuid(), new Date());
            personToAdd.getGroupMemberships().add(newGroupMembership);
            group.getGroupMemberships().add(newGroupMembership);
   }
   groupRepository.save(group);
}

What it tries to do is that, it searches for a existing relationship between the personToAdd and the group. If it returns null, that is, no relationship exists, it adds it to the group.

The problem is sometimes the same person is added multiple times to the same group. This is happenning when two people are running the application and both of them tries to add the same person to the same group.

How do I prevent this from happenning? I need to have a single relationship between a person and the group and not multiple ones.

There will be only one relationship created between two given entities provided all properties on the relationship entity are equal. You have a timestamp which is the culprit here- SDN realises that the two relationships differ because they have different values for a property and goes ahead and creates the second one.

At the moment, SDN does not have configuration to allow you to specify a merge vs create for relationship entities.

You'll probably have to manage some synchronization at the application level.

I had the same issue, and was able to "solve" it by using a custom cypher query. Given that you have added successfully Person and Group entity you can run the following query:

 @Query("MATCH (group:Group) " +
        "MATCH (person:Person) " +
        "WHERE person.uuid={0} AND group.uuid={1} "+
        "MERGE (person)-[r:MEMBER_OF]->(group) " +
        "SET r.uuid ={2} , r.date={3} " +
        "RETURN r")
GroupMembership isMemberOf(String personUuid,String groupUuid, String uuid, Date date);

by calling it like this:

personRepository.isMemberOf(personUuid,groupUuid,uuid,date);

However , don't take it for granted. I haven't done extensive tests to ensure that this approach is thread-safe.

This answer by William Lyon on atomic execution of MERGE may be an extra step that you have to take.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM