Skip to content

Node(ID) already exists with label X and properties y = 'z', a = 'b' on repository save with unicity #2456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
fabienOuedraogo opened this issue Dec 9, 2021 · 10 comments
Assignees
Labels
status: waiting-for-feedback We need additional information before we can continue

Comments

@fabienOuedraogo
Copy link

fabienOuedraogo commented Dec 9, 2021

Hi the Team,

I got the error Node(ID) already exists with label Entity and properties prop1 = v1, prop2 = v2 when try trying to save an entity defined as:

class Entity {
  @Id
  @GeneratedValue
  private Long id;
  
  @RelationShip(type = "LINKED_TO")
  private List<LinkedEntity> linkedTo;

  @DynamicLabels
  private List<String> additionalLabels;
}

class LinkedEntity {
  @Id
  private String prop1;
  private Strint prop2;

   @DynamicLabels
  private List<String> additionalLabels;
}

I created an unicity constraint for LinkedEntity where prop1 and prop2 must be unique (ASSERT (n.prop1, n.prop2))

Now when a LinkedEntity already exists I get a Constraint violation exception when I try to create a new Entity linkedTo the existing LinkedEntity.

It seems Spring data Neo4j try to create the existing LinkedEntity again.

Is it possible to only create not existing nodes and the create the relationship?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 9, 2021
@meistermeier meistermeier self-assigned this Dec 10, 2021
@meistermeier
Copy link
Collaborator

meistermeier commented Dec 10, 2021

Spring Data Neo4j won't create entities already existing in the database.
All save operation are using some kind of "MATCH first (on id), if nothing was found, CREATE"-logic.
I am wondering why the message mentions the Entity class and not the LinkedEntity class in ... with label Entity ...
Is it possible that you do have a saved node of the type Entity with the properties and now persist a LinkedEntity with the additional dynamic label :Entity?
If not, could you please provide a simple reproducer for the problem? I took the example from above and cannot reproduce it with e.g. something like:

Entity entity = new Entity();
LinkedEntity linkedEntity = new LinkedEntity();
linkedEntity.prop1 = "a";
linkedEntity.prop2 = "b";
entity.linkedTo = new ArrayList<>();
entity.linkedTo.add(linkedEntity);
// if we would have the same properties in Entity and would add this label, it could fail
// linkedEntity.additionalLabels.add("Entity");

repository.save(entity);

@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 10, 2021
@fabienOuedraogo
Copy link
Author

@meistermeier thank you for your response.

Here is what I did:

  @Override
  public Mono<Entity> save(String tenant, Entity entity) {
    return entityRepository.save(entity.toBuilder().tenant(tenant).build().withAdditionalLabel());
  }

When a want to create a new Entity linked to an existing LinkedEntity, I set the tenant property and add an additionalLabel which is "BU" concatenated to the tenant. The objectif is to get a new relation as :

(n:`Entity`:`BU15`) - [ r: LINKED_TO ]-> (p:`LinkedEntity`:`BU15` { code: 'TEST', tenant: '15') 

Please note that I have an unicity constraint on LinkedEntity code and tenant as we can get another LinkedEntity like (p:LinkedEntity:BU16 { code: 'TEST', tenant: '16')

2021-12-09 17:39:12.467 DEBUG 32340 --- [4jDriverIO-2-21] org.springframework.data.neo4j.cypher    : Executing:
MATCH (n) WHERE id(n) = $__id__ UNWIND labels(n) AS label WITH label WHERE NOT (label IN $__staticLabels__) RETURN collect(label) AS __nodeLabels__
2021-12-09 17:39:13.184 DEBUG 32340 --- [4jDriverIO-2-21] org.springframework.data.neo4j.cypher    : Executing:
OPTIONAL MATCH (hlp:`Entity`) WHERE id(hlp) = $__id__ WITH hlp WHERE hlp IS NULL CREATE (n:`Entity`) SET n = $__properties__ SET n:`BU15` RETURN id(n) UNION MATCH (n:`Entity`) WHERE id(n) = $__id__ SET n += $__properties__ SET n:`BU15` RETURN id(n)
2021-12-09 17:39:19.746 DEBUG 32340 --- [4jDriverIO-2-21] org.springframework.data.neo4j.cypher    : Executing:
MATCH (n) WHERE n.code = $__id__ UNWIND labels(n) AS label WITH label WHERE NOT (label IN $__staticLabels__) RETURN collect(label) AS __nodeLabels__
2021-12-09 17:40:30.504 DEBUG 32340 --- [4jDriverIO-2-21] org.springframework.data.neo4j.cypher    : Executing:
MERGE (n:`LinkedEntity` {code: $__id__}) SET n += $__properties__ REMOVE n:`BU15`:`BU16` SET n:`BU15` RETURN id(n)
2021-12-09 17:41:03.099 DEBUG 32340 --- [4jDriverIO-2-21] .d.n.c.t.ReactiveNeo4jTransactionManager : Participating transaction failed - marking existing transaction as rollback-only
2021-12-09 17:41:03.100 DEBUG 32340 --- [4jDriverIO-2-21] .d.n.c.t.ReactiveNeo4jTransactionManager : Participating transaction failed - marking existing transaction as rollback-only
2021-12-09 17:41:03.101 DEBUG 32340 --- [4jDriverIO-2-21] .d.n.c.t.ReactiveNeo4jTransactionManager : Participating transaction failed - marking existing transaction as rollback-only
2021-12-09 17:41:03.102 DEBUG 32340 --- [4jDriverIO-2-21] .d.n.c.t.ReactiveNeo4jTransactionManager : Initiating transaction rollback
2021-12-09 17:41:03.126 ERROR 32340 --- [4jDriverIO-2-21] c.a.c.s.e.api.CostingApiException        : UNEXPECTED_ERROR - Node(2348) already exists with label `LinkedEntity` and properties `tenant` = '15', `code` = 'JANDJ'; Error code 'Neo.ClientError.Schema.ConstraintValidationFailed'; nested exception is org.neo4j.driver.exceptions.ClientException: Node(2348) already exists with label `LinkedEntity` and properties `tenant` = '15', `code` = 'JANDJ' - Node(2348) already exists with label `LinkedEntity` and properties `tenant` = '15', `code` = 'JANDJ'; Error code 'Neo.ClientError.Schema.ConstraintValidationFailed'; nested exception is org.neo4j.driver.exceptions.ClientException: Node(2348) already exists with label `LinkedEntity` and properties `tenant` = '15', `code` = 'TEST'

At this line I can't understand why removing all additional labels: MERGE (n:`LinkedEntity` {code: $__id__}) SET n += $__properties__ REMOVE n:`BU15`:`BU16` SET n:`BU15` RETURN id(n)

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 10, 2021
@meistermeier
Copy link
Collaborator

We remove and set the labels because we need to be sure that after the persist operation you are left only with the labels defined on you entity at the moment of calling save.
I am currently testing if there is any possible constellation that would somehow trigger the creation.

@fabienOuedraogo
Copy link
Author

@meistermeier Thank for your response and your investigation.
I'm currently trying to setup a compound key (code, tenant) for my nodes to check if I can avoid constraint violation. I assume it is possible to have compound key like @Id MyCompoundKey id;

@meistermeier
Copy link
Collaborator

FYI: A composite key won't work, yet.
There is already an open issue for this enhancement #2448

@meistermeier
Copy link
Collaborator

I am currently wondering what version of Spring Data Neo4j 6 you are using.
The generated query for the related node:
MATCH (n) WHERE n.code....
should be
MATCH (linkedEntity:LinkedEntity) WHERE linkedEntity.code = $__id__
in the more current versions.
Also the REMOVE part for the labels is not explicit anymore in the queries.
A more current version could help (6.1.latest / 6.2.latest)

@fabienOuedraogo
Copy link
Author

@meistermeier I used version 6.0.6 and then migrated to 6.1.1. The log I posted was for both versions, I think. Do you mean an higher version than 6.1.1 could help?

@meistermeier
Copy link
Collaborator

Yes definitely, we did some enhancement in this area. If possible use 6.1.7 (latest 6.1 as of this writing) or 6.2.0.

@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Dec 27, 2021
@fabienOuedraogo
Copy link
Author

Ok thank you @meistermeier

@meistermeier
Copy link
Collaborator

meistermeier commented Jan 3, 2022

Closing this for now. If the problem persists if using more current versions, feel free to open this issue again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-feedback We need additional information before we can continue
Projects
None yet
Development

No branches or pull requests

3 participants