Browse Source

DATAMONGO-1245 - Initial documentation for Query by Example.

Adopt changes from query by example API refactoring.

Related tickets: DATACMNS-810.
Original pull request: #341.
pull/346/merge
Mark Paluch 10 years ago committed by Oliver Gierke
parent
commit
5a78d99af0
  1. 112
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 98
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java
  3. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  4. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
  5. 7
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java
  6. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java
  7. 38
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoRepository.java
  8. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
  9. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
  10. 24
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
  11. 11
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
  12. 23
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java
  13. 67
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
  14. 38
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SerializationUtilsUnitTests.java
  15. 136
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java
  16. 29
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/temp/QueryByExampleTests.java
  17. 65
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
  18. 3
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Contact.java
  19. 54
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepositoryIntegrationTests.java
  20. 1
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
  21. 10
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
  22. 83
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java
  23. 2
      src/main/asciidoc/index.adoc
  24. 4
      src/main/asciidoc/reference/mongodb.adoc
  25. 71
      src/main/asciidoc/reference/query-by-example.adoc

112
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 the original author or authors.
* Copyright 2010-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -51,7 +51,6 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; @@ -51,7 +51,6 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.annotation.Id;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.convert.EntityReader;
import org.springframework.data.domain.Example;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
@ -139,7 +138,6 @@ import com.mongodb.util.JSONParseException; @@ -139,7 +138,6 @@ import com.mongodb.util.JSONParseException;
* @author Chuong Ngo
* @author Christoph Strobl
* @author Doménique Tilleuil
* @author Mark Paluch
*/
@SuppressWarnings("deprecation")
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@ -341,8 +339,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -341,8 +339,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
DBCursor cursor = collection.find(mappedQuery, mappedFields);
QueryCursorPreparer cursorPreparer = new QueryCursorPreparer(query, entityType);
ReadDbObjectCallback<T> readCallback = new ReadDbObjectCallback<T>(mongoConverter, entityType, collection
.getName());
ReadDbObjectCallback<T> readCallback = new ReadDbObjectCallback<T>(mongoConverter, entityType,
collection.getName());
return new CloseableIterableCursorAdapter<T>(cursorPreparer.prepare(cursor), exceptionTranslator, readCallback);
}
@ -375,8 +373,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -375,8 +373,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
*/
@Deprecated
public CommandResult executeCommand(final DBObject command, final int options) {
return executeCommand(command, (options & Bytes.QUERYOPTION_SLAVEOK) != 0 ? ReadPreference.secondaryPreferred()
: ReadPreference.primary());
return executeCommand(command,
(options & Bytes.QUERYOPTION_SLAVEOK) != 0 ? ReadPreference.secondaryPreferred() : ReadPreference.primary());
}
/*
@ -422,7 +420,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -422,7 +420,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param preparer allows for customization of the {@link DBCursor} used when iterating over the result set, (apply
* limits, skips and so on).
*/
protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch, CursorPreparer preparer) {
protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch,
CursorPreparer preparer) {
Assert.notNull(query);
@ -637,15 +636,23 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -637,15 +636,23 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return doFindOne(collectionName, new BasicDBObject(idKey, id), null, entityClass);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperations#findByExample(java.lang.Object)
*/
public <S extends T, T> List<T> findByExample(S sample) {
return findByExample(new Example<S>(sample));
return findByExample(Example.of(sample));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperations#findByExample(org.springframework.data.domain.Example)
*/
@SuppressWarnings("unchecked")
public <S extends T, T> List<T> findByExample(Example<S> sample) {
Assert.notNull(sample, "Sample object must not be null!");
return (List<T>) find(new Query(new Criteria().alike(sample)), sample.getSampleType());
return (List<T>) find(new Query(new Criteria().alike(sample)), sample.getResultType());
}
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass) {
@ -683,8 +690,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -683,8 +690,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
List<Object> results = (List<Object>) commandResult.get("results");
results = results == null ? Collections.emptyList() : results;
DbObjectCallback<GeoResult<T>> callback = new GeoNearResultDbObjectCallback<T>(new ReadDbObjectCallback<T>(
mongoConverter, entityClass, collectionName), near.getMetric());
DbObjectCallback<GeoResult<T>> callback = new GeoNearResultDbObjectCallback<T>(
new ReadDbObjectCallback<T>(mongoConverter, entityClass, collectionName), near.getMetric());
List<GeoResult<T>> result = new ArrayList<GeoResult<T>>(results.size());
int index = 0;
@ -760,7 +767,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -760,7 +767,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public long count(Query query, Class<?> entityClass, String collectionName) {
Assert.hasText(collectionName);
final DBObject dbObject = query == null ? null : queryMapper.getMappedObject(query.getQueryObject(),
final DBObject dbObject = query == null ? null
: queryMapper.getMappedObject(query.getQueryObject(),
entityClass == null ? null : mappingContext.getPersistentEntity(entityClass));
return execute(collectionName, new CollectionCallback<Long>() {
@ -1040,8 +1048,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1040,8 +1048,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName,
entityClass, dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult writeResult = writeConcernToUse == null ? collection.insert(dbDoc) : collection.insert(dbDoc,
writeConcernToUse);
WriteResult writeResult = writeConcernToUse == null ? collection.insert(dbDoc)
: collection.insert(dbDoc, writeConcernToUse);
handleAnyWriteResultErrors(writeResult, dbDoc, MongoActionOperation.INSERT);
return dbDoc.get(ID_FIELD);
}
@ -1062,8 +1070,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1062,8 +1070,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
null, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult writeResult = writeConcernToUse == null ? collection.insert(dbDocList) : collection.insert(
dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
WriteResult writeResult = writeConcernToUse == null ? collection.insert(dbDocList)
: collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
handleAnyWriteResultErrors(writeResult, null, MongoActionOperation.INSERT_LIST);
return null;
}
@ -1093,8 +1101,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1093,8 +1101,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult writeResult = writeConcernToUse == null ? collection.save(dbDoc) : collection.save(dbDoc,
writeConcernToUse);
WriteResult writeResult = writeConcernToUse == null ? collection.save(dbDoc)
: collection.save(dbDoc, writeConcernToUse);
handleAnyWriteResultErrors(writeResult, dbDoc, MongoActionOperation.SAVE);
return dbDoc.get(ID_FIELD);
}
@ -1147,10 +1155,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1147,10 +1155,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
increaseVersionForUpdateIfNecessary(entity, update);
DBObject queryObj = query == null ? new BasicDBObject() : queryMapper.getMappedObject(query.getQueryObject(),
entity);
DBObject updateObj = update == null ? new BasicDBObject() : updateMapper.getMappedObject(
update.getUpdateObject(), entity);
DBObject queryObj = query == null ? new BasicDBObject()
: queryMapper.getMappedObject(query.getQueryObject(), entity);
DBObject updateObj = update == null ? new BasicDBObject()
: updateMapper.getMappedObject(update.getUpdateObject(), entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Calling update using query: {} and update: {} in collection: {}",
@ -1291,9 +1299,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1291,9 +1299,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
Object idValue = persistentEntity.getPropertyAccessor(entity).getProperty(idProperty);
if (idValue == null && !MongoSimpleTypes.AUTOGENERATED_ID_TYPES.contains(idProperty.getType())) {
throw new InvalidDataAccessApiUsageException(String.format(
"Cannot autogenerate id of type %s for entity of type %s!", idProperty.getType().getName(), entity.getClass()
.getName()));
throw new InvalidDataAccessApiUsageException(
String.format("Cannot autogenerate id of type %s for entity of type %s!", idProperty.getType().getName(),
entity.getClass().getName()));
}
}
@ -1332,12 +1340,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1332,12 +1340,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Remove using query: {} in collection: {}.", new Object[] { serializeToJsonSafely(dboq),
collection.getName() });
LOGGER.debug("Remove using query: {} in collection: {}.",
new Object[] { serializeToJsonSafely(dboq), collection.getName() });
}
WriteResult wr = writeConcernToUse == null ? collection.remove(dboq) : collection.remove(dboq,
writeConcernToUse);
WriteResult wr = writeConcernToUse == null ? collection.remove(dboq)
: collection.remove(dboq, writeConcernToUse);
handleAnyWriteResultErrors(wr, dboq, MongoActionOperation.REMOVE);
@ -1353,8 +1361,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1353,8 +1361,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public <T> List<T> findAll(Class<T> entityClass, String collectionName) {
return executeFindMultiInternal(new FindCallback(null), null, new ReadDbObjectCallback<T>(mongoConverter,
entityClass, collectionName), collectionName);
return executeFindMultiInternal(new FindCallback(null), null,
new ReadDbObjectCallback<T>(mongoConverter, entityClass, collectionName), collectionName);
}
public <T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
@ -1370,8 +1378,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1370,8 +1378,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction,
String reduceFunction, Class<T> entityClass) {
return mapReduce(query, inputCollectionName, mapFunction, reduceFunction,
new MapReduceOptions().outputTypeInline(), entityClass);
return mapReduce(query, inputCollectionName, mapFunction, reduceFunction, new MapReduceOptions().outputTypeInline(),
entityClass);
}
public <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction,
@ -1382,8 +1390,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1382,8 +1390,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
DBCollection inputCollection = getCollection(inputCollectionName);
MapReduceCommand command = new MapReduceCommand(inputCollection, mapFunc, reduceFunc,
mapReduceOptions.getOutputCollection(), mapReduceOptions.getOutputType(), query == null
|| query.getQueryObject() == null ? null : queryMapper.getMappedObject(query.getQueryObject(), null));
mapReduceOptions.getOutputCollection(), mapReduceOptions.getOutputType(),
query == null || query.getQueryObject() == null ? null
: queryMapper.getMappedObject(query.getQueryObject(), null));
copyMapReduceOptionsToCommand(query, mapReduceOptions, command);
@ -1719,8 +1728,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1719,8 +1728,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
mappedFields, entityClass, collectionName);
}
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields), new ReadDbObjectCallback<T>(
this.mongoConverter, entityClass, collectionName), collectionName);
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields),
new ReadDbObjectCallback<T>(this.mongoConverter, entityClass, collectionName), collectionName);
}
/**
@ -1734,8 +1743,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1734,8 +1743,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @return the List of converted objects.
*/
protected <T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<T> entityClass) {
return doFind(collectionName, query, fields, entityClass, null, new ReadDbObjectCallback<T>(this.mongoConverter,
entityClass, collectionName));
return doFind(collectionName, query, fields, entityClass, null,
new ReadDbObjectCallback<T>(this.mongoConverter, entityClass, collectionName));
}
/**
@ -1753,8 +1762,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1753,8 +1762,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
*/
protected <T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<T> entityClass,
CursorPreparer preparer) {
return doFind(collectionName, query, fields, entityClass, preparer, new ReadDbObjectCallback<T>(mongoConverter,
entityClass, collectionName));
return doFind(collectionName, query, fields, entityClass, preparer,
new ReadDbObjectCallback<T>(mongoConverter, entityClass, collectionName));
}
protected <S, T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<S> entityClass,
@ -1907,8 +1916,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1907,8 +1916,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
DbObjectCallback<T> objectCallback, String collectionName) {
try {
T result = objectCallback.doWith(collectionCallback.doInCollection(getAndPrepareCollection(getDb(),
collectionName)));
T result = objectCallback
.doWith(collectionCallback.doInCollection(getAndPrepareCollection(getDb(), collectionName)));
return result;
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e, exceptionTranslator);
@ -1933,8 +1942,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1933,8 +1942,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param collectionName the collection to be queried
* @return
*/
private <T> List<T> executeFindMultiInternal(CollectionCallback<DBCursor> collectionCallback,
CursorPreparer preparer, DbObjectCallback<T> objectCallback, String collectionName) {
private <T> List<T> executeFindMultiInternal(CollectionCallback<DBCursor> collectionCallback, CursorPreparer preparer,
DbObjectCallback<T> objectCallback, String collectionName) {
try {
@ -2024,8 +2033,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -2024,8 +2033,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (entity == null) {
throw new InvalidDataAccessApiUsageException("No Persistent Entity information found for the class "
+ entityClass.getName());
throw new InvalidDataAccessApiUsageException(
"No Persistent Entity information found for the class " + entityClass.getName());
}
return entity.getCollection();
}
@ -2089,8 +2098,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -2089,8 +2098,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
String error = result.getErrorMessage();
error = error == null ? "NO MESSAGE" : error;
throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = "
+ source, ex);
throw new InvalidDataAccessApiUsageException(
"Command execution failed: Error [" + error + "], Command = " + source, ex);
}
}
@ -2286,7 +2295,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -2286,7 +2295,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
class UnwrapAndReadDbObjectCallback<T> extends ReadDbObjectCallback<T> {
public UnwrapAndReadDbObjectCallback(EntityReader<? super T, DBObject> reader, Class<T> type, String collectionName) {
public UnwrapAndReadDbObjectCallback(EntityReader<? super T, DBObject> reader, Class<T> type,
String collectionName) {
super(reader, type, collectionName);
}

98
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,22 +17,30 @@ package org.springframework.data.mongodb.core.convert; @@ -17,22 +17,30 @@ package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Pattern;
import org.bson.BasicBSONObject;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Example.NullHandler;
import org.springframework.data.domain.Example.StringMatcher;
import org.springframework.data.domain.PropertySpecifier;
import org.springframework.data.domain.ExampleSpec;
import org.springframework.data.domain.ExampleSpec.NullHandler;
import org.springframework.data.domain.ExampleSpec.PropertyValueTransformer;
import org.springframework.data.domain.ExampleSpec.StringMatcher;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.MongoRegexCreator;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.repository.core.support.ExampleSpecAccessor;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@ -41,17 +49,25 @@ import com.mongodb.DBObject; @@ -41,17 +49,25 @@ import com.mongodb.DBObject;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.8
*/
public class MongoExampleMapper {
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final MongoConverter converter;
private final Map<StringMatcher, Type> stringMatcherPartMapping = new HashMap<StringMatcher, Type>();
public MongoExampleMapper(MongoConverter converter) {
this.converter = converter;
this.mappingContext = converter.getMappingContext();
stringMatcherPartMapping.put(StringMatcher.EXACT, Type.SIMPLE_PROPERTY);
stringMatcherPartMapping.put(StringMatcher.CONTAINING, Type.CONTAINING);
stringMatcherPartMapping.put(StringMatcher.STARTING, Type.STARTING_WITH);
stringMatcherPartMapping.put(StringMatcher.ENDING, Type.ENDING_WITH);
stringMatcherPartMapping.put(StringMatcher.REGEX, Type.REGEX);
}
/**
@ -63,7 +79,7 @@ public class MongoExampleMapper { @@ -63,7 +79,7 @@ public class MongoExampleMapper {
* @since 1.8
*/
public DBObject getMappedExample(Example<?> example) {
return getMappedExample(example, mappingContext.getPersistentEntity(example.getSampleType()));
return getMappedExample(example, mappingContext.getPersistentEntity(example.getProbeType()));
}
/**
@ -77,21 +93,27 @@ public class MongoExampleMapper { @@ -77,21 +93,27 @@ public class MongoExampleMapper {
*/
public DBObject getMappedExample(Example<?> example, MongoPersistentEntity<?> entity) {
DBObject reference = (DBObject) converter.convertToMongoType(example.getSampleObject());
DBObject reference = (DBObject) converter.convertToMongoType(example.getProbe());
if (entity.hasIdProperty() && entity.getIdentifierAccessor(example.getSampleObject()).getIdentifier() == null) {
if (entity.hasIdProperty() && entity.getIdentifierAccessor(example.getProbe()).getIdentifier() == null) {
reference.removeField(entity.getIdProperty().getFieldName());
}
applyPropertySpecs("", reference, example);
ExampleSpecAccessor exampleSpecAccessor = new ExampleSpecAccessor(example.getExampleSpec());
applyPropertySpecs("", reference, example.getProbeType(), exampleSpecAccessor);
if (exampleSpecAccessor.isTyped()) {
this.converter.getTypeMapper().writeTypeRestrictions(reference, (Set) Collections.singleton(example.getResultType()));
}
return ObjectUtils.nullSafeEquals(NullHandler.INCLUDE, example.getNullHandler()) ? reference : new BasicDBObject(
SerializationUtils.flatMap(reference));
return ObjectUtils.nullSafeEquals(NullHandler.INCLUDE, exampleSpecAccessor.getNullHandler()) ? reference
: new BasicDBObject(SerializationUtils.flattenMap(reference));
}
private String getMappedPropertyPath(String path, Example<?> example) {
private String getMappedPropertyPath(String path, Class<?> probeType) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(example.getSampleType());
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(probeType);
Iterator<String> parts = Arrays.asList(path.split("\\.")).iterator();
@ -136,7 +158,8 @@ public class MongoExampleMapper { @@ -136,7 +158,8 @@ public class MongoExampleMapper {
}
private void applyPropertySpecs(String path, DBObject source, Example<?> example) {
private void applyPropertySpecs(String path, DBObject source, Class<?> probeType,
ExampleSpecAccessor exampleSpecAccessor) {
if (!(source instanceof BasicDBObject)) {
return;
@ -147,47 +170,37 @@ public class MongoExampleMapper { @@ -147,47 +170,37 @@ public class MongoExampleMapper {
while (iter.hasNext()) {
Map.Entry<String, Object> entry = iter.next();
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType);
if (entry.getKey().equals("_id") && entry.getValue() == null) {
if(isEmptyIdProperty(entry)) {
iter.remove();
continue;
}
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
String mappedPropertyPath = getMappedPropertyPath(propertyPath, example);
if (example.isIgnoredPath(propertyPath) || example.isIgnoredPath(mappedPropertyPath)) {
if (exampleSpecAccessor.isIgnoredPath(propertyPath) || exampleSpecAccessor.isIgnoredPath(mappedPropertyPath)) {
iter.remove();
continue;
}
PropertySpecifier specifier = null;
StringMatcher stringMatcher = example.getDefaultStringMatcher();
StringMatcher stringMatcher = exampleSpecAccessor.getDefaultStringMatcher();
Object value = entry.getValue();
boolean ignoreCase = example.isIngnoreCaseEnabled();
if (example.hasPropertySpecifiers()) {
boolean ignoreCase = exampleSpecAccessor.isIgnoreCaseEnabled();
mappedPropertyPath = example.hasPropertySpecifier(propertyPath) ? propertyPath : getMappedPropertyPath(
propertyPath, example);
if (exampleSpecAccessor.hasPropertySpecifiers()) {
specifier = example.getPropertySpecifier(mappedPropertyPath);
mappedPropertyPath = exampleSpecAccessor.hasPropertySpecifier(propertyPath) ? propertyPath
: getMappedPropertyPath(propertyPath, probeType);
if (specifier != null) {
if (specifier.hasStringMatcher()) {
stringMatcher = specifier.getStringMatcher();
}
if (specifier.getIgnoreCase() != null) {
ignoreCase = specifier.getIgnoreCase();
}
}
stringMatcher = exampleSpecAccessor.getStringMatcherForPath(mappedPropertyPath);
ignoreCase = exampleSpecAccessor.isIgnoreCaseForPath(mappedPropertyPath);
}
// TODO: should a PropertySpecifier outrule the later on string matching?
if (specifier != null) {
if (exampleSpecAccessor.hasPropertySpecifier(mappedPropertyPath)) {
value = specifier.transformValue(value);
PropertyValueTransformer valueTransformer = exampleSpecAccessor.getValueTransformerForPath(mappedPropertyPath);
value = valueTransformer.convert(value);
if (value == null) {
iter.remove();
continue;
@ -199,11 +212,15 @@ public class MongoExampleMapper { @@ -199,11 +212,15 @@ public class MongoExampleMapper {
if (entry.getValue() instanceof String) {
applyStringMatcher(entry, stringMatcher, ignoreCase);
} else if (entry.getValue() instanceof BasicDBObject) {
applyPropertySpecs(propertyPath, (BasicDBObject) entry.getValue(), example);
applyPropertySpecs(propertyPath, (BasicDBObject) entry.getValue(), probeType, exampleSpecAccessor);
}
}
}
private boolean isEmptyIdProperty(Entry<String, Object> entry) {
return entry.getKey().equals("_id") && entry.getValue() == null;
}
private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher stringMatcher, boolean ignoreCase) {
BasicDBObject dbo = new BasicDBObject();
@ -216,8 +233,8 @@ public class MongoExampleMapper { @@ -216,8 +233,8 @@ public class MongoExampleMapper {
}
} else {
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(),
stringMatcher.getPartType());
Type type = stringMatcherPartMapping.get(stringMatcher);
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), type);
dbo.put("$regex", expression);
entry.setValue(dbo);
}
@ -226,4 +243,5 @@ public class MongoExampleMapper { @@ -226,4 +243,5 @@ public class MongoExampleMapper {
dbo.put("$options", "i");
}
}
}

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

@ -261,8 +261,8 @@ public class QueryMapper { @@ -261,8 +261,8 @@ public class QueryMapper {
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
Object value = keyword.getValue();
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue(
property.with(keyword.getKey()), value);
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property)
: getMappedValue(property.with(keyword.getKey()), value);
return new BasicDBObject(keyword.key, convertedValue);
}
@ -484,8 +484,8 @@ public class QueryMapper { @@ -484,8 +484,8 @@ public class QueryMapper {
}
try {
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService
.convert(id, ObjectId.class) : delegateConvertToMongoType(id, null);
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
: delegateConvertToMongoType(id, null);
} catch (ConversionException o_O) {
return delegateConvertToMongoType(id, null);
}

2
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java

@ -98,7 +98,7 @@ public class Criteria implements CriteriaDefinition { @@ -98,7 +98,7 @@ public class Criteria implements CriteriaDefinition {
* @since 1.8
*/
public static Criteria byExample(Object example) {
return byExample(new Example<Object>(example));
return byExample(Example.of(example));
}
/**

7
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import org.springframework.util.ObjectUtils; @@ -22,6 +22,7 @@ import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.8
*/
public enum MongoRegexCreator {
@ -67,6 +68,10 @@ public enum MongoRegexCreator { @@ -67,6 +68,10 @@ public enum MongoRegexCreator {
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Type type) {
if (ObjectUtils.nullSafeEquals(Type.REGEX, type)) {
return source;
}
if (!ObjectUtils.nullSafeEquals(Type.LIKE, type)) {
return PUNCTATION_PATTERN.matcher(source).find() ? Pattern.quote(source) : source;
}

4
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -63,7 +63,7 @@ public abstract class SerializationUtils { @@ -63,7 +63,7 @@ public abstract class SerializationUtils {
* @return {@link Collections#emptyMap()} when source is {@literal null}
* @since 1.8
*/
public static Map<String, Object> flatMap(DBObject source) {
public static Map<String, Object> flattenMap(DBObject source) {
if (source == null) {
return Collections.emptyMap();

38
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoRepository.java

@ -19,11 +19,10 @@ import java.io.Serializable; @@ -19,11 +19,10 @@ import java.io.Serializable;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
/**
* Mongo specific {@link org.springframework.data.repository.Repository} interface.
@ -31,9 +30,11 @@ import org.springframework.data.repository.PagingAndSortingRepository; @@ -31,9 +30,11 @@ import org.springframework.data.repository.PagingAndSortingRepository;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Thomas Darimont
* @author Mark Paluch
*/
@NoRepositoryBean
public interface MongoRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
public interface MongoRepository<T, ID extends Serializable>
extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
/*
* (non-Javadoc)
@ -75,33 +76,14 @@ public interface MongoRepository<T, ID extends Serializable> extends PagingAndSo @@ -75,33 +76,14 @@ public interface MongoRepository<T, ID extends Serializable> extends PagingAndSo
*/
<S extends T> List<S> insert(Iterable<S> entities);
/**
* Returns all instances of the type specified by the given {@link Example}.
*
* @param example must not be {@literal null}.
* @return
* @since 1.8
/* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
*/
<S extends T> List<T> findAllByExample(Example<S> example);
<S extends T> List<S> findAll(Example<S> example);
/**
* Returns all instances of the type specified by the given {@link Example}.
*
* @param example must not be {@literal null}.
* @param sort can be {@literal null}.
* @return all entities sorted by the given options
* @since 1.8
/* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
*/
<S extends T> List<T> findAllByExample(Example<S> example, Sort sort);
<S extends T> List<S> findAll(Example<S> example, Sort sort);
/**
* Returns a {@link Page} of entities meeting the paging restriction specified by the given {@link Example} limited to
* criteria provided in the {@code Pageable} object.
*
* @param example must not be {@literal null}.
* @param pageable can be {@literal null}.
* @return a {@link Page} of entities
* @since 1.8
*/
<S extends T> Page<T> findAllByExample(Example<S> example, Pageable pageable);
}

6
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java

@ -21,7 +21,6 @@ import java.util.Collections; @@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort;
@ -133,11 +132,6 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { @@ -133,11 +132,6 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return delegate.getFullText();
}
@Override
public Example<?> getSampleObject() {
return delegate.getSampleObject();
}
/**
* Converts the given value with the underlying {@link MongoWriter}.
*

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.repository.query;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
@ -61,12 +60,4 @@ public interface MongoParameterAccessor extends ParameterAccessor { @@ -61,12 +60,4 @@ public interface MongoParameterAccessor extends ParameterAccessor {
* @since 1.8
*/
Object[] getValues();
/**
* Get the sample for query by example
*
* @return
* @since 1.8
*/
Example<?> getSampleObject();
}

24
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java

@ -20,7 +20,6 @@ import java.util.Arrays; @@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
@ -43,7 +42,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -43,7 +42,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
private final int rangeIndex;
private final int maxDistanceIndex;
private final Integer fullTextIndex;
private final int sampleObjectIndex;
private Integer nearIndex;
@ -71,12 +69,10 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -71,12 +69,10 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
} else if (this.nearIndex == null) {
this.nearIndex = -1;
}
this.sampleObjectIndex = parameterTypes.indexOf(Example.class);
}
private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, Integer nearIndex,
Integer fullTextIndex, int rangeIndex, int sampleObjectIndex) {
Integer fullTextIndex, int rangeIndex) {
super(parameters);
@ -84,7 +80,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -84,7 +80,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
this.fullTextIndex = fullTextIndex;
this.maxDistanceIndex = maxDistanceIndex;
this.rangeIndex = rangeIndex;
this.sampleObjectIndex = sampleObjectIndex;
}
private final int getNearIndex(List<Class<?>> parameterTypes) {
@ -187,22 +182,13 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -187,22 +182,13 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
return rangeIndex;
}
/**
* @return
* @since 1.8
*/
public int getSampleObjectParameterIndex() {
return sampleObjectIndex;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List)
*/
@Override
protected MongoParameters createFrom(List<MongoParameter> parameters) {
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex,
this.sampleObjectIndex);
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex);
}
private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type, Class<?> componentType) {
@ -254,7 +240,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -254,7 +240,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
@Override
public boolean isSpecialParameter() {
return super.isSpecialParameter() || Distance.class.isAssignableFrom(getType()) || isNearParameter()
|| TextCriteria.class.isAssignableFrom(getType()) || isExample();
|| TextCriteria.class.isAssignableFrom(getType());
}
private boolean isNearParameter() {
@ -274,10 +260,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> @@ -274,10 +260,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
return parameter.getParameterAnnotation(Near.class) != null;
}
private boolean isExample() {
return Example.class.isAssignableFrom(getType());
}
}
}

11
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java

@ -18,7 +18,6 @@ package org.springframework.data.mongodb.repository.query; @@ -18,7 +18,6 @@ package org.springframework.data.mongodb.repository.query;
import java.util.Arrays;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
@ -126,17 +125,11 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso @@ -126,17 +125,11 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
return ((TextCriteria) fullText);
}
throw new IllegalArgumentException(String.format(
"Expected full text parameter to be one of String, Term or TextCriteria but found %s.",
throw new IllegalArgumentException(
String.format("Expected full text parameter to be one of String, Term or TextCriteria but found %s.",
ClassUtils.getShortName(fullText.getClass())));
}
public Example<?> getSampleObject() {
int index = method.getParameters().getSampleObjectParameterIndex();
return index >= 0 ? (Example<?>) getValue(index) : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getValues()

23
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java

@ -151,20 +151,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> { @@ -151,20 +151,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
@Override
protected Query complete(Criteria criteria, Sort sort) {
Criteria toUse = null;
if (accessor.getSampleObject() != null) {
toUse = new Criteria().alike(accessor.getSampleObject());
}
if (criteria != null) {
if (toUse == null) {
toUse = criteria;
} else {
toUse.andOperator(criteria);
}
}
Query query = (toUse == null ? new Query() : new Query(toUse)).with(sort);
Query query = (criteria == null ? new Query() : new Query(criteria)).with(sort);
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query);
@ -298,8 +285,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> { @@ -298,8 +285,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
case ALWAYS:
if (path.getType() != String.class) {
throw new IllegalArgumentException(String.format("Part %s must be of type String but was %s", path,
path.getType()));
throw new IllegalArgumentException(
String.format("Part %s must be of type String but was %s", path, path.getType()));
}
// fall-through
@ -385,8 +372,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> { @@ -385,8 +372,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return (T) parameter;
}
throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type,
parameter.getClass()));
throw new IllegalArgumentException(
String.format("Expected parameter type of %s but got %s!", type, parameter.getClass()));
}
private Object[] nextAsArray(Iterator<Object> iterator) {

67
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,6 +30,7 @@ import org.springframework.data.domain.Page; @@ -30,6 +30,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.TypedExampleSpec;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
@ -44,6 +45,7 @@ import org.springframework.util.Assert; @@ -44,6 +45,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Thomas Darimont
* @author Mark Paluch
*/
public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {
@ -54,7 +56,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR @@ -54,7 +56,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
* Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
*
* @param metadata must not be {@literal null}.
* @param template must not be {@literal null}.
* @param mongoOperations must not be {@literal null}.
*/
public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
@ -265,18 +267,18 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR @@ -265,18 +267,18 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable)
*/
@Override
public <S extends T> Page<T> findAllByExample(Example<S> example, Pageable pageable) {
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example)).with(pageable);
long count = mongoOperations.count(q, entityInformation.getJavaType(), entityInformation.getCollectionName());
long count = mongoOperations.count(q, getResultType(example), entityInformation.getCollectionName());
if (count == 0) {
return new PageImpl<T>(Collections.<T> emptyList());
return new PageImpl<S>(Collections.<S> emptyList());
}
return new PageImpl<T>(mongoOperations.find(q, entityInformation.getJavaType(),
entityInformation.getCollectionName()), pageable, count);
return new PageImpl<S>(mongoOperations.find(q, getResultType(example), entityInformation.getCollectionName()), pageable,
count);
}
/*
@ -284,7 +286,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR @@ -284,7 +286,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
*/
@Override
public <S extends T> List<T> findAllByExample(Example<S> example, Sort sort) {
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
Assert.notNull(example, "Sample must not be null!");
@ -294,7 +296,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR @@ -294,7 +296,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
q.with(sort);
}
return findAll(q);
return mongoOperations.find(q, getResultType(example), entityInformation.getCollectionName());
}
/*
@ -302,13 +304,56 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR @@ -302,13 +304,56 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example)
*/
@Override
public <S extends T> List<T> findAllByExample(Example<S> example) {
public <S extends T> List<S> findAll(Example<S> example) {
return findAll(example, (Sort) null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findOne(org.springframework.data.domain.Example)
*/
@Override
public <S extends T> S findOne(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example));
return mongoOperations.findOne(q, getResultType(example), entityInformation.getCollectionName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#count(org.springframework.data.domain.Example)
*/
@Override
public <S extends T> long count(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example));
return mongoOperations.count(q, getResultType(example), entityInformation.getCollectionName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#exists(org.springframework.data.domain.Example)
*/
@Override
public <S extends T> boolean exists(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example));
return mongoOperations.exists(q, getResultType(example), entityInformation.getCollectionName());
}
private <S extends T> Class<S> getResultType(Example<S> example) {
if (example.getExampleSpec() instanceof TypedExampleSpec<?>) {
return example.getResultType();
}
return findAll(q);
return (Class<S>) entityInformation.getJavaType();
}
private List<T> findAll(Query query) {

38
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SerializationUtilsUnitTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ import java.util.Arrays; @@ -23,8 +23,6 @@ import java.util.Arrays;
import java.util.Map;
import org.hamcrest.Matcher;
import org.hamcrest.collection.IsMapContaining;
import org.hamcrest.core.Is;
import org.junit.Test;
import org.springframework.data.mongodb.core.query.SerializationUtils;
@ -70,68 +68,68 @@ public class SerializationUtilsUnitTests { @@ -70,68 +68,68 @@ public class SerializationUtilsUnitTests {
* @see DATAMONGO-1245
*/
@Test
public void flatMapShouldFlatOutNestedStructureCorrectly() {
public void flattenMapShouldFlatOutNestedStructureCorrectly() {
DBObject dbo = new BasicDBObjectBuilder().add("_id", 1).add("nested", new BasicDBObject("value", "conflux")).get();
assertThat(flatMap(dbo), IsMapContaining.<String, Object> hasEntry("_id", 1));
assertThat(flatMap(dbo), IsMapContaining.<String, Object> hasEntry("nested.value", "conflux"));
assertThat(flattenMap(dbo), hasEntry("_id", (Object) 1));
assertThat(flattenMap(dbo), hasEntry("nested.value", (Object) "conflux"));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void flatMapShouldFlatOutNestedStructureWithListCorrectly() {
public void flattenMapShouldFlatOutNestedStructureWithListCorrectly() {
BasicDBList dbl = new BasicDBList();
dbl.addAll(Arrays.asList("nightwielder", "calamity"));
DBObject dbo = new BasicDBObjectBuilder().add("_id", 1).add("nested", new BasicDBObject("value", dbl)).get();
assertThat(flatMap(dbo), IsMapContaining.<String, Object> hasEntry("_id", 1));
assertThat(flatMap(dbo), IsMapContaining.<String, Object> hasEntry("nested.value", dbl));
assertThat(flattenMap(dbo), hasEntry("_id", (Object) 1));
assertThat(flattenMap(dbo), hasEntry("nested.value", (Object) dbl));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void flatMapShouldLeaveKeywordsUntouched() {
public void flattenMapShouldLeaveKeywordsUntouched() {
DBObject dbo = new BasicDBObjectBuilder().add("_id", 1).add("nested", new BasicDBObject("$regex", "^conflux$"))
.get();
Map<String, Object> map = flatMap(dbo);
Map<String, Object> map = flattenMap(dbo);
assertThat(map, IsMapContaining.<String, Object> hasEntry("_id", 1));
assertThat(map, hasEntry("_id", (Object) 1));
assertThat(map.get("nested"), notNullValue());
assertThat(((Map<String, Object>) map.get("nested")).get("$regex"), Is.<Object> is("^conflux$"));
assertThat(((Map<String, Object>) map.get("nested")).get("$regex"), is((Object) "^conflux$"));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void flatMapSouldAppendCommandsCorrectly() {
public void flattenMapShouldAppendCommandsCorrectly() {
DBObject dbo = new BasicDBObjectBuilder().add("_id", 1)
.add("nested", new BasicDBObjectBuilder().add("$regex", "^conflux$").add("$options", "i").get()).get();
Map<String, Object> map = flatMap(dbo);
Map<String, Object> map = flattenMap(dbo);
assertThat(map, IsMapContaining.<String, Object> hasEntry("_id", 1));
assertThat(map, hasEntry("_id", (Object) 1));
assertThat(map.get("nested"), notNullValue());
assertThat(((Map<String, Object>) map.get("nested")).get("$regex"), Is.<Object> is("^conflux$"));
assertThat(((Map<String, Object>) map.get("nested")).get("$options"), Is.<Object> is("i"));
assertThat(((Map<String, Object>) map.get("nested")).get("$regex"), is((Object) "^conflux$"));
assertThat(((Map<String, Object>) map.get("nested")).get("$options"), is((Object) "i"));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void flatMapShouldReturnEmptyMapWhenSourceIsNull() {
assertThat(flatMap(null).isEmpty(), is(true));
public void flattenMapShouldReturnEmptyMapWhenSourceIsNull() {
assertThat(flattenMap(null).isEmpty(), is(true));
}
static class Complex {

136
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,7 +18,6 @@ package org.springframework.data.mongodb.core.convert; @@ -18,7 +18,6 @@ package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.domain.Example.*;
import static org.springframework.data.domain.PropertySpecifier.*;
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
@ -34,6 +33,9 @@ import org.mockito.Mock; @@ -34,6 +33,9 @@ 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.ExampleSpec;
import org.springframework.data.domain.ExampleSpec.GenericPropertyMatcher;
import org.springframework.data.domain.ExampleSpec.StringMatcher;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes;
@ -49,6 +51,7 @@ import com.mongodb.DBObject; @@ -49,6 +51,7 @@ import com.mongodb.DBObject;
/**
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class MongoExampleMapperUnitTests {
@ -79,7 +82,7 @@ public class MongoExampleMapperUnitTests { @@ -79,7 +82,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument();
probe.id = "steelheart";
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("_id", "steelheart").get()));
}
@ -95,11 +98,10 @@ public class MongoExampleMapperUnitTests { @@ -95,11 +98,10 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.intValue = 100;
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo,
is(new BasicDBObjectBuilder().add("_id", "steelheart").add("stringValue", "firefight").add("intValue", 100)
.get()));
assertThat(dbo, is(new BasicDBObjectBuilder().add("_id", "steelheart").add("stringValue", "firefight")
.add("intValue", 100).get()));
}
/**
@ -112,7 +114,7 @@ public class MongoExampleMapperUnitTests { @@ -112,7 +114,7 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.intValue = 100;
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", "firefight").add("intValue", 100).get()));
}
@ -126,7 +128,7 @@ public class MongoExampleMapperUnitTests { @@ -126,7 +128,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument();
probe.listOfString = Arrays.asList("Prof", "Tia", "David");
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("listOfString", Arrays.asList("Prof", "Tia", "David")).get()));
}
@ -140,7 +142,7 @@ public class MongoExampleMapperUnitTests { @@ -140,7 +142,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument();
probe.customNamedField = "Mitosis";
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("custom_field_name", "Mitosis").get()));
}
@ -149,13 +151,30 @@ public class MongoExampleMapperUnitTests { @@ -149,13 +151,30 @@ public class MongoExampleMapperUnitTests {
* @see DATAMONGO-1245
*/
@Test
public void exampleShouldBeMappedAsFlatMapWhenGivenNestedElementsWithLenienMatchMode() {
public void typedExampleShouldContainTypeRestriction() {
WrapperDocument probe = new WrapperDocument();
probe.flatDoc = new FlatDocument();
probe.flatDoc.stringValue = "conflux";
DBObject dbo = mapper.getMappedExample(of(probe, ExampleSpec.typed(WrapperDocument.class)),
context.getPersistentEntity(WrapperDocument.class));
assertThat(dbo,
isBsonObject().containing("_class", new BasicDBObject("$in", new String[] { probe.getClass().getName() })));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void exampleShouldBeMappedAsFlatMapWhenGivenNestedElementsWithLenientMatchMode() {
WrapperDocument probe = new WrapperDocument();
probe.flatDoc = new FlatDocument();
probe.flatDoc.stringValue = "conflux";
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(WrapperDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(WrapperDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("flatDoc.stringValue", "conflux").get()));
}
@ -164,13 +183,13 @@ public class MongoExampleMapperUnitTests { @@ -164,13 +183,13 @@ public class MongoExampleMapperUnitTests {
* @see DATAMONGO-1245
*/
@Test
public void exampleShouldBeMappedAsExactObjectWhenGivenNestedElementsWithStriktMatchMode() {
public void exampleShouldBeMappedAsExactObjectWhenGivenNestedElementsWithStrictMatchMode() {
WrapperDocument probe = new WrapperDocument();
probe.flatDoc = new FlatDocument();
probe.flatDoc.stringValue = "conflux";
Example<?> example = newExampleOf(probe).includeNullValues().get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withIncludeNullValues());
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class));
@ -187,15 +206,32 @@ public class MongoExampleMapperUnitTests { @@ -187,15 +206,32 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.intValue = 100;
Example<?> example = newExampleOf(probe).matchStringsStartingWith().get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withStringMatcher(StringMatcher.STARTING));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(dbo,
is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "^firefight"))
assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "^firefight"))
.add("intValue", 100).get()));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void exampleShouldBeMappedCorrectlyForFlatTypeContainingDotsWhenStringMatchModeIsStarting() {
FlatDocument probe = new FlatDocument();
probe.stringValue = "fire.ight";
probe.intValue = 100;
Example<?> example = Example.of(probe, ExampleSpec.untyped().withStringMatcherStarting());
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder()
.add("stringValue", new BasicDBObject("$regex", "^" + Pattern.quote("fire.ight"))).add("intValue", 100).get()));
}
/**
* @see DATAMONGO-1245
*/
@ -206,15 +242,32 @@ public class MongoExampleMapperUnitTests { @@ -206,15 +242,32 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.intValue = 100;
Example<?> example = newExampleOf(probe).matchStringsEndingWith().get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withStringMatcher(StringMatcher.ENDING));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(dbo,
is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "firefight$"))
assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "firefight$"))
.add("intValue", 100).get()));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void exampleShouldBeMappedCorrectlyForFlatTypeWhenStringMatchModeRegex() {
FlatDocument probe = new FlatDocument();
probe.stringValue = "firefight";
probe.customNamedField = "^(cat|dog).*shelter\\d?";
Example<?> example = Example.of(probe, ExampleSpec.untyped().withStringMatcher(StringMatcher.REGEX));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "firefight"))
.add("custom_field_name", new BasicDBObject("$regex", "^(cat|dog).*shelter\\d?")).get()));
}
/**
* @see DATAMONGO-1245
*/
@ -225,12 +278,12 @@ public class MongoExampleMapperUnitTests { @@ -225,12 +278,12 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.intValue = 100;
Example<?> example = newExampleOf(probe).matchStringsEndingWith().matchStringsWithIgnoreCase().get();
Example<?> example = Example.of(probe,
ExampleSpec.untyped().withStringMatcher(StringMatcher.ENDING).withIgnoreCase());
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(
dbo,
assertThat(dbo,
is(new BasicDBObjectBuilder()
.add("stringValue", new BasicDBObjectBuilder().add("$regex", "firefight$").add("$options", "i").get())
.add("intValue", 100).get()));
@ -246,12 +299,11 @@ public class MongoExampleMapperUnitTests { @@ -246,12 +299,11 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.intValue = 100;
Example<?> example = newExampleOf(probe).matchStringsWithIgnoreCase().get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withIgnoreCase());
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(
dbo,
assertThat(dbo,
is(new BasicDBObjectBuilder()
.add("stringValue",
new BasicDBObjectBuilder().add("$regex", Pattern.quote("firefight")).add("$options", "i").get())
@ -269,7 +321,7 @@ public class MongoExampleMapperUnitTests { @@ -269,7 +321,7 @@ public class MongoExampleMapperUnitTests {
probe.referenceDocument = new ReferenceDocument();
probe.referenceDocument.id = "200";
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(WithDBRef.class));
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"));
@ -285,7 +337,7 @@ public class MongoExampleMapperUnitTests { @@ -285,7 +337,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument();
probe.stringValue = "steelheart";
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", "steelheart").get()));
}
@ -299,7 +351,7 @@ public class MongoExampleMapperUnitTests { @@ -299,7 +351,7 @@ public class MongoExampleMapperUnitTests {
ClassWithGeoTypes probe = new ClassWithGeoTypes();
probe.legacyPoint = new Point(10D, 20D);
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(WithDBRef.class));
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));
@ -316,7 +368,7 @@ public class MongoExampleMapperUnitTests { @@ -316,7 +368,7 @@ public class MongoExampleMapperUnitTests {
probe.intValue = 10;
probe.stringValue = "string";
Example<?> example = newExampleOf(probe).ignore("customNamedField").get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withIgnorePaths("customNamedField"));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
@ -334,7 +386,7 @@ public class MongoExampleMapperUnitTests { @@ -334,7 +386,7 @@ public class MongoExampleMapperUnitTests {
probe.intValue = 10;
probe.stringValue = "string";
Example<?> example = newExampleOf(probe).ignore("stringValue").get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withIgnorePaths("stringValue"));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
@ -353,12 +405,12 @@ public class MongoExampleMapperUnitTests { @@ -353,12 +405,12 @@ public class MongoExampleMapperUnitTests {
probe.flatDoc.intValue = 10;
probe.flatDoc.stringValue = "string";
Example<?> example = newExampleOf(probe).ignore("flatDoc.stringValue").get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withIgnorePaths("flatDoc.stringValue"));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("flatDoc.custom_field_name", "foo").add("flatDoc.intValue", 10)
.get()));
assertThat(dbo,
is(new BasicDBObjectBuilder().add("flatDoc.custom_field_name", "foo").add("flatDoc.intValue", 10).get()));
}
/**
@ -373,12 +425,12 @@ public class MongoExampleMapperUnitTests { @@ -373,12 +425,12 @@ public class MongoExampleMapperUnitTests {
probe.flatDoc.intValue = 10;
probe.flatDoc.stringValue = "string";
Example<?> example = newExampleOf(probe).ignore("flatDoc.customNamedField").get();
Example<?> example = Example.of(probe, ExampleSpec.untyped().withIgnorePaths("flatDoc.customNamedField"));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("flatDoc.stringValue", "string").add("flatDoc.intValue", 10)
.get()));
assertThat(dbo,
is(new BasicDBObjectBuilder().add("flatDoc.stringValue", "string").add("flatDoc.intValue", 10).get()));
}
/**
@ -391,14 +443,12 @@ public class MongoExampleMapperUnitTests { @@ -391,14 +443,12 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight";
probe.customNamedField = "steelheart";
Example<?> example = newExampleOf(probe).specify(newPropertySpecifier("stringValue").matchStringContaining().get())
.get();
Example<?> example = Example.of(probe,
ExampleSpec.untyped().withMatcher("stringValue", new GenericPropertyMatcher().contains()));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(
dbo,
is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", ".*firefight.*"))
assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", ".*firefight.*"))
.add("custom_field_name", "steelheart").get()));
}
@ -413,7 +463,7 @@ public class MongoExampleMapperUnitTests { @@ -413,7 +463,7 @@ public class MongoExampleMapperUnitTests {
probe.customNamedField = "steelheart";
probe.anotherStringValue = "calamity";
DBObject dbo = mapper.getMappedExample(exampleOf(probe), context.getPersistentEntity(FlatDocument.class));
DBObject dbo = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, isBsonObject().containing("anotherStringValue", "calamity"));
}

29
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/temp/QueryByExampleTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,6 +32,10 @@ import org.springframework.data.mongodb.core.query.Query; @@ -32,6 +32,10 @@ import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.MongoClient;
/**
* @author Christoph Strobl
* @author Mark Paluch
*/
public class QueryByExampleTests {
MongoTemplate template;
@ -54,7 +58,9 @@ public class QueryByExampleTests { @@ -54,7 +58,9 @@ public class QueryByExampleTests {
Person sample = new Person();
sample.lastname = "stark";
List<Person> result = template.findByExample(sample);
Query query = new Query(new Criteria().alike(Example.of(sample)));
List<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(2));
}
@ -70,7 +76,9 @@ public class QueryByExampleTests { @@ -70,7 +76,9 @@ public class QueryByExampleTests {
sample.lastname = "stark";
sample.firstname = "arya";
List<Person> result = template.findByExample(sample);
Query query = new Query(new Criteria().alike(Example.of(sample)));
List<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(1));
}
@ -88,7 +96,9 @@ public class QueryByExampleTests { @@ -88,7 +96,9 @@ public class QueryByExampleTests {
Person sample = new Person();
sample.id = p4.id;
List<Person> result = template.findByExample(sample);
Query query = new Query(new Criteria().alike(Example.of(sample)));
List<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(1));
}
@ -104,7 +114,10 @@ public class QueryByExampleTests { @@ -104,7 +114,10 @@ public class QueryByExampleTests {
sample.firstname = "jon";
sample.firstname = "stark";
List<Person> result = template.findByExample(sample);
Query query = new Query(new Criteria().alike(Example.of(sample)));
List<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(0));
}
@ -118,7 +131,9 @@ public class QueryByExampleTests { @@ -118,7 +131,9 @@ public class QueryByExampleTests {
Person sample = new Person();
List<Person> result = template.findByExample(sample);
Query query = new Query(new Criteria().alike(Example.of(sample)));
List<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(3));
}
@ -133,7 +148,7 @@ public class QueryByExampleTests { @@ -133,7 +148,7 @@ public class QueryByExampleTests {
Person sample = new Person();
sample.lastname = "stark";
Query query = new Query(new Criteria().alike(new Example<Person>(sample)).and("firstname").regex("^ary*"));
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));

65
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,8 +28,6 @@ import java.util.stream.Collectors; @@ -28,8 +28,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.hamcrest.Matchers;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@ -66,6 +64,7 @@ import org.springframework.test.util.ReflectionTestUtils; @@ -66,6 +64,7 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractPersonRepositoryIntegrationTests {
@ -179,8 +178,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -179,8 +178,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void executesPagedFinderCorrectly() throws Exception {
Page<Person> page = repository.findByLastnameLike("*a*", new PageRequest(0, 2, Direction.ASC, "lastname",
"firstname"));
Page<Person> page = repository.findByLastnameLike("*a*",
new PageRequest(0, 2, Direction.ASC, "lastname", "firstname"));
assertThat(page.isFirst(), is(true));
assertThat(page.isLast(), is(false));
assertThat(page.getNumberOfElements(), is(2));
@ -190,8 +189,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -190,8 +189,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void executesPagedFinderWithAnnotatedQueryCorrectly() throws Exception {
Page<Person> page = repository.findByLastnameLikeWithPageable(".*a.*", new PageRequest(0, 2, Direction.ASC,
"lastname", "firstname"));
Page<Person> page = repository.findByLastnameLikeWithPageable(".*a.*",
new PageRequest(0, 2, Direction.ASC, "lastname", "firstname"));
assertThat(page.isFirst(), is(true));
assertThat(page.isLast(), is(false));
assertThat(page.getNumberOfElements(), is(2));
@ -315,8 +314,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -315,8 +314,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void findsPagedPeopleByPredicate() throws Exception {
Page<Person> page = repository.findAll(person.lastname.contains("a"), new PageRequest(0, 2, Direction.ASC,
"lastname"));
Page<Person> page = repository.findAll(person.lastname.contains("a"),
new PageRequest(0, 2, Direction.ASC, "lastname"));
assertThat(page.isFirst(), is(true));
assertThat(page.isLast(), is(false));
assertThat(page.getNumberOfElements(), is(2));
@ -402,8 +401,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -402,8 +401,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point);
repository.save(dave);
GeoResults<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
Metrics.KILOMETERS));
GeoResults<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
new Distance(2000, Metrics.KILOMETERS));
assertThat(results.getContent().isEmpty(), is(false));
}
@ -414,8 +413,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -414,8 +413,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point);
repository.save(dave);
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
Metrics.KILOMETERS), new PageRequest(0, 20));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
new Distance(2000, Metrics.KILOMETERS), new PageRequest(0, 20));
assertThat(results.getContent().isEmpty(), is(false));
// DATAMONGO-607
@ -625,8 +624,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -625,8 +624,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
repository.save(Arrays.asList(dave, oliver, carter, boyd, leroi));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
Metrics.KILOMETERS), new PageRequest(1, 2));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
new Distance(2000, Metrics.KILOMETERS), new PageRequest(1, 2));
assertThat(results.getContent().isEmpty(), is(false));
assertThat(results.getNumberOfElements(), is(2));
@ -650,8 +649,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -650,8 +649,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
repository.save(Arrays.asList(dave, oliver, carter));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
Metrics.KILOMETERS), new PageRequest(1, 2));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
new Distance(2000, Metrics.KILOMETERS), new PageRequest(1, 2));
assertThat(results.getContent().isEmpty(), is(false));
assertThat(results.getNumberOfElements(), is(1));
assertThat(results.isFirst(), is(false));
@ -669,8 +668,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -669,8 +668,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point);
repository.save(dave);
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
Metrics.KILOMETERS), new PageRequest(0, 2));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
new Distance(2000, Metrics.KILOMETERS), new PageRequest(0, 2));
assertThat(results.getContent().isEmpty(), is(false));
assertThat(results.getNumberOfElements(), is(1));
@ -688,8 +687,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -688,8 +687,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(new Point(-73.99171, 40.738868));
repository.save(dave);
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
Metrics.KILOMETERS), new PageRequest(1, 2));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
new Distance(2000, Metrics.KILOMETERS), new PageRequest(1, 2));
assertThat(results.getContent().isEmpty(), is(true));
assertThat(results.getNumberOfElements(), is(0));
@ -939,8 +938,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -939,8 +938,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void shouldLimitCollectionQueryToMaxResultsWhenPresent() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3",
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
List<Person> result = repository.findTop3ByLastnameStartingWith("Dylan");
assertThat(result.size(), is(3));
}
@ -951,8 +950,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -951,8 +950,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void shouldNotLimitPagedQueryWhenPageRequestWithinBounds() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3",
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
Page<Person> result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(0, 2));
assertThat(result.getContent().size(), is(2));
}
@ -963,8 +962,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -963,8 +962,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void shouldLimitPagedQueryWhenPageRequestExceedsUpperBoundary() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3",
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
Page<Person> result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(1, 2));
assertThat(result.getContent().size(), is(1));
}
@ -975,8 +974,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -975,8 +974,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test
public void shouldReturnEmptyWhenPageRequestedPageIsTotallyOutOfScopeForLimit() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3",
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
Page<Person> result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(2, 2));
assertThat(result.getContent().size(), is(0));
}
@ -1240,8 +1239,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -1240,8 +1239,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
ReflectionTestUtils.setField(sample, "createdAt", null);
ReflectionTestUtils.setField(sample, "email", null);
Page<Person> result = repository.findAllByExample(new Example<Person>(sample), new PageRequest(0, 10));
Assert.assertThat(result.getNumberOfElements(), Is.is(2));
Page<Person> result = repository.findAll(Example.of(sample), new PageRequest(0, 10));
assertThat(result.getNumberOfElements(), is(2));
}
/**
@ -1258,8 +1257,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -1258,8 +1257,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
ReflectionTestUtils.setField(sample, "createdAt", null);
ReflectionTestUtils.setField(sample, "email", null);
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
Assert.assertThat(result.size(), Is.is(2));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result.size(), is(2));
}
}

3
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Contact.java

@ -27,7 +27,8 @@ import org.springframework.data.mongodb.core.mapping.Document; @@ -27,7 +27,8 @@ import org.springframework.data.mongodb.core.mapping.Document;
@Document
public abstract class Contact {
@Id protected String id;
@Id
protected final String id;
public Contact() {
this.id = new ObjectId().toString();

54
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepositoryIntegrationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,11 +15,15 @@ @@ -15,11 +15,15 @@
*/
package org.springframework.data.mongodb.repository;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleSpec;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ -27,6 +31,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -27,6 +31,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* Integration tests for {@link ContactRepository}. Mostly related to mapping inheritance.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("config/MongoNamespaceIntegrationTests-context.xml")
@ -35,6 +40,11 @@ public class ContactRepositoryIntegrationTests { @@ -35,6 +40,11 @@ public class ContactRepositoryIntegrationTests {
@Autowired
ContactRepository repository;
@Before
public void setUp() throws Exception {
repository.deleteAll();
}
@Test
public void readsAndWritesContactCorrectly() {
@ -43,4 +53,46 @@ public class ContactRepositoryIntegrationTests { @@ -43,4 +53,46 @@ public class ContactRepositoryIntegrationTests {
assertTrue(repository.findOne(result.getId().toString()) instanceof Person);
}
/**
* @see DATAMONGO-1245
*/
@Test
public void findsContactByUntypedExample() {
Person person = new Person("Oliver", "Gierke");
Contact result = repository.save(person);
Example<Person> example = Example.of(person, ExampleSpec.untyped());
assertThat(repository.findOne(example), instanceOf(Person.class));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void findsContactByTypedExample() {
Person person = new Person("Oliver", "Gierke");
Contact result = repository.save(person);
Example<Person> example = Example.of(person, ExampleSpec.typed(Person.class));
assertThat(repository.findOne(example), instanceOf(Person.class));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void findsNoContactByExample() {
Person person = new Person("Oliver", "Gierke");
Contact result = repository.save(person);
Example<Person> example = Example.of(person, ExampleSpec.typed(Contact.class));
assertThat(repository.findOne(example), is(nullValue()));
}
}

1
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

@ -352,5 +352,4 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query @@ -352,5 +352,4 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
*/
@Query("{ firstname : :#{#firstname}}")
List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterVariableOnly(@Param("firstname") String firstname);
}

10
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java

@ -18,7 +18,6 @@ package org.springframework.data.mongodb.repository.query; @@ -18,7 +18,6 @@ package org.springframework.data.mongodb.repository.query;
import java.util.Arrays;
import java.util.Iterator;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort;
@ -148,13 +147,4 @@ class StubParameterAccessor implements MongoParameterAccessor { @@ -148,13 +147,4 @@ class StubParameterAccessor implements MongoParameterAccessor {
public Class<?> getDynamicProjection() {
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getSampleObject()
*/
@Override
public Example<?> getSampleObject() {
return null;
}
}

83
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,6 +32,8 @@ import org.junit.Test; @@ -32,6 +32,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleSpec;
import org.springframework.data.domain.ExampleSpec.StringMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.geo.Point;
@ -51,6 +53,7 @@ import org.springframework.test.util.ReflectionTestUtils; @@ -51,6 +53,7 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author <a href="mailto:kowsercse@gmail.com">A. B. M. Kowser</a>
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@ -130,7 +133,7 @@ public class SimpleMongoRepositoryTests { @@ -130,7 +133,7 @@ public class SimpleMongoRepositoryTests {
* @see DATAMONGO-1054
*/
@Test
public void shouldInsertMutlipleFromList() {
public void shouldInsertMultipleFromList() {
String randomId = UUID.randomUUID().toString();
Map<String, Person> idToPerson = new HashMap<String, Person>();
@ -180,7 +183,7 @@ public class SimpleMongoRepositoryTests { @@ -180,7 +183,7 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email");
Page<Person> result = repository.findAllByExample(new Example<Person>(sample), new PageRequest(0, 10));
Page<Person> result = repository.findAll(Example.of(sample), new PageRequest(0, 10));
assertThat(result.getContent(), hasItems(dave, oliver));
assertThat(result.getContent(), hasSize(2));
@ -196,9 +199,9 @@ public class SimpleMongoRepositoryTests { @@ -196,9 +199,9 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result, hasItems(dave, oliver));
assertThat(result, containsInAnyOrder(dave, oliver));
assertThat(result, hasSize(2));
}
@ -218,7 +221,7 @@ public class SimpleMongoRepositoryTests { @@ -218,7 +221,7 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(dave.getAddress());
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result, hasItem(dave));
assertThat(result, hasSize(1));
@ -240,7 +243,7 @@ public class SimpleMongoRepositoryTests { @@ -240,7 +243,7 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(new Address(null, null, "Washington"));
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result, hasItems(dave, oliver));
assertThat(result, hasSize(2));
@ -259,7 +262,8 @@ public class SimpleMongoRepositoryTests { @@ -259,7 +262,8 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(new Address(null, null, "Washington"));
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(Example.newExampleOf(sample).includeNullValues().get());
Example<Person> example = Example.of(sample, ExampleSpec.typed(Person.class).withIncludeNullValues());
List<Person> result = repository.findAll(example);
assertThat(result, empty());
}
@ -277,7 +281,8 @@ public class SimpleMongoRepositoryTests { @@ -277,7 +281,8 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(dave.getAddress());
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(Example.newExampleOf(sample).includeNullValues().get());
Example<Person> example = Example.of(sample, ExampleSpec.untyped().withIncludeNullValues());
List<Person> result = repository.findAll(example);
assertThat(result, hasItem(dave));
assertThat(result, hasSize(1));
@ -293,7 +298,8 @@ public class SimpleMongoRepositoryTests { @@ -293,7 +298,8 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Mat");
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(Example.newExampleOf(sample).matchStringsStartingWith().get());
Example<Person> example = Example.of(sample, ExampleSpec.untyped().withStringMatcher(StringMatcher.STARTING));
List<Person> result = repository.findAll(example);
assertThat(result, hasItems(dave, oliver));
assertThat(result, hasSize(2));
@ -319,7 +325,7 @@ public class SimpleMongoRepositoryTests { @@ -319,7 +325,7 @@ public class SimpleMongoRepositoryTests {
sample.setCreator(user);
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result, hasItem(megan));
assertThat(result, hasSize(1));
@ -340,7 +346,7 @@ public class SimpleMongoRepositoryTests { @@ -340,7 +346,7 @@ public class SimpleMongoRepositoryTests {
sample.setLocation(megan.getLocation());
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result, hasItem(megan));
assertThat(result, hasSize(1));
@ -361,7 +367,7 @@ public class SimpleMongoRepositoryTests { @@ -361,7 +367,7 @@ public class SimpleMongoRepositoryTests {
sample.setLocation(megan.getLocation());
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<Person>(sample));
List<Person> result = repository.findAll(Example.of(sample));
assertThat(result, hasItem(megan));
assertThat(result, hasSize(1));
@ -377,12 +383,59 @@ public class SimpleMongoRepositoryTests { @@ -377,12 +383,59 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email");
List<Person> result = repository.findAllByExample(new Example<PersonExtended>(sample));
List<PersonExtended> result = repository.findAll(Example.of(sample));
assertThat(result, hasItems(dave, oliver));
assertThat(result, containsInAnyOrder(dave, oliver));
assertThat(result, hasSize(2));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void findOneByExampleShouldLookUpEntriesCorrectly() {
Person sample = new Person();
sample.setFirstname("Dave");
sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email");
Person result = repository.findOne(Example.of(sample));
assertThat(result, is(equalTo(dave)));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void existsByExampleShouldLookUpEntriesCorrectly() {
Person sample = new Person();
sample.setFirstname("Dave");
sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email");
boolean result = repository.exists(Example.of(sample));
assertThat(result, is(true));
}
/**
* @see DATAMONGO-1245
*/
@Test
public void countByExampleShouldLookUpEntriesCorrectly() {
Person sample = new Person();
sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email");
long result = repository.count(Example.of(sample));
assertThat(result, is(equalTo(2L)));
}
@Document(collection = "customizedPerson")
static class PersonExtended extends Person {

2
src/main/asciidoc/index.adoc

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
= Spring Data MongoDB - Reference Documentation
Mark Pollack; Thomas Risberg; Oliver Gierke; Costin Leau; Jon Brisbin; Thomas Darimont; Christoph Strobl
Mark Pollack; Thomas Risberg; Oliver Gierke; Costin Leau; Jon Brisbin; Thomas Darimont; Christoph Strobl; Mark Paluch
:revnumber: {version}
:revdate: {localdate}
:toc:

4
src/main/asciidoc/reference/mongodb.adoc

@ -1330,6 +1330,10 @@ TextQuery.searching(new TextCriteria().matching("\"coffee cake\"")); @@ -1330,6 +1330,10 @@ TextQuery.searching(new TextCriteria().matching("\"coffee cake\""));
TextQuery.searching(new TextCriteria().phrase("coffee cake"));
----
include::../{spring-data-commons-docs}/query-by-example.adoc[]
include::query-by-example.adoc[]
[[mongo.mapreduce]]
== Map-Reduce Operations

71
src/main/asciidoc/reference/query-by-example.adoc

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
[[query.by.example.execution]]
=== Executing Example
.Query by Example using a Repository
====
[source, java]
----
public interface PersonRepository extends QueryByExampleExecutor<Person> {
}
public class PersonService {
@Autowired PersonRepository personRepository;
public List<Person> findPeople(Person probe) {
return personRepository.findAll(Example.of(probe));
}
}
----
====
An `Example` containing an untyped `ExampleSpec` uses the Repository type and its collection name. Typed `ExampleSpec` use their type as result type and the collection name from the Repository.
NOTE: When including `null` values in the `ExampleSpec` Spring Data Mongo uses embedded document matching instead of dot notation property matching. This forces exact document matching for all property values and the property order in the embedded document.
Spring Data MongoDB provides support for the following matching options:
[cols="1,2", options="header"]
.`StringMatcher` options
|===
| Matching
| Logical result
| `DEFAULT` (case-sensitive)
| `{"firstname" : firstname}`
| `DEFAULT` (case-insensitive)
| `{"firstname" : { $regex: firstname, $options: 'i'}}`
| `EXACT` (case-sensitive)
| `{"firstname" : { $regex: /^firstname$/}}`
| `EXACT` (case-insensitive)
| `{"firstname" : { $regex: /^firstname$/, $options: 'i'}}`
| `STARTING` (case-sensitive)
| `{"firstname" : { $regex: /^firstname/}}`
| `STARTING` (case-insensitive)
| `{"firstname" : { $regex: /^firstname/, $options: 'i'}}`
| `ENDING` (case-sensitive)
| `{"firstname" : { $regex: /firstname$/}}`
| `ENDING` (case-insensitive)
| `{"firstname" : { $regex: /firstname$/, $options: 'i'}}`
| `CONTAINING` (case-sensitive)
| `{"firstname" : { $regex: /.\*firstname.*/}}`
| `CONTAINING` (case-insensitive)
| `{"firstname" : { $regex: /.\*firstname.*/, $options: 'i'}}`
| `REGEX` (case-sensitive)
| `{"firstname" : { $regex: /firstname/}}`
| `REGEX` (case-insensitive)
| `{"firstname" : { $regex: /firstname/, $options: 'i'}}`
|===
Loading…
Cancel
Save