Skip to content

Commit 08d7c20

Browse files
committed
GH-2529 - More stable entity detection with dynamic labels.
Closes #2529
1 parent 0e055f9 commit 08d7c20

File tree

3 files changed

+72
-6
lines changed

3 files changed

+72
-6
lines changed

src/main/java/org/springframework/data/neo4j/core/mapping/NodeDescriptionStore.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Map;
2525
import java.util.Set;
2626
import java.util.function.BiFunction;
27+
import java.util.function.Function;
2728

2829
import org.springframework.data.mapping.context.AbstractMappingContext;
2930
import org.springframework.lang.Nullable;
@@ -122,10 +123,10 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
122123
List<String> mostMatchingStaticLabels = null;
123124

124125
// Remove is faster than "stream, filter, count".
125-
BiFunction<NodeDescription<?>, List<String>, Integer> unmatchedLabelsCount =
126-
(nodeDescription, staticLabels) -> {
127-
Set<String> staticLabelsClone = new HashSet<>(staticLabels);
128-
labels.forEach(staticLabelsClone::remove);
126+
Function<NodeDescription<?>, Integer> unmatchedLabelsCount =
127+
(nodeDescription) -> {
128+
Set<String> staticLabelsClone = new HashSet<>(labels);
129+
nodeDescription.getStaticLabels().forEach(staticLabelsClone::remove);
129130
return staticLabelsClone.size();
130131
};
131132

@@ -138,14 +139,16 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
138139
return new NodeDescriptionAndLabels(nd, surplusLabels);
139140
}
140141

141-
unmatchedLabelsCache.put(nd, unmatchedLabelsCount.apply(nd, staticLabels));
142+
unmatchedLabelsCache.put(nd, unmatchedLabelsCount.apply(nd));
142143
if (mostMatchingNodeDescription == null) {
143144
mostMatchingNodeDescription = nd;
144145
mostMatchingStaticLabels = staticLabels;
145146
continue;
146147
}
147148

148-
if (unmatchedLabelsCache.get(nd) < unmatchedLabelsCache.get(mostMatchingNodeDescription)) {
149+
Integer newUnmatchedLabelCount = unmatchedLabelsCache.get(nd);
150+
Integer existingUnmatchedLabelCount = unmatchedLabelsCache.get(mostMatchingNodeDescription);
151+
if (newUnmatchedLabelCount < existingUnmatchedLabelCount) {
149152
mostMatchingNodeDescription = nd;
150153
}
151154
}

src/test/java/org/springframework/data/neo4j/integration/imperative/DynamicLabelsIT.java

+31
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import org.springframework.beans.factory.annotation.Autowired;
4242
import org.springframework.context.annotation.Bean;
4343
import org.springframework.context.annotation.Configuration;
44+
import org.springframework.data.neo4j.repository.Neo4jRepository;
45+
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
4446
import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration;
4547
import org.springframework.data.neo4j.config.Neo4jEntityScanner;
4648
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
@@ -434,6 +436,34 @@ void shouldReadDynamicLabelsInInheritance(@Autowired Neo4jTemplate template) {
434436
}
435437
}
436438

439+
@Nested
440+
class ClassesWithInheritanceAndDynamicLabels extends SpringTestBase {
441+
442+
@Override
443+
Long createTestEntity(Transaction t) {
444+
return null;
445+
}
446+
447+
@Test
448+
void instantiateConcreteEntityType(@Autowired AbstractBaseEntityWithDynamicLabelsRepository repository) {
449+
EntitiesWithDynamicLabels.EntityWithMultilevelInheritanceAndDynamicLabels entity =
450+
new EntitiesWithDynamicLabels.EntityWithMultilevelInheritanceAndDynamicLabels();
451+
entity.labels = Collections.singleton("AdditionalLabel");
452+
entity.name = "Name";
453+
entity.id = "ID1";
454+
455+
repository.save(entity);
456+
457+
EntitiesWithDynamicLabels.EntityWithMultilevelInheritanceAndDynamicLabels loadedEntity =
458+
(EntitiesWithDynamicLabels.EntityWithMultilevelInheritanceAndDynamicLabels) repository.findById("ID1").get();
459+
460+
assertThat(loadedEntity.labels).contains("AdditionalLabel");
461+
}
462+
463+
}
464+
465+
interface AbstractBaseEntityWithDynamicLabelsRepository extends Neo4jRepository<EntitiesWithDynamicLabels.AbstractBaseEntityWithDynamicLabels, String> {}
466+
437467
@ExtendWith(SpringExtension.class)
438468
@ContextConfiguration(classes = SpringTestBase.Config.class)
439469
@DirtiesContext
@@ -486,6 +516,7 @@ protected final List<String> getLabels(Condition idCondition, Object id) {
486516

487517
@Configuration
488518
@EnableTransactionManagement
519+
@EnableNeo4jRepositories(considerNestedRepositories = true)
489520
static class Config extends Neo4jImperativeTestConfiguration {
490521

491522
@Bean

src/test/java/org/springframework/data/neo4j/integration/shared/common/EntitiesWithDynamicLabels.java

+32
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,37 @@ public static class EntityWithCustomIdAndDynamicLabels {
178178
@DynamicLabels public Set<String> myLabels;
179179
}
180180

181+
/**
182+
* Base entity for the multi-level abstraction
183+
*/
184+
@Node
185+
public static abstract class BaseEntityWithoutDynamicLabels {
186+
@Id public String id;
187+
}
188+
189+
/**
190+
* adds the labels
191+
*/
192+
@Node
193+
public static abstract class AbstractBaseEntityWithDynamicLabels extends BaseEntityWithoutDynamicLabels {
194+
@DynamicLabels public Set<String> labels;
195+
}
196+
197+
/**
198+
* This might be the wrong most concrete class to be found
199+
*/
200+
@Node
201+
public static abstract class AbstractEntityWithDynamicLabels extends AbstractBaseEntityWithDynamicLabels {
202+
203+
}
204+
205+
/**
206+
* ...but this is the right one
207+
*/
208+
@Node
209+
public static class EntityWithMultilevelInheritanceAndDynamicLabels extends AbstractEntityWithDynamicLabels {
210+
public String name;
211+
}
212+
181213
private EntitiesWithDynamicLabels() {}
182214
}

0 commit comments

Comments
 (0)