DATAGRAPH-992 - extend support for contains keyword.
Spring Data's `contains` keyword gets mapped to a Neo4j `in` clause for collections. Still supports the `contains` keyword for Strings. (cherry picked from commit 32e2479)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd."
|
||||
* 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.
|
||||
@@ -12,11 +12,22 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.derived;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.*;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.BetweenComparisonBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.BooleanComparisonBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.ContainsComparisonBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.DistanceComparisonBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.ExistsFilterBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.FilterBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.IsNullFilterBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.derived.builder.PropertyComparisonBuilder;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
@@ -25,6 +36,7 @@ import org.springframework.data.repository.query.parser.Part;
|
||||
* @author Luanne Misquitta
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
public class CypherFinderQuery implements DerivedQueryDefinition {
|
||||
|
||||
@@ -76,7 +88,10 @@ public class CypherFinderQuery implements DerivedQueryDefinition {
|
||||
return new DistanceComparisonBuilder(part, booleanOperator, entityType);
|
||||
case BETWEEN:
|
||||
return new BetweenComparisonBuilder(part, booleanOperator, entityType);
|
||||
case IS_NULL:
|
||||
case NOT_CONTAINING:
|
||||
case CONTAINING:
|
||||
return resolveMatchingContainsFilterBuilder(part, booleanOperator);
|
||||
case IS_NULL:
|
||||
case IS_NOT_NULL:
|
||||
return new IsNullFilterBuilder(part, booleanOperator, entityType);
|
||||
case EXISTS:
|
||||
@@ -88,4 +103,12 @@ public class CypherFinderQuery implements DerivedQueryDefinition {
|
||||
return new PropertyComparisonBuilder(part, booleanOperator, entityType);
|
||||
}
|
||||
}
|
||||
|
||||
private FilterBuilder resolveMatchingContainsFilterBuilder(Part part, BooleanOperator booleanOperator) {
|
||||
boolean usePropertyComparison = !part.getProperty().getTypeInformation().isCollectionLike();
|
||||
if (usePropertyComparison) {
|
||||
return new PropertyComparisonBuilder(part, booleanOperator, entityType);
|
||||
}
|
||||
return new ContainsComparisonBuilder(part, booleanOperator, entityType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.repository.query.derived.builder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.neo4j.ogm.cypher.function.ContainsAnyComparison;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* Filter for entities having a collection like property (not) containing a given element.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
public class ContainsComparisonBuilder extends FilterBuilder {
|
||||
|
||||
public ContainsComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
final Object containingValue = params.pop();
|
||||
Filter containingFilter = new Filter(propertyName(), ComparisonOperator.IN, containingValue);
|
||||
containingFilter.setOwnerEntityType(entityType);
|
||||
containingFilter.setBooleanOperator(booleanOperator);
|
||||
containingFilter.setNegated(isNegated());
|
||||
containingFilter.setFunction(new ContainsAnyComparison(containingValue));
|
||||
setNestedAttributes(part, containingFilter);
|
||||
|
||||
return Collections.singletonList(containingFilter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,15 @@ package org.springframework.data.neo4j.examples.movies;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.neo4j.ogm.testutil.GraphTestUtils.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -33,14 +41,26 @@ import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.neo4j.ogm.testutil.MultiDriverTestClass;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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.dao.DataAccessException;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.*;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.*;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Actor;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Cinema;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Genre;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Movie;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.Rating;
|
||||
import org.springframework.data.neo4j.examples.movies.domain.ReleasedMovie;
|
||||
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.repo.AbstractAnnotatedEntityRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.AbstractEntityRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.ActorRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.CinemaRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.RatingRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.TempMovieRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.repo.UserRepository;
|
||||
import org.springframework.data.neo4j.examples.movies.service.UserService;
|
||||
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
|
||||
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
|
||||
@@ -59,11 +79,15 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
@ContextConfiguration(classes = {MoviesIntegrationTests.MoviesContext.class})
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class MoviesIntegrationTests extends MultiDriverTestClass {
|
||||
|
||||
private static final String KNOWN_MAIL_ADDRESS_1 = "a@example.org";
|
||||
private static final String KNOWN_MAIL_ADDRESS_2 = "b@example.org";
|
||||
private static final String UNKNOWN_MAIL_ADDRESS = "c@example.org";
|
||||
private final Logger logger = LoggerFactory.getLogger(MoviesIntegrationTests.class);
|
||||
|
||||
@Autowired
|
||||
@@ -650,6 +674,62 @@ public class MoviesIntegrationTests extends MultiDriverTestClass {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-992
|
||||
*/
|
||||
@Test
|
||||
public void findUserByContainingEmailAddresses() {
|
||||
createUserForContainsTest();
|
||||
|
||||
User foundUser = userRepository.findByEmailAddressesContains(Collections.singletonList(KNOWN_MAIL_ADDRESS_1));
|
||||
assertNotNull(foundUser);
|
||||
|
||||
foundUser = userRepository.findByEmailAddressesContains(Arrays.asList(KNOWN_MAIL_ADDRESS_2, UNKNOWN_MAIL_ADDRESS));
|
||||
assertNotNull(foundUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-992
|
||||
*/
|
||||
@Test
|
||||
public void findNoUserByContainingEmailAddresses() {
|
||||
createUserForContainsTest();
|
||||
|
||||
User foundUser = userRepository.findByEmailAddressesContains(Collections.singletonList(UNKNOWN_MAIL_ADDRESS));
|
||||
assertNull(foundUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-992
|
||||
*/
|
||||
@Test
|
||||
public void findUserByNotContainingEmailAddresses() {
|
||||
createUserForContainsTest();
|
||||
|
||||
List<User> foundUser = userRepository.findByEmailAddressesNotContaining(UNKNOWN_MAIL_ADDRESS);
|
||||
assertNotNull(foundUser.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAGRAPH-992
|
||||
*/
|
||||
@Test
|
||||
public void findNoUserByNotContainingEmailAddresses() {
|
||||
createUserForContainsTest();
|
||||
|
||||
List<User> foundUser = userRepository.findByEmailAddressesNotContaining(KNOWN_MAIL_ADDRESS_1);
|
||||
assertTrue(foundUser.isEmpty());
|
||||
}
|
||||
|
||||
private void createUserForContainsTest() {
|
||||
User user = new User("Somebody");
|
||||
Set<String> emailAddresses = new HashSet<>();
|
||||
emailAddresses.add(KNOWN_MAIL_ADDRESS_1);
|
||||
emailAddresses.add(KNOWN_MAIL_ADDRESS_2);
|
||||
user.setEmailAddresses(emailAddresses);
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
private Calendar createDate(int y, int m, int d, String tz) {
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd."
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import java.util.Set;
|
||||
/**
|
||||
* @author Michal Bachman
|
||||
* @author Luanne Misquitta
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
public class User extends Person{
|
||||
|
||||
@@ -36,7 +37,10 @@ public class User extends Person{
|
||||
@Relationship(type = "RATED")
|
||||
private Set<Rating> ratings = new HashSet<>();
|
||||
|
||||
public User() {
|
||||
private Set<String> emailAddresses;
|
||||
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String name) {
|
||||
@@ -89,4 +93,12 @@ public class User extends Person{
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
public Set<String> getEmailAddresses() {
|
||||
return emailAddresses;
|
||||
}
|
||||
|
||||
public void setEmailAddresses(Set<String> emailAddresses) {
|
||||
this.emailAddresses = emailAddresses;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd."
|
||||
* 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.
|
||||
@@ -34,6 +34,7 @@ import org.springframework.stereotype.Repository;
|
||||
/**
|
||||
* @author Michal Bachman
|
||||
* @author Luanne Misquitta
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
@Repository
|
||||
public interface UserRepository extends PersonRepository<User, Long> {
|
||||
@@ -127,4 +128,8 @@ public interface UserRepository extends PersonRepository<User, Long> {
|
||||
|
||||
@Query("invalid")
|
||||
void invalidQuery();
|
||||
|
||||
User findByEmailAddressesContains(List<String> emails);
|
||||
|
||||
List<User> findByEmailAddressesNotContaining(String email);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user