Skip to content

Commit 1b05891

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 # Conflicts: # src/main/java/org/springframework/data/neo4j/core/mapping/NodeDescriptionStore.java
1 parent d397076 commit 1b05891

File tree

7 files changed

+237
-14
lines changed

7 files changed

+237
-14
lines changed

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

+17-14
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;
@@ -121,15 +122,12 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
121122
Map<NodeDescription<?>, Integer> unmatchedLabelsCache = new HashMap<>();
122123
List<String> mostMatchingStaticLabels = null;
123124

124-
// 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);
129-
return staticLabelsClone.size();
130-
};
131-
132125
for (NodeDescription<?> nd : haystack) {
126+
127+
if (Modifier.isAbstract(nd.getUnderlyingClass().getModifiers())) {
128+
continue;
129+
}
130+
133131
List<String> staticLabels = nd.getStaticLabels();
134132

135133
if (staticLabels.containsAll(labels)) {
@@ -138,15 +136,20 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
138136
return new NodeDescriptionAndLabels(nd, surplusLabels);
139137
}
140138

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

148-
if (unmatchedLabelsCache.get(nd) < unmatchedLabelsCache.get(mostMatchingNodeDescription)) {
149+
unmatchedLabelsCache.put(nd, unmatchedLabelsCount);
150+
if (mostMatchingNodeDescription == null || unmatchedLabelsCount < unmatchedLabelsCache.get(mostMatchingNodeDescription)) {
149151
mostMatchingNodeDescription = nd;
152+
mostMatchingStaticLabels = matchingLabels;
150153
}
151154
}
152155

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

+43
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,23 @@
1919
import static org.neo4j.cypherdsl.core.Conditions.not;
2020
import static org.neo4j.cypherdsl.core.Predicates.exists;
2121

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 reactor.core.publisher.Flux;
2330
import reactor.core.publisher.Mono;
2431
import reactor.test.StepVerifier;
2532

33+
import java.util.Arrays;
2634
import java.util.Collections;
35+
import java.util.HashMap;
2736
import java.util.HashSet;
37+
import java.util.List;
38+
import java.util.Map;
2839
import java.util.UUID;
2940
import java.util.concurrent.atomic.AtomicReference;
3041
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(Transaction 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)