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
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.neo4j.core.mapping;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -121,15 +122,12 @@ final class NodeDescriptionStore {
|
||||
Map<NodeDescription<?>, Integer> unmatchedLabelsCache = new HashMap<>();
|
||||
List<String> mostMatchingStaticLabels = null;
|
||||
|
||||
// Remove is faster than "stream, filter, count".
|
||||
BiFunction<NodeDescription<?>, List<String>, Integer> unmatchedLabelsCount =
|
||||
(nodeDescription, staticLabels) -> {
|
||||
Set<String> staticLabelsClone = new HashSet<>(staticLabels);
|
||||
labels.forEach(staticLabelsClone::remove);
|
||||
return staticLabelsClone.size();
|
||||
};
|
||||
|
||||
for (NodeDescription<?> nd : haystack) {
|
||||
|
||||
if (Modifier.isAbstract(nd.getUnderlyingClass().getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> staticLabels = nd.getStaticLabels();
|
||||
|
||||
if (staticLabels.containsAll(labels)) {
|
||||
@@ -138,15 +136,20 @@ final class NodeDescriptionStore {
|
||||
return new NodeDescriptionAndLabels(nd, surplusLabels);
|
||||
}
|
||||
|
||||
unmatchedLabelsCache.put(nd, unmatchedLabelsCount.apply(nd, staticLabels));
|
||||
if (mostMatchingNodeDescription == null) {
|
||||
mostMatchingNodeDescription = nd;
|
||||
mostMatchingStaticLabels = staticLabels;
|
||||
continue;
|
||||
int unmatchedLabelsCount = 0;
|
||||
List<String> matchingLabels = new ArrayList<>();
|
||||
for (String staticLabel : staticLabels) {
|
||||
if (labels.contains(staticLabel)) {
|
||||
matchingLabels.add(staticLabel);
|
||||
} else {
|
||||
unmatchedLabelsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (unmatchedLabelsCache.get(nd) < unmatchedLabelsCache.get(mostMatchingNodeDescription)) {
|
||||
unmatchedLabelsCache.put(nd, unmatchedLabelsCount);
|
||||
if (mostMatchingNodeDescription == null || unmatchedLabelsCount < unmatchedLabelsCache.get(mostMatchingNodeDescription)) {
|
||||
mostMatchingNodeDescription = nd;
|
||||
mostMatchingStaticLabels = matchingLabels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,23 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.neo4j.cypherdsl.core.Conditions.not;
|
||||
import static org.neo4j.cypherdsl.core.Predicates.exists;
|
||||
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.integration.shared.common.CounterMetric;
|
||||
import org.springframework.data.neo4j.integration.shared.common.GaugeMetric;
|
||||
import org.springframework.data.neo4j.integration.shared.common.HistogramMetric;
|
||||
import org.springframework.data.neo4j.integration.shared.common.Metric;
|
||||
import org.springframework.data.neo4j.integration.shared.common.SummaryMetric;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
@@ -80,6 +91,38 @@ public class ReactiveDynamicLabelsIT {
|
||||
|
||||
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
|
||||
|
||||
|
||||
@Nested
|
||||
class DynamicLabelsAndOrderOfClassesBeingLoaded extends SpringTestBase {
|
||||
|
||||
@Override
|
||||
Long createTestEntity(Transaction t) {
|
||||
return t.run("CREATE (m:Metric:Counter:A:B:C:D {timestamp: datetime()}) RETURN id(m)").single().get(0).asLong();
|
||||
|
||||
}
|
||||
|
||||
@RepeatedTest(100) // GH-2619
|
||||
void ownLabelsShouldNotEndUpWithDynamicLabels(@Autowired Neo4jMappingContext mappingContext, @Autowired ReactiveNeo4jTemplate template) {
|
||||
|
||||
List<Class<? extends Metric>> metrics = Arrays.asList(GaugeMetric.class, SummaryMetric.class, HistogramMetric.class, CounterMetric.class);
|
||||
Collections.shuffle(metrics);
|
||||
for (Class<?> type : metrics) {
|
||||
assertThat(mappingContext.getPersistentEntity(type)).isNotNull();
|
||||
}
|
||||
|
||||
Map<String, Object> args = new HashMap<>();
|
||||
args.put("agentIdLabel", "B");
|
||||
template.findAll("MATCH (m:Metric) WHERE $agentIdLabel in labels(m) RETURN m ORDER BY m.timestamp DESC", args, Metric.class)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(cm -> {
|
||||
assertThat(cm).isInstanceOf(CounterMetric.class);
|
||||
assertThat(cm.getId()).isEqualTo(existingEntityId);
|
||||
assertThat(cm.getDynamicLabels()).containsExactlyInAnyOrder("A", "B", "C", "D");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class EntityWithSingleStaticLabelAndGeneratedId extends SpringTestBase {
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2011-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.integration.shared.common;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Node("Counter")
|
||||
public class CounterMetric extends Metric {
|
||||
|
||||
public CounterMetric(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2011-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.integration.shared.common;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Node("Gauge")
|
||||
public class GaugeMetric extends Metric {
|
||||
|
||||
public GaugeMetric(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2011-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.integration.shared.common;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Node("Histogram")
|
||||
public class HistogramMetric extends Metric {
|
||||
|
||||
public HistogramMetric(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2011-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.integration.shared.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.DynamicLabels;
|
||||
import org.springframework.data.neo4j.core.schema.GeneratedValue;
|
||||
import org.springframework.data.neo4j.core.schema.Id;
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Node("Metric")
|
||||
public abstract class Metric {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
Long id;
|
||||
|
||||
@DynamicLabels
|
||||
public List<String> dynamicLabels = new ArrayList<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<String> getDynamicLabels() {
|
||||
return dynamicLabels;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
public Metric(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2011-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.integration.shared.common;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Node("Summary")
|
||||
public class SummaryMetric extends Metric {
|
||||
|
||||
public SummaryMetric(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user