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. 114
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 106
      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. 44
      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. 13
      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. 142
      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. 7
      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

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

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

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.bson.BasicBSONObject;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.domain.Example.NullHandler; import org.springframework.data.domain.ExampleSpec;
import org.springframework.data.domain.Example.StringMatcher; import org.springframework.data.domain.ExampleSpec.NullHandler;
import org.springframework.data.domain.PropertySpecifier; 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.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.MongoRegexCreator; import org.springframework.data.mongodb.core.query.MongoRegexCreator;
import org.springframework.data.mongodb.core.query.SerializationUtils; 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.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -41,35 +49,43 @@ import com.mongodb.DBObject;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
* @since 1.8 * @since 1.8
*/ */
public class MongoExampleMapper { public class MongoExampleMapper {
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final MongoConverter converter; private final MongoConverter converter;
private final Map<StringMatcher, Type> stringMatcherPartMapping = new HashMap<StringMatcher, Type>();
public MongoExampleMapper(MongoConverter converter) { public MongoExampleMapper(MongoConverter converter) {
this.converter = converter; this.converter = converter;
this.mappingContext = converter.getMappingContext(); 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);
} }
/** /**
* Returns the given {@link Example} as {@link DBObject} holding matching values extracted from * Returns the given {@link Example} as {@link DBObject} holding matching values extracted from
* {@link Example#getProbe()}. * {@link Example#getProbe()}.
* *
* @param example * @param example
* @return * @return
* @since 1.8 * @since 1.8
*/ */
public DBObject getMappedExample(Example<?> example) { public DBObject getMappedExample(Example<?> example) {
return getMappedExample(example, mappingContext.getPersistentEntity(example.getSampleType())); return getMappedExample(example, mappingContext.getPersistentEntity(example.getProbeType()));
} }
/** /**
* Returns the given {@link Example} as {@link DBObject} holding matching values extracted from * Returns the given {@link Example} as {@link DBObject} holding matching values extracted from
* {@link Example#getProbe()}. * {@link Example#getProbe()}.
* *
* @param example * @param example
* @param entity * @param entity
* @return * @return
@ -77,21 +93,27 @@ public class MongoExampleMapper {
*/ */
public DBObject getMappedExample(Example<?> example, MongoPersistentEntity<?> entity) { 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()); reference.removeField(entity.getIdProperty().getFieldName());
} }
applyPropertySpecs("", reference, example); ExampleSpecAccessor exampleSpecAccessor = new ExampleSpecAccessor(example.getExampleSpec());
applyPropertySpecs("", reference, example.getProbeType(), exampleSpecAccessor);
return ObjectUtils.nullSafeEquals(NullHandler.INCLUDE, example.getNullHandler()) ? reference : new BasicDBObject( if (exampleSpecAccessor.isTyped()) {
SerializationUtils.flatMap(reference)); this.converter.getTypeMapper().writeTypeRestrictions(reference, (Set) Collections.singleton(example.getResultType()));
}
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(); Iterator<String> parts = Arrays.asList(path.split("\\.")).iterator();
@ -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)) { if (!(source instanceof BasicDBObject)) {
return; return;
@ -147,47 +170,37 @@ public class MongoExampleMapper {
while (iter.hasNext()) { while (iter.hasNext()) {
Map.Entry<String, Object> entry = iter.next(); Map.Entry<String, Object> entry = iter.next();
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
if (entry.getKey().equals("_id") && entry.getValue() == null) { String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType);
if(isEmptyIdProperty(entry)) {
iter.remove(); iter.remove();
continue; continue;
} }
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey(); if (exampleSpecAccessor.isIgnoredPath(propertyPath) || exampleSpecAccessor.isIgnoredPath(mappedPropertyPath)) {
String mappedPropertyPath = getMappedPropertyPath(propertyPath, example);
if (example.isIgnoredPath(propertyPath) || example.isIgnoredPath(mappedPropertyPath)) {
iter.remove(); iter.remove();
continue; continue;
} }
PropertySpecifier specifier = null; StringMatcher stringMatcher = exampleSpecAccessor.getDefaultStringMatcher();
StringMatcher stringMatcher = example.getDefaultStringMatcher();
Object value = entry.getValue(); Object value = entry.getValue();
boolean ignoreCase = example.isIngnoreCaseEnabled(); boolean ignoreCase = exampleSpecAccessor.isIgnoreCaseEnabled();
if (example.hasPropertySpecifiers()) { if (exampleSpecAccessor.hasPropertySpecifiers()) {
mappedPropertyPath = example.hasPropertySpecifier(propertyPath) ? propertyPath : getMappedPropertyPath( mappedPropertyPath = exampleSpecAccessor.hasPropertySpecifier(propertyPath) ? propertyPath
propertyPath, example); : getMappedPropertyPath(propertyPath, probeType);
specifier = example.getPropertySpecifier(mappedPropertyPath); stringMatcher = exampleSpecAccessor.getStringMatcherForPath(mappedPropertyPath);
ignoreCase = exampleSpecAccessor.isIgnoreCaseForPath(mappedPropertyPath);
if (specifier != null) {
if (specifier.hasStringMatcher()) {
stringMatcher = specifier.getStringMatcher();
}
if (specifier.getIgnoreCase() != null) {
ignoreCase = specifier.getIgnoreCase();
}
}
} }
// TODO: should a PropertySpecifier outrule the later on string matching? // 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) { if (value == null) {
iter.remove(); iter.remove();
continue; continue;
@ -199,11 +212,15 @@ public class MongoExampleMapper {
if (entry.getValue() instanceof String) { if (entry.getValue() instanceof String) {
applyStringMatcher(entry, stringMatcher, ignoreCase); applyStringMatcher(entry, stringMatcher, ignoreCase);
} else if (entry.getValue() instanceof BasicDBObject) { } 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) { private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher stringMatcher, boolean ignoreCase) {
BasicDBObject dbo = new BasicDBObject(); BasicDBObject dbo = new BasicDBObject();
@ -216,8 +233,8 @@ public class MongoExampleMapper {
} }
} else { } else {
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), Type type = stringMatcherPartMapping.get(stringMatcher);
stringMatcher.getPartType()); String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), type);
dbo.put("$regex", expression); dbo.put("$regex", expression);
entry.setValue(dbo); entry.setValue(dbo);
} }
@ -226,4 +243,5 @@ public class MongoExampleMapper {
dbo.put("$options", "i"); 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 {
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists(); boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
Object value = keyword.getValue(); Object value = keyword.getValue();
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue( Object convertedValue = needsAssociationConversion ? convertAssociation(value, property)
property.with(keyword.getKey()), value); : getMappedValue(property.with(keyword.getKey()), value);
return new BasicDBObject(keyword.key, convertedValue); return new BasicDBObject(keyword.key, convertedValue);
} }
@ -484,8 +484,8 @@ public class QueryMapper {
} }
try { try {
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
.convert(id, ObjectId.class) : delegateConvertToMongoType(id, null); : delegateConvertToMongoType(id, null);
} catch (ConversionException o_O) { } catch (ConversionException o_O) {
return delegateConvertToMongoType(id, null); 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 {
* @since 1.8 * @since 1.8
*/ */
public static Criteria byExample(Object example) { 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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import org.springframework.util.ObjectUtils;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
* @since 1.8 * @since 1.8
*/ */
public enum MongoRegexCreator { public enum MongoRegexCreator {
@ -67,6 +68,10 @@ public enum MongoRegexCreator {
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Type type) { private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Type type) {
if (ObjectUtils.nullSafeEquals(Type.REGEX, type)) {
return source;
}
if (!ObjectUtils.nullSafeEquals(Type.LIKE, type)) { if (!ObjectUtils.nullSafeEquals(Type.LIKE, type)) {
return PUNCTATION_PATTERN.matcher(source).find() ? Pattern.quote(source) : source; 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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -63,7 +63,7 @@ public abstract class SerializationUtils {
* @return {@link Collections#emptyMap()} when source is {@literal null} * @return {@link Collections#emptyMap()} when source is {@literal null}
* @since 1.8 * @since 1.8
*/ */
public static Map<String, Object> flatMap(DBObject source) { public static Map<String, Object> flattenMap(DBObject source) {
if (source == null) { if (source == null) {
return Collections.emptyMap(); return Collections.emptyMap();

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

@ -19,21 +19,22 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
import org.springframework.data.domain.Example; 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.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
/** /**
* Mongo specific {@link org.springframework.data.repository.Repository} interface. * Mongo specific {@link org.springframework.data.repository.Repository} interface.
* *
* @author Oliver Gierke * @author Oliver Gierke
* @author Christoph Strobl * @author Christoph Strobl
* @author Thomas Darimont * @author Thomas Darimont
* @author Mark Paluch
*/ */
@NoRepositoryBean @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) * (non-Javadoc)
@ -57,7 +58,7 @@ public interface MongoRepository<T, ID extends Serializable> extends PagingAndSo
* Inserts the given a given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use * Inserts the given a given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use
* the returned instance for further operations as the save operation might have changed the entity instance * the returned instance for further operations as the save operation might have changed the entity instance
* completely. Prefer using {@link #save(Object)} instead to avoid the usage of store-specific API. * completely. Prefer using {@link #save(Object)} instead to avoid the usage of store-specific API.
* *
* @param entity must not be {@literal null}. * @param entity must not be {@literal null}.
* @return the saved entity * @return the saved entity
* @since 1.7 * @since 1.7
@ -68,40 +69,21 @@ public interface MongoRepository<T, ID extends Serializable> extends PagingAndSo
* Inserts the given entities. Assumes the given entities to have not been persisted yet and thus will optimize the * Inserts the given entities. Assumes the given entities to have not been persisted yet and thus will optimize the
* insert over a call to {@link #save(Iterable)}. Prefer using {@link #save(Iterable)} to avoid the usage of store * insert over a call to {@link #save(Iterable)}. Prefer using {@link #save(Iterable)} to avoid the usage of store
* specific API. * specific API.
* *
* @param entities must not be {@literal null}. * @param entities must not be {@literal null}.
* @return the saved entities * @return the saved entities
* @since 1.7 * @since 1.7
*/ */
<S extends T> List<S> insert(Iterable<S> entities); <S extends T> List<S> insert(Iterable<S> entities);
/** /* (non-Javadoc)
* Returns all instances of the type specified by the given {@link Example}. * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
*
* @param example must not be {@literal null}.
* @return
* @since 1.8
*/ */
<S extends T> List<T> findAllByExample(Example<S> example); <S extends T> List<S> findAll(Example<S> example);
/** /* (non-Javadoc)
* Returns all instances of the type specified by the given {@link Example}. * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
*
* @param example must not be {@literal null}.
* @param sort can be {@literal null}.
* @return all entities sorted by the given options
* @since 1.8
*/ */
<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;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range; import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@ -133,11 +132,6 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return delegate.getFullText(); return delegate.getFullText();
} }
@Override
public Example<?> getSampleObject() {
return delegate.getSampleObject();
}
/** /**
* Converts the given value with the underlying {@link MongoWriter}. * 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 @@
*/ */
package org.springframework.data.mongodb.repository.query; package org.springframework.data.mongodb.repository.query;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Range; import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance; import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point; import org.springframework.data.geo.Point;
@ -61,12 +60,4 @@ public interface MongoParameterAccessor extends ParameterAccessor {
* @since 1.8 * @since 1.8
*/ */
Object[] getValues(); 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;
import java.util.List; import java.util.List;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Range; import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance; import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point; import org.springframework.data.geo.Point;
@ -43,7 +42,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
private final int rangeIndex; private final int rangeIndex;
private final int maxDistanceIndex; private final int maxDistanceIndex;
private final Integer fullTextIndex; private final Integer fullTextIndex;
private final int sampleObjectIndex;
private Integer nearIndex; private Integer nearIndex;
@ -71,12 +69,10 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
} else if (this.nearIndex == null) { } else if (this.nearIndex == null) {
this.nearIndex = -1; this.nearIndex = -1;
} }
this.sampleObjectIndex = parameterTypes.indexOf(Example.class);
} }
private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, Integer nearIndex, private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, Integer nearIndex,
Integer fullTextIndex, int rangeIndex, int sampleObjectIndex) { Integer fullTextIndex, int rangeIndex) {
super(parameters); super(parameters);
@ -84,7 +80,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
this.fullTextIndex = fullTextIndex; this.fullTextIndex = fullTextIndex;
this.maxDistanceIndex = maxDistanceIndex; this.maxDistanceIndex = maxDistanceIndex;
this.rangeIndex = rangeIndex; this.rangeIndex = rangeIndex;
this.sampleObjectIndex = sampleObjectIndex;
} }
private final int getNearIndex(List<Class<?>> parameterTypes) { private final int getNearIndex(List<Class<?>> parameterTypes) {
@ -187,22 +182,13 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
return rangeIndex; return rangeIndex;
} }
/**
* @return
* @since 1.8
*/
public int getSampleObjectParameterIndex() {
return sampleObjectIndex;
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List) * @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List)
*/ */
@Override @Override
protected MongoParameters createFrom(List<MongoParameter> parameters) { protected MongoParameters createFrom(List<MongoParameter> parameters) {
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex, return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex);
this.sampleObjectIndex);
} }
private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type, Class<?> componentType) { private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type, Class<?> componentType) {
@ -254,7 +240,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
@Override @Override
public boolean isSpecialParameter() { public boolean isSpecialParameter() {
return super.isSpecialParameter() || Distance.class.isAssignableFrom(getType()) || isNearParameter() return super.isSpecialParameter() || Distance.class.isAssignableFrom(getType()) || isNearParameter()
|| TextCriteria.class.isAssignableFrom(getType()) || isExample(); || TextCriteria.class.isAssignableFrom(getType());
} }
private boolean isNearParameter() { private boolean isNearParameter() {
@ -274,10 +260,6 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
return parameter.getParameterAnnotation(Near.class) != null; return parameter.getParameterAnnotation(Near.class) != null;
} }
private boolean isExample() {
return Example.class.isAssignableFrom(getType());
}
} }
} }

13
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;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Range; import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance; import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point; import org.springframework.data.geo.Point;
@ -126,15 +125,9 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
return ((TextCriteria) fullText); return ((TextCriteria) fullText);
} }
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(
"Expected full text parameter to be one of String, Term or TextCriteria but found %s.", String.format("Expected full text parameter to be one of String, Term or TextCriteria but found %s.",
ClassUtils.getShortName(fullText.getClass()))); ClassUtils.getShortName(fullText.getClass())));
}
public Example<?> getSampleObject() {
int index = method.getParameters().getSampleObjectParameterIndex();
return index >= 0 ? (Example<?>) getValue(index) : null;
} }
/* /*

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> {
@Override @Override
protected Query complete(Criteria criteria, Sort sort) { protected Query complete(Criteria criteria, Sort sort) {
Criteria toUse = null; Query query = (criteria == null ? new Query() : new Query(criteria)).with(sort);
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);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query); LOG.debug("Created query " + query);
@ -298,8 +285,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
case ALWAYS: case ALWAYS:
if (path.getType() != String.class) { if (path.getType() != String.class) {
throw new IllegalArgumentException(String.format("Part %s must be of type String but was %s", path, throw new IllegalArgumentException(
path.getType())); String.format("Part %s must be of type String but was %s", path, path.getType()));
} }
// fall-through // fall-through
@ -385,8 +372,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return (T) parameter; return (T) parameter;
} }
throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type, throw new IllegalArgumentException(
parameter.getClass())); String.format("Expected parameter type of %s but got %s!", type, parameter.getClass()));
} }
private Object[] nextAsArray(Iterator<Object> iterator) { 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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,6 +30,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; 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.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Criteria;
@ -44,6 +45,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke * @author Oliver Gierke
* @author Christoph Strobl * @author Christoph Strobl
* @author Thomas Darimont * @author Thomas Darimont
* @author Mark Paluch
*/ */
public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> { public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {
@ -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}. * Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
* *
* @param metadata must not be {@literal null}. * @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) { public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
@ -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) * @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable)
*/ */
@Override @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!"); Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example)).with(pageable); 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) { 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(), return new PageImpl<S>(mongoOperations.find(q, getResultType(example), entityInformation.getCollectionName()), pageable,
entityInformation.getCollectionName()), pageable, count); count);
} }
/* /*
@ -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) * @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
*/ */
@Override @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!"); Assert.notNull(example, "Sample must not be null!");
@ -294,7 +296,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
q.with(sort); 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
* @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example) * @see org.springframework.data.mongodb.repository.MongoRepository#findAllByExample(org.springframework.data.domain.Example)
*/ */
@Override @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!"); Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example)); 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) { private List<T> findAll(Query query) {

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

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

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.springframework.data.domain.Example.*; 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.core.DBObjectTestUtils.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*; import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
@ -34,6 +33,9 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Example; 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.geo.Point;
import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes; import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes;
@ -49,6 +51,7 @@ import com.mongodb.DBObject;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class MongoExampleMapperUnitTests { public class MongoExampleMapperUnitTests {
@ -79,7 +82,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument(); FlatDocument probe = new FlatDocument();
probe.id = "steelheart"; 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())); assertThat(dbo, is(new BasicDBObjectBuilder().add("_id", "steelheart").get()));
} }
@ -95,11 +98,10 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.intValue = 100; 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, assertThat(dbo, is(new BasicDBObjectBuilder().add("_id", "steelheart").add("stringValue", "firefight")
is(new BasicDBObjectBuilder().add("_id", "steelheart").add("stringValue", "firefight").add("intValue", 100) .add("intValue", 100).get()));
.get()));
} }
/** /**
@ -112,7 +114,7 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.intValue = 100; 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())); assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", "firefight").add("intValue", 100).get()));
} }
@ -126,7 +128,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument(); FlatDocument probe = new FlatDocument();
probe.listOfString = Arrays.asList("Prof", "Tia", "David"); 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())); assertThat(dbo, is(new BasicDBObjectBuilder().add("listOfString", Arrays.asList("Prof", "Tia", "David")).get()));
} }
@ -140,7 +142,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument(); FlatDocument probe = new FlatDocument();
probe.customNamedField = "Mitosis"; 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())); assertThat(dbo, is(new BasicDBObjectBuilder().add("custom_field_name", "Mitosis").get()));
} }
@ -149,13 +151,30 @@ public class MongoExampleMapperUnitTests {
* @see DATAMONGO-1245 * @see DATAMONGO-1245
*/ */
@Test @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(); WrapperDocument probe = new WrapperDocument();
probe.flatDoc = new FlatDocument(); probe.flatDoc = new FlatDocument();
probe.flatDoc.stringValue = "conflux"; 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())); assertThat(dbo, is(new BasicDBObjectBuilder().add("flatDoc.stringValue", "conflux").get()));
} }
@ -164,13 +183,13 @@ public class MongoExampleMapperUnitTests {
* @see DATAMONGO-1245 * @see DATAMONGO-1245
*/ */
@Test @Test
public void exampleShouldBeMappedAsExactObjectWhenGivenNestedElementsWithStriktMatchMode() { public void exampleShouldBeMappedAsExactObjectWhenGivenNestedElementsWithStrictMatchMode() {
WrapperDocument probe = new WrapperDocument(); WrapperDocument probe = new WrapperDocument();
probe.flatDoc = new FlatDocument(); probe.flatDoc = new FlatDocument();
probe.flatDoc.stringValue = "conflux"; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class));
@ -187,13 +206,30 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.intValue = 100; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "^firefight"))
is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "^firefight")) .add("intValue", 100).get()));
.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()));
} }
/** /**
@ -206,13 +242,30 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.intValue = 100; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat(dbo, assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "firefight$"))
is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", "firefight$")) .add("intValue", 100).get()));
.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()));
} }
/** /**
@ -225,12 +278,12 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.intValue = 100; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat( assertThat(dbo,
dbo,
is(new BasicDBObjectBuilder() is(new BasicDBObjectBuilder()
.add("stringValue", new BasicDBObjectBuilder().add("$regex", "firefight$").add("$options", "i").get()) .add("stringValue", new BasicDBObjectBuilder().add("$regex", "firefight$").add("$options", "i").get())
.add("intValue", 100).get())); .add("intValue", 100).get()));
@ -246,12 +299,11 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.intValue = 100; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat( assertThat(dbo,
dbo,
is(new BasicDBObjectBuilder() is(new BasicDBObjectBuilder()
.add("stringValue", .add("stringValue",
new BasicDBObjectBuilder().add("$regex", Pattern.quote("firefight")).add("$options", "i").get()) new BasicDBObjectBuilder().add("$regex", Pattern.quote("firefight")).add("$options", "i").get())
@ -269,7 +321,7 @@ public class MongoExampleMapperUnitTests {
probe.referenceDocument = new ReferenceDocument(); probe.referenceDocument = new ReferenceDocument();
probe.referenceDocument.id = "200"; 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); com.mongodb.DBRef reference = getTypedValue(dbo, "referenceDocument", com.mongodb.DBRef.class);
assertThat(reference.getId(), Is.<Object> is("200")); assertThat(reference.getId(), Is.<Object> is("200"));
@ -285,7 +337,7 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument(); FlatDocument probe = new FlatDocument();
probe.stringValue = "steelheart"; 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())); assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", "steelheart").get()));
} }
@ -299,7 +351,7 @@ public class MongoExampleMapperUnitTests {
ClassWithGeoTypes probe = new ClassWithGeoTypes(); ClassWithGeoTypes probe = new ClassWithGeoTypes();
probe.legacyPoint = new Point(10D, 20D); 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.x"), Is.<Object> is(10D));
assertThat(dbo.get("legacyPoint.y"), Is.<Object> is(20D)); assertThat(dbo.get("legacyPoint.y"), Is.<Object> is(20D));
@ -316,7 +368,7 @@ public class MongoExampleMapperUnitTests {
probe.intValue = 10; probe.intValue = 10;
probe.stringValue = "string"; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
@ -334,7 +386,7 @@ public class MongoExampleMapperUnitTests {
probe.intValue = 10; probe.intValue = 10;
probe.stringValue = "string"; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
@ -353,12 +405,12 @@ public class MongoExampleMapperUnitTests {
probe.flatDoc.intValue = 10; probe.flatDoc.intValue = 10;
probe.flatDoc.stringValue = "string"; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("flatDoc.custom_field_name", "foo").add("flatDoc.intValue", 10) assertThat(dbo,
.get())); is(new BasicDBObjectBuilder().add("flatDoc.custom_field_name", "foo").add("flatDoc.intValue", 10).get()));
} }
/** /**
@ -373,12 +425,12 @@ public class MongoExampleMapperUnitTests {
probe.flatDoc.intValue = 10; probe.flatDoc.intValue = 10;
probe.flatDoc.stringValue = "string"; 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)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class));
assertThat(dbo, is(new BasicDBObjectBuilder().add("flatDoc.stringValue", "string").add("flatDoc.intValue", 10) assertThat(dbo,
.get())); is(new BasicDBObjectBuilder().add("flatDoc.stringValue", "string").add("flatDoc.intValue", 10).get()));
} }
/** /**
@ -391,15 +443,13 @@ public class MongoExampleMapperUnitTests {
probe.stringValue = "firefight"; probe.stringValue = "firefight";
probe.customNamedField = "steelheart"; probe.customNamedField = "steelheart";
Example<?> example = newExampleOf(probe).specify(newPropertySpecifier("stringValue").matchStringContaining().get()) Example<?> example = Example.of(probe,
.get(); ExampleSpec.untyped().withMatcher("stringValue", new GenericPropertyMatcher().contains()));
DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)); DBObject dbo = mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class));
assertThat( assertThat(dbo, is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", ".*firefight.*"))
dbo, .add("custom_field_name", "steelheart").get()));
is(new BasicDBObjectBuilder().add("stringValue", new BasicDBObject("$regex", ".*firefight.*"))
.add("custom_field_name", "steelheart").get()));
} }
/** /**
@ -413,7 +463,7 @@ public class MongoExampleMapperUnitTests {
probe.customNamedField = "steelheart"; probe.customNamedField = "steelheart";
probe.anotherStringValue = "calamity"; 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")); 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 @@
/* /*
* Copyright 2015 the original author or authors. * Copyright 2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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;
import com.mongodb.MongoClient; import com.mongodb.MongoClient;
/**
* @author Christoph Strobl
* @author Mark Paluch
*/
public class QueryByExampleTests { public class QueryByExampleTests {
MongoTemplate template; MongoTemplate template;
@ -54,7 +58,9 @@ public class QueryByExampleTests {
Person sample = new Person(); Person sample = new Person();
sample.lastname = "stark"; 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)); Assert.assertThat(result.size(), Is.is(2));
} }
@ -70,7 +76,9 @@ public class QueryByExampleTests {
sample.lastname = "stark"; sample.lastname = "stark";
sample.firstname = "arya"; 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)); Assert.assertThat(result.size(), Is.is(1));
} }
@ -88,7 +96,9 @@ public class QueryByExampleTests {
Person sample = new Person(); Person sample = new Person();
sample.id = p4.id; 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)); Assert.assertThat(result.size(), Is.is(1));
} }
@ -104,7 +114,10 @@ public class QueryByExampleTests {
sample.firstname = "jon"; sample.firstname = "jon";
sample.firstname = "stark"; 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)); Assert.assertThat(result.size(), Is.is(0));
} }
@ -118,7 +131,9 @@ public class QueryByExampleTests {
Person sample = new Person(); 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)); Assert.assertThat(result.size(), Is.is(3));
} }
@ -133,7 +148,7 @@ public class QueryByExampleTests {
Person sample = new Person(); Person sample = new Person();
sample.lastname = "stark"; 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); List<Person> result = template.find(query, Person.class);
Assert.assertThat(result.size(), Is.is(1)); 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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,8 +28,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -66,6 +64,7 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author Oliver Gierke * @author Oliver Gierke
* @author Thomas Darimont * @author Thomas Darimont
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractPersonRepositoryIntegrationTests { public abstract class AbstractPersonRepositoryIntegrationTests {
@ -179,8 +178,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void executesPagedFinderCorrectly() throws Exception { public void executesPagedFinderCorrectly() throws Exception {
Page<Person> page = repository.findByLastnameLike("*a*", new PageRequest(0, 2, Direction.ASC, "lastname", Page<Person> page = repository.findByLastnameLike("*a*",
"firstname")); new PageRequest(0, 2, Direction.ASC, "lastname", "firstname"));
assertThat(page.isFirst(), is(true)); assertThat(page.isFirst(), is(true));
assertThat(page.isLast(), is(false)); assertThat(page.isLast(), is(false));
assertThat(page.getNumberOfElements(), is(2)); assertThat(page.getNumberOfElements(), is(2));
@ -190,8 +189,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void executesPagedFinderWithAnnotatedQueryCorrectly() throws Exception { public void executesPagedFinderWithAnnotatedQueryCorrectly() throws Exception {
Page<Person> page = repository.findByLastnameLikeWithPageable(".*a.*", new PageRequest(0, 2, Direction.ASC, Page<Person> page = repository.findByLastnameLikeWithPageable(".*a.*",
"lastname", "firstname")); new PageRequest(0, 2, Direction.ASC, "lastname", "firstname"));
assertThat(page.isFirst(), is(true)); assertThat(page.isFirst(), is(true));
assertThat(page.isLast(), is(false)); assertThat(page.isLast(), is(false));
assertThat(page.getNumberOfElements(), is(2)); assertThat(page.getNumberOfElements(), is(2));
@ -315,8 +314,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void findsPagedPeopleByPredicate() throws Exception { public void findsPagedPeopleByPredicate() throws Exception {
Page<Person> page = repository.findAll(person.lastname.contains("a"), new PageRequest(0, 2, Direction.ASC, Page<Person> page = repository.findAll(person.lastname.contains("a"),
"lastname")); new PageRequest(0, 2, Direction.ASC, "lastname"));
assertThat(page.isFirst(), is(true)); assertThat(page.isFirst(), is(true));
assertThat(page.isLast(), is(false)); assertThat(page.isLast(), is(false));
assertThat(page.getNumberOfElements(), is(2)); assertThat(page.getNumberOfElements(), is(2));
@ -402,8 +401,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point); dave.setLocation(point);
repository.save(dave); repository.save(dave);
GeoResults<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, GeoResults<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
Metrics.KILOMETERS)); new Distance(2000, Metrics.KILOMETERS));
assertThat(results.getContent().isEmpty(), is(false)); assertThat(results.getContent().isEmpty(), is(false));
} }
@ -414,8 +413,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point); dave.setLocation(point);
repository.save(dave); repository.save(dave);
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
Metrics.KILOMETERS), new PageRequest(0, 20)); new Distance(2000, Metrics.KILOMETERS), new PageRequest(0, 20));
assertThat(results.getContent().isEmpty(), is(false)); assertThat(results.getContent().isEmpty(), is(false));
// DATAMONGO-607 // DATAMONGO-607
@ -625,8 +624,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
repository.save(Arrays.asList(dave, oliver, carter, boyd, leroi)); repository.save(Arrays.asList(dave, oliver, carter, boyd, leroi));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
Metrics.KILOMETERS), new PageRequest(1, 2)); new Distance(2000, Metrics.KILOMETERS), new PageRequest(1, 2));
assertThat(results.getContent().isEmpty(), is(false)); assertThat(results.getContent().isEmpty(), is(false));
assertThat(results.getNumberOfElements(), is(2)); assertThat(results.getNumberOfElements(), is(2));
@ -650,8 +649,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
repository.save(Arrays.asList(dave, oliver, carter)); repository.save(Arrays.asList(dave, oliver, carter));
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
Metrics.KILOMETERS), new PageRequest(1, 2)); new Distance(2000, Metrics.KILOMETERS), new PageRequest(1, 2));
assertThat(results.getContent().isEmpty(), is(false)); assertThat(results.getContent().isEmpty(), is(false));
assertThat(results.getNumberOfElements(), is(1)); assertThat(results.getNumberOfElements(), is(1));
assertThat(results.isFirst(), is(false)); assertThat(results.isFirst(), is(false));
@ -669,8 +668,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point); dave.setLocation(point);
repository.save(dave); repository.save(dave);
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
Metrics.KILOMETERS), new PageRequest(0, 2)); new Distance(2000, Metrics.KILOMETERS), new PageRequest(0, 2));
assertThat(results.getContent().isEmpty(), is(false)); assertThat(results.getContent().isEmpty(), is(false));
assertThat(results.getNumberOfElements(), is(1)); assertThat(results.getNumberOfElements(), is(1));
@ -688,8 +687,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(new Point(-73.99171, 40.738868)); dave.setLocation(new Point(-73.99171, 40.738868));
repository.save(dave); repository.save(dave);
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73),
Metrics.KILOMETERS), new PageRequest(1, 2)); new Distance(2000, Metrics.KILOMETERS), new PageRequest(1, 2));
assertThat(results.getContent().isEmpty(), is(true)); assertThat(results.getContent().isEmpty(), is(true));
assertThat(results.getNumberOfElements(), is(0)); assertThat(results.getNumberOfElements(), is(0));
@ -939,8 +938,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void shouldLimitCollectionQueryToMaxResultsWhenPresent() { public void shouldLimitCollectionQueryToMaxResultsWhenPresent() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3", repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan")));
List<Person> result = repository.findTop3ByLastnameStartingWith("Dylan"); List<Person> result = repository.findTop3ByLastnameStartingWith("Dylan");
assertThat(result.size(), is(3)); assertThat(result.size(), is(3));
} }
@ -951,8 +950,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void shouldNotLimitPagedQueryWhenPageRequestWithinBounds() { public void shouldNotLimitPagedQueryWhenPageRequestWithinBounds() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3", repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "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)); Page<Person> result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(0, 2));
assertThat(result.getContent().size(), is(2)); assertThat(result.getContent().size(), is(2));
} }
@ -963,8 +962,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void shouldLimitPagedQueryWhenPageRequestExceedsUpperBoundary() { public void shouldLimitPagedQueryWhenPageRequestExceedsUpperBoundary() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3", repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "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)); Page<Person> result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(1, 2));
assertThat(result.getContent().size(), is(1)); assertThat(result.getContent().size(), is(1));
} }
@ -975,8 +974,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
@Test @Test
public void shouldReturnEmptyWhenPageRequestedPageIsTotallyOutOfScopeForLimit() { public void shouldReturnEmptyWhenPageRequestedPageIsTotallyOutOfScopeForLimit() {
repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), new Person("Bob-3", repository.save(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"),
"Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "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)); Page<Person> result = repository.findTop3ByLastnameStartingWith("Dylan", new PageRequest(2, 2));
assertThat(result.getContent().size(), is(0)); assertThat(result.getContent().size(), is(0));
} }
@ -1240,8 +1239,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
ReflectionTestUtils.setField(sample, "createdAt", null); ReflectionTestUtils.setField(sample, "createdAt", null);
ReflectionTestUtils.setField(sample, "email", null); ReflectionTestUtils.setField(sample, "email", null);
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));
Assert.assertThat(result.getNumberOfElements(), Is.is(2)); assertThat(result.getNumberOfElements(), is(2));
} }
/** /**
@ -1258,8 +1257,8 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
ReflectionTestUtils.setField(sample, "createdAt", null); ReflectionTestUtils.setField(sample, "createdAt", null);
ReflectionTestUtils.setField(sample, "email", null); ReflectionTestUtils.setField(sample, "email", null);
List<Person> result = repository.findAllByExample(new Example<Person>(sample)); List<Person> result = repository.findAll(Example.of(sample));
Assert.assertThat(result.size(), Is.is(2)); 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;
@Document @Document
public abstract class Contact { public abstract class Contact {
@Id protected String id; @Id
protected final String id;
public Contact() { public Contact() {
this.id = new ObjectId().toString(); this.id = new ObjectId().toString();

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,11 +15,15 @@
*/ */
package org.springframework.data.mongodb.repository; package org.springframework.data.mongodb.repository;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; 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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 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. * Integration tests for {@link ContactRepository}. Mostly related to mapping inheritance.
* *
* @author Oliver Gierke * @author Oliver Gierke
* @author Mark Paluch
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("config/MongoNamespaceIntegrationTests-context.xml") @ContextConfiguration("config/MongoNamespaceIntegrationTests-context.xml")
@ -35,6 +40,11 @@ public class ContactRepositoryIntegrationTests {
@Autowired @Autowired
ContactRepository repository; ContactRepository repository;
@Before
public void setUp() throws Exception {
repository.deleteAll();
}
@Test @Test
public void readsAndWritesContactCorrectly() { public void readsAndWritesContactCorrectly() {
@ -43,4 +53,46 @@ public class ContactRepositoryIntegrationTests {
assertTrue(repository.findOne(result.getId().toString()) instanceof Person); 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()));
}
} }

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

@ -334,23 +334,22 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
*/ */
@Query("{ firstname : { $in : ?0 }}") @Query("{ firstname : { $in : ?0 }}")
Stream<Person> findByCustomQueryWithStreamingCursorByFirstnames(List<String> firstnames); Stream<Person> findByCustomQueryWithStreamingCursorByFirstnames(List<String> firstnames);
/** /**
* @see DATAMONGO-990 * @see DATAMONGO-990
*/ */
@Query("{ firstname : ?#{[0]}}") @Query("{ firstname : ?#{[0]}}")
List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly(String firstname); List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly(String firstname);
/** /**
* @see DATAMONGO-990 * @see DATAMONGO-990
*/ */
@Query("{ firstname : ?#{[0]}, email: ?#{principal.email} }") @Query("{ firstname : ?#{[0]}, email: ?#{principal.email} }")
List<Person> findWithSpelByFirstnameAndCurrentUserWithCustomQuery(String firstname); List<Person> findWithSpelByFirstnameAndCurrentUserWithCustomQuery(String firstname);
/** /**
* @see DATAMONGO-990 * @see DATAMONGO-990
*/ */
@Query("{ firstname : :#{#firstname}}") @Query("{ firstname : :#{#firstname}}")
List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterVariableOnly(@Param("firstname") String 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;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range; import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@ -148,13 +147,4 @@ class StubParameterAccessor implements MongoParameterAccessor {
public Class<?> getDynamicProjection() { public Class<?> getDynamicProjection() {
return null; 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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -32,6 +32,8 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example; 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.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.geo.Point; import org.springframework.data.geo.Point;
@ -51,6 +53,7 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author <a href="mailto:kowsercse@gmail.com">A. B. M. Kowser</a> * @author <a href="mailto:kowsercse@gmail.com">A. B. M. Kowser</a>
* @author Thomas Darimont * @author Thomas Darimont
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml") @ContextConfiguration("classpath:infrastructure.xml")
@ -130,7 +133,7 @@ public class SimpleMongoRepositoryTests {
* @see DATAMONGO-1054 * @see DATAMONGO-1054
*/ */
@Test @Test
public void shouldInsertMutlipleFromList() { public void shouldInsertMultipleFromList() {
String randomId = UUID.randomUUID().toString(); String randomId = UUID.randomUUID().toString();
Map<String, Person> idToPerson = new HashMap<String, Person>(); Map<String, Person> idToPerson = new HashMap<String, Person>();
@ -180,7 +183,7 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Matthews"); sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email"); 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(), hasItems(dave, oliver));
assertThat(result.getContent(), hasSize(2)); assertThat(result.getContent(), hasSize(2));
@ -196,9 +199,9 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Matthews"); sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email"); 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)); assertThat(result, hasSize(2));
} }
@ -218,7 +221,7 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(dave.getAddress()); sample.setAddress(dave.getAddress());
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItem(dave));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
@ -240,7 +243,7 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(new Address(null, null, "Washington")); sample.setAddress(new Address(null, null, "Washington"));
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItems(dave, oliver));
assertThat(result, hasSize(2)); assertThat(result, hasSize(2));
@ -259,7 +262,8 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(new Address(null, null, "Washington")); sample.setAddress(new Address(null, null, "Washington"));
trimDomainType(sample, "id", "createdAt", "email"); 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()); assertThat(result, empty());
} }
@ -277,7 +281,8 @@ public class SimpleMongoRepositoryTests {
sample.setAddress(dave.getAddress()); sample.setAddress(dave.getAddress());
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItem(dave));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
@ -293,7 +298,8 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Mat"); sample.setLastname("Mat");
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItems(dave, oliver));
assertThat(result, hasSize(2)); assertThat(result, hasSize(2));
@ -319,7 +325,7 @@ public class SimpleMongoRepositoryTests {
sample.setCreator(user); sample.setCreator(user);
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItem(megan));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
@ -340,7 +346,7 @@ public class SimpleMongoRepositoryTests {
sample.setLocation(megan.getLocation()); sample.setLocation(megan.getLocation());
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItem(megan));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
@ -361,7 +367,7 @@ public class SimpleMongoRepositoryTests {
sample.setLocation(megan.getLocation()); sample.setLocation(megan.getLocation());
trimDomainType(sample, "id", "createdAt", "email"); 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, hasItem(megan));
assertThat(result, hasSize(1)); assertThat(result, hasSize(1));
@ -377,12 +383,59 @@ public class SimpleMongoRepositoryTests {
sample.setLastname("Matthews"); sample.setLastname("Matthews");
trimDomainType(sample, "id", "createdAt", "email"); 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)); 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") @Document(collection = "customizedPerson")
static class PersonExtended extends Person { static class PersonExtended extends Person {

2
src/main/asciidoc/index.adoc

@ -1,5 +1,5 @@
= Spring Data MongoDB - Reference Documentation = 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} :revnumber: {version}
:revdate: {localdate} :revdate: {localdate}
:toc: :toc:

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

@ -1330,6 +1330,10 @@ TextQuery.searching(new TextCriteria().matching("\"coffee cake\""));
TextQuery.searching(new TextCriteria().phrase("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]] [[mongo.mapreduce]]
== Map-Reduce Operations == Map-Reduce Operations

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

@ -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