/* * Copyright (c) [2011-2017] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product may include a number of subcomponents with * separate copyright notices and license terms. Your use of the source * code for these subcomponents is subject to the terms and * conditions of the subcomponent's license, as noted in the LICENSE file. * */ /* * Copyright (c) [2011-2017] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product may include a number of subcomponents with * separate copyright notices and license terms. Your use of the source * code for these subcomponents is subject to the terms and * conditions of the subcomponent's license, as noted in the LICENSE file. * */ package org.springframework.data.neo4j.queries; import static org.junit.Assert.*; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; import org.neo4j.ogm.testutil.MultiDriverTestClass; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.neo4j.examples.movies.domain.Cinema; import org.springframework.data.neo4j.examples.movies.domain.Director; 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.EntityWrappingQueryResult; import org.springframework.data.neo4j.examples.movies.repo.CinemaRepository; import org.springframework.data.neo4j.examples.movies.repo.DirectorRepository; import org.springframework.data.neo4j.examples.movies.repo.UserRepository; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; import org.springframework.data.neo4j.transaction.Neo4jTransactionManager; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; /** * @author Luanne Misquitta * @author Mark Angrish */ @ContextConfiguration(classes = {DerivedQueryTests.MoviesContext.class}) @RunWith(SpringJUnit4ClassRunner.class) public class DerivedQueryTests extends MultiDriverTestClass { @Autowired PlatformTransactionManager platformTransactionManager; @Autowired private Session session; @Autowired private UserRepository userRepository; @Autowired private CinemaRepository cinemaRepository; @Autowired private DirectorRepository directorRepository; private TransactionTemplate transactionTemplate; @Before public void clearDatabase() { transactionTemplate = new TransactionTemplate(platformTransactionManager); getGraphDatabaseService().execute("MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE r, n"); } private void executeUpdate(String cypher) { getGraphDatabaseService().execute(cypher); } @Test public void shouldFindUsersByName() { executeUpdate("CREATE (m:User {name:'Michal'})<-[:FRIEND_OF]-(a:User {name:'Adam'})"); Collection<User> users = userRepository.findByName("Michal"); Iterator<User> iterator = users.iterator(); assertTrue(iterator.hasNext()); User user = iterator.next(); assertEquals("Michal", user.getName()); assertEquals(1, user.getFriends().size()); assertEquals("Adam", user.getFriends().iterator().next().getName()); assertFalse(iterator.hasNext()); } @Test public void shouldFindUsersByMiddleName() { executeUpdate("CREATE (m:User {middleName:'Joseph'})<-[:FRIEND_OF]-(a:User {middleName:'Mary', name: 'Joseph'})"); Collection<User> users = userRepository.findByMiddleName("Joseph"); Iterator<User> iterator = users.iterator(); assertTrue(iterator.hasNext()); User user = iterator.next(); assertEquals("Joseph", user.getMiddleName()); assertEquals(1, user.getFriends().size()); User friend = user.getFriends().iterator().next(); assertEquals("Mary", friend.getMiddleName()); assertEquals("Joseph", friend.getName()); assertFalse(iterator.hasNext()); } /** * /* * @see DATAGRAPH-628 */ @Test public void shouldFindNodeEntitiesWithLabels() { executeUpdate("CREATE (u:User {name:'Michal'}) CREATE (p:Theatre {name:'Picturehouse', city:'London'}) CREATE (r:Theatre {name:'Ritzy', city:'London'}) CREATE (u)-[:VISITED]->(p)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { Collection<Cinema> cinemas = cinemaRepository.findByName("Picturehouse"); Iterator<Cinema> iterator = cinemas.iterator(); assertTrue(iterator.hasNext()); Cinema cinema = iterator.next(); assertEquals("Picturehouse", cinema.getName()); assertEquals(1, cinema.getVisited().size()); assertEquals("Michal", cinema.getVisited().iterator().next().getName()); assertFalse(iterator.hasNext()); List<Cinema> theatres = cinemaRepository.findByLocation("London"); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("Ritzy"))); } }); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntitiesMultipleAndedProperties() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London'}) CREATE (r:Theatre {name:'Ritzy', city:'London'})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); List<Cinema> theatres = cinemaRepository.findByNameAndLocation("Ritzy", "London"); assertEquals(1, theatres.size()); assertEquals("Michal", theatres.get(0).getVisited().iterator().next().getName()); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesMultipleOredProperties() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London'}) CREATE (r:Theatre {name:'Ritzy', city:'London'})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); List<Cinema> theatres = cinemaRepository.findByNameOrLocation("Ritzy", "London"); assertEquals(2, theatres.size()); } /** * @see DATAGRAPH-629 */ @Test public void shouldReturnNoResultsCorrectly() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London'}) CREATE (r:Theatre {name:'Ritzy', city:'London'})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); Collection<Cinema> theatres = cinemaRepository.findByName("Does not exist"); assertEquals(0, theatres.size()); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithComparisonOperators() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { List<Cinema> theatres = cinemaRepository.findByCapacityGreaterThan(3000); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("Ritzy"))); theatres = cinemaRepository.findByCapacityGreaterThan(6000); assertEquals(1, theatres.size()); assertEquals("Ritzy", theatres.get(0).getName()); theatres = cinemaRepository.findByCapacityLessThan(8000); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("Ritzy"))); theatres = cinemaRepository.findByCapacityLessThan(7000); assertEquals(1, theatres.size()); assertEquals("Picturehouse", theatres.get(0).getName()); } }); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithMultipleComparisonOperatorsAnded() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) CREATE (m:Theatre {name:'Regal', city:'Bombay', capacity: 4500})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { List<Cinema> theatres = cinemaRepository.findByLocationAndCapacityGreaterThan("London", 3000); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("Ritzy"))); theatres = cinemaRepository.findByCapacityLessThanAndLocation(6000, "Bombay"); assertEquals(1, theatres.size()); assertEquals("Regal", theatres.get(0).getName()); } }); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithMultipleComparisonOperatorsOred() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) CREATE (m:Theatre {name:'Regal', city:'Bombay', capacity: 9000})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { List<Cinema> theatres = cinemaRepository.findByLocationOrCapacityLessThan("London", 100); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("Ritzy"))); theatres = cinemaRepository.findByCapacityGreaterThanOrLocation(8000, "Paris"); assertEquals(1, theatres.size()); assertEquals("Regal", theatres.get(0).getName()); } }); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithNestedProperty() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r)"); List<Cinema> theatres = cinemaRepository.findByVisitedName("Michal"); assertEquals(1, theatres.size()); assertTrue(theatres.contains(new Cinema("Ritzy"))); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithBaseAndNestedProperty() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) CREATE (m:Theatre {name:'Regal', city:'Bombay', capacity: 5000})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r) CREATE (u)-[:VISITED]->(m)"); List<Cinema> theatres = cinemaRepository.findByLocationAndVisitedName("London", "Michal"); assertEquals(1, theatres.size()); assertTrue(theatres.contains(new Cinema("Ritzy"))); } /** * @see DATAGRAPH-662 * //TODO FIXME */ @Test(expected = UnsupportedOperationException.class) public void shouldFindNodeEntititiesWithBaseOrNestedProperty() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) CREATE (m:Theatre {name:'The Old Vic', city:'London', capacity: 5000})" + " CREATE (u:User {name:'Michal'}) CREATE (u)-[:VISITED]->(r) CREATE (u)-[:VISITED]->(m)"); List<Cinema> theatres = cinemaRepository.findByLocationOrVisitedName("P", "Michal"); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Ritzy"))); //assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("The Old Vic"))); } /** * @see DATAGRAPH-632 */ @Test public void shouldFindNodeEntitiesWithNestedRelationshipEntityProperty() { executeUpdate("CREATE (m1:Movie {title:'Speed'}) CREATE (m2:Movie {title:'The Matrix'}) CREATE (m:Movie {title:'Chocolat'})" + " CREATE (u:User {name:'Michal'}) CREATE (u1:User {name:'Vince'}) " + " CREATE (u)-[:RATED {stars:3}]->(m1) CREATE (u)-[:RATED {stars:4}]->(m2) CREATE (u1)-[:RATED {stars:3}]->(m)"); List<User> users = userRepository.findByRatingsStars(3); assertEquals(2, users.size()); assertTrue(users.contains(new User("Michal"))); assertTrue(users.contains(new User("Vince"))); } /** * @see DATAGRAPH-629 * @see DATAGRAPH-705 */ @Test public void shouldFindNodeEntititiesWithTwoNestedPropertiesAndedAcrossDifferentRelatedNodeEntities() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) " + " CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) " + " CREATE (u:User {name:'Michal'}) " + " CREATE (u)-[:VISITED]->(r) CREATE (u)-[:VISITED]->(p)" + " CREATE (m1:Movie {name:'San Andreas'}) " + " CREATE (m2:Movie {name:'Pitch Perfect 2'})" + " CREATE (p)-[:BLOCKBUSTER]->(m1)" + " CREATE (r)-[:BLOCKBUSTER]->(m2)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { List<Cinema> theatres = cinemaRepository.findByVisitedNameAndBlockbusterOfTheWeekName("Michal", "San Andreas"); assertEquals(1, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); theatres = cinemaRepository.findByVisitedNameAndBlockbusterOfTheWeekName("Michal", "Tomorrowland"); assertEquals(0, theatres.size()); } }); } /** * @see DATAGRAPH-662 */ //FIXME: OR is not supported for nested properties on an entity @Test(expected = UnsupportedOperationException.class) public void shouldFindNodeEntititiesWithTwoNestedPropertiesOred() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) " + " CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) " + " CREATE (u:User {name:'Michal'}) " + " CREATE (u)-[:VISITED]->(r) CREATE (u)-[:VISITED]->(p)" + " CREATE (m1:Movie {title:'San Andreas'}) " + " CREATE (m2:Movie {title:'Pitch Perfect 2'})" + " CREATE (p)-[:BLOCKBUSTER]->(m1)" + " CREATE (r)-[:BLOCKBUSTER]->(m2)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { List<Cinema> theatres = cinemaRepository.findByVisitedNameOrBlockbusterOfTheWeekName("Michal", "San Andreas"); assertEquals(2, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); assertTrue(theatres.contains(new Cinema("Ritzy"))); theatres = cinemaRepository.findByVisitedNameOrBlockbusterOfTheWeekName("Vince", "Tomorrowland"); assertEquals(0, theatres.size()); } }); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithMultipleNestedPropertiesAnded() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) " + " CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) " + " CREATE (u:User {name:'Michal', middleName:'M'}) CREATE (u1:User {name:'Vince', middleName:'M'}) " + " CREATE (u)-[:VISITED]->(p) CREATE (u1)-[:VISITED]->(r)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { List<Cinema> theatres = cinemaRepository.findByVisitedNameAndVisitedMiddleName("Michal", "M"); assertEquals(1, theatres.size()); assertTrue(theatres.contains(new Cinema("Picturehouse"))); theatres = cinemaRepository.findByVisitedNameAndVisitedMiddleName("Vince", "V"); assertEquals(0, theatres.size()); } }); } /** * @see DATAGRAPH-629 */ @Test public void shouldFindNodeEntititiesWithRelationshipEntityAndNestedProperty() { executeUpdate("CREATE (m1:Movie {title:'Speed'}) CREATE (m2:Movie {title:'The Matrix'}) CREATE (m:Movie {title:'Chocolat'})" + " CREATE (u:User {name:'Michal'}) CREATE (u1:User {name:'Vince'}) CREATE (g:Genre {name:'Thriller'}) CREATE (u)-[:INTERESTED]->(g) " + " CREATE (u)-[:RATED {stars:3}]->(m1) CREATE (u)-[:RATED {stars:4}]->(m2) CREATE (u1)-[:RATED {stars:3}]->(m)"); List<User> users = userRepository.findByRatingsStarsAndInterestedName(3, "Thriller"); assertEquals(1, users.size()); assertTrue(users.contains(new User("Michal"))); } /** * Relates to DATAGRAPH-601 and, to an extent, DATAGRAPH-761 */ @Test public void shouldFindNodeEntitiesByRegularExpressionMatchingOnPropertiesInDerivedFinderMethods() { executeUpdate("CREATE (:Theatre {name:'Odeon', city:'Preston'}), " + "(:Theatre {name:'Vue', city:'Dumfries'}), " + "(:Theatre {name:'PVR', city:'Mumbai'}) "); // ideally, I'd name this to be "findWhereNameMatches" or "findByNameMatching" List<Cinema> cinemas = cinemaRepository.findByNameMatches("^[Vv].+$"); assertEquals("The wrong number of cinemas was returned", 1, cinemas.size()); assertEquals("An unexpected cinema was retrieved", "Dumfries", cinemas.get(0).getLocation()); } /** * DATAGRAPH-761 */ @Test public void shouldMatchNodeEntitiesUsingCaseInsensitiveLikeWithWildcards() { executeUpdate("CREATE (:Theatre {name:'IMAX', city:'Chesterfield'}), " + "(:Theatre {name:'Odeon', city:'Manchester'}), " + "(:Theatre {name:'IMAX', city:'Edinburgh'}) "); List<Cinema> cinemas = cinemaRepository.findByLocationLike("*chest*"); assertEquals("The wrong number of cinemas was returned", 2, cinemas.size()); } /** * DATAGRAPH-761 */ @Test public void shouldMatchNodeEntitiesUsingLikeWithWildcardsAndSpecialCharacters() { executeUpdate("CREATE (:Theatre {name:'IMAX', city:'Kolkata (Calcutta)'}), " + "(:Theatre {name:'PVR', city:'Bengaluru (Bangalore)'}), " + "(:Theatre {name:'Metro Big Cinema', city:'Mumbai (Bombay)'}) "); List<Cinema> indianCinemas = cinemaRepository.findByLocationLike("*(B*"); assertEquals("The wrong number of cinemas was returned", 2, indianCinemas.size()); } /** * DATAGRAPH-761 */ @Test public void shouldMatchNodeEntitiesUsingNotLikeWithAsteriskWildcards() { executeUpdate("CREATE (:User {name:'Jeff'}), " + "(:User {name:'Jeremy'}), " + "(:User {name:'Alan'})"); List<User> nonMatchingUsers = userRepository.findByNameIsNotLike("Je*"); assertEquals("The wrong number of users was returned", 1, nonMatchingUsers.size()); assertEquals("The wrong user was returned", "Alan", nonMatchingUsers.get(0).getName()); } /** * @see DATAGRAPH-787 */ @Test public void shouldFindDirectorsByName() { executeUpdate("CREATE (m:User {name:'Michal'})<-[:FRIEND_OF]-(a:User {name:'Adam'}) CREATE (d:Director {name:'Vince'})"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { Collection<Director> directors = directorRepository.findByName("Vince"); Iterator<Director> iterator = directors.iterator(); assertTrue(iterator.hasNext()); Director director = iterator.next(); assertEquals("Vince", director.getName()); assertFalse(iterator.hasNext()); directors = directorRepository.findByName("Michal"); iterator = directors.iterator(); assertFalse(iterator.hasNext()); } }); } /** * @see DATAGRAPH-744 */ @Test public void shouldFindUserWithCustomDepth() { executeUpdate("CREATE (p:Theatre {name:'Picturehouse', city:'London', capacity:5000}) " + " CREATE (r:Theatre {name:'Ritzy', city:'London', capacity: 7500}) " + " CREATE (u:User {name:'Michal'}) " + " CREATE (u)-[:VISITED]->(r) CREATE (u)-[:VISITED]->(p)" + " CREATE (m1:Movie {name:'San Andreas'}) " + " CREATE (m2:Movie {name:'Pitch Perfect 2'})" + " CREATE (p)-[:BLOCKBUSTER]->(m1)" + " CREATE (r)-[:BLOCKBUSTER]->(m2)" + " CREATE (u)-[:RATED {stars :3}]->(m1)"); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { Cinema cinema = cinemaRepository.findByName("Picturehouse", 0); assertNotNull(cinema); assertEquals("Picturehouse", cinema.getName()); assertEquals(0, cinema.getVisited().size()); assertEquals(null, cinema.getBlockbusterOfTheWeek()); cinema = cinemaRepository.findByName("Picturehouse", 1); assertNotNull(cinema); assertEquals("Picturehouse", cinema.getName()); assertEquals(1, cinema.getVisited().size()); assertEquals(0, cinema.getVisited().iterator().next().getRatings().size()); assertEquals("San Andreas", cinema.getBlockbusterOfTheWeek().getName()); cinema = cinemaRepository.findByName("Picturehouse", 2); assertNotNull(cinema); assertEquals("Picturehouse", cinema.getName()); assertEquals(1, cinema.getVisited().size()); assertEquals(1, cinema.getVisited().iterator().next().getRatings().size()); assertEquals("San Andreas", cinema.getBlockbusterOfTheWeek().getName()); } }); } /** * @see DATAGRAPH-744 */ @Test public void shouldFindUsersByNameWithStaticDepth() { executeUpdate("CREATE (m:User {name:'Michal', surname:'Bachman'})<-[:FRIEND_OF]-(a:User {name:'Adam', surname:'George'})"); User user = userRepository.findBySurname("Bachman"); assertNotNull(user); assertEquals("Michal", user.getName()); assertEquals("Bachman", user.getSurname()); assertEquals(0, user.getFriends().size()); } /** * @see DATAGRAPH-864 */ @Test public void shouldSupportLiteralMapsInQueryResults() { executeUpdate("CREATE (m1:Movie {title:'Speed'}) CREATE (m2:Movie {title:'The Matrix'}) CREATE (m:Movie {title:'Chocolat'})" + " CREATE (u:User {name:'Michal'}) CREATE (u1:User {name:'Vince'}) " + " CREATE (u)-[:RATED {stars:3}]->(m1) CREATE (u)-[:RATED {stars:4}]->(m2) CREATE (u1)-[:RATED {stars:3}]->(m)"); List<EntityWrappingQueryResult> result = userRepository.findRatingsWithLiteralMap(); assertNotNull(result); assertEquals(2, result.size()); EntityWrappingQueryResult row = result.get(0); if (row.getUser().getName().equals("Vince")) { assertEquals(1, row.getLiteralMap().size()); assertEquals("Chocolat", row.getLiteralMap().iterator().next().get("movietitle")); assertEquals(3L, row.getLiteralMap().iterator().next().get("stars")); } else { assertEquals(2, row.getLiteralMap().size()); } } /** * @throws InterruptedException * @see DATAGRAPH-876 */ @Test public void shouldAllowMultiThreadedDerivedFinderExecution() throws InterruptedException { int numThreads = 3; executeUpdate("CREATE (m:User {name:'Michal', surname:'Bachman'}), (a:User {name:'Adam', surname:'George'}), (l:User {name:'Luanne', surname:'Misquitta'})"); String[] firstNames = new String[]{"Michal", "Adam", "Luanne"}; String[] lastNames = new String[]{"Bachman", "George", "Misquitta"}; ExecutorService executor = Executors.newFixedThreadPool(numThreads); CountDownLatch latch = new CountDownLatch(numThreads); for (int i = 0; i < numThreads; i++) { executor.submit(new DerivedQueryRunner(latch, firstNames[i], lastNames[i])); } latch.await(); // pause until the count reaches 0 // force termination of all threads executor.shutdownNow(); } /** * @see DATAGRAPH-680 */ @Test @Transactional public void shouldPageDerivedFinderQueries() { for (int i = 0; i < 10; i++) { userRepository.save(new User("A", "B")); } userRepository.save(new User("A", "C")); Pageable pageable = new PageRequest(0, 4); Page<User> page = userRepository.findByNameAndSurname("A", "B", pageable); assertEquals(4, page.getNumberOfElements()); assertEquals(10, page.getTotalElements()); assertEquals(0, page.getNumber()); assertTrue(page.isFirst()); assertFalse(page.isLast()); assertTrue(page.hasNext()); page = userRepository.findByNameAndSurname("A", "B", page.nextPageable()); assertEquals(4, page.getNumberOfElements()); assertEquals(10, page.getTotalElements()); assertEquals(1, page.getNumber()); assertFalse(page.isFirst()); assertFalse(page.isLast()); assertTrue(page.hasNext()); page = userRepository.findByNameAndSurname("A", "B", page.nextPageable()); assertEquals(2, page.getNumberOfElements()); assertEquals(10, page.getTotalElements()); assertEquals(2, page.getNumber()); assertFalse(page.isFirst()); assertTrue(page.isLast()); assertFalse(page.hasNext()); page = userRepository.findByNameAndSurname("A", "B", new PageRequest(0, 10)); assertEquals(10, page.getNumberOfElements()); assertEquals(10, page.getTotalElements()); assertEquals(0, page.getNumber()); assertTrue(page.isFirst()); assertTrue(page.isLast()); assertFalse(page.hasNext()); } /** * @see DATAGRAPH-680 */ @Test @Transactional public void shouldSliceDerivedFinderQueries() { for (int i = 0; i < 10; i++) { User user = new User("A"); user.rate(new TempMovie("Temp"), 5, "just okay"); userRepository.save(user); } userRepository.save(new User("A", "C")); Pageable pageable = new PageRequest(0, 4); Slice<User> page = userRepository.findByNameAndRatingsStars("A", 5, pageable); assertEquals(4, page.getNumberOfElements()); assertTrue(page.hasNext()); page = userRepository.findByNameAndRatingsStars("A", 5, page.nextPageable()); assertEquals(4, page.getNumberOfElements()); assertTrue(page.hasNext()); page = userRepository.findByNameAndRatingsStars("A", 5, page.nextPageable()); assertEquals(2, page.getNumberOfElements()); assertFalse(page.hasNext()); page = userRepository.findByNameAndRatingsStars("A", 5, new PageRequest(0, 10)); assertEquals(10, page.getNumberOfElements()); assertFalse(page.hasNext()); } class DerivedQueryRunner implements Runnable { private final CountDownLatch latch; private final String firstName; private final String lastName; public DerivedQueryRunner(CountDownLatch latch, String firstName, String lastName) { this.latch = latch; this.firstName = firstName; this.lastName = lastName; } @Override public void run() { try { User user = userRepository.findBySurname(lastName); assertNotNull(user); assertEquals(firstName, user.getName()); assertEquals(lastName, user.getSurname()); } finally { latch.countDown(); } } } @Configuration @ComponentScan({"org.springframework.data.neo4j.examples.movies.service"}) @EnableNeo4jRepositories("org.springframework.data.neo4j.examples.movies.repo") @EnableTransactionManagement static class MoviesContext { @Bean public PlatformTransactionManager transactionManager() { return new Neo4jTransactionManager(sessionFactory()); } @Bean public SessionFactory sessionFactory() { return new SessionFactory(getBaseConfiguration().build(), "org.springframework.data.neo4j.examples.movies.domain"); } } }