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.
This commit is contained in:
Oliver Gierke
2016-07-06 15:42:25 +02:00
parent 9361fc3c71
commit 3fa17272bb
3 changed files with 100 additions and 61 deletions

View File

@@ -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<DBObject> foo = new ArrayList<DBObject>(source.keySet().size());
for (String key : source.keySet()) {
foo.add(new BasicDBObject(key, source.get(key)));
}
return new BasicDBObject("$or", foo);
}
private Set<Class<?>> getTypesToMatch(Example<?> example) {

View File

@@ -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<Person> result = operations.find(query, Person.class);
List<Person> 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<Person> result = operations.find(query, Person.class);
List<Person> 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<Person> result = operations.find(query, Person.class);
List<Person> 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<Person> result = operations.find(query, Person.class);
List<Person> 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<Person> result = operations.find(query, Person.class);
List<Person> 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<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(1));
List<Person> 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<Person> 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 + "]";
}
}
}

View File

@@ -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.<Object> is("200"));
assertThat(reference.getId(), Is.<Object>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.<Object> is(10D));
assertThat(dbo.get("legacyPoint.y"), Is.<Object> is(20D));
assertThat(dbo.get("legacyPoint.x"), Is.<Object>is(10D));
assertThat(dbo.get("legacyPoint.y"), Is.<Object>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<FlatDocument> example = Example.of(probe, ExampleMatcher.matchingAny());
assertThat(mapper.getMappedExample(example), isBsonObject().containing("$or").containing("_class"));
}
static class FlatDocument {
@Id String id;