From 3fa17272bb6b4df4debb1ded623be5660c6106ff Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 6 Jul 2016 15:42:25 +0200 Subject: [PATCH] DATAMONGO-1459 - Added support for any-match mode in Query-by-example. MongoExampleMapper now $or-concatenates the predicates derived from the example in case the ExampleMatcher expresses any-match binding to be desired. Moved integration tests for Query-by-example to the appropriate package and polished the code a little. Related ticket: DATACMNS-879. --- .../core/convert/MongoExampleMapper.java | 21 +++- .../core/{temp => }/QueryByExampleTests.java | 118 ++++++++++-------- .../convert/MongoExampleMapperUnitTests.java | 22 +++- 3 files changed, 100 insertions(+), 61 deletions(-) rename spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/{temp => }/QueryByExampleTests.java (62%) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java index 184439071..50726ba72 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java @@ -92,7 +92,6 @@ public class MongoExampleMapper { * @param entity must not be {@literal null}. * @return */ - @SuppressWarnings({ "unchecked", "rawtypes" }) public DBObject getMappedExample(Example example, MongoPersistentEntity entity) { Assert.notNull(example, "Example must not be null!"); @@ -108,10 +107,24 @@ public class MongoExampleMapper { applyPropertySpecs("", reference, example.getProbeType(), matcherAccessor); - this.converter.getTypeMapper().writeTypeRestrictions(reference, getTypesToMatch(example)); - - return ObjectUtils.nullSafeEquals(NullHandler.INCLUDE, matcherAccessor.getNullHandler()) ? reference + DBObject flattened = ObjectUtils.nullSafeEquals(NullHandler.INCLUDE, matcherAccessor.getNullHandler()) ? reference : new BasicDBObject(SerializationUtils.flattenMap(reference)); + DBObject result = example.getMatcher().isAllMatching() ? flattened : orConcatenate(flattened); + + this.converter.getTypeMapper().writeTypeRestrictions(result, getTypesToMatch(example)); + + return result; + } + + private static DBObject orConcatenate(DBObject source) { + + List foo = new ArrayList(source.keySet().size()); + + for (String key : source.keySet()) { + foo.add(new BasicDBObject(key, source.get(key))); + } + + return new BasicDBObject("$or", foo); } private Set> getTypesToMatch(Example example) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/temp/QueryByExampleTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java similarity index 62% rename from spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/temp/QueryByExampleTests.java rename to spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java index 57a92e08c..faa45f77f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/temp/QueryByExampleTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java @@ -13,18 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb.core.temp; +package org.springframework.data.mongodb.core; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import lombok.EqualsAndHashCode; +import lombok.ToString; import java.net.UnknownHostException; import java.util.List; -import org.hamcrest.core.Is; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Example; -import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.query.Criteria; @@ -33,18 +37,39 @@ import org.springframework.data.mongodb.core.query.Query; import com.mongodb.MongoClient; /** + * Integration tests for Query-by-example. + * * @author Christoph Strobl * @author Mark Paluch + * @author Oliver Gierke */ public class QueryByExampleTests { - MongoTemplate template; + MongoOperations operations; + Person p1, p2, p3; @Before public void setUp() throws UnknownHostException { - template = new MongoTemplate(new MongoClient(), "query-by-example"); - template.remove(new Query(), Person.class); + operations = new MongoTemplate(new MongoClient(), "query-by-example"); + operations.remove(new Query(), Person.class); + + p1 = new Person(); + p1.firstname = "bran"; + p1.middlename = "a"; + p1.lastname = "stark"; + + p2 = new Person(); + p2.firstname = "jon"; + p2.lastname = "snow"; + + p3 = new Person(); + p3.firstname = "arya"; + p3.lastname = "stark"; + + operations.save(p1); + operations.save(p2); + operations.save(p3); } /** @@ -53,15 +78,14 @@ public class QueryByExampleTests { @Test public void findByExampleShouldWorkForSimpleProperty() { - init(); - Person sample = new Person(); sample.lastname = "stark"; Query query = new Query(new Criteria().alike(Example.of(sample))); + List result = operations.find(query, Person.class); - List result = template.find(query, Person.class); - Assert.assertThat(result.size(), Is.is(2)); + assertThat(result, hasSize(2)); + assertThat(result, hasItems(p1, p3)); } /** @@ -70,16 +94,15 @@ public class QueryByExampleTests { @Test public void findByExampleShouldWorkForMultipleProperties() { - init(); - Person sample = new Person(); sample.lastname = "stark"; sample.firstname = "arya"; Query query = new Query(new Criteria().alike(Example.of(sample))); + List result = operations.find(query, Person.class); - List result = template.find(query, Person.class); - Assert.assertThat(result.size(), Is.is(1)); + assertThat(result, hasSize(1)); + assertThat(result, hasItem(p3)); } /** @@ -88,18 +111,17 @@ public class QueryByExampleTests { @Test public void findByExampleShouldWorkForIdProperty() { - init(); - Person p4 = new Person(); - template.save(p4); + operations.save(p4); Person sample = new Person(); sample.id = p4.id; Query query = new Query(new Criteria().alike(Example.of(sample))); + List result = operations.find(query, Person.class); - List result = template.find(query, Person.class); - Assert.assertThat(result.size(), Is.is(1)); + assertThat(result, hasSize(1)); + assertThat(result, hasItem(p4)); } /** @@ -108,17 +130,14 @@ public class QueryByExampleTests { @Test public void findByExampleShouldReturnEmptyListIfNotMatching() { - init(); - Person sample = new Person(); sample.firstname = "jon"; sample.firstname = "stark"; - Query query = new Query(new Criteria().alike(Example.of(sample))); + List result = operations.find(query, Person.class); - List result = template.find(query, Person.class); - Assert.assertThat(result.size(), Is.is(0)); + assertThat(result, is(empty())); } /** @@ -127,14 +146,13 @@ public class QueryByExampleTests { @Test public void findByExampleShouldReturnEverythingWhenSampleIsEmpty() { - init(); - Person sample = new Person(); Query query = new Query(new Criteria().alike(Example.of(sample))); + List result = operations.find(query, Person.class); - List result = template.find(query, Person.class); - Assert.assertThat(result.size(), Is.is(3)); + assertThat(result, hasSize(3)); + assertThat(result, hasItems(p1, p2, p3)); } /** @@ -143,47 +161,39 @@ public class QueryByExampleTests { @Test public void findByExampleWithCriteria() { - init(); - Person sample = new Person(); sample.lastname = "stark"; Query query = new Query(new Criteria().alike(Example.of(sample)).and("firstname").regex("^ary*")); - List result = template.find(query, Person.class); - Assert.assertThat(result.size(), Is.is(1)); + List result = operations.find(query, Person.class); + assertThat(result.size(), is(1)); } - public void init() { + /** + * @see DATAMONGO-1459 + */ + @Test + public void findsExampleUsingAnyMatch() { - Person p1 = new Person(); - p1.firstname = "bran"; - p1.lastname = "stark"; + Person probe = new Person(); + probe.lastname = "snow"; + probe.middlename = "a"; - Person p2 = new Person(); - p2.firstname = "jon"; - p2.lastname = "snow"; + Query query = Query.query(Criteria.byExample(Example.of(probe, ExampleMatcher.matchingAny()))); + List result = operations.find(query, Person.class); - Person p3 = new Person(); - p3.firstname = "arya"; - p3.lastname = "stark"; - - template.save(p1); - template.save(p2); - template.save(p3); + assertThat(result, hasSize(2)); + assertThat(result, hasItems(p1, p2)); } @Document(collection = "dramatis-personae") + @EqualsAndHashCode + @ToString static class Person { @Id String id; - String firstname; - + String firstname, middlename; @Field("last_name") String lastname; - - @Override - public String toString() { - return "Person [id=" + id + ", firstname=" + firstname + ", lastname=" + lastname + "]"; - } } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java index 7f09c500e..db4b2a4c8 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java @@ -35,6 +35,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers; import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.geo.Point; @@ -332,7 +333,7 @@ public class MongoExampleMapperUnitTests { DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(WithDBRef.class)); com.mongodb.DBRef reference = getTypedValue(dbo, "referenceDocument", com.mongodb.DBRef.class); - assertThat(reference.getId(), Is. is("200")); + assertThat(reference.getId(), Is.is("200")); assertThat(reference.getCollectionName(), is("refDoc")); } @@ -361,8 +362,8 @@ public class MongoExampleMapperUnitTests { DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(WithDBRef.class)); - assertThat(dbo.get("legacyPoint.x"), Is. is(10D)); - assertThat(dbo.get("legacyPoint.y"), Is. is(20D)); + assertThat(dbo.get("legacyPoint.x"), Is.is(10D)); + assertThat(dbo.get("legacyPoint.y"), Is.is(20D)); } /** @@ -482,6 +483,21 @@ public class MongoExampleMapperUnitTests { assertThat(dbo, isBsonObject().containing("anotherStringValue", "calamity")); } + /** + * @see DATAMONGO-1459 + */ + @Test + public void mapsAnyMatchingExampleCorrectly() { + + FlatDocument probe = new FlatDocument(); + probe.stringValue = "firefight"; + probe.customNamedField = "steelheart"; + + Example example = Example.of(probe, ExampleMatcher.matchingAny()); + + assertThat(mapper.getMappedExample(example), isBsonObject().containing("$or").containing("_class")); + } + static class FlatDocument { @Id String id;