Skip to content

Commit 985da3a

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.
1 parent 69395ad commit 985da3a

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
@@ -17,13 +17,24 @@
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
1919

20+
import org.junit.jupiter.api.RepeatedTest;
21+
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
22+
import org.springframework.data.neo4j.integration.shared.common.CounterMetric;
23+
import org.springframework.data.neo4j.integration.shared.common.GaugeMetric;
24+
import org.springframework.data.neo4j.integration.shared.common.HistogramMetric;
25+
import org.springframework.data.neo4j.integration.shared.common.Metric;
26+
import org.springframework.data.neo4j.integration.shared.common.SummaryMetric;
2027
import org.springframework.data.neo4j.test.Neo4jReactiveTestConfiguration;
2128
import reactor.core.publisher.Flux;
2229
import reactor.core.publisher.Mono;
2330
import reactor.test.StepVerifier;
2431

32+
import java.util.Arrays;
2533
import java.util.Collections;
34+
import java.util.HashMap;
2635
import java.util.HashSet;
36+
import java.util.List;
37+
import java.util.Map;
2738
import java.util.UUID;
2839
import java.util.concurrent.atomic.AtomicReference;
2940
import java.util.function.Predicate;
@@ -78,6 +89,38 @@ public class ReactiveDynamicLabelsIT {
7889

7990
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
8091

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

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)