DATAGRAPH-700 - Support mapping of domain entities in @QueryResults.
This commit is contained in:
@@ -14,23 +14,19 @@
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
|
||||
import org.neo4j.ogm.MetaData;
|
||||
import org.neo4j.ogm.annotations.EntityFactory;
|
||||
import org.neo4j.ogm.context.SingleUseEntityMapper;
|
||||
import org.neo4j.ogm.cypher.query.DefaultRowModelRequest;
|
||||
import org.neo4j.ogm.model.RowModel;
|
||||
import org.neo4j.ogm.request.Request;
|
||||
import org.neo4j.ogm.request.RowModelRequest;
|
||||
import org.neo4j.ogm.response.Response;
|
||||
import org.neo4j.ogm.session.GraphCallback;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.transaction.Transaction;
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
import org.neo4j.ogm.MetaData;
|
||||
import org.neo4j.ogm.annotations.EntityFactory;
|
||||
import org.neo4j.ogm.context.SingleUseEntityMapper;
|
||||
import org.neo4j.ogm.request.Request;
|
||||
import org.neo4j.ogm.session.GraphCallback;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.transaction.Transaction;
|
||||
|
||||
/**
|
||||
* Specialisation of {@link GraphRepositoryQuery} that handles mapping to objects annotated with <code>@QueryResult</code>.
|
||||
@@ -61,22 +57,20 @@ public class QueryResultGraphRepositoryQuery extends GraphRepositoryQuery {
|
||||
return resultObjects.isEmpty() ? null : resultObjects.iterator().next();
|
||||
}
|
||||
|
||||
private Collection<Object> mapToConcreteType(final Class<?> targetType, String cypherQuery, Map<String, Object> queryParams) {
|
||||
private Collection<Object> mapToConcreteType(final Class<?> targetType, final String cypherQuery, final Map<String, Object> queryParams) {
|
||||
|
||||
|
||||
final RowModelRequest qry = new DefaultRowModelRequest(cypherQuery, queryParams);
|
||||
|
||||
return this.session.doInTransaction(new GraphCallback<Collection<Object>>() {
|
||||
@Override
|
||||
public Collection<Object> apply(Request requestHandler, Transaction transaction, MetaData metaData) {
|
||||
try (Response<RowModel> response = requestHandler.execute(qry)) {
|
||||
Collection<Object> toReturn = new ArrayList<>();
|
||||
|
||||
SingleUseEntityMapper entityMapper = new SingleUseEntityMapper(metaData, new EntityFactory(metaData));
|
||||
for (RowModel rowModel = response.next(); rowModel != null; rowModel = response.next()) {
|
||||
toReturn.add(entityMapper.map(targetType, response.columns(), rowModel));
|
||||
}
|
||||
return toReturn;
|
||||
Collection<Object> toReturn = new ArrayList<>();
|
||||
SingleUseEntityMapper entityMapper = new SingleUseEntityMapper(metaData, new EntityFactory(metaData));
|
||||
Iterable<Map<String,Object>> results = session.query(cypherQuery, queryParams);
|
||||
for (Map<String,Object> result : results) {
|
||||
toReturn.add(entityMapper.map(targetType, result));
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
|
||||
package org.springframework.data.neo4j.examples.movies.domain.queryresult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.neo4j.annotation.QueryResult;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Rating;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.TempMovie;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.User;
|
||||
|
||||
/**
|
||||
@@ -23,9 +28,28 @@ import org.springframework.data.neo4j.examples.movies.domain.User;
|
||||
public class EntityWrappingQueryResult {
|
||||
|
||||
private User user;
|
||||
private Set<User> friends;
|
||||
private List<Rating> ratings;
|
||||
private TempMovie[] movies;
|
||||
private float avgRating;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public Set<User> getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
public List<Rating> getRatings() {
|
||||
return ratings;
|
||||
}
|
||||
|
||||
public float getAvgRating() {
|
||||
return avgRating;
|
||||
}
|
||||
|
||||
public TempMovie[] getMovies() {
|
||||
return movies;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ package org.springframework.data.neo4j.examples.movies.domain.queryresult;
|
||||
|
||||
import org.neo4j.ogm.annotation.Property;
|
||||
import org.springframework.data.neo4j.annotation.QueryResult;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.User;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.UserRepository;
|
||||
|
||||
/**
|
||||
@@ -31,4 +32,6 @@ public interface UserQueryResultInterface {
|
||||
|
||||
int getAgeOfUser();
|
||||
|
||||
User getUser();
|
||||
|
||||
}
|
||||
|
||||
@@ -69,12 +69,27 @@ public interface UserRepository extends GraphRepository<User> {
|
||||
@Query("MATCH (user:User) WHERE user.name={0} RETURN user.name, user.age AS ageOfUser")
|
||||
UserQueryResultInterface findIndividualUserAsProxiedObject(String name);
|
||||
|
||||
@Query("MATCH (user:User) WHERE user.name={0} RETURN user as user, user.age AS ageOfUser")
|
||||
UserQueryResultInterface findWrappedUserAsProxiedObject(String name);
|
||||
|
||||
@Query("MATCH (user:User) WHERE user.gender={0} RETURN user.name AS UserName, user.gender AS UserGender, user.account as UserAccount, user.deposits as UserDeposits")
|
||||
Iterable<RichUserQueryResult> findUsersByGender(Gender gender);
|
||||
|
||||
@Query("MATCH (user:User) WHERE user.name={0} RETURN user")
|
||||
EntityWrappingQueryResult findWrappedUserByName(String userName);
|
||||
|
||||
@Query("MATCH (user:User)-[:FRIEND_OF]->(f) WHERE user.name={0} RETURN user, collect(f) as friends")
|
||||
EntityWrappingQueryResult findWrappedUserAndFriendsDepth0(String userName);
|
||||
|
||||
@Query("MATCH (user:User)-[r:FRIEND_OF]->(f) WHERE user.name={0} RETURN user, collect(r) as rels, collect(f) as friends")
|
||||
EntityWrappingQueryResult findWrappedUserAndFriendsDepth1(String userName);
|
||||
|
||||
@Query("MATCH (user:User)-[r:RATED]->(m) WHERE user.name={0} RETURN user, collect(r) as ratings, collect(m) as movies, avg(r.stars) as avgRating")
|
||||
EntityWrappingQueryResult findWrappedUserAndRatingsByName(String userName);
|
||||
|
||||
@Query("MATCH (user:User)-[r:RATED]->(m) RETURN user, collect(r) as ratings, collect(m) as movies, avg(r.stars) as avgRating order by user.name desc")
|
||||
List<EntityWrappingQueryResult> findAllUserRatings();
|
||||
|
||||
@Query("MATCH (user:User) RETURN ID(user)")
|
||||
List<Long> getUserNodeIds();
|
||||
|
||||
|
||||
@@ -13,9 +13,24 @@
|
||||
|
||||
package org.springframework.data.neo4j.queries;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
@@ -23,8 +38,14 @@ import org.neo4j.ogm.exception.MappingException;
|
||||
import org.neo4j.ogm.testutil.MultiDriverTestClass;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.neo4j.examples.movies.context.MoviesContext;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Rating;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.TempMovie;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.User;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.queryresult.*;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.queryresult.EntityWrappingQueryResult;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.queryresult.Gender;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.queryresult.RichUserQueryResult;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.queryresult.UserQueryResult;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.queryresult.UserQueryResultInterface;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.CinemaRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.UnmanagedUserPojo;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.UserRepository;
|
||||
@@ -32,12 +53,6 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Vince Bickers
|
||||
* @author Luanne Misquitta
|
||||
@@ -209,21 +224,6 @@ public class QueryIntegrationTest extends MultiDriverTestClass {
|
||||
assertFalse(userIterator.hasNext());
|
||||
}
|
||||
|
||||
/**
|
||||
* I'm not sure whether we should actually support this because you could just return an entity!
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void shouldMapNodeEntitiesIntoQueryResultObjects() {
|
||||
executeUpdate("CREATE (:User {name:'Abraham'}), (:User {name:'Barry'}), (:User {name:'Colin'})");
|
||||
|
||||
EntityWrappingQueryResult wrappedUser = userRepository.findWrappedUserByName("Barry");
|
||||
assertNotNull("The loaded wrapper object shouldn't be null", wrappedUser);
|
||||
assertNotNull("The enclosed user shouldn't be null", wrappedUser.getUser());
|
||||
assertEquals("Barry", wrappedUser.getUser().getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-694
|
||||
*/
|
||||
@@ -298,4 +298,150 @@ public class QueryIntegrationTest extends MultiDriverTestClass {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-700
|
||||
*/
|
||||
@Test
|
||||
public void shouldMapNodeEntitiesIntoQueryResultObjects() {
|
||||
executeUpdate("CREATE (:User {name:'Abraham'}), (:User {name:'Barry'}), (:User {name:'Colin'})");
|
||||
|
||||
EntityWrappingQueryResult wrappedUser = userRepository.findWrappedUserByName("Barry");
|
||||
assertNotNull("The loaded wrapper object shouldn't be null", wrappedUser);
|
||||
assertNotNull("The enclosed user shouldn't be null", wrappedUser.getUser());
|
||||
assertEquals("Barry", wrappedUser.getUser().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-700
|
||||
*/
|
||||
@Test
|
||||
public void shouldMapNodeCollectionsIntoQueryResultObjects() {
|
||||
executeUpdate("CREATE (d:User {name:'Daniela'}), (e:User {name:'Ethan'}), (f:User {name:'Finn'}), (d)-[:FRIEND_OF]->(e), (d)-[:FRIEND_OF]->(f)");
|
||||
|
||||
EntityWrappingQueryResult result = userRepository.findWrappedUserAndFriendsDepth1("Daniela");
|
||||
assertNotNull("The result shouldn't be null", result);
|
||||
assertNotNull("The enclosed user shouldn't be null", result.getUser());
|
||||
assertEquals("Daniela", result.getUser().getName());
|
||||
assertEquals(2, result.getFriends().size());
|
||||
List<String> friends = new ArrayList<>();
|
||||
for (User u : result.getFriends()) {
|
||||
friends.add(u.getName());
|
||||
}
|
||||
assertTrue(friends.contains("Ethan"));
|
||||
assertTrue(friends.contains("Finn"));
|
||||
assertEquals(2, result.getUser().getFriends().size()); //we expect friends to be mapped since the relationships were returned
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-700
|
||||
*/
|
||||
@Test
|
||||
public void shouldMapRECollectionsIntoQueryResultObjects() {
|
||||
executeUpdate("CREATE (g:User {name:'Gary'}), (sw:Movie {name: 'Star Wars: The Force Awakens'}), (hob:Movie {name:'The Hobbit: An Unexpected Journey'}), (g)-[:RATED {stars : 5}]->(sw), (g)-[:RATED {stars: 4}]->(hob) ");
|
||||
|
||||
EntityWrappingQueryResult result = userRepository.findWrappedUserAndRatingsByName("Gary");
|
||||
assertNotNull("The loaded wrapper object shouldn't be null", result);
|
||||
assertNotNull("The enclosed user shouldn't be null", result.getUser());
|
||||
assertEquals("Gary", result.getUser().getName());
|
||||
assertEquals(2, result.getRatings().size());
|
||||
for (Rating rating : result.getRatings()) {
|
||||
if (rating.getStars() == 4) {
|
||||
assertEquals("The Hobbit: An Unexpected Journey", rating.getMovie().getName());
|
||||
}
|
||||
else {
|
||||
assertEquals("Star Wars: The Force Awakens", rating.getMovie().getName());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(4.5f, result.getAvgRating(),0);
|
||||
assertEquals(2, result.getMovies().length);
|
||||
List<String> titles = new ArrayList<>();
|
||||
for (TempMovie movie : result.getMovies()) {
|
||||
titles.add(movie.getName());
|
||||
}
|
||||
assertTrue(titles.contains("The Hobbit: An Unexpected Journey"));
|
||||
assertTrue(titles.contains("Star Wars: The Force Awakens"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-700
|
||||
*/
|
||||
@Test
|
||||
public void shouldMapRelationshipCollectionsWithDepth0IntoQueryResultObjects() {
|
||||
executeUpdate("CREATE (i:User {name:'Ingrid'}), (j:User {name:'Jake'}), (k:User {name:'Kate'}), (i)-[:FRIEND_OF]->(j), (i)-[:FRIEND_OF]->(k)");
|
||||
|
||||
EntityWrappingQueryResult result = userRepository.findWrappedUserAndFriendsDepth0("Ingrid");
|
||||
assertNotNull("The result shouldn't be null", result);
|
||||
assertNotNull("The enclosed user shouldn't be null", result.getUser());
|
||||
assertEquals("Ingrid", result.getUser().getName());
|
||||
assertEquals(2, result.getFriends().size());
|
||||
List<String> friends = new ArrayList<>();
|
||||
for (User u : result.getFriends()) {
|
||||
friends.add(u.getName());
|
||||
}
|
||||
assertTrue(friends.contains("Kate"));
|
||||
assertTrue(friends.contains("Jake"));
|
||||
assertEquals(0, result.getUser().getFriends().size()); //we do not expect friends to be mapped since the relationships were not returned
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-700
|
||||
*/
|
||||
@Test
|
||||
public void shouldReturnMultipleQueryResultObjects() {
|
||||
executeUpdate("CREATE (g:User {name:'Gary'}), (h:User {name:'Harry'}), (sw:Movie {name: 'Star Wars: The Force Awakens'}), (hob:Movie {name:'The Hobbit: An Unexpected Journey'}), (g)-[:RATED {stars : 5}]->(sw), (g)-[:RATED {stars: 4}]->(hob), (h)-[:RATED {stars: 3}]->(hob) ");
|
||||
|
||||
List<EntityWrappingQueryResult> results = userRepository.findAllUserRatings();
|
||||
assertEquals(2, results.size());
|
||||
EntityWrappingQueryResult result = results.get(0);
|
||||
|
||||
assertNotNull("The loaded wrapper object shouldn't be null", result);
|
||||
assertNotNull("The enclosed user shouldn't be null", result.getUser());
|
||||
assertEquals("Harry", result.getUser().getName());
|
||||
assertEquals(1, result.getRatings().size());
|
||||
Rating rating = result.getRatings().get(0);
|
||||
assertEquals("The Hobbit: An Unexpected Journey", rating.getMovie().getName());
|
||||
assertEquals(3, rating.getStars());
|
||||
assertEquals(3f, result.getAvgRating(),0);
|
||||
assertEquals(1, result.getMovies().length);
|
||||
assertEquals("The Hobbit: An Unexpected Journey", result.getMovies()[0].getName());
|
||||
|
||||
result = results.get(1);
|
||||
assertNotNull("The loaded wrapper object shouldn't be null", result);
|
||||
assertNotNull("The enclosed user shouldn't be null", result.getUser());
|
||||
assertEquals("Gary", result.getUser().getName());
|
||||
for (Rating r : result.getRatings()) {
|
||||
if (r.getStars() == 4) {
|
||||
assertEquals("The Hobbit: An Unexpected Journey", r.getMovie().getName());
|
||||
}
|
||||
else {
|
||||
assertEquals("Star Wars: The Force Awakens", r.getMovie().getName());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(4.5f, result.getAvgRating(),0);
|
||||
assertEquals(2, result.getMovies().length);
|
||||
List<String> titles = new ArrayList<>();
|
||||
for (TempMovie movie : result.getMovies()) {
|
||||
titles.add(movie.getName());
|
||||
}
|
||||
assertTrue(titles.contains("The Hobbit: An Unexpected Journey"));
|
||||
assertTrue(titles.contains("Star Wars: The Force Awakens"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-700
|
||||
*/
|
||||
@Test
|
||||
public void shouldMapEntitiesToProxiedQueryResultInterface() {
|
||||
executeUpdate("CREATE (:User {name:'Morne', age:30}), (:User {name:'Abraham', age:31}), (:User {name:'Virat', age:27})");
|
||||
|
||||
UserQueryResultInterface result = userRepository.findWrappedUserAsProxiedObject("Abraham");
|
||||
assertNotNull("The query result shouldn't be null", result);
|
||||
assertNotNull("The mapped user shouldn't be null", result.getUser());
|
||||
assertEquals("The wrong user was returned", "Abraham", result.getUser().getName());
|
||||
assertEquals("The wrong user was returned", 31, result.getAgeOfUser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ public class BusinessService {
|
||||
}
|
||||
|
||||
private void insertNode() {
|
||||
new Neo4jTemplate(session).execute("CREATE node");
|
||||
new Neo4jTemplate(session).execute("CREATE (node {name: 'n'})");
|
||||
}
|
||||
|
||||
public Iterable<Map<String, Object>> loadNodes() {
|
||||
return new Neo4jTemplate(session).query("MATCH n RETURN n", new HashMap<String, Object>());
|
||||
return new Neo4jTemplate(session).query("MATCH n RETURN n.name", new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
public void purge() {
|
||||
|
||||
Reference in New Issue
Block a user