Check for equality of type before shortcutting projection.
This fixes #2415 by checking for exact equality of the type before shortcutting an unnessary projection.
This commit is contained in:
@@ -13,7 +13,7 @@ and annotations.
|
||||
----
|
||||
MATCH (c:Java)-[:ANNOTATED_BY]->(a)-[:OF_TYPE]->(t:Type {fqn: 'org.apiguardian.api.API'}),
|
||||
(p)-[:DECLARES]->(c)
|
||||
WHERE c:Member AND NOT c:Constructor
|
||||
WHERE c:Member AND NOT (c:Constructor OR c:Method)
|
||||
RETURN p.fqn, c.name
|
||||
----
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ public final class Neo4jTemplate implements
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resultType.isInstance(instance)) {
|
||||
if (resultType.equals(instance.getClass())) {
|
||||
return (R) save(instance);
|
||||
}
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ public final class ReactiveNeo4jTemplate implements
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resultType.isInstance(instance)) {
|
||||
if (resultType.equals(instance.getClass())) {
|
||||
return (Mono<R>) save(instance);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,18 @@ public final class QueryFragmentsAndParameters {
|
||||
return QueryFragmentsAndParameters.forExample(mappingContext, example, pageable, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for creating a query fragment including parameters for a given condition.
|
||||
*
|
||||
* @param entityMetaData The metadata of a given and known entity
|
||||
* @param condition A Cypher-DSL condition
|
||||
* @return Fully populated fragments and parameter
|
||||
*/
|
||||
@API(status = API.Status.EXPERIMENTAL, since = "6.1.7")
|
||||
public static QueryFragmentsAndParameters forCondition(Neo4jPersistentEntity<?> entityMetaData, Condition condition) {
|
||||
return forCondition(entityMetaData, condition, null, null);
|
||||
}
|
||||
|
||||
static QueryFragmentsAndParameters forCondition(Neo4jPersistentEntity<?> entityMetaData,
|
||||
Condition condition,
|
||||
@Nullable Pageable pageable,
|
||||
|
||||
@@ -51,6 +51,9 @@ import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
|
||||
import org.springframework.data.neo4j.core.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
|
||||
import org.springframework.data.neo4j.integration.issues.gh2415.BaseNodeEntity;
|
||||
import org.springframework.data.neo4j.integration.issues.gh2415.NodeEntity;
|
||||
import org.springframework.data.neo4j.integration.issues.gh2415.NodeWithDefinedCredentials;
|
||||
import org.springframework.data.neo4j.integration.shared.common.Person;
|
||||
import org.springframework.data.neo4j.integration.shared.common.PersonWithAllConstructor;
|
||||
import org.springframework.data.neo4j.integration.shared.common.PersonWithAssignedId;
|
||||
@@ -111,6 +114,14 @@ class Neo4jTemplateIT {
|
||||
transaction.run("CREATE (p:Person{firstName: 'Bela', lastName: 'B.'})");
|
||||
transaction.run("CREATE (p:PersonWithAssignedId{id: 'x', firstName: 'John', lastName: 'Doe'})");
|
||||
|
||||
transaction.run(
|
||||
"CREATE (root:NodeEntity:BaseNodeEntity{nodeId: 'root'}) " +
|
||||
"CREATE (company:NodeEntity:BaseNodeEntity{nodeId: 'comp'}) " +
|
||||
"CREATE (cred:Credential{id: 'uuid-1', name: 'Creds'}) " +
|
||||
"CREATE (company)-[:CHILD_OF]->(root) " +
|
||||
"CREATE (root)-[:HAS_CREDENTIAL]->(cred) " +
|
||||
"CREATE (company)-[:WITH_CREDENTIAL]->(cred)");
|
||||
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmark());
|
||||
}
|
||||
@@ -799,6 +810,23 @@ class Neo4jTemplateIT {
|
||||
assertThat(people).extracting(Person::getLastName).containsExactly("Schnitzel");
|
||||
}
|
||||
|
||||
@Test // GH-2415
|
||||
void saveWithProjectionImplementedByEntity() {
|
||||
|
||||
NodeEntity nodeEntity = neo4jTemplate
|
||||
.find(BaseNodeEntity.class)
|
||||
.as(NodeEntity.class)
|
||||
.matching(
|
||||
"MATCH p=(n:BaseNodeEntity)-[r]-(t) WHERE n.nodeId = $nodeId WITH n, collect([x in relationships(p) |x]) AS r, collect(t) AS o RETURN n, r, o",
|
||||
Collections.singletonMap("nodeId", "root")
|
||||
)
|
||||
.one().get();
|
||||
neo4jTemplate.saveAs(nodeEntity, NodeWithDefinedCredentials.class);
|
||||
|
||||
nodeEntity = neo4jTemplate.findById(nodeEntity.getNodeId(), NodeEntity.class).get();
|
||||
assertThat(nodeEntity.getChildren()).hasSize(1);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
static class Config extends AbstractNeo4jConfig {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2011-2021 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.issues.gh2415;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.NonFinal;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.GeneratedValue;
|
||||
import org.springframework.data.neo4j.core.schema.Id;
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
import org.springframework.data.neo4j.core.support.UUIDStringGenerator;
|
||||
|
||||
/**
|
||||
* @author Andreas Berger
|
||||
*/
|
||||
@Node
|
||||
@NonFinal
|
||||
@Data
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@SuperBuilder(toBuilder = true)
|
||||
public class BaseNodeEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(UUIDStringGenerator.class)
|
||||
@EqualsAndHashCode.Include
|
||||
private String nodeId;
|
||||
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " - " + getName() + " (" + getNodeId() + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2011-2021 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.issues.gh2415;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import org.springframework.data.annotation.Immutable;
|
||||
import org.springframework.data.neo4j.core.schema.GeneratedValue;
|
||||
import org.springframework.data.neo4j.core.schema.Id;
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
import org.springframework.data.neo4j.core.support.UUIDStringGenerator;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* @author Andreas Berger
|
||||
*/
|
||||
@Node
|
||||
@Value
|
||||
@With
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@Immutable
|
||||
@Builder(toBuilder = true)
|
||||
public class Credential {
|
||||
|
||||
@JsonIgnore
|
||||
@Id
|
||||
@GeneratedValue(UUIDStringGenerator.class)
|
||||
@EqualsAndHashCode.Include
|
||||
String id;
|
||||
|
||||
String name;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2011-2021 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.issues.gh2415;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
import org.springframework.data.neo4j.core.schema.Relationship;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* @author Andreas Berger
|
||||
*/
|
||||
@Node
|
||||
@Data
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
|
||||
@SuperBuilder(toBuilder = true)
|
||||
public class NodeEntity extends BaseNodeEntity implements NodeWithDefinedCredentials {
|
||||
|
||||
@JsonIgnore
|
||||
@Relationship(type = "CHILD_OF", direction = Relationship.Direction.INCOMING)
|
||||
private Set<BaseNodeEntity> children;
|
||||
|
||||
@Relationship(type = "HAS_CREDENTIAL")
|
||||
private Set<Credential> definedCredentials;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2011-2021 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.issues.gh2415;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Andreas Berger
|
||||
*/
|
||||
public interface NodeWithDefinedCredentials {
|
||||
|
||||
String getNodeId();
|
||||
|
||||
String getName();
|
||||
|
||||
Set<Credential> getDefinedCredentials();
|
||||
}
|
||||
@@ -53,6 +53,9 @@ import org.springframework.data.neo4j.core.ReactiveDatabaseSelectionProvider;
|
||||
import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
|
||||
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager;
|
||||
import org.springframework.data.neo4j.integration.issues.gh2415.BaseNodeEntity;
|
||||
import org.springframework.data.neo4j.integration.issues.gh2415.NodeEntity;
|
||||
import org.springframework.data.neo4j.integration.issues.gh2415.NodeWithDefinedCredentials;
|
||||
import org.springframework.data.neo4j.integration.shared.common.Person;
|
||||
import org.springframework.data.neo4j.integration.shared.common.PersonWithAllConstructor;
|
||||
import org.springframework.data.neo4j.integration.shared.common.PersonWithAssignedId;
|
||||
@@ -116,6 +119,14 @@ class ReactiveNeo4jTemplateIT {
|
||||
transaction.run("CREATE (p:Person{firstName: 'Bela', lastName: 'B.'})");
|
||||
transaction.run("CREATE (p:PersonWithAssignedId{id: 'x', firstName: 'John', lastName: 'Doe'})");
|
||||
|
||||
transaction.run(
|
||||
"CREATE (root:NodeEntity:BaseNodeEntity{nodeId: 'root'}) " +
|
||||
"CREATE (company:NodeEntity:BaseNodeEntity{nodeId: 'comp'}) " +
|
||||
"CREATE (cred:Credential{id: 'uuid-1', name: 'Creds'}) " +
|
||||
"CREATE (company)-[:CHILD_OF]->(root) " +
|
||||
"CREATE (root)-[:HAS_CREDENTIAL]->(cred) " +
|
||||
"CREATE (company)-[:WITH_CREDENTIAL]->(cred)");
|
||||
|
||||
transaction.commit();
|
||||
|
||||
bookmarkCapture.seedWith(session.lastBookmark());
|
||||
@@ -803,6 +814,24 @@ class ReactiveNeo4jTemplateIT {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-2415
|
||||
void saveWithProjectionImplementedByEntity() {
|
||||
|
||||
neo4jTemplate
|
||||
.find(BaseNodeEntity.class)
|
||||
.as(NodeEntity.class)
|
||||
.matching(
|
||||
"MATCH p=(n:BaseNodeEntity)-[r]-(t) WHERE n.nodeId = $nodeId WITH n, collect([x in relationships(p) |x]) AS r, collect(t) AS o RETURN n, r, o",
|
||||
Collections.singletonMap("nodeId", "root")
|
||||
)
|
||||
.one()
|
||||
.flatMap(nodeEntity -> neo4jTemplate.saveAs(nodeEntity, NodeWithDefinedCredentials.class))
|
||||
.flatMap(nodeEntity -> neo4jTemplate.findById(nodeEntity.getNodeId(), NodeEntity.class))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(nodeEntity -> assertThat(nodeEntity.getChildren()).hasSize(1))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
static class Config extends AbstractReactiveNeo4jConfig {
|
||||
|
||||
Reference in New Issue
Block a user