Skip to content

Commit 79902fa

Browse files
GH-2581 - Add examples for nested projections.
Closes #2581.
1 parent f35026c commit 79902fa

File tree

8 files changed

+471
-0
lines changed

8 files changed

+471
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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.issues.projections;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.Optional;
23+
24+
import org.junit.jupiter.api.BeforeAll;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.RepeatedTest;
27+
import org.neo4j.driver.Driver;
28+
import org.neo4j.driver.Session;
29+
import org.springframework.beans.factory.annotation.Autowired;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.ComponentScan;
32+
import org.springframework.context.annotation.Configuration;
33+
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
34+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
35+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
36+
import org.springframework.data.neo4j.integration.issues.projections.model.SourceNodeA;
37+
import org.springframework.data.neo4j.integration.issues.projections.projection.SourceNodeAProjection;
38+
import org.springframework.data.neo4j.integration.issues.projections.repository.SourceNodeARepository;
39+
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
40+
import org.springframework.data.neo4j.test.BookmarkCapture;
41+
import org.springframework.data.neo4j.test.Neo4jExtension;
42+
import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration;
43+
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
44+
import org.springframework.transaction.PlatformTransactionManager;
45+
import org.springframework.transaction.annotation.EnableTransactionManagement;
46+
47+
/**
48+
* @author Michael J. Simons
49+
*/
50+
@Neo4jIntegrationTest
51+
class NestedProjectionsIT {
52+
53+
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
54+
55+
@BeforeAll
56+
static void setupData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
57+
try (Session session = driver.session()) {
58+
session.run("MATCH (n) DETACH DELETE n").consume();
59+
session.run("CREATE (l:SourceNodeA {id: 'L-l1', version: 1})-[:A_TO_CENTRAL]->(e:CentralNode {id: 'E-l1', version: 1})<-[:B_TO_CENTRAL]-(c:SourceNodeB {id: 'C-l1', version: 1}) RETURN id(l)").consume();
60+
bookmarkCapture.seedWith(session.lastBookmark());
61+
}
62+
}
63+
64+
@BeforeEach
65+
void clearChangedProperties(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
66+
try (Session session = driver.session()) {
67+
session.run("MATCH (n:SourceNodeA) SET n.value = null").consume();
68+
session.run("MATCH (n:CentralNode) SET n.name = null").consume();
69+
bookmarkCapture.seedWith(session.lastBookmark());
70+
}
71+
}
72+
73+
@RepeatedTest(20) // GH-2581
74+
void excludedHopMustNotVanish(@Autowired SourceNodeARepository repository) {
75+
76+
Optional<SourceNodeA> optionalSourceNode = repository.findById("L-l1");
77+
assertThat(optionalSourceNode).isPresent();
78+
79+
SourceNodeA toUpdate = optionalSourceNode.get();
80+
toUpdate.setValue("newValue");
81+
toUpdate.getCentralNode().setName("whatever");
82+
SourceNodeAProjection projectedSourceNode = repository.saveWithProjection(toUpdate);
83+
84+
85+
SourceNodeA updatedNode = repository.findById("L-l1").orElseThrow(IllegalStateException::new);
86+
87+
assertThat(updatedNode.getCentralNode()).isNotNull();
88+
assertThat(updatedNode.getCentralNode().getSourceNodeB()).isNotNull();
89+
90+
assertThat(projectedSourceNode.getCentralNode().getName()).isEqualTo("whatever");
91+
assertThat(updatedNode.getCentralNode().getName()).isEqualTo(projectedSourceNode.getCentralNode().getName());
92+
93+
assertThat(projectedSourceNode.getValue()).isEqualTo("newValue");
94+
assertThat(updatedNode.getValue()).isEqualTo(projectedSourceNode.getValue());
95+
}
96+
97+
@Configuration
98+
@EnableTransactionManagement
99+
@EnableNeo4jRepositories(considerNestedRepositories = true)
100+
@ComponentScan
101+
static class Config extends Neo4jImperativeTestConfiguration {
102+
103+
@Bean
104+
public BookmarkCapture bookmarkCapture() {
105+
return new BookmarkCapture();
106+
}
107+
108+
@Override
109+
public PlatformTransactionManager transactionManager(
110+
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
111+
112+
BookmarkCapture bookmarkCapture = bookmarkCapture();
113+
return new Neo4jTransactionManager(driver, databaseNameProvider,
114+
Neo4jBookmarkManager.create(bookmarkCapture));
115+
}
116+
117+
@Override
118+
protected Collection<String> getMappingBasePackages() {
119+
return Collections.singleton(NestedProjectionsIT.class.getPackage().getName());
120+
}
121+
122+
@Bean
123+
public Driver driver() {
124+
125+
return neo4jConnectionSupport.getDriver();
126+
}
127+
128+
@Override
129+
public boolean isCypher5Compatible() {
130+
return neo4jConnectionSupport.isCypher5SyntaxCompatible();
131+
}
132+
}
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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.issues.projections.model;
17+
18+
import java.util.Objects;
19+
20+
import lombok.AllArgsConstructor;
21+
import lombok.Builder;
22+
import lombok.Getter;
23+
import lombok.NoArgsConstructor;
24+
25+
import org.springframework.data.annotation.Version;
26+
import org.springframework.data.neo4j.core.schema.Id;
27+
import org.springframework.data.neo4j.core.schema.Node;
28+
import org.springframework.data.neo4j.core.schema.Property;
29+
import org.springframework.data.neo4j.core.schema.Relationship;
30+
31+
/**
32+
* @author Michael J. Simons
33+
*/
34+
@Getter
35+
@Builder
36+
@NoArgsConstructor
37+
@AllArgsConstructor
38+
@Node
39+
public class CentralNode {
40+
41+
@Id
42+
@Property(name = "id")
43+
private String id;
44+
45+
@Version Long version;
46+
47+
private String name;
48+
49+
@Relationship(value = "B_TO_CENTRAL", direction = Relationship.Direction.INCOMING)
50+
private SourceNodeB sourceNodeB;
51+
52+
@Override public boolean equals(Object o) {
53+
if (this == o) {
54+
return true;
55+
}
56+
if (o == null || getClass() != o.getClass()) {
57+
return false;
58+
}
59+
CentralNode centralNode = (CentralNode) o;
60+
return Objects.equals(id, centralNode.id) && Objects.equals(name, centralNode.name);
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return Objects.hash(id, name);
66+
}
67+
68+
public void setSourceNodeB(SourceNodeB sourceNodeB) {
69+
this.sourceNodeB = sourceNodeB;
70+
}
71+
72+
public void setName(String name) {
73+
this.name = name;
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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.issues.projections.model;
17+
18+
import java.util.Objects;
19+
20+
import lombok.AllArgsConstructor;
21+
import lombok.Builder;
22+
import lombok.Getter;
23+
import lombok.NoArgsConstructor;
24+
25+
import org.springframework.data.annotation.Version;
26+
import org.springframework.data.neo4j.core.schema.Id;
27+
import org.springframework.data.neo4j.core.schema.Node;
28+
import org.springframework.data.neo4j.core.schema.Relationship;
29+
30+
/**
31+
* @author Michael J. Simons
32+
*/
33+
@Getter
34+
@Builder
35+
@NoArgsConstructor
36+
@AllArgsConstructor
37+
@Node
38+
public class SourceNodeA {
39+
40+
@Id
41+
private String id;
42+
43+
@Version Long version;
44+
45+
private String value;
46+
47+
@Relationship("A_TO_CENTRAL")
48+
private CentralNode centralNode;
49+
50+
@Override public boolean equals(Object o) {
51+
if (this == o) {
52+
return true;
53+
}
54+
if (o == null || getClass() != o.getClass()) {
55+
return false;
56+
}
57+
SourceNodeA sourceNodeA = (SourceNodeA) o;
58+
return Objects.equals(id, sourceNodeA.id) && Objects.equals(value, sourceNodeA.value);
59+
}
60+
61+
@Override public int hashCode() {
62+
return Objects.hash(id, value);
63+
}
64+
65+
public void setValue(String value) {
66+
this.value = value;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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.issues.projections.model;
17+
18+
import lombok.AllArgsConstructor;
19+
import lombok.Builder;
20+
import lombok.Getter;
21+
import lombok.NoArgsConstructor;
22+
23+
import java.util.List;
24+
import java.util.Objects;
25+
26+
import org.springframework.data.annotation.Version;
27+
import org.springframework.data.neo4j.core.schema.Id;
28+
import org.springframework.data.neo4j.core.schema.Node;
29+
import org.springframework.data.neo4j.core.schema.Property;
30+
import org.springframework.data.neo4j.core.schema.Relationship;
31+
32+
import com.fasterxml.jackson.annotation.JsonIgnore;
33+
34+
/**
35+
* @author Michael J. Simons
36+
*/
37+
@Getter
38+
@Builder
39+
@NoArgsConstructor
40+
@AllArgsConstructor
41+
@Node
42+
public class SourceNodeB {
43+
44+
@Id
45+
@Property(name = "id")
46+
private String id;
47+
48+
@Version Long version;
49+
50+
private String name;
51+
52+
@JsonIgnore
53+
@Relationship("B_TO_CENTRAL")
54+
private List<CentralNode> centralNodes;
55+
56+
@Override
57+
public boolean equals(Object o) {
58+
if (this == o) {
59+
return true;
60+
}
61+
if (o == null || getClass() != o.getClass()) {
62+
return false;
63+
}
64+
SourceNodeB sourceNodeB = (SourceNodeB) o;
65+
return Objects.equals(id, sourceNodeB.id) && Objects.equals(name, sourceNodeB.name);
66+
}
67+
68+
@Override
69+
public int hashCode() {
70+
return Objects.hash(id, name);
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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.issues.projections.projection;
17+
18+
/**
19+
* @author Michael J. Simons
20+
*/
21+
public interface SourceNodeAProjection {
22+
String getValue();
23+
24+
CentralNode getCentralNode();
25+
26+
/**
27+
* Nested projection.
28+
*/
29+
interface CentralNode {
30+
String getId();
31+
32+
String getName();
33+
}
34+
}

0 commit comments

Comments
 (0)