Skip to content

Commit 8464d1d

Browse files
GH-2619 - Correctly count most matching, static labels.
In case another matching node description is found, the value of the `mostMatchingStaticLabels` must be updated. Fixes #2619. # Conflicts: # src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveDynamicLabelsIT.java
1 parent f28a081 commit 8464d1d

File tree

7 files changed

+237
-17
lines changed

7 files changed

+237
-17
lines changed

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

+17-17
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.neo4j.core.mapping;
1717

1818
import java.lang.reflect.Modifier;
19+
import java.util.ArrayList;
1920
import java.util.Collection;
2021
import java.util.Collections;
2122
import java.util.HashMap;
@@ -25,7 +26,6 @@
2526
import java.util.Set;
2627
import java.util.concurrent.ConcurrentHashMap;
2728
import java.util.function.BiFunction;
28-
import java.util.function.Function;
2929

3030
import org.springframework.data.mapping.context.AbstractMappingContext;
3131
import org.springframework.lang.Nullable;
@@ -123,15 +123,12 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
123123
Map<NodeDescription<?>, Integer> unmatchedLabelsCache = new HashMap<>();
124124
List<String> mostMatchingStaticLabels = null;
125125

126-
// Remove is faster than "stream, filter, count".
127-
Function<NodeDescription<?>, Integer> unmatchedLabelsCount =
128-
(nodeDescription) -> {
129-
Set<String> staticLabelsClone = new HashSet<>(labels);
130-
nodeDescription.getStaticLabels().forEach(staticLabelsClone::remove);
131-
return staticLabelsClone.size();
132-
};
133-
134126
for (NodeDescription<?> nd : haystack) {
127+
128+
if (Modifier.isAbstract(nd.getUnderlyingClass().getModifiers())) {
129+
continue;
130+
}
131+
135132
List<String> staticLabels = nd.getStaticLabels();
136133

137134
if (staticLabels.containsAll(labels)) {
@@ -140,17 +137,20 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
140137
return new NodeDescriptionAndLabels(nd, surplusLabels);
141138
}
142139

143-
unmatchedLabelsCache.put(nd, unmatchedLabelsCount.apply(nd));
144-
if (mostMatchingNodeDescription == null) {
145-
mostMatchingNodeDescription = nd;
146-
mostMatchingStaticLabels = staticLabels;
147-
continue;
140+
int unmatchedLabelsCount = 0;
141+
List<String> matchingLabels = new ArrayList<>();
142+
for (String staticLabel : staticLabels) {
143+
if (labels.contains(staticLabel)) {
144+
matchingLabels.add(staticLabel);
145+
} else {
146+
unmatchedLabelsCount++;
147+
}
148148
}
149149

150-
Integer newUnmatchedLabelCount = unmatchedLabelsCache.get(nd);
151-
Integer existingUnmatchedLabelCount = unmatchedLabelsCache.get(mostMatchingNodeDescription);
152-
if (newUnmatchedLabelCount < existingUnmatchedLabelCount) {
150+
unmatchedLabelsCache.put(nd, unmatchedLabelsCount);
151+
if (mostMatchingNodeDescription == null || unmatchedLabelsCount < unmatchedLabelsCache.get(mostMatchingNodeDescription)) {
153152
mostMatchingNodeDescription = nd;
153+
mostMatchingStaticLabels = matchingLabels;
154154
}
155155
}
156156

src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveDynamicLabelsIT.java

+43
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,26 @@
1919

2020
import org.neo4j.cypherdsl.core.Functions;
2121
import org.neo4j.driver.TransactionContext;
22+
import org.junit.jupiter.api.RepeatedTest;
23+
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
24+
import org.springframework.data.neo4j.integration.shared.common.CounterMetric;
25+
import org.springframework.data.neo4j.integration.shared.common.GaugeMetric;
26+
import org.springframework.data.neo4j.integration.shared.common.HistogramMetric;
27+
import org.springframework.data.neo4j.integration.shared.common.Metric;
28+
import org.springframework.data.neo4j.integration.shared.common.SummaryMetric;
2229
import org.springframework.data.neo4j.test.Neo4jReactiveTestConfiguration;
2330

2431
import reactor.adapter.JdkFlowAdapter;
2532
import reactor.core.publisher.Flux;
2633
import reactor.core.publisher.Mono;
2734
import reactor.test.StepVerifier;
2835

36+
import java.util.Arrays;
2937
import java.util.Collections;
38+
import java.util.HashMap;
3039
import java.util.HashSet;
40+
import java.util.List;
41+
import java.util.Map;
3142
import java.util.UUID;
3243
import java.util.concurrent.atomic.AtomicReference;
3344
import java.util.function.Predicate;
@@ -80,6 +91,38 @@ public class ReactiveDynamicLabelsIT {
8091

8192
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
8293

94+
95+
@Nested
96+
class DynamicLabelsAndOrderOfClassesBeingLoaded extends SpringTestBase {
97+
98+
@Override
99+
Long createTestEntity(TransactionContext t) {
100+
return t.run("CREATE (m:Metric:Counter:A:B:C:D {timestamp: datetime()}) RETURN id(m)").single().get(0).asLong();
101+
102+
}
103+
104+
@RepeatedTest(100) // GH-2619
105+
void ownLabelsShouldNotEndUpWithDynamicLabels(@Autowired Neo4jMappingContext mappingContext, @Autowired ReactiveNeo4jTemplate template) {
106+
107+
List<Class<? extends Metric>> metrics = Arrays.asList(GaugeMetric.class, SummaryMetric.class, HistogramMetric.class, CounterMetric.class);
108+
Collections.shuffle(metrics);
109+
for (Class<?> type : metrics) {
110+
assertThat(mappingContext.getPersistentEntity(type)).isNotNull();
111+
}
112+
113+
Map<String, Object> args = new HashMap<>();
114+
args.put("agentIdLabel", "B");
115+
template.findAll("MATCH (m:Metric) WHERE $agentIdLabel in labels(m) RETURN m ORDER BY m.timestamp DESC", args, Metric.class)
116+
.as(StepVerifier::create)
117+
.assertNext(cm -> {
118+
assertThat(cm).isInstanceOf(CounterMetric.class);
119+
assertThat(cm.getId()).isEqualTo(existingEntityId);
120+
assertThat(cm.getDynamicLabels()).containsExactlyInAnyOrder("A", "B", "C", "D");
121+
})
122+
.verifyComplete();
123+
}
124+
}
125+
83126
@Nested
84127
class EntityWithSingleStaticLabelAndGeneratedId extends SpringTestBase {
85128

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
/**
21+
* @author Michael J. Simons
22+
*/
23+
@Node("Counter")
24+
public class CounterMetric extends Metric {
25+
26+
public CounterMetric(String name) {
27+
super(name);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
21+
/**
22+
* @author Michael J. Simons
23+
*/
24+
@Node("Gauge")
25+
public class GaugeMetric extends Metric {
26+
27+
public GaugeMetric(String name) {
28+
super(name);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
/**
21+
* @author Michael J. Simons
22+
*/
23+
@Node("Histogram")
24+
public class HistogramMetric extends Metric {
25+
26+
public HistogramMetric(String name) {
27+
super(name);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import org.springframework.data.neo4j.core.schema.DynamicLabels;
22+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
23+
import org.springframework.data.neo4j.core.schema.Id;
24+
import org.springframework.data.neo4j.core.schema.Node;
25+
26+
/**
27+
* @author Michael J. Simons
28+
*/
29+
@Node("Metric")
30+
public abstract class Metric {
31+
32+
@Id
33+
@GeneratedValue
34+
Long id;
35+
36+
@DynamicLabels
37+
public List<String> dynamicLabels = new ArrayList<>();
38+
39+
public Long getId() {
40+
return id;
41+
}
42+
43+
public List<String> getDynamicLabels() {
44+
return dynamicLabels;
45+
}
46+
47+
private String name;
48+
49+
public Metric(String name) {
50+
this.name = name;
51+
}
52+
53+
public String getName() {
54+
return name;
55+
}
56+
57+
public void setName(String name) {
58+
this.name = name;
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
/**
21+
* @author Michael J. Simons
22+
*/
23+
@Node("Summary")
24+
public class SummaryMetric extends Metric {
25+
26+
public SummaryMetric(String name) {
27+
super(name);
28+
}
29+
}

0 commit comments

Comments
 (0)