Compare commits

..

42 Commits

Author SHA1 Message Date
Spring Buildmaster
41dc57c84f DATAMONGO-1106 - Release version 1.7.0.M1 (Fowler M1). 2014-12-01 13:36:32 +01:00
Oliver Gierke
85d1fe1ce6 DATAMONGO-1106 - Prepare 1.7.0.M1 (Fowler M1). 2014-12-01 12:26:52 +01:00
Oliver Gierke
ac6067ad53 DATAMONGO-1106 - Updated changelog. 2014-12-01 12:25:53 +01:00
Thomas Darimont
173a62b5ce DATAMONGO-1085 - Fixed sorting with Querydsl in QueryDslMongoRepository.
We now translate QSort's OrderSpecifiers into appropriate sort criteria.
Previously the OrderSpecifiers were not correctly translated to appropriate property path expressions.

We're now overriding support for findAll(Pageable) and findAll(Sort) to QueryDslMongoRepository to apply special QSort handling.

Original pull request: #236.
2014-12-01 12:09:14 +01:00
Oliver Gierke
cbbafce73d DATAMONGO-1043 - Make sure we dynamically lookup SpEL based collection names for query execution.
Changed SimpleMongoEntityMetadata to keep a reference to the collection entity instead of the eagerly resolved collection name. This is to make sure the name gets re-evaluated for every query execution to support dynamically changing collections defined via SpEL expressions.

Related pull request: #238.
2014-11-28 20:26:23 +01:00
Oliver Gierke
2e74c19995 DATAMONGO-1054 - Polishing.
Tweaked JavaDoc of the APIs to be less specific about implementation internals and rather point to the save(…) methods. Changed SimpleMongoRepository.save(…) methods to inspect the given entity/entities and use the optimized insert(All)-calls if all entities are considered new.

Original pull request: #253.
2014-11-28 18:33:19 +01:00
Thomas Darimont
a212b7566c DATAMONGO-1054 - Add support for fast insertion via MongoRepository.insert(..).
Introduced new insert(..) method variants on MongoRepositories that delegates to MongoTemplate.insert(..). This bypasses ID-population, save event generation and version checking and allows for fast insertion of bulk data.

Original pull request: #253.
2014-11-28 18:33:18 +01:00
Oliver Gierke
08faa52ef4 DATAMONGO-1108 - Performance improvements in BasicMongoPersistentEntity.
BasicMongoPersistentEntity.getCollection() now avoids repeated SpEL-parsing and evaluating in case no SpEL expression is used. Parsing is happening at most once now. Evaluation is skipped entirely if the configured collection String is not or does not contain an expression.
2014-11-28 16:24:25 +01:00
Oliver Gierke
33bc4fffd9 DATAMONGO-1079 - Updated changelog. 2014-11-28 12:06:05 +01:00
Christoph Strobl
eca2108e15 DATAMONGO-1087 - Fix index resolver detecting cycles for partial match.
We now check for presence of a dot path to verify that we’ve detected a cycle.

Original pull request: #240.
2014-11-28 12:03:38 +01:00
Christoph Strobl
dab6034eb9 DATAMONGO-943 - Add support for $position to Update $push $each.
We now support $position on update.push.

Original pull request: #248.
2014-11-28 11:41:51 +01:00
Christoph Strobl
461e7d05d7 DATAMONGO-1092 - Ensure compatibility with MongoDB 2.8.0.rc0 and java driver 2.13.0-rc0.
We updated GroupByResults to allow working with changed data types returned for count and keys and fixed assertion on error message for duplicate keys.
Using java-driver 2.12.x when connecting to an 2.8.0.rc-0 instance is likely to cause trouble with authentication. This is the intended behavior.

2.8.0-rc0 throws error when removing elements from a collection that does not yet exist, which is different to what 2.6.x does.

The java-driver 2.13.0-rc0 works perfectly fine with a 2.6.x Server instance.
We deprecated Index.Duplicates#DROP since it has been removed in MongoDB 2.8

Original pull request: #246.
2014-11-28 11:33:12 +01:00
Oliver Gierke
10c37b101d DATAMONGO-1105 - Added implementation of QueryDslPredicateExecutor.findAll(OrderSpecifier<?>... orders).
Renamed QuerydslRepositorySupportUnitTests to QuerydslRepositorySupportTests as it's an integration test.
2014-11-28 10:37:21 +01:00
Christoph Strobl
81f2c910f7 DATAMONGO-1075 - Containing keyword is now correctly translated for collection properties.
We now inspect the properties type when creating criteria for CONTAINS keyword so that, if the target property is of type String, we use an expression, and if the property is collection like we try to finds an exact match within the collection using $in.

Added support for NotContaining along the way.

Original pull request: #241.
2014-11-27 17:06:39 +01:00
Thomas Darimont
1fd97713c1 DATAMONGO-1093 - Added hashCode() and equals(…) in BasicQuery.
We now have equals(…) and hashCode(…) methods on BasicQuery. Previously we solely relied on Query.hashCode()/equals(…) which didn't consider the fields of BasicQuery.

Introduced equals verifier library to automatically test equals contracts.
Added some additional test cases to BasicQueryUnitTests.

Original pull request: #252.
2014-11-27 16:45:35 +01:00
Oliver Gierke
2d3eeed9ec DATAMONGO-1102 - Added support for Java 8 date/time types.
We're now able to persist and read non-time-zoned JDK 8 date/time types (LocalDate, LocalTime, LocalDateTime) to and from Date instances.
2014-11-27 16:28:36 +01:00
Christoph Strobl
b22eb6f12f DATAMONGO-1101 - Add support for $bit to Update.
We now support bitwise and/or/xor operations for Update.
2014-11-26 11:26:56 +01:00
Mikhail Mikhaylenko
dfb0a2a368 DATAMONGO-1096 - Use null-safe toString representation of query for debug logging.
We now use the null-safe serailizeToJsonSafely to avoid potential RuntimeExceptions during debug query printing in MongoTemplate.

Based on original PR: #247.

Original pull request: #251.
2014-11-26 09:40:30 +01:00
Thomas Darimont
03bcc56429 DATAMONGO-1094 - Fixed ambiguous field mapping error message in BasicMongoPersistentEntity.
Original pull request: #245.
2014-11-25 17:32:16 +01:00
Christoph Strobl
457fda3fc3 DATAMONGO-1097 - Add support for $mul to Update.
We now support multiply on Update allowing to multiply the value of the given key by a multiplier.
2014-11-24 20:38:44 +01:00
Oliver Gierke
54cee64610 DATAMONGO-1100 - Upgrade to new PersistentPropertyAccessor API. 2014-11-20 15:12:25 +01:00
Christoph Strobl
477499248a DATAMONGO-1086 - Mapping fails for collection with two embbeded types that extend a generic abstract.
We now use the type information of the raw property type to check if we need to include _class.
2014-11-20 15:12:25 +01:00
Oliver Gierke
3b70b6aeee DATAMONGO-1078 - Polishing.
Polished test cases. Simplified equals(…)/hashCode() for sample entity and its identifier type.

Original pull request: #239.
2014-11-10 16:38:03 +01:00
Christoph Strobl
163762e99e DATAMONGO-1078 - @Query annotated repository method fails for complex Id when used with Collection type.
Remove object type hint defaulting.
2014-11-10 16:37:56 +01:00
Oliver Gierke
b99833df75 DATAMONGO-1080 - AbstractMongoQuery now refrains from eagerly post-processing the query execution results.
To properly support general post processing of query execution results (in QueryExecutorMethodInterceptor) we need to remove the eager post-processing of query execution results in AbstractMongoQuery.

Removed the usage of the local ConversionService all together.
2014-10-30 11:35:51 +01:00
Thomas Darimont
4be6231426 DATAMONGO-1076 - Avoid resolving lazy-loading proxy for DBRefs during finalize.
We now handle intercepted finalize method invocations by not resolving the proxy. Previously the LazyLoadingProxy tried to resolve the proxy during finalization which could lead to unnecessary database accesses.

Original pull request: #234.
2014-10-29 10:16:12 +01:00
Christoph Strobl
4673e3d511 DATAMONGO-1077 - Fix Update removing $ operator for DBRef.
We now retain the positional parameter "$" when mapping field names for associations.

Orignal pull request: #235.
2014-10-28 14:28:22 +01:00
Christoph Strobl
00e48cc424 DATAMONGO-1050 - Explicitly annotated Field should not be considered Id.
We changed the id resolution to skip properties having an explicit name set via @Field unless they are marked with @Id. This means that

@Field(“id”) String id;

will be stored as “id” within mongodb. Prior to this change the fieldname would have been changed to “_id”.
Added tests to ensure proper field mapping for various "id" field variants.

Original pull request: #225.
2014-10-23 11:39:17 +02:00
Christoph Strobl
f8453825fb DATAMONGO-1072 - Fix annotated query placeholders not replaced correctly.
We now also check field names for potential placeholder matches to ensure those are registered for binding parameters.

Original pull request: #233.
2014-10-22 13:55:50 +02:00
Christoph Strobl
6cda9ab939 DATAMONGO-1068 - Fix getCritieriaObject returns empty DBO when no key defined.
We now check for the presence of a Critieria key.

Original pull request: #232.
2014-10-21 11:36:15 +02:00
Oliver Gierke
831d667896 DATAMONGO-1070 - Fixed a few glitches in DBRef binding for repository query methods.
The QueryMapping for derived repository queries pointing to the identifier of the referenced document. We now reduce the query field's key from reference.id to reference so that the generated DBRef is applied correctly and also take care that the id's are potentially converted to ObjectIds. This is mainly achieved by using the AssociationConverter pulled up from UpdateMapper in ObjectMapper.getMappedKey().

MongoQueryCreator now refrains from translating the field keys as that will fail the QueryMapper to correctly detect id properties.

Fixed DBRef handling for StringBasedMongoQuery which previously didn't parse the DBRef instance created after JSON parsing for placeholders.
2014-10-15 10:13:53 +02:00
Christoph Strobl
17c342895a DATAMONGO-1063 - Fix application of Querydsl'S any().in() throwing Exception.
We now only convert paths that point to either a property or variable.

Original pull request: #230.
2014-10-10 11:35:45 +02:00
Christoph Strobl
6ef518e6a0 DATAMONGO-1053 - Type check is now only performed on explicit language properties.
We now only perform a type check on via @Language explicitly defined language properties. Prior to this change non-String properties named language caused errors on entity validation.

Original pull request: #228.
2014-10-10 11:31:30 +02:00
Oliver Gierke
ddee2fbb12 DATAMONGO-1057 - Polishing.
Slightly tweaked the changes in SlicedExecution to simplify the implementation. We now apply the given pageable but tweak the limit the query uses to peek into the next page.

Original pull request: #226.
2014-10-08 07:06:18 +02:00
Christoph Strobl
6512c2cdfb DATAMONGO-1057 - Fix SliceExecution skipping elements.
We now directly set the offset to use instead of reading it from the used pageable. This asserts that every single element is read from the store.
Prior to this change the altered pageSize lead to an unintended increase of the number of elements to skip.

Original pull request: #226.
2014-10-08 07:06:14 +02:00
Oliver Gierke
0eee05adaa DATAMONGO-1062 - Polishing.
Removed exploded static imports. Updated copyright header.

Original pull request: #229.
2014-10-07 15:32:18 +02:00
Christoph Strobl
17e0154ff3 DATAMONGO-1058 - DBRef should respect explicit field name.
We now use property.getFieldName() for mapping DbRefs. This assures we also capture explicitly defined names set via @Field.

Original pull request: #227.
2014-10-01 10:06:22 +02:00
Thomas Darimont
2780f60c65 DATAMONGO-1062 - Fix failing test in ServerAddressPropertyEditorUnitTests.
The test rejectsAddressConfigWithoutASingleParsableServerAddress fails because the supposedly non-existing hostname "bar" "now" resolves to a real host-address.

The addresses "gugu.nonexistant.example.org, gaga.nonexistant.example.org" shouldn't be resolvable TM.

Original pull request: #229.
2014-09-30 12:55:24 +02:00
Christoph Strobl
7dd3450362 DATAMONGO-1049 - Check for explicitly declared language field.
We now check for an explicitly declared language field for setting language_override within a text index. Therefore the attribute (even if named with the reserved keyword language) has to be explicitly marked with @Language. Prior to this change having:

@Language String lang;
String language;

would have caused trouble when trying to resolve index structures as one cannot set language override to more than one property.

Original pull request: #224.
2014-09-25 12:40:26 +02:00
Oliver Gierke
ca4b2a61b8 DATAMONGO-1046 - After release cleanups. 2014-09-15 14:30:23 +02:00
Oliver Gierke
d2ecd65ca5 DATAMONGO-1046 - After release cleanups. 2014-09-05 14:27:21 +02:00
Spring Buildmaster
03bd49f6c8 DATAMONGO-1046 - Prepare next development iteration. 2014-09-05 03:12:04 -07:00
82 changed files with 1049 additions and 2736 deletions

26
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.4.RELEASE</version>
<version>1.7.0.M1</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,8 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.5.4.RELEASE</version>
<version>1.6.0.M1</version>
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
</parent>
<modules>
@@ -28,9 +29,9 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>1.9.4.RELEASE</springdata.commons>
<mongo>2.12.5</mongo>
<mongo.osgi>2.12.5</mongo.osgi>
<springdata.commons>1.10.0.M1</springdata.commons>
<mongo>2.12.3</mongo>
<mongo.osgi>2.12.3</mongo.osgi>
</properties>
<developers>
@@ -107,7 +108,7 @@
<id>mongo-next</id>
<properties>
<mongo>2.14.0-SNAPSHOT</mongo>
<mongo>2.12.5-SNAPSHOT</mongo>
</properties>
<repositories>
@@ -119,15 +120,6 @@
</profile>
<profile>
<id>mongo3</id>
<properties>
<mongo>3.0.2</mongo>
</properties>
</profile>
<profile>
<id>mongo-3-next</id>
@@ -156,8 +148,8 @@
<repositories>
<repository>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
<id>spring-libs-milestone</id>
<url>http://repo.spring.io/libs-milestone</url>
</repository>
</repositories>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.4.RELEASE</version>
<version>1.7.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -14,7 +14,7 @@
<name>Spring Data MongoDB - Cross-Store Support</name>
<properties>
<jpa>2.0.0</jpa>
<jpa>1.0.0.Final</jpa>
<hibernate>3.6.10.Final</hibernate>
</properties>
@@ -48,7 +48,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.6.4.RELEASE</version>
<version>1.7.0.M1</version>
</dependency>
<dependency>
@@ -59,8 +59,8 @@
<!-- JPA -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>${jpa}</version>
<optional>true</optional>
</dependency>

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.4.RELEASE</version>
<version>1.7.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.4.RELEASE</version>
<version>1.7.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.4.RELEASE</version>
<version>1.7.0.M1</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -25,7 +25,7 @@ import com.mongodb.DBCursor;
interface CursorPreparer {
/**
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
* Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.
*
* @param cursor
*/

View File

@@ -49,7 +49,7 @@ public class MongoAction {
* @param collectionName the collection name, must not be {@literal null} or empty.
* @param entityType the POJO that is being operated against
* @param document the converted DBObject from the POJO or Spring Update object
* @param query the converted DBObject from the Spring Query object
* @param query the converted DBOjbect from the Spring Query object
*/
public MongoAction(WriteConcern defaultWriteConcern, MongoActionOperation mongoActionOperation,
String collectionName, Class<?> entityType, DBObject document, DBObject query) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -415,9 +415,7 @@ public interface MongoOperations {
/**
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}. Will consider entity mapping
* information to determine the collection the query is ran against. Note, that MongoDB limits the number of results
* by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a particular number of
* results.
* information to determine the collection the query is ran against.
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
@@ -426,9 +424,7 @@ public interface MongoOperations {
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass);
/**
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}. Note, that MongoDB limits the
* number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a
* particular number of results.
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}.
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
@@ -656,27 +652,13 @@ public interface MongoOperations {
long count(Query query, Class<?> entityClass);
/**
* Returns the number of documents for the given {@link Query} querying the given collection. The given {@link Query}
* must solely consist of document field references as we lack type information to map potential property references
* onto document fields. TO make sure the query gets mapped, use {@link #count(Query, Class, String)}.
* Returns the number of documents for the given {@link Query} querying the given collection.
*
* @param query
* @param collectionName must not be {@literal null} or empty.
* @return
* @see #count(Query, Class, String)
*/
long count(Query query, String collectionName);
/**
* Returns the number of documents for the given {@link Query} by querying the given collection using the given entity
* class to map the given {@link Query}.
*
* @param query
* @param entityClass must not be {@literal null}.
* @param collectionName must not be {@literal null} or empty.
* @return
*/
long count(Query query, Class<?> entityClass, String collectionName);
long count(Query query, String collectionName);
/**
* Insert the object into the collection for the entity type of the object to save.

View File

@@ -55,8 +55,9 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
@@ -335,7 +336,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
CommandResult result = execute(new DbCallback<CommandResult>() {
public CommandResult doInDB(DB db) throws MongoException, DataAccessException {
return readPreference != null ? db.command(command, readPreference) : db.command(command);
return db.command(command, options);
}
});
@@ -566,7 +567,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
BasicDBObject command = new BasicDBObject("geoNear", collection);
command.putAll(near.toDBObject());
CommandResult commandResult = executeCommand(command, getDb().getOptions());
CommandResult commandResult = executeCommand(command);
List<Object> results = (List<Object>) commandResult.get("results");
results = results == null ? Collections.emptyList() : results;
@@ -641,11 +642,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return count(query, null, collectionName);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperations#count(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
*/
public long count(Query query, Class<?> entityClass, String collectionName) {
private long count(Query query, Class<?> entityClass, String collectionName) {
Assert.hasText(collectionName);
final DBObject dbObject = query == null ? null : queryMapper.getMappedObject(query.getQueryObject(),
@@ -749,8 +746,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(entity.getClass());
if (mongoPersistentEntity != null && mongoPersistentEntity.hasVersionProperty()) {
BeanWrapper<Object> wrapper = BeanWrapper.create(entity, this.mongoConverter.getConversionService());
wrapper.setProperty(mongoPersistentEntity.getVersionProperty(), 0);
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(
mongoPersistentEntity.getPropertyAccessor(entity), mongoConverter.getConversionService());
accessor.setProperty(mongoPersistentEntity.getVersionProperty(), 0);
}
}
@@ -767,33 +765,27 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
protected <T> void doInsertAll(Collection<? extends T> listToSave, MongoWriter<T> writer) {
Map<String, List<T>> objs = new HashMap<String, List<T>>();
Map<String, List<T>> elementsByCollection = new HashMap<String, List<T>>();
for (T element : listToSave) {
if (element == null) {
continue;
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(element.getClass());
for (T o : listToSave) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(o.getClass());
if (entity == null) {
throw new InvalidDataAccessApiUsageException("No PersistentEntity information found for " + element.getClass());
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
+ o.getClass().getName());
}
String collection = entity.getCollection();
List<T> collectionElements = elementsByCollection.get(collection);
if (null == collectionElements) {
collectionElements = new ArrayList<T>();
elementsByCollection.put(collection, collectionElements);
List<T> objList = objs.get(collection);
if (null == objList) {
objList = new ArrayList<T>();
objs.put(collection, objList);
}
objList.add(o);
collectionElements.add(element);
}
for (Map.Entry<String, List<T>> entry : elementsByCollection.entrySet()) {
for (Map.Entry<String, List<T>> entry : objs.entrySet()) {
doInsertBatch(entry.getKey(), entry.getValue(), this.mongoConverter);
}
}
@@ -849,11 +841,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private <T> void doSaveVersioned(T objectToSave, MongoPersistentEntity<?> entity, String collectionName) {
BeanWrapper<T> beanWrapper = BeanWrapper.create(objectToSave, this.mongoConverter.getConversionService());
ConvertingPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor(
entity.getPropertyAccessor(objectToSave), mongoConverter.getConversionService());
MongoPersistentProperty idProperty = entity.getIdProperty();
MongoPersistentProperty versionProperty = entity.getVersionProperty();
Number version = beanWrapper.getProperty(versionProperty, Number.class);
Object version = convertingAccessor.getProperty(versionProperty);
Number versionNumber = convertingAccessor.getProperty(versionProperty, Number.class);
// Fresh instance -> initialize version property
if (version == null) {
@@ -863,12 +858,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
assertUpdateableIdIfNotSet(objectToSave);
// Create query for entity with the id and old version
Object id = beanWrapper.getProperty(idProperty);
Object id = convertingAccessor.getProperty(idProperty);
Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(versionProperty.getName()).is(version));
// Bump version number
Number number = beanWrapper.getProperty(versionProperty, Number.class);
beanWrapper.setProperty(versionProperty, number.longValue() + 1);
convertingAccessor.setProperty(versionProperty, versionNumber.longValue() + 1);
BasicDBObject dbObject = new BasicDBObject();
@@ -1018,7 +1012,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Calling update using query: %s and update: %s in collection: %s",
serializeToJsonSafely(queryObj), serializeToJsonSafely(updateObj), collectionName));
serializeToJsonSafely(queryObj), serializeToJsonSafely(updateObj), collectionName));
}
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName,
@@ -1099,12 +1093,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(objectType);
MongoPersistentProperty idProp = entity == null ? null : entity.getIdProperty();
if (idProp == null) {
if (idProp == null || entity == null) {
throw new MappingException("No id property found for object of type " + objectType);
}
Object idValue = BeanWrapper.create(object, mongoConverter.getConversionService())
.getProperty(idProp, Object.class);
Object idValue = entity.getPropertyAccessor(object).getProperty(idProp);
return Collections.singletonMap(idProp.getFieldName(), idValue).entrySet().iterator().next();
}
@@ -1148,12 +1141,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entity.getClass());
MongoPersistentProperty idProperty = persistentEntity == null ? null : persistentEntity.getIdProperty();
if (idProperty == null) {
if (idProperty == null || persistentEntity == null) {
return;
}
ConversionService service = mongoConverter.getConversionService();
Object idValue = BeanWrapper.create(entity, service).getProperty(idProperty, Object.class);
Object idValue = persistentEntity.getPropertyAccessor(entity).getProperty(idProperty);
if (idValue == null && !MongoSimpleTypes.AUTOGENERATED_ID_TYPES.contains(idProperty.getType())) {
throw new InvalidDataAccessApiUsageException(String.format(
@@ -1197,8 +1189,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Remove using query: {} in collection: {}.", new Object[] { serializeToJsonSafely(dboq),
collection.getName() });
LOGGER.debug("Remove using query: {} in collection: {}.",
new Object[] { serializeToJsonSafely(dboq), collection.getName() });
}
WriteResult wr = writeConcernToUse == null ? collection.remove(dboq) : collection.remove(dboq,
@@ -1426,7 +1418,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
LOGGER.debug("Executing aggregation: {}", serializeToJsonSafely(command));
}
CommandResult commandResult = executeCommand(command, getDb().getOptions());
CommandResult commandResult = executeCommand(command);
handleCommandError(commandResult, command);
return new AggregationResults<O>(returnPotentiallyMappedResults(outputType, commandResult), commandResult);
@@ -1672,7 +1664,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
EntityReader<? super T, DBObject> readerToUse = this.mongoConverter;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s",
serializeToJsonSafely(query), fields, sort, entityClass, collectionName));
serializeToJsonSafely(query), fields, sort, entityClass, collectionName));
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
return executeFindOneInternal(new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort),
@@ -1696,9 +1688,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
DBObject mappedUpdate = updateMapper.getMappedObject(update.getUpdateObject(), entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s "
+ "in collection: %s", serializeToJsonSafely(mappedQuery), fields, sort, entityClass,
serializeToJsonSafely(mappedUpdate), collectionName));
LOGGER.debug(String.format("findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s " +
"in collection: %s", serializeToJsonSafely(mappedQuery), fields, sort, entityClass,
serializeToJsonSafely(mappedUpdate), collectionName));
}
return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate, options),
@@ -1730,15 +1722,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
ConversionService conversionService = mongoConverter.getConversionService();
BeanWrapper<Object> wrapper = BeanWrapper.create(savedObject, conversionService);
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(savedObject.getClass());
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
Object idValue = wrapper.getProperty(idProp, idProp.getType());
if (idValue != null) {
if (accessor.getProperty(idProp) != null) {
return;
}
wrapper.setProperty(idProp, id);
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, id);
}
private DBCollection getAndPrepareCollection(DB db, String collectionName) {
@@ -2007,14 +1998,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException {
if (fields == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("findOne using query: %s in db.collection: %s", serializeToJsonSafely(query),
collection.getFullName()));
LOGGER.debug(String.format("findOne using query: %s in db.collection: %s",
serializeToJsonSafely(query), collection.getFullName()));
}
return collection.findOne(query);
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("findOne using query: %s fields: %s in db.collection: %s",
serializeToJsonSafely(query), fields, collection.getFullName()));
serializeToJsonSafely(query), fields, collection.getFullName()));
}
return collection.findOne(query, fields);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@ import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedFi
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.util.Assert;
@@ -292,19 +291,6 @@ public class Aggregation {
return Fields.from(field(name, target));
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the{@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
*
* @param query must not be {@literal null}.
* @param distanceField must not be {@literal null} or empty.
* @return
* @since 1.7
*/
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
return new GeoNearOperation(query, distanceField);
}
/**
* Returns a new {@link AggregationOptions.Builder}.
*

View File

@@ -88,7 +88,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
}
/**
* Creates a new {@link ExposedFields} instance for the given fields in either synthetic or non-synthetic way.
* Creates a new {@link ExposedFields} instance for the given fields in either sythetic or non-synthetic way.
*
* @param fields must not be {@literal null}.
* @param synthetic
@@ -107,7 +107,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
}
/**
* Creates a new {@link ExposedFields} with the given originals and synthetics.
* Creates a new {@link ExposedFields} with the given orignals and synthetics.
*
* @param originals must not be {@literal null}.
* @param synthetic must not be {@literal null}.
@@ -363,7 +363,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
}
/**
* Returns the reference value for the given field reference. Will return 1 for a synthetic, unaliased field or the
* Returns the referenve value for the given field reference. Will return 1 for a synthetic, unaliased field or the
* raw rendering of the reference otherwise.
*
* @return

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -84,15 +84,6 @@ public final class Fields implements Iterable<Field> {
return new AggregationField(name);
}
/**
* Creates a {@link Field} with the given {@code name} and {@code target}.
* <p>
* The {@code target} is the name of the backing document field that will be aliased with {@code name}.
*
* @param name
* @param target must not be {@literal null} or empty
* @return
*/
public static Field field(String name, String target) {
Assert.hasText(target, "Target must not be null or empty!");
return new AggregationField(name, target);
@@ -196,24 +187,15 @@ public final class Fields implements Iterable<Field> {
private final String target;
/**
* Creates an aggregation field with the given {@code name}.
* Creates an aggregation field with the given name. As no target is set explicitly, the name will be used as target
* as well.
*
* @see AggregationField#AggregationField(String, String).
* @param name must not be {@literal null} or empty
* @param key
*/
public AggregationField(String name) {
this(name, null);
public AggregationField(String key) {
this(key, null);
}
/**
* Creates an aggregation field with the given {@code name} and {@code target}.
* <p>
* The {@code name} serves as an alias for the actual backing document field denoted by {@code target}. If no target
* is set explicitly, the name will be used as target.
*
* @param name must not be {@literal null} or empty
* @param target
*/
public AggregationField(String name, String target) {
String nameToSet = cleanUp(name);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,33 +22,17 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Represents a {@code geoNear} aggregation operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#geoNear(NearQuery, String)} instead of creating
* instances of this class directly.
*
* @author Thomas Darimont
* @since 1.3
*/
public class GeoNearOperation implements AggregationOperation {
private final NearQuery nearQuery;
private final String distanceField;
/**
* Creates a new {@link GeoNearOperation} from the given {@link NearQuery} and the given distance field. The
* {@code distanceField} defines output field that contains the calculated distance.
*
* @param query must not be {@literal null}.
* @param distanceField must not be {@literal null}.
*/
public GeoNearOperation(NearQuery nearQuery, String distanceField) {
Assert.notNull(nearQuery, "NearQuery must not be null.");
Assert.hasLength(distanceField, "Distance field must not be null or empty.");
public GeoNearOperation(NearQuery nearQuery) {
Assert.notNull(nearQuery);
this.nearQuery = nearQuery;
this.distanceField = distanceField;
}
/*
@@ -57,10 +41,6 @@ public class GeoNearOperation implements AggregationOperation {
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
BasicDBObject command = (BasicDBObject) context.getMappedObject(nearQuery.toDBObject());
command.put("distanceField", distanceField);
return new BasicDBObject("$geoNear", command);
return new BasicDBObject("$geoNear", context.getMappedObject(nearQuery.toDBObject()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,9 +31,6 @@ import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $group}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#group(Fields)} instead of creating instances of this
* class directly.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/group/#stage._S_group
* @author Sebastian Herold

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,7 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the {@code $limit}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#limit(long)} instead of creating instances of this
* class directly.
* Encapsulates the {@code $limit}-operation
*
* @see http://docs.mongodb.org/manual/reference/aggregation/limit/
* @author Thomas Darimont

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,11 +22,7 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the {@code $match}-operation.
* <p>
* We recommend to use the static factory method
* {@link Aggregation#match(org.springframework.data.mongodb.core.query.Criteria)} instead of creating instances of this
* class directly.
* Encapsulates the {@code $match}-operation
*
* @see http://docs.mongodb.org/manual/reference/aggregation/match/
* @author Sebastian Herold

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,13 +28,10 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $project}-operation.
* Encapsulates the aggregation framework {@code $project}-operation. Projection of field to be used in an
* {@link Aggregation}. A projection is similar to a {@link Field} inclusion/exclusion but more powerful. It can
* generate new fields, change values of given field etc.
* <p>
* Projection of field to be used in an {@link Aggregation}. A projection is similar to a {@link Field}
* inclusion/exclusion but more powerful. It can generate new fields, change values of given field etc.
* <p>
* We recommend to use the static factory method {@link Aggregation#project(Fields)} instead of creating instances of
* this class directly.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/project/
* @author Tobias Trelle

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,9 +22,6 @@ import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $skip}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#skip(int)} instead of creating instances of this
* class directly.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/skip/
* @author Thomas Darimont

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,6 @@ import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $sort}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#sort(Direction, String...)} instead of creating
* instances of this class directly.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/sort/#pipe._S_sort
* @author Thomas Darimont

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,9 +23,6 @@ import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $unwind}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#unwind(String)} instead of creating instances of
* this class directly.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/unwind/#pipe._S_unwind
* @author Thomas Darimont

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import java.math.BigInteger;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
@@ -46,8 +46,10 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
*
* @param conversionService
*/
@SuppressWarnings("deprecation")
public AbstractMongoConverter(GenericConversionService conversionService) {
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService()
: conversionService;
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,14 @@ package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,6 +36,7 @@ import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.JodaTimeConverters;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -72,13 +72,10 @@ public class CustomConversions {
private final Set<ConvertiblePair> writingPairs;
private final Set<Class<?>> customSimpleTypes;
private final SimpleTypeHolder simpleTypeHolder;
private final ConcurrentMap<ConvertiblePair, CacheValue> customReadTargetTypes;
private final List<Object> converters;
private final Map<ConvertiblePair, CacheValue> customReadTargetTypes;
private final Map<ConvertiblePair, CacheValue> customWriteTargetTypes;
private final Map<Class<?>, CacheValue> rawWriteTargetTypes;
/**
* Creates an empty {@link CustomConversions} object.
*/
@@ -98,9 +95,7 @@ public class CustomConversions {
this.readingPairs = new LinkedHashSet<ConvertiblePair>();
this.writingPairs = new LinkedHashSet<ConvertiblePair>();
this.customSimpleTypes = new HashSet<Class<?>>();
this.customReadTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue>();
this.customWriteTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue>();
this.rawWriteTargetTypes = new ConcurrentHashMap<Class<?>, CacheValue>();
this.customReadTargetTypes = new ConcurrentHashMap<GenericConverter.ConvertiblePair, CacheValue>();
List<Object> toRegister = new ArrayList<Object>();
@@ -118,6 +113,7 @@ public class CustomConversions {
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
toRegister.addAll(GeoConverters.getConvertersToRegister());
toRegister.addAll(Jsr310Converters.getConvertersToRegister());
for (Object c : toRegister) {
registerConversion(c);
@@ -244,103 +240,70 @@ public class CustomConversions {
* @param sourceType must not be {@literal null}
* @return
*/
public Class<?> getCustomWriteTarget(final Class<?> sourceType) {
return getOrCreateAndCache(sourceType, rawWriteTargetTypes, new Producer() {
@Override
public Class<?> get() {
return getCustomTarget(sourceType, null, writingPairs);
}
});
public Class<?> getCustomWriteTarget(Class<?> sourceType) {
return getCustomWriteTarget(sourceType, null);
}
/**
* Returns the target type we can readTargetWriteLocl an inject of the given source type to. The returned type might
* be a subclass of the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply
* return the first target type matching or {@literal null} if no conversion can be found.
* Returns the target type we can write an onject of the given source type to. The returned type might be a subclass
* oth the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply return the
* first target type matching or {@literal null} if no conversion can be found.
*
* @param sourceType must not be {@literal null}
* @param requestedTargetType
* @return
*/
public Class<?> getCustomWriteTarget(final Class<?> sourceType, final Class<?> requestedTargetType) {
public Class<?> getCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
if (requestedTargetType == null) {
return getCustomWriteTarget(sourceType);
}
Assert.notNull(sourceType);
return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customWriteTargetTypes,
new Producer() {
@Override
public Class<?> get() {
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
}
});
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
}
/**
* Returns whether we have a custom conversion registered to readTargetWriteLocl into a Mongo native type. The
* returned type might be a subclass of the given expected type though.
* Returns whether we have a custom conversion registered to write into a Mongo native type. The returned type might
* be a subclass of the given expected type though.
*
* @param sourceType must not be {@literal null}
* @return
*/
public boolean hasCustomWriteTarget(Class<?> sourceType) {
Assert.notNull(sourceType);
return hasCustomWriteTarget(sourceType, null);
}
/**
* Returns whether we have a custom conversion registered to readTargetWriteLocl an object of the given source type
* into an object of the given Mongo native target type.
* Returns whether we have a custom conversion registered to write an object of the given source type into an object
* of the given Mongo native target type.
*
* @param sourceType must not be {@literal null}.
* @param requestedTargetType
* @return
*/
public boolean hasCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
Assert.notNull(sourceType);
return getCustomWriteTarget(sourceType, requestedTargetType) != null;
}
/**
* Returns whether we have a custom conversion registered to readTargetReadLock the given source into the given target
* type.
* Returns whether we have a custom conversion registered to read the given source into the given target type.
*
* @param sourceType must not be {@literal null}
* @param requestedTargetType must not be {@literal null}
* @return
*/
public boolean hasCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
Assert.notNull(sourceType);
Assert.notNull(requestedTargetType);
return getCustomReadTarget(sourceType, requestedTargetType) != null;
}
/**
* Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
* returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
*
* @param sourceType must not be {@literal null}.
* @param requestedTargetType can be {@literal null}.
* @return
*/
private Class<?> getCustomReadTarget(final Class<?> sourceType, final Class<?> requestedTargetType) {
if (requestedTargetType == null) {
return null;
}
return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customReadTargetTypes,
new Producer() {
@Override
public Class<?> get() {
return getCustomTarget(sourceType, requestedTargetType, readingPairs);
}
});
}
/**
* Inspects the given {@link ConvertiblePair}s for ones that have a source compatible type as source. Additionally
* Inspects the given {@link ConvertiblePair} for ones that have a source compatible type as source. Additionally
* checks assignability of the target type if one is given.
*
* @param sourceType must not be {@literal null}.
@@ -349,15 +312,11 @@ public class CustomConversions {
* @return
*/
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
Collection<ConvertiblePair> pairs) {
Iterable<ConvertiblePair> pairs) {
Assert.notNull(sourceType);
Assert.notNull(pairs);
if (requestedTargetType != null && pairs.contains(new ConvertiblePair(sourceType, requestedTargetType))) {
return requestedTargetType;
}
for (ConvertiblePair typePair : pairs) {
if (typePair.getSourceType().isAssignableFrom(sourceType)) {
Class<?> targetType = typePair.getTargetType();
@@ -371,31 +330,32 @@ public class CustomConversions {
}
/**
* Will try to find a value for the given key in the given cache or produce one using the given {@link Producer} and
* store it in the cache.
* Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
* returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
*
* @param key the key to lookup a potentially existing value, must not be {@literal null}.
* @param cache the cache to find the value in, must not be {@literal null}.
* @param producer the {@link Producer} to create values to cache, must not be {@literal null}.
* @param sourceType must not be {@literal null}.
* @param requestedTargetType can be {@literal null}.
* @return
*/
private static <T> Class<?> getOrCreateAndCache(T key, Map<T, CacheValue> cache, Producer producer) {
private Class<?> getCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
CacheValue cacheValue = cache.get(key);
Assert.notNull(sourceType);
if (cacheValue != null) {
return cacheValue.getType();
if (requestedTargetType == null) {
return null;
}
Class<?> type = producer.get();
cache.put(key, CacheValue.of(type));
ConvertiblePair lookupKey = new ConvertiblePair(sourceType, requestedTargetType);
CacheValue readTargetTypeValue = customReadTargetTypes.get(lookupKey);
return type;
}
if (readTargetTypeValue != null) {
return readTargetTypeValue.getType();
}
private interface Producer {
readTargetTypeValue = CacheValue.of(getCustomTarget(sourceType, requestedTargetType, readingPairs));
CacheValue cacheValue = customReadTargetTypes.putIfAbsent(lookupKey, readTargetTypeValue);
Class<?> get();
return cacheValue != null ? cacheValue.getType() : readTargetTypeValue.getType();
}
@WritingConverter

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import com.mongodb.DBObject;
*/
class DBObjectAccessor {
private final BasicDBObject dbObject;
private final DBObject dbObject;
/**
* Creates a new {@link DBObjectAccessor} for the given {@link DBObject}.
@@ -46,7 +46,7 @@ class DBObjectAccessor {
Assert.notNull(dbObject, "DBObject must not be null!");
Assert.isInstanceOf(BasicDBObject.class, dbObject, "Given DBObject must be a BasicDBObject!");
this.dbObject = (BasicDBObject) dbObject;
this.dbObject = dbObject;
}
/**
@@ -62,11 +62,6 @@ class DBObjectAccessor {
Assert.notNull(prop, "MongoPersistentProperty must not be null!");
String fieldName = prop.getFieldName();
if (!fieldName.contains(".")) {
dbObject.put(fieldName, value);
return;
}
Iterator<String> parts = Arrays.asList(fieldName.split("\\.")).iterator();
DBObject dbObject = this.dbObject;
@@ -92,16 +87,12 @@ class DBObjectAccessor {
* @param property must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public Object get(MongoPersistentProperty property) {
String fieldName = property.getFieldName();
if (!fieldName.contains(".")) {
return this.dbObject.get(fieldName);
}
Iterator<String> parts = Arrays.asList(fieldName.split("\\.")).iterator();
Map<String, Object> source = this.dbObject;
Map<Object, Object> source = this.dbObject.toMap();
Object result = null;
while (source != null && parts.hasNext()) {
@@ -117,14 +108,14 @@ class DBObjectAccessor {
}
@SuppressWarnings("unchecked")
private Map<String, Object> getAsMap(Object source) {
private Map<Object, Object> getAsMap(Object source) {
if (source instanceof BasicDBObject) {
return (BasicDBObject) source;
return ((DBObject) source).toMap();
}
if (source instanceof Map) {
return (Map<String, Object>) source;
return (Map<Object, Object>) source;
}
return null;

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.data.mongodb.core.convert;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
@@ -60,19 +60,19 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
return proxy;
}
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(property);
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
if(idProperty.usePropertyAccess()) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(property);
MongoPersistentProperty idProperty = entity.getIdProperty();
if (idProperty.usePropertyAccess()) {
return proxy;
}
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(proxy, spELContext);
BeanWrapper<Object> proxyWrapper = BeanWrapper.create(proxy, null);
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy);
DBObject object = new BasicDBObject(idProperty.getFieldName(), source.getId());
ObjectPath objectPath = ObjectPath.ROOT.push(proxy, persistentEntity, null);
proxyWrapper.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath));
ObjectPath objectPath = ObjectPath.ROOT.push(proxy, entity, null);
accessor.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath));
return proxy;
}

View File

@@ -37,10 +37,11 @@ import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.ParameterValueProvider;
@@ -135,8 +136,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param typeMapper the typeMapper to set
*/
public void setTypeMapper(MongoTypeMapper typeMapper) {
this.typeMapper = typeMapper == null
? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext) : typeMapper;
this.typeMapper = typeMapper == null ? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY,
mappingContext) : typeMapper;
}
/*
@@ -237,8 +238,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
entity, provider, path.getCurrentObject());
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider,
path);
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, path);
}
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, final ObjectPath path) {
@@ -249,16 +249,18 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
S instance = instantiator.createInstance(entity, provider);
final BeanWrapper<S> wrapper = BeanWrapper.create(instance, conversionService);
final PersistentPropertyAccessor accessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance),
conversionService);
final MongoPersistentProperty idProperty = entity.getIdProperty();
final S result = wrapper.getBean();
final S result = instance;
// make sure id property is set before all other properties
Object idValue = null;
if (idProperty != null) {
idValue = getValueInternal(idProperty, dbo, evaluator, path);
wrapper.setProperty(idProperty, idValue);
accessor.setProperty(idProperty, idValue);
}
final ObjectPath currentPath = path.push(result, entity, idValue);
@@ -276,7 +278,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return;
}
wrapper.setProperty(prop, getValueInternal(prop, dbo, evaluator, currentPath));
accessor.setProperty(prop, getValueInternal(prop, dbo, evaluator, currentPath));
}
});
@@ -298,7 +300,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
DbRefResolverCallback callback = new DefaultDbRefResolverCallback(dbo, currentPath, evaluator,
MappingMongoConverter.this);
wrapper.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
accessor.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
}
});
@@ -398,13 +400,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
throw new MappingException("No mapping metadata found for entity of type " + obj.getClass().getName());
}
final BeanWrapper<Object> wrapper = BeanWrapper.create(obj, conversionService);
final PersistentPropertyAccessor accessor = entity.getPropertyAccessor(obj);
final MongoPersistentProperty idProperty = entity.getIdProperty();
if (!dbo.containsField("_id") && null != idProperty) {
try {
Object id = wrapper.getProperty(idProperty, Object.class);
Object id = accessor.getProperty(idProperty);
dbo.put("_id", idMapper.convertId(id));
} catch (ConversionException ignored) {}
}
@@ -417,7 +419,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return;
}
Object propertyObj = wrapper.getProperty(prop);
Object propertyObj = accessor.getProperty(prop);
if (null != propertyObj) {
@@ -431,10 +433,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
});
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
public void doWithAssociation(Association<MongoPersistentProperty> association) {
MongoPersistentProperty inverseProp = association.getInverse();
Class<?> type = inverseProp.getType();
Object propertyObj = wrapper.getProperty(inverseProp, type);
Object propertyObj = accessor.getProperty(inverseProp);
if (null != propertyObj) {
writePropertyInternal(propertyObj, dbo, inverseProp);
}
@@ -504,10 +508,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Object existingValue = accessor.get(prop);
BasicDBObject propDbObj = existingValue instanceof BasicDBObject ? (BasicDBObject) existingValue
: new BasicDBObject();
addCustomTypeKeyIfNecessary(type, obj, propDbObj);
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, propDbObj);
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass()) ? mappingContext
.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
writeInternal(obj, propDbObj, entity);
accessor.put(prop, propDbObj);
@@ -587,7 +591,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (conversions.isSimpleType(key.getClass())) {
String simpleKey = prepareMapKey(key.toString());
String simpleKey = potentiallyEscapeMapKey(key.toString());
dbObject.put(simpleKey, value != null ? createDBRef(value, property) : null);
} else {
@@ -639,13 +643,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected DBObject writeMapInternal(Map<Object, Object> obj, DBObject dbo, TypeInformation<?> propertyType) {
for (Map.Entry<Object, Object> entry : obj.entrySet()) {
Object key = entry.getKey();
Object val = entry.getValue();
if (conversions.isSimpleType(key.getClass())) {
String simpleKey = prepareMapKey(key);
// Don't use conversion service here as removal of ObjectToString converter results in some primitive types not
// being convertable
String simpleKey = potentiallyEscapeMapKey(key.toString());
if (val == null || conversions.isSimpleType(val.getClass())) {
writeSimpleInternal(val, dbo, simpleKey);
} else if (val instanceof Collection || val.getClass().isArray()) {
@@ -666,21 +669,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return dbo;
}
/**
* Prepares the given {@link Map} key to be converted into a {@link String}. Will invoke potentially registered custom
* conversions and escape dots from the result as they're not supported as {@link Map} key in MongoDB.
*
* @param key must not be {@literal null}.
* @return
*/
private String prepareMapKey(Object key) {
Assert.notNull(key, "Map key must not be null!");
String convertedKey = potentiallyConvertMapKey(key);
return potentiallyEscapeMapKey(convertedKey);
}
/**
* Potentially replaces dots in the given map key with the configured map key replacement if configured or aborts
* conversion if none is configured.
@@ -696,31 +684,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (mapKeyDotReplacement == null) {
throw new MappingException(String.format(
"Map key %s contains dots but no replacement was configured! Make "
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!",
source));
throw new MappingException(String.format("Map key %s contains dots but no replacement was configured! Make "
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!", source));
}
return source.replaceAll("\\.", mapKeyDotReplacement);
}
/**
* Returns a {@link String} representation of the given {@link Map} key
*
* @param key
* @return
*/
private String potentiallyConvertMapKey(Object key) {
if (key instanceof String) {
return (String) key;
}
return conversions.hasCustomWriteTarget(key.getClass(), String.class)
? (String) getPotentiallyConvertedSimpleWrite(key) : key.toString();
}
/**
* Translates the map key replacements in the given key just read with a dot in case a map key replacement has been
* configured.
@@ -735,7 +705,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
/**
* Adds custom type information to the given {@link DBObject} if necessary. That is if the value is not the same as
* the one given. This is usually the case if you store a subtype of the actual declared type of the property.
*
*
* @param type
* @param value must not be {@literal null}.
* @param dbObject must not be {@literal null}.
@@ -801,7 +771,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
@SuppressWarnings({ "rawtypes", "unchecked" })
private Object getPotentiallyConvertedSimpleRead(Object value, Class<?> target) {
if (value == null || target == null || target.isAssignableFrom(value.getClass())) {
if (value == null || target == null) {
return value;
}
@@ -813,7 +783,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return Enum.valueOf((Class<Enum>) target, value.toString());
}
return conversionService.convert(value, target);
return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target);
}
protected DBRef createDBRef(Object target, MongoPersistentProperty property) {
@@ -842,8 +812,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (target.getClass().equals(idProperty.getType())) {
id = target;
} else {
BeanWrapper<Object> wrapper = BeanWrapper.create(target, conversionService);
id = wrapper.getProperty(idProperty, Object.class);
PersistentPropertyAccessor accessor = targetEntity.getPropertyAccessor(target);
id = accessor.getProperty(idProperty);
}
if (null == id) {
@@ -887,16 +857,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
: CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size());
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory
.createCollection(collectionType, rawComponentType, sourceValue.size());
for (int i = 0; i < sourceValue.size(); i++) {
Object dbObjItem = sourceValue.get(i);
if (dbObjItem instanceof DBRef) {
items.add(
DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem), path));
items.add(DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem),
path));
} else if (dbObjItem instanceof DBObject) {
items.add(read(componentType, (DBObject) dbObjItem, path));
} else {
@@ -1014,14 +984,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.write(obj, newDbo);
if (typeInformation == null) {
return removeTypeInfo(newDbo, true);
return removeTypeInfoRecursively(newDbo);
}
if (typeInformation.getType().equals(NestedDocument.class)) {
return removeTypeInfo(newDbo, false);
}
return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfo(newDbo, true);
return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfoRecursively(newDbo);
}
public BasicDBList maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {
@@ -1035,13 +1001,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
/**
* Removes the type information from the entire conversion result.
* Removes the type information from the conversion result.
*
* @param object
* @param recursively whether to apply the removal recursively
* @return
*/
private Object removeTypeInfo(Object object, boolean recursively) {
private Object removeTypeInfoRecursively(Object object) {
if (!(object instanceof DBObject)) {
return object;
@@ -1049,29 +1014,19 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
DBObject dbObject = (DBObject) object;
String keyToRemove = null;
for (String key : dbObject.keySet()) {
if (recursively) {
Object value = dbObject.get(key);
if (value instanceof BasicDBList) {
for (Object element : (BasicDBList) value) {
removeTypeInfo(element, recursively);
}
} else {
removeTypeInfo(value, recursively);
}
if (typeMapper.isTypeKey(key)) {
keyToRemove = key;
}
if (typeMapper.isTypeKey(key)) {
keyToRemove = key;
if (!recursively) {
break;
Object value = dbObject.get(key);
if (value instanceof BasicDBList) {
for (Object element : (BasicDBList) value) {
removeTypeInfoRecursively(element);
}
} else {
removeTypeInfoRecursively(value);
}
}
@@ -1135,8 +1090,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
*
* @author Oliver Gierke
*/
private class ConverterAwareSpELExpressionParameterValueProvider
extends SpELExpressionParameterValueProvider<MongoPersistentProperty> {
private class ConverterAwareSpELExpressionParameterValueProvider extends
SpELExpressionParameterValueProvider<MongoPersistentProperty> {
private final ObjectPath path;
@@ -1148,8 +1103,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param delegate must not be {@literal null}.
*/
public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator,
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate,
ObjectPath path) {
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate, ObjectPath path) {
super(evaluator, conversionService, delegate);
this.path = path;
@@ -1208,15 +1162,4 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
DBObject readRef(DBRef ref) {
return ref.fetch();
}
/**
* Marker class used to indicate we have a non root document object here that might be used within an update - so we
* need to preserve type hints for potential nested elements but need to remove it on top level.
*
* @author Christoph Strobl
* @since 1.8
*/
static class NestedDocument {
}
}

View File

@@ -34,13 +34,10 @@ import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.mongodb.BasicDBList;
@@ -61,7 +58,6 @@ public class QueryMapper {
private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
private static final DBObject META_TEXT_SCORE = new BasicDBObject("$meta", "textScore");
static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
private enum MetaMapping {
FORCE, WHEN_PRESENT, IGNORE;
@@ -254,8 +250,8 @@ public class QueryMapper {
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
Object value = keyword.getValue();
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property)
: getMappedValue(property.with(keyword.getKey()), value);
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue(
property.with(keyword.getKey()), value);
return new BasicDBObject(keyword.key, convertedValue);
}
@@ -468,20 +464,13 @@ public class QueryMapper {
*/
public Object convertId(Object id) {
if (id == null) {
return null;
}
if (id instanceof String) {
return ObjectId.isValid(id.toString()) ? conversionService.convert(id, ObjectId.class) : id;
}
try {
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
: delegateConvertToMongoType(id, null);
} catch (ConversionException o_O) {
return delegateConvertToMongoType(id, null);
return conversionService.convert(id, ObjectId.class);
} catch (ConversionException e) {
// Ignore
}
return delegateConvertToMongoType(id, null);
}
/**
@@ -661,10 +650,6 @@ public class QueryMapper {
public Association<MongoPersistentProperty> getAssociation() {
return null;
}
public TypeInformation<?> getTypeHint() {
return ClassTypeInformation.OBJECT;
}
}
/**
@@ -824,7 +809,7 @@ public class QueryMapper {
try {
PropertyPath path = PropertyPath.from(pathExpression.replaceAll("\\.\\d", ""), entity.getTypeInformation());
PropertyPath path = PropertyPath.from(pathExpression, entity.getTypeInformation());
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(path);
Iterator<MongoPersistentProperty> iterator = propertyPath.iterator();
@@ -870,27 +855,6 @@ public class QueryMapper {
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
return new AssociationConverter(getAssociation());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint()
*/
@Override
public TypeInformation<?> getTypeHint() {
MongoPersistentProperty property = getProperty();
if (property == null) {
return super.getTypeHint();
}
if (property.getActualType().isInterface()
|| java.lang.reflect.Modifier.isAbstract(property.getActualType().getModifiers())) {
return ClassTypeInformation.OBJECT;
}
return NESTED_DOCUMENT;
}
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,6 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update.Modifier;
import org.springframework.data.mongodb.core.query.Update.Modifiers;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
@@ -66,8 +65,8 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity<?> entity) {
return converter.convertToMongoType(source,
entity == null ? ClassTypeInformation.OBJECT : getTypeHintForEntity(source, entity));
return entity == null ? super.delegateConvertToMongoType(source, null) : converter.convertToMongoType(source,
entity.getTypeInformation());
}
/*
@@ -90,7 +89,7 @@ public class UpdateMapper extends QueryMapper {
return getMappedUpdateModifier(field, rawValue);
}
return super.getMappedObjectForField(field, rawValue);
return super.getMappedObjectForField(field, getMappedValue(field, rawValue));
}
private Entry<String, Object> getMappedUpdateModifier(Field field, Object rawValue) {
@@ -98,14 +97,14 @@ public class UpdateMapper extends QueryMapper {
if (rawValue instanceof Modifier) {
value = getMappedValue(field, (Modifier) rawValue);
value = getMappedValue((Modifier) rawValue);
} else if (rawValue instanceof Modifiers) {
DBObject modificationOperations = new BasicDBObject();
for (Modifier modifier : ((Modifiers) rawValue).getModifiers()) {
modificationOperations.putAll(getMappedValue(field, modifier).toMap());
modificationOperations.putAll(getMappedValue(modifier).toMap());
}
value = modificationOperations;
@@ -133,31 +132,12 @@ public class UpdateMapper extends QueryMapper {
return value instanceof Query;
}
private DBObject getMappedValue(Field field, Modifier modifier) {
private DBObject getMappedValue(Modifier modifier) {
TypeInformation<?> typeHint = field == null ? ClassTypeInformation.OBJECT : field.getTypeHint();
Object value = converter.convertToMongoType(modifier.getValue(), typeHint);
Object value = converter.convertToMongoType(modifier.getValue(), ClassTypeInformation.OBJECT);
return new BasicDBObject(modifier.getKey(), value);
}
private TypeInformation<?> getTypeHintForEntity(Object source, MongoPersistentEntity<?> entity) {
return processTypeHintForNestedDocuments(source, entity.getTypeInformation());
}
private TypeInformation<?> processTypeHintForNestedDocuments(Object source, TypeInformation<?> info) {
Class<?> type = info.getActualType().getType();
if (type.isInterface() || java.lang.reflect.Modifier.isAbstract(type.getModifiers())) {
return info;
}
if (!type.equals(source.getClass())) {
return info;
}
return NESTED_DOCUMENT;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.QueryMapper#createPropertyField(org.springframework.data.mongodb.core.mapping.MongoPersistentEntity, java.lang.String, org.springframework.data.mapping.context.MappingContext)
@@ -166,8 +146,8 @@ public class UpdateMapper extends QueryMapper {
protected Field createPropertyField(MongoPersistentEntity<?> entity, String key,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
return entity == null ? super.createPropertyField(entity, key, mappingContext)
: new MetadataBackedUpdateField(entity, key, mappingContext);
return entity == null ? super.createPropertyField(entity, key, mappingContext) : //
new MetadataBackedUpdateField(entity, key, mappingContext);
}
/**
@@ -253,35 +233,7 @@ public class UpdateMapper extends QueryMapper {
protected String mapPropertyName(MongoPersistentProperty property) {
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
boolean inspect = iterator.hasNext();
while (inspect) {
String partial = iterator.next();
boolean isPositional = isPositionalParameter(partial);
if (isPositional) {
mappedName += "." + partial;
}
inspect = isPositional && iterator.hasNext();
}
return mappedName;
}
boolean isPositionalParameter(String partial) {
if (partial.equals("$")) {
return true;
}
try {
Long.valueOf(partial);
return true;
} catch (NumberFormatException e) {
return false;
}
return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName;
}
}

View File

@@ -35,7 +35,17 @@ import com.mongodb.DBObject;
public class Index implements IndexDefinition {
public enum Duplicates {
RETAIN, DROP
RETAIN, //
/**
* Dropping Duplicates was removed in MongoDB Server 2.8.0-rc0.
* <p>
* See https://jira.mongodb.org/browse/SERVER-14710
*
* @deprecated since 1.7.
*/
@Deprecated//
DROP
}
private final Map<String, Direction> fieldSpec = new LinkedHashMap<String, Direction>();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,10 +60,4 @@ public class MongoMappingEventPublisher implements ApplicationEventPublisher {
indexCreator.onApplicationEvent((MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>) event);
}
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)
*/
public void publishEvent(Object event) {}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexRes
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
/**
@@ -42,7 +43,8 @@ import org.springframework.util.Assert;
* @author Laurent Canet
* @author Christoph Strobl
*/
public class MongoPersistentEntityIndexCreator implements ApplicationListener<MappingContextEvent<?, ?>> {
public class MongoPersistentEntityIndexCreator implements
ApplicationListener<MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>> {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoPersistentEntityIndexCreator.class);
@@ -52,7 +54,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
private final IndexResolver indexResolver;
/**
* Creates a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
* Creats a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
* {@link MongoDbFactory}.
*
* @param mappingContext must not be {@literal null}.
@@ -63,7 +65,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
}
/**
* Creates a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
* Creats a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
* {@link MongoDbFactory}.
*
* @param mappingContext must not be {@literal null}.
@@ -90,7 +92,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(MappingContextEvent<?, ?> event) {
public void onApplicationEvent(MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty> event) {
if (!event.wasEmittedBy(mappingContext)) {
return;
@@ -100,7 +102,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
// Double check type as Spring infrastructure does not consider nested generics
if (entity instanceof MongoPersistentEntity) {
checkForIndexes((MongoPersistentEntity<?>) entity);
checkForIndexes(event.getPersistentEntity());
}
}
@@ -130,8 +132,8 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
}
private void createIndex(IndexDefinitionHolder indexDefinition) {
mongoDbFactory.getDb().getCollection(indexDefinition.getCollection()).createIndex(indexDefinition.getIndexKeys(),
indexDefinition.getIndexOptions());
mongoDbFactory.getDb().getCollection(indexDefinition.getCollection())
.createIndex(indexDefinition.getIndexKeys(), indexDefinition.getIndexOptions());
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,7 +117,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
indexInformation.add(indexDefinitionHolder);
}
} catch (CyclicPropertyReferenceException e) {
LOGGER.info(e.getMessage());
LOGGER.warn(e.getMessage());
}
}
});
@@ -155,7 +155,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
indexInformation.addAll(resolveIndexForClass(persistentProperty.getActualType(), propertyDotPath,
collection, guard));
} catch (CyclicPropertyReferenceException e) {
LOGGER.info(e.getMessage());
LOGGER.warn(e.getMessage());
}
}
@@ -205,7 +205,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
appendTextIndexInformation("", indexDefinitionBuilder, root,
new TextIndexIncludeOptions(IncludeStrategy.DEFAULT), new CycleGuard());
} catch (CyclicPropertyReferenceException e) {
LOGGER.info(e.getMessage());
LOGGER.warn(e.getMessage());
}
TextIndexDefinition indexDefinition = indexDefinitionBuilder.build();
@@ -256,10 +256,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
appendTextIndexInformation(propertyDotPath, indexDefinitionBuilder,
mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
} catch (CyclicPropertyReferenceException e) {
LOGGER.info(e.getMessage(), e);
LOGGER.warn(e.getMessage(), e);
} catch (InvalidDataAccessApiUsageException e) {
LOGGER.info(
String.format("Potentially invalid index structure discovered. Breaking operation for %s.",
LOGGER.warn(
String.format("Potentially invald index structure discovered. Breaking operation for %s.",
entity.getName()), e);
}
} else if (includeOptions.isForce() || indexed != null) {
@@ -466,9 +466,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
for (Path existingPath : paths) {
if (existingPath.cycles(property, path) && property.isEntity()) {
if (existingPath.cycles(property, path)) {
paths.add(new Path(property, path));
throw new CyclicPropertyReferenceException(property.getFieldName(), property.getOwner().getType(),
existingPath.getPath());
}

View File

@@ -16,7 +16,6 @@
package org.springframework.data.mongodb.core.mapping;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@@ -306,44 +305,28 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
*/
private static class PropertyTypeAssertionHandler implements PropertyHandler<MongoPersistentProperty> {
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PropertyHandler#doWithPersistentProperty(org.springframework.data.mapping.PersistentProperty)
*/
@Override
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
potentiallyAssertTextScoreType(persistentProperty);
potentiallyAssertLanguageType(persistentProperty);
potentiallyAssertDBRefTargetType(persistentProperty);
}
private static void potentiallyAssertLanguageType(MongoPersistentProperty persistentProperty) {
private void potentiallyAssertLanguageType(MongoPersistentProperty persistentProperty) {
if (persistentProperty.isExplicitLanguageProperty()) {
assertPropertyType(persistentProperty, String.class);
}
}
private static void potentiallyAssertTextScoreType(MongoPersistentProperty persistentProperty) {
private void potentiallyAssertTextScoreType(MongoPersistentProperty persistentProperty) {
if (persistentProperty.isTextScoreProperty()) {
assertPropertyType(persistentProperty, Float.class, Double.class);
}
}
private static void potentiallyAssertDBRefTargetType(MongoPersistentProperty persistentProperty) {
if (persistentProperty.isDbReference() && persistentProperty.getDBRef().lazy()) {
if (persistentProperty.isArray() || Modifier.isFinal(persistentProperty.getActualType().getModifiers())) {
throw new MappingException(String.format(
"Invalid lazy DBRef property for %s. Found %s which must not be an array nor a final class.",
persistentProperty.getField(), persistentProperty.getActualType()));
}
}
}
private static void assertPropertyType(MongoPersistentProperty persistentProperty, Class<?>... validMatches) {
private void assertPropertyType(MongoPersistentProperty persistentProperty, Class<?>... validMatches) {
for (Class<?> potentialMatch : validMatches) {
if (ClassUtils.isAssignable(potentialMatch, persistentProperty.getActualType())) {
@@ -351,9 +334,10 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
}
}
throw new MappingException(
String.format("Missmatching types for %s. Found %s expected one of %s.", persistentProperty.getField(),
persistentProperty.getActualType(), StringUtils.arrayToCommaDelimitedString(validMatches)));
throw new MappingException(String.format("Missmatching types for %s. Found %s expected one of %s.",
persistentProperty.getField(), persistentProperty.getActualType(),
StringUtils.arrayToCommaDelimitedString(validMatches)));
}
}
}

View File

@@ -100,7 +100,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
}
// We need to support a wider range of ID types than just the ones that can be converted to an ObjectId
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName());
// but still we need to check if there happens to be an explicit name set
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName()) && !hasExplicitFieldName();
}
/*
@@ -134,10 +135,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
}
}
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
if (hasExplicitFieldName()) {
return getAnnotatedFieldName();
}
String fieldName = fieldNamingStrategy.getFieldName(this);
@@ -150,6 +149,26 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
return fieldName;
}
/**
* @return true if {@link org.springframework.data.mongodb.core.mapping.Field} having non blank
* {@link org.springframework.data.mongodb.core.mapping.Field#value()} present.
* @since 1.7
*/
protected boolean hasExplicitFieldName() {
return StringUtils.hasText(getAnnotatedFieldName());
}
private String getAnnotatedFieldName() {
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
}
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#getFieldOrder()

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,8 +31,6 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
private Boolean isIdProperty;
private Boolean isAssociation;
private String fieldName;
private Boolean usePropertyAccess;
private Boolean isTransient;
/**
* Creates a new {@link CachingMongoPersistentProperty}.
@@ -87,32 +85,4 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
return this.fieldName;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#usePropertyAccess()
*/
@Override
public boolean usePropertyAccess() {
if (this.usePropertyAccess == null) {
this.usePropertyAccess = super.usePropertyAccess();
}
return this.usePropertyAccess;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isTransient()
*/
@Override
public boolean isTransient() {
if (this.isTransient == null) {
this.isTransient = super.isTransient();
}
return this.isTransient;
}
}

View File

@@ -45,7 +45,7 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
int getFieldOrder();
/**
* Returns whether the property is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect
* Returns whether the propert is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect
* {@link #getDBRef()} to return an non-{@literal null} value.
*
* @return

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011 - 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,8 @@ import com.mongodb.DBObject;
* Collects the results of executing a group operation.
*
* @author Mark Pollack
* @param <T> The class in which the results are mapped onto, accessible via an interator.
* @author Christoph Strobl
* @param <T> The class in which the results are mapped onto, accessible via an {@link Iterator}.
*/
public class GroupByResults<T> implements Iterable<T> {
@@ -38,6 +39,7 @@ public class GroupByResults<T> implements Iterable<T> {
private String serverUsed;
public GroupByResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
this.mappedResults = mappedResults;
@@ -68,21 +70,24 @@ public class GroupByResults<T> implements Iterable<T> {
}
private void parseCount() {
Object object = rawResults.get("count");
if (object instanceof Double) {
count = (Double) object;
if (object instanceof Number) {
count = ((Number) object).doubleValue();
}
}
private void parseKeys() {
Object object = rawResults.get("keys");
if (object instanceof Integer) {
keys = (Integer) object;
if (object instanceof Number) {
keys = ((Number) object).intValue();
}
}
private void parseServerUsed() {
// "serverUsed" : "127.0.0.1:27017"
Object object = rawResults.get("serverUsed");
if (object instanceof String) {

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core.query;
import java.util.Arrays;
import java.util.Collections;
import com.mongodb.BasicDBObject;
@@ -88,8 +87,12 @@ public class BasicUpdate extends Update {
@Override
public Update pullAll(String key, Object[] values) {
Object[] convertedValues = new Object[values.length];
for (int i = 0; i < values.length; i++) {
convertedValues[i] = values[i];
}
DBObject keyValue = new BasicDBObject();
keyValue.put(key, Arrays.copyOf(values, values.length));
keyValue.put(key, convertedValues);
updateObject.put("$pullAll", keyValue);
return this;
}

View File

@@ -176,7 +176,7 @@ public class Query {
for (Order order : sort) {
if (order.isIgnoreCase()) {
throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case! "
throw new IllegalArgumentException(String.format("Gven sort contained an Order for %s with ignore case! "
+ "MongoDB does not support sorting ignoreing case currently!", order.getProperty()));
}
}

View File

@@ -63,7 +63,7 @@ public class TextCriteria implements CriteriaDefinition {
}
/**
* For a full list of supported languages see the mongodb reference manual for <a
* For a full list of supported languages see the mongdodb reference manual for <a
* href="http://docs.mongodb.org/manual/reference/text-search-languages/">Text Search Languages</a>.
*
* @param language

View File

@@ -64,7 +64,7 @@ public class Update {
}
/**
* Creates an {@link Update} instance from the given {@link DBObject}. Allows to explicitly exclude fields from making
* Creates an {@link Update} instance from the given {@link DBObject}. Allows to explicitly exlude fields from making
* it into the created {@link Update} object. Note, that this will set attributes directly and <em>not</em> use
* {@literal $set}. This means fields not given in the {@link DBObject} will be nulled when executing the update. To
* create an only-updating {@link Update} instance of a {@link DBObject}, call {@link #set(String, Object)} for each
@@ -163,7 +163,8 @@ public class Update {
/**
* Update using {@code $push} modifier. <br/>
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values.
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values as well as using
* {@code $position}.
*
* @see http://docs.mongodb.org/manual/reference/operator/update/push/
* @see http://docs.mongodb.org/manual/reference/operator/update/each/
@@ -189,7 +190,12 @@ public class Update {
* @return
*/
public Update pushAll(String key, Object[] values) {
addMultiFieldOperation("$pushAll", key, Arrays.copyOf(values, values.length));
Object[] convertedValues = new Object[values.length];
for (int i = 0; i < values.length; i++) {
convertedValues[i] = values[i];
}
addMultiFieldOperation("$pushAll", key, convertedValues);
return this;
}
@@ -253,7 +259,12 @@ public class Update {
* @return
*/
public Update pullAll(String key, Object[] values) {
addFieldOperation("$pullAll", key, Arrays.copyOf(values, values.length));
Object[] convertedValues = new Object[values.length];
for (int i = 0; i < values.length; i++) {
convertedValues[i] = values[i];
}
addFieldOperation("$pullAll", key, convertedValues);
return this;
}
@@ -298,6 +309,33 @@ public class Update {
return this;
}
/**
* Multiply the value of given key by the given number.
*
* @see http://docs.mongodb.org/manual/reference/operator/update/mul/
* @param key must not be {@literal null}.
* @param multiplier must not be {@literal null}.
* @return
* @since 1.7
*/
public Update multiply(String key, Number multiplier) {
Assert.notNull(multiplier, "Multiplier must not be 'null'.");
addMultiFieldOperation("$mul", key, multiplier.doubleValue());
return this;
}
/**
* The operator supports bitwise {@code and}, bitwise {@code or}, and bitwise {@code xor} operations.
*
* @param key
* @return
* @since 1.7
*/
public BitwiseOperatorBuilder bitwise(String key) {
return new BitwiseOperatorBuilder(this, key);
}
public DBObject getUpdateObject() {
DBObject dbo = new BasicDBObject();
@@ -485,7 +523,13 @@ public class Update {
return ((Collection<?>) values[0]).toArray();
}
return Arrays.copyOf(values, values.length);
Object[] convertedValues = new Object[values.length];
for (int i = 0; i < values.length; i++) {
convertedValues[i] = values[i];
}
return convertedValues;
}
/*
@@ -534,6 +578,31 @@ public class Update {
}
}
/**
* {@link Modifier} implementation used to propagate {@code $position}.
*
* @author Christoph Strobl
* @since 1.7
*/
private static class PositionModifier implements Modifier {
private final int position;
public PositionModifier(int position) {
this.position = position;
}
@Override
public String getKey() {
return "$position";
}
@Override
public Object getValue() {
return position;
}
}
/**
* Builder for creating {@code $push} modifiers
*
@@ -562,6 +631,42 @@ public class Update {
return Update.this.push(key, this.modifiers);
}
/**
* Forces values to be added at the given {@literal position}.
*
* @param position needs to be greater than or equal to zero.
* @return
* @since 1.7
*/
public PushOperatorBuilder atPosition(int position) {
if (position < 0) {
throw new IllegalArgumentException("Position must be greater than or equal to zero.");
}
this.modifiers.addModifier(new PositionModifier(position));
return this;
}
/**
* Forces values to be added at given {@literal position}.
*
* @param position can be {@literal null} which will be appended at the last position.
* @return
* @since 1.7
*/
public PushOperatorBuilder atPosition(Position position) {
if (position == null || Position.LAST.equals(position)) {
return this;
}
this.modifiers.addModifier(new PositionModifier(0));
return this;
}
/**
* Propagates {@link #value(Object)} to {@code $push}
*
@@ -651,4 +756,79 @@ public class Update {
return Update.this.addToSet(this.key, value);
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
public static class BitwiseOperatorBuilder {
private final String key;
private final Update reference;
private static final String BIT_OPERATOR = "$bit";
private enum BitwiseOperator {
AND, OR, XOR;
@Override
public String toString() {
return super.toString().toLowerCase();
};
}
/**
* Creates a new {@link BitwiseOperatorBuilder}.
*
* @param reference must not be {@literal null}
* @param key must not be {@literal null}
*/
protected BitwiseOperatorBuilder(Update reference, String key) {
Assert.notNull(reference, "Reference must not be null!");
Assert.notNull(key, "Key must not be null!");
this.reference = reference;
this.key = key;
}
/**
* Updates to the result of a bitwise and operation between the current value and the given one.
*
* @param value
* @return
*/
public Update and(long value) {
addFieldOperation(BitwiseOperator.AND, value);
return reference;
}
/**
* Updates to the result of a bitwise or operation between the current value and the given one.
*
* @param value
* @return
*/
public Update or(long value) {
addFieldOperation(BitwiseOperator.OR, value);
return reference;
}
/**
* Updates to the result of a bitwise xor operation between the current value and the given one.
*
* @param value
* @return
*/
public Update xor(long value) {
addFieldOperation(BitwiseOperator.XOR, value);
return reference;
}
private void addFieldOperation(BitwiseOperator operator, Number value) {
reference.addMultiFieldOperation(BIT_OPERATOR, key, new BasicDBObject(operator.toString(), value));
}
}
}

View File

@@ -38,7 +38,7 @@ import org.springframework.data.annotation.QueryAnnotation;
public @interface Query {
/**
* Takes a MongoDB JSON string to define the actual query to be executed. This one will take precedence over the
* Takes a MongoDB JSON string to define the actual query to be executed. This one will take precendece over the
* method name then.
*
* @return

View File

@@ -259,11 +259,9 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
Object execute(Query query) {
MongoEntityMetadata<?> metadata = method.getEntityInformation();
String collectionName = metadata.getCollectionName();
Class<?> type = metadata.getJavaType();
int overallLimit = query.getLimit();
long count = operations.count(query, type, collectionName);
long count = operations.count(query, metadata.getCollectionName());
count = overallLimit != 0 ? Math.min(count, query.getLimit()) : count;
boolean pageableOutOfScope = pageable.getOffset() > count;
@@ -280,7 +278,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
query.limit(overallLimit - pageable.getOffset());
}
List<?> result = operations.find(query, type, collectionName);
List<?> result = operations.find(query, metadata.getJavaType(), metadata.getCollectionName());
return new PageImpl(result, pageable, count);
}
}

View File

@@ -20,16 +20,13 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Shape;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
@@ -43,7 +40,6 @@ import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Custom query creator to create Mongo criterias.
@@ -55,7 +51,6 @@ import org.springframework.util.ObjectUtils;
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private static final Logger LOG = LoggerFactory.getLogger(MongoQueryCreator.class);
private static final Pattern PUNCTATION_PATTERN = Pattern.compile("\\p{Punct}");
private final MongoParameterAccessor accessor;
private final boolean isGeoNearQuery;
@@ -197,6 +192,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
case ENDING_WITH:
case CONTAINING:
return createContainingCriteria(part, property, criteria, parameters);
case NOT_CONTAINING:
return createContainingCriteria(part, property, criteria, parameters).not();
case REGEX:
return criteria.regex(parameters.next().toString());
case EXISTS:
@@ -214,7 +211,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
if (distance == null) {
return criteria.near(point);
} else {
if (!Metrics.NEUTRAL.equals(distance.getMetric())) {
if (distance.getMetric() != null) {
criteria.nearSphere(point);
} else {
criteria.near(point);
@@ -267,23 +264,19 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private Criteria createLikeRegexCriteriaOrThrow(Part part, MongoPersistentProperty property, Criteria criteria,
PotentiallyConvertingIterator parameters, boolean shouldNegateExpression) {
PropertyPath path = part.getProperty().getLeafProperty();
switch (part.shouldIgnoreCase()) {
case ALWAYS:
if (path.getType() != String.class) {
throw new IllegalArgumentException(
String.format("Part %s must be of type String but was %s", path, path.getType()));
if (part.getProperty().getType() != String.class) {
throw new IllegalArgumentException(String.format("part %s must be of type String but was %s",
part.getProperty(), part.getType()));
}
// fall-through
case WHEN_POSSIBLE:
if (shouldNegateExpression) {
criteria = criteria.not();
}
return addAppropriateLikeRegexTo(criteria, part, parameters.nextConverted(property).toString());
case NEVER:
@@ -360,8 +353,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return (T) parameter;
}
throw new IllegalArgumentException(
String.format("Expected parameter type of %s but got %s!", type, parameter.getClass()));
throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type,
parameter.getClass()));
}
private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersistentProperty property) {
@@ -379,57 +372,24 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private String toLikeRegex(String source, Part part) {
Type type = part.getType();
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, part);
switch (type) {
case STARTING_WITH:
regex = "^" + regex;
source = "^" + source;
break;
case ENDING_WITH:
regex = regex + "$";
source = source + "$";
break;
case CONTAINING:
regex = ".*" + regex + ".*";
case NOT_CONTAINING:
source = "*" + source + "*";
break;
case SIMPLE_PROPERTY:
case NEGATING_SIMPLE_PROPERTY:
regex = "^" + regex + "$";
source = "^" + source + "$";
default:
}
return regex;
}
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Part qpart) {
if (!ObjectUtils.nullSafeEquals(Type.LIKE, qpart.getType())) {
return PUNCTATION_PATTERN.matcher(source).find() ? Pattern.quote(source) : source;
}
if (source.equals("*")) {
return ".*";
}
StringBuilder sb = new StringBuilder();
boolean leadingWildcard = source.startsWith("*");
boolean trailingWildcard = source.endsWith("*");
String valueToUse = source.substring(leadingWildcard ? 1 : 0,
trailingWildcard ? source.length() - 1 : source.length());
if (PUNCTATION_PATTERN.matcher(valueToUse).find()) {
valueToUse = Pattern.quote(valueToUse);
}
if (leadingWildcard) {
sb.append(".*");
}
sb.append(valueToUse);
if (trailingWildcard) {
sb.append(".*");
}
return sb.toString();
return source.replaceAll("\\*", ".*");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,8 +97,8 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
return result;
} catch (JSONParseException o_O) {
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod()),
o_O);
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod(),
o_O));
}
}

View File

@@ -17,11 +17,9 @@ package org.springframework.data.mongodb.repository.support;
import java.io.Serializable;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
import org.springframework.data.repository.core.support.PersistentEntityInformation;
/**
* {@link MongoEntityInformation} implementation using a {@link MongoPersistentEntity} instance to lookup the necessary
@@ -30,7 +28,7 @@ import org.springframework.data.repository.core.support.AbstractEntityInformatio
*
* @author Oliver Gierke
*/
public class MappingMongoEntityInformation<T, ID extends Serializable> extends AbstractEntityInformation<T, ID>
public class MappingMongoEntityInformation<T, ID extends Serializable> extends PersistentEntityInformation<T, ID>
implements MongoEntityInformation<T, ID> {
private final MongoPersistentEntity<T> entityMetadata;
@@ -50,42 +48,16 @@ public class MappingMongoEntityInformation<T, ID extends Serializable> extends A
* collection name.
*
* @param entity must not be {@literal null}.
* @param customCollectionName
* @param customCollectionName can be {@literal null}.
*/
public MappingMongoEntityInformation(MongoPersistentEntity<T> entity, String customCollectionName) {
super(entity.getType());
super(entity);
this.entityMetadata = entity;
this.customCollectionName = customCollectionName;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.EntityInformation#getId(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public ID getId(T entity) {
MongoPersistentProperty idProperty = entityMetadata.getIdProperty();
if (idProperty == null) {
return null;
}
try {
return (ID) BeanWrapper.create(entity, null).getProperty(idProperty);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
*/
@SuppressWarnings("unchecked")
public Class<ID> getIdType() {
return (Class<ID>) entityMetadata.getIdProperty().getType();
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
*/

View File

@@ -35,7 +35,7 @@ import com.mysema.query.apt.Configuration;
import com.mysema.query.apt.DefaultConfiguration;
/**
* Annotation processor to create Querydsl query types for QueryDsl annotated classes.
* Annotation processor to create Querydsl query types for QueryDsl annoated classes.
*
* @author Oliver Gierke
*/

View File

@@ -110,6 +110,15 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
return createQueryFor(predicate).orderBy(orders).list();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.OrderSpecifier[])
*/
@Override
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
return createQuery().orderBy(orders).list();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Pageable)

View File

@@ -74,7 +74,6 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBObject;
@@ -106,6 +105,8 @@ public class MongoTemplateTests {
private static final org.springframework.data.util.Version TWO_DOT_FOUR = org.springframework.data.util.Version
.parse("2.4");
private static final org.springframework.data.util.Version TWO_DOT_EIGHT = org.springframework.data.util.Version
.parse("2.8");
@Autowired MongoTemplate template;
@Autowired MongoDbFactory factory;
@@ -116,7 +117,6 @@ public class MongoTemplateTests {
@Rule public ExpectedException thrown = ExpectedException.none();
@Autowired
@SuppressWarnings("unchecked")
public void setMongo(Mongo mongo) throws Exception {
CustomConversions conversions = new CustomConversions(Arrays.asList(DateToDateTimeConverter.INSTANCE,
@@ -186,14 +186,9 @@ public class MongoTemplateTests {
template.dropCollection(DocumentWithCollection.class);
template.dropCollection(DocumentWithCollectionOfSimpleType.class);
template.dropCollection(DocumentWithMultipleCollections.class);
template.dropCollection(DocumentWithNestedCollection.class);
template.dropCollection(DocumentWithEmbeddedDocumentWithCollection.class);
template.dropCollection(DocumentWithNestedList.class);
template.dropCollection(DocumentWithDBRefCollection.class);
template.dropCollection(SomeContent.class);
template.dropCollection(SomeTemplate.class);
template.dropCollection(Address.class);
template.dropCollection(DocumentWithCollectionOfSamples.class);
}
@Test
@@ -241,7 +236,7 @@ public class MongoTemplateTests {
template.insert(person);
fail("Expected DataIntegrityViolationException!");
} catch (DataIntegrityViolationException e) {
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_ dup key:"));
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_"));
}
}
@@ -295,8 +290,7 @@ public class MongoTemplateTests {
template.save(person);
fail("Expected DataIntegrityViolationException!");
} catch (DataIntegrityViolationException e) {
assertThat(e.getMessage(),
containsString("E11000 duplicate key error index: database.person.$firstName_-1 dup key:"));
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$firstName_-1"));
}
}
@@ -324,6 +318,7 @@ public class MongoTemplateTests {
}
@Test
@SuppressWarnings("deprecation")
public void testEnsureIndex() throws Exception {
Person p1 = new Person("Oliver");
@@ -345,19 +340,29 @@ public class MongoTemplateTests {
if ("age_-1".equals(ix.get("name"))) {
indexKey = ix.get("key").toString();
unique = (Boolean) ix.get("unique");
dropDupes = (Boolean) ix.get("dropDups");
if (mongoVersion.isLessThan(TWO_DOT_EIGHT)) {
dropDupes = (Boolean) ix.get("dropDups");
assertThat(dropDupes, is(true));
} else {
assertThat(ix.get("dropDups"), is(nullValue()));
}
}
}
assertThat(indexKey, is("{ \"age\" : -1}"));
assertThat(unique, is(true));
assertThat(dropDupes, is(true));
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();
assertThat(indexInfoList.size(), is(2));
IndexInfo ii = indexInfoList.get(1);
assertThat(ii.isUnique(), is(true));
assertThat(ii.isDropDuplicates(), is(true));
if (mongoVersion.isLessThan(TWO_DOT_EIGHT)) {
assertThat(ii.isDropDuplicates(), is(true));
} else {
assertThat(ii.isDropDuplicates(), is(false));
}
assertThat(ii.isSparse(), is(false));
List<IndexField> indexFields = ii.getIndexFields();
@@ -683,8 +688,8 @@ public class MongoTemplateTests {
Query q = new Query(Criteria.where("text").regex("^Hello.*"));
Message found1 = template.findAndRemove(q, Message.class);
Message found2 = template.findAndRemove(q, Message.class);
// Message notFound = template.findAndRemove(q, Message.class);
DBObject notFound = template.getCollection("").findAndRemove(q.getQueryObject());
Message notFound = template.findAndRemove(q, Message.class);
assertThat(found1, notNullValue());
assertThat(found2, notNullValue());
assertThat(notFound, nullValue());
@@ -2227,243 +2232,6 @@ public class MongoTemplateTests {
assertThat(retrieved.model.value(), equalTo("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnDocumentWithNestedCollectionWhenWholeCollectionIsReplaced() {
DocumentWithNestedCollection doc = new DocumentWithNestedCollection();
Map<String, Model> entry = new HashMap<String, Model>();
entry.put("key1", new ModelA("value1"));
doc.models.add(entry);
template.save(doc);
entry.put("key2", new ModelA("value2"));
Query query = query(where("id").is(doc.id));
Update update = Update.update("models", Collections.singletonList(entry));
assertThat(template.findOne(query, DocumentWithNestedCollection.class), notNullValue());
template.findAndModify(query, update, DocumentWithNestedCollection.class);
DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class);
assertThat(retrieved, is(notNullValue()));
assertThat(retrieved.id, is(doc.id));
assertThat(retrieved.models.get(0).entrySet(), hasSize(2));
assertThat(retrieved.models.get(0).get("key1"), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get("key1").value(), equalTo("value1"));
assertThat(retrieved.models.get(0).get("key2"), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get("key2").value(), equalTo("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnDocumentWithNestedCollectionWhenFirstElementIsReplaced() {
DocumentWithNestedCollection doc = new DocumentWithNestedCollection();
Map<String, Model> entry = new HashMap<String, Model>();
entry.put("key1", new ModelA("value1"));
doc.models.add(entry);
template.save(doc);
entry.put("key2", new ModelA("value2"));
Query query = query(where("id").is(doc.id));
Update update = Update.update("models.0", entry);
assertThat(template.findOne(query, DocumentWithNestedCollection.class), notNullValue());
template.findAndModify(query, update, DocumentWithNestedCollection.class);
DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class);
assertThat(retrieved, is(notNullValue()));
assertThat(retrieved.id, is(doc.id));
assertThat(retrieved.models.get(0).entrySet(), hasSize(2));
assertThat(retrieved.models.get(0).get("key1"), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get("key1").value(), equalTo("value1"));
assertThat(retrieved.models.get(0).get("key2"), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get("key2").value(), equalTo("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldAddTypeInformationOnDocumentWithNestedCollectionObjectInsertedAtSecondIndex() {
DocumentWithNestedCollection doc = new DocumentWithNestedCollection();
Map<String, Model> entry = new HashMap<String, Model>();
entry.put("key1", new ModelA("value1"));
doc.models.add(entry);
template.save(doc);
Query query = query(where("id").is(doc.id));
Update update = Update.update("models.1", Collections.singletonMap("key2", new ModelA("value2")));
assertThat(template.findOne(query, DocumentWithNestedCollection.class), notNullValue());
template.findAndModify(query, update, DocumentWithNestedCollection.class);
DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class);
assertThat(retrieved, is(notNullValue()));
assertThat(retrieved.id, is(doc.id));
assertThat(retrieved.models.get(0).entrySet(), hasSize(1));
assertThat(retrieved.models.get(1).entrySet(), hasSize(1));
assertThat(retrieved.models.get(0).get("key1"), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get("key1").value(), equalTo("value1"));
assertThat(retrieved.models.get(1).get("key2"), instanceOf(ModelA.class));
assertThat(retrieved.models.get(1).get("key2").value(), equalTo("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenUpdatingPositionedElement()
throws Exception {
List<Model> models = new ArrayList<Model>();
models.add(new ModelA("value1"));
DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection(
new DocumentWithCollection(models));
template.save(doc);
Query query = query(where("id").is(doc.id));
Update update = Update.update("embeddedDocument.models.0", new ModelA("value2"));
assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class), notNullValue());
template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class);
DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query,
DocumentWithEmbeddedDocumentWithCollection.class);
assertThat(retrieved, notNullValue());
assertThat(retrieved.embeddedDocument.models, hasSize(1));
assertThat(retrieved.embeddedDocument.models.get(0).value(), is("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenUpdatingSecondElement()
throws Exception {
List<Model> models = new ArrayList<Model>();
models.add(new ModelA("value1"));
DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection(
new DocumentWithCollection(models));
template.save(doc);
Query query = query(where("id").is(doc.id));
Update update = Update.update("embeddedDocument.models.1", new ModelA("value2"));
assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class), notNullValue());
template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class);
DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query,
DocumentWithEmbeddedDocumentWithCollection.class);
assertThat(retrieved, notNullValue());
assertThat(retrieved.embeddedDocument.models, hasSize(2));
assertThat(retrieved.embeddedDocument.models.get(0).value(), is("value1"));
assertThat(retrieved.embeddedDocument.models.get(1).value(), is("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenRewriting()
throws Exception {
List<Model> models = Arrays.<Model> asList(new ModelA("value1"));
DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection(
new DocumentWithCollection(models));
template.save(doc);
Query query = query(where("id").is(doc.id));
Update update = Update.update("embeddedDocument",
new DocumentWithCollection(Arrays.<Model> asList(new ModelA("value2"))));
assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class), notNullValue());
template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class);
DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query,
DocumentWithEmbeddedDocumentWithCollection.class);
assertThat(retrieved, notNullValue());
assertThat(retrieved.embeddedDocument.models, hasSize(1));
assertThat(retrieved.embeddedDocument.models.get(0).value(), is("value2"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnDocumentWithNestedLists() {
DocumentWithNestedList doc = new DocumentWithNestedList();
List<Model> entry = new ArrayList<Model>();
entry.add(new ModelA("value1"));
doc.models.add(entry);
template.save(doc);
Query query = query(where("id").is(doc.id));
assertThat(template.findOne(query, DocumentWithNestedList.class), notNullValue());
Update update = Update.update("models.0.1", new ModelA("value2"));
template.findAndModify(query, update, DocumentWithNestedList.class);
DocumentWithNestedList retrieved = template.findOne(query, DocumentWithNestedList.class);
assertThat(retrieved, is(notNullValue()));
assertThat(retrieved.id, is(doc.id));
assertThat(retrieved.models.get(0), hasSize(2));
assertThat(retrieved.models.get(0).get(0), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get(0).value(), equalTo("value1"));
assertThat(retrieved.models.get(0).get(1), instanceOf(ModelA.class));
assertThat(retrieved.models.get(0).get(1).value(), equalTo("value2"));
}
/**
* @see DATAMONGO-407
*/
@@ -2869,33 +2637,6 @@ public class MongoTemplateTests {
assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values, hasSize(3));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void findAndModifyAddToSetWithEachShouldNotAddDuplicatesNorTypeHintForSimpleDocuments() {
DocumentWithCollectionOfSamples doc = new DocumentWithCollectionOfSamples();
doc.samples = Arrays.asList(new Sample(null, "sample1"));
template.save(doc);
Query query = query(where("id").is(doc.id));
assertThat(template.findOne(query, DocumentWithCollectionOfSamples.class), notNullValue());
Update update = new Update().addToSet("samples").each(new Sample(null, "sample2"), new Sample(null, "sample1"));
template.findAndModify(query, update, DocumentWithCollectionOfSamples.class);
DocumentWithCollectionOfSamples retrieved = template.findOne(query, DocumentWithCollectionOfSamples.class);
assertThat(retrieved, notNullValue());
assertThat(retrieved.samples, hasSize(2));
assertThat(retrieved.samples.get(0).field, is("sample1"));
assertThat(retrieved.samples.get(1).field, is("sample2"));
}
/**
* @see DATAMONGO-888
*/
@@ -3010,23 +2751,6 @@ public class MongoTemplateTests {
assertThat(template.findAll(DBObject.class, "collection"), hasSize(0));
}
/**
* @see DATAMONGO-1207
*/
@Test
public void ignoresNullElementsForInsertAll() {
Address newYork = new Address("NY", "New York");
Address washington = new Address("DC", "Washington");
template.insertAll(Arrays.asList(newYork, null, washington));
List<Address> result = template.findAll(Address.class);
assertThat(result, hasSize(2));
assertThat(result, hasItems(newYork, washington));
}
static class DoucmentWithNamedIdField {
@Id String someIdKey;
@@ -3102,36 +2826,12 @@ public class MongoTemplateTests {
List<String> values;
}
static class DocumentWithCollectionOfSamples {
@Id String id;
List<Sample> samples;
}
static class DocumentWithMultipleCollections {
@Id String id;
List<String> string1;
List<String> string2;
}
static class DocumentWithNestedCollection {
@Id String id;
List<Map<String, Model>> models = new ArrayList<Map<String, Model>>();
}
static class DocumentWithNestedList {
@Id String id;
List<List<Model>> models = new ArrayList<List<Model>>();
}
static class DocumentWithEmbeddedDocumentWithCollection {
@Id String id;
DocumentWithCollection embeddedDocument;
DocumentWithEmbeddedDocumentWithCollection(DocumentWithCollection embeddedDocument) {
this.embeddedDocument = embeddedDocument;
}
}
static interface Model {
String value();
@@ -3237,41 +2937,6 @@ public class MongoTemplateTests {
String state;
String city;
Address() {}
Address(String state, String city) {
this.state = state;
this.city = city;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Address)) {
return false;
}
Address that = (Address) obj;
return ObjectUtils.nullSafeEquals(this.city, that.city) && //
ObjectUtils.nullSafeEquals(this.state, that.state);
}
@Override
public int hashCode() {
int result = 17;
result += 31 * ObjectUtils.nullSafeHashCode(this.city);
result += 31 * ObjectUtils.nullSafeHashCode(this.state);
return result;
}
}
static class VersionedPerson {

View File

@@ -43,9 +43,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
@@ -54,21 +52,18 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCre
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.ReadPreference;
/**
* Unit tests for {@link MongoTemplate}.
@@ -358,74 +353,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
assertThat(captor.getValue(), equalTo(new BasicDBObjectBuilder().add("foo", 1).get()));
}
/**
* @see DATAMONGO-1166
*/
@Test
public void aggregateShouldHonorReadPreferenceWhenSet() {
CommandResult result = mock(CommandResult.class);
when(result.get("result")).thenReturn(Collections.emptySet());
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class))).thenReturn(result);
when(db.command(Mockito.any(DBObject.class))).thenReturn(result);
template.setReadPreference(ReadPreference.secondary());
template.aggregate(Aggregation.newAggregation(Aggregation.unwind("foo")), "collection-1", Wrapper.class);
verify(this.db, times(1)).command(Mockito.any(DBObject.class), eq(ReadPreference.secondary()));
}
/**
* @see DATAMONGO-1166
*/
@Test
public void aggregateShouldIgnoreReadPreferenceWhenNotSet() {
CommandResult result = mock(CommandResult.class);
when(result.get("result")).thenReturn(Collections.emptySet());
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class))).thenReturn(result);
when(db.command(Mockito.any(DBObject.class))).thenReturn(result);
template.aggregate(Aggregation.newAggregation(Aggregation.unwind("foo")), "collection-1", Wrapper.class);
verify(this.db, times(1)).command(Mockito.any(DBObject.class));
}
/**
* @see DATAMONGO-1166
*/
@Test
public void geoNearShouldHonorReadPreferenceWhenSet() {
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class)))
.thenReturn(mock(CommandResult.class));
when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
template.setReadPreference(ReadPreference.secondary());
NearQuery query = NearQuery.near(new Point(1, 1));
template.geoNear(query, Wrapper.class);
verify(this.db, times(1)).command(Mockito.any(DBObject.class), eq(ReadPreference.secondary()));
}
/**
* @see DATAMONGO-1166
*/
@Test
public void geoNearShouldIgnoreReadPreferenceWhenNotSet() {
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class)))
.thenReturn(mock(CommandResult.class));
when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
NearQuery query = NearQuery.near(new Point(1, 1));
template.geoNear(query, Wrapper.class);
verify(this.db, times(1)).command(Mockito.any(DBObject.class));
}
class AutogenerateableId {
@Id BigInteger id;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,14 +47,11 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.Metrics;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.Person;
import org.springframework.data.util.Version;
@@ -123,8 +120,6 @@ public class AggregationTests {
mongoTemplate.dropCollection(User.class);
mongoTemplate.dropCollection(Person.class);
mongoTemplate.dropCollection(Reservation.class);
mongoTemplate.dropCollection(Venue.class);
mongoTemplate.dropCollection(MeterData.class);
}
/**
@@ -1023,53 +1018,6 @@ public class AggregationTests {
assertThat(dbo.get("dayOfYearPlus1DayManually"), is((Object) dateTime.plusDays(1).getDayOfYear()));
}
/**
* @see DATAMONGO-1127
*/
@Test
public void shouldSupportGeoNearQueriesForAggregationWithDistanceField() {
mongoTemplate.insert(new Venue("Penn Station", -73.99408, 40.75057));
mongoTemplate.insert(new Venue("10gen Office", -73.99171, 40.738868));
mongoTemplate.insert(new Venue("Flatiron Building", -73.988135, 40.741404));
mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"));
NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150);
Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance"));
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, Venue.class, DBObject.class);
assertThat(result.getMappedResults(), hasSize(3));
DBObject firstResult = result.getMappedResults().get(0);
assertThat(firstResult.containsField("distance"), is(true));
assertThat((Double) firstResult.get("distance"), closeTo(117.620092203928, 0.00001));
}
/**
* @see DATAMONGO-1133
*/
@Test
public void shouldHonorFieldAliasesForFieldReferences() {
mongoTemplate.insert(new MeterData("m1", "counter1", 42));
mongoTemplate.insert(new MeterData("m1", "counter1", 13));
mongoTemplate.insert(new MeterData("m1", "counter1", 45));
TypedAggregation<MeterData> agg = newAggregation(MeterData.class, //
match(where("resourceId").is("m1")), //
group("counterName").sum("counterVolume").as("totalValue"));
AggregationResults<DBObject> results = mongoTemplate.aggregate(agg, DBObject.class);
assertThat(results.getMappedResults(), hasSize(1));
DBObject result = results.getMappedResults().get(0);
assertThat(result.get("_id"), is(equalTo((Object) "counter1")));
assertThat(result.get("totalValue"), is(equalTo((Object) 100.0)));
}
private void assertLikeStats(LikeStats like, String id, long count) {
assertThat(like, is(notNullValue()));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,30 +22,23 @@ import org.junit.Test;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
import org.springframework.data.mongodb.core.query.NearQuery;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Unit tests for {@link GeoNearOperation}.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class GeoNearOperationUnitTests {
/**
* @see DATAMONGO-1127
*/
@Test
public void rendersNearQueryAsAggregationOperation() {
NearQuery query = NearQuery.near(10.0, 10.0);
GeoNearOperation operation = new GeoNearOperation(query, "distance");
GeoNearOperation operation = new GeoNearOperation(query);
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
DBObject nearClause = DBObjectTestUtils.getAsDBObject(dbObject, "$geoNear");
DBObject expected = (DBObject) new BasicDBObject(query.toDBObject().toMap()).append("distanceField", "distance");
assertThat(nearClause, is(expected));
assertThat(nearClause, is(query.toDBObject()));
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Field;
/**
* @author Thomas Darimont
*/
public class MeterData {
@Id String id;
String resourceId;
@Field("counter_name") String counterName;
double counterVolume;
public MeterData(String resourceId, String counterName, double counterVolume) {
this.resourceId = resourceId;
this.counterName = counterName;
this.counterVolume = counterVolume;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -169,24 +169,6 @@ public class TypeBasedAggregationOperationContextUnitTests {
assertThat(dbo.get("cursor"), is((Object) new BasicDBObject("foo", 1)));
}
/**
* @see DATAMONGO-1133
*/
@Test
public void shouldHonorAliasedFieldsInGroupExpressions() {
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
TypedAggregation<MeterData> agg = newAggregation(MeterData.class,
group("counterName").sum("counterVolume").as("totalCounterVolume"));
DBObject dbo = agg.toDbObject("meterData", context);
DBObject group = getPipelineElementFromAggregationAt(dbo, 0);
DBObject definition = (DBObject) group.get("$group");
assertThat(definition.get("_id"), is(equalTo((Object) "$counter_name")));
}
@Document(collection = "person")
public static class FooPerson {

View File

@@ -40,8 +40,8 @@ import org.springframework.data.annotation.AccessType;
import org.springframework.data.annotation.AccessType.Type;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.Person;
@@ -505,6 +505,7 @@ public class DbRefMappingMongoConverterUnitTests {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(ClassWithLazyDbRefs.class);
MongoPersistentProperty property = entity.getPersistentProperty("dbRefToConcreteType");
MongoPersistentEntity<?> propertyEntity = mappingContext.getPersistentEntity(property);
String idValue = new ObjectId().toString();
DBRef dbRef = converter.toDBRef(new LazyDbRefTarget(idValue), property);
@@ -513,10 +514,10 @@ public class DbRefMappingMongoConverterUnitTests {
ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, object);
BeanWrapper<LazyDbRefTarget> wrapper = BeanWrapper.create(result.dbRefToConcreteType, null);
PersistentPropertyAccessor accessor = propertyEntity.getPropertyAccessor(result.dbRefToConcreteType);
MongoPersistentProperty idProperty = mappingContext.getPersistentEntity(LazyDbRefTarget.class).getIdProperty();
assertThat(wrapper.getProperty(idProperty), is(notNullValue()));
assertThat(accessor.getProperty(idProperty), is(notNullValue()));
assertProxyIsResolved(result.dbRefToConcreteType, false);
}

View File

@@ -23,6 +23,7 @@ import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -53,15 +54,12 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
@@ -74,7 +72,6 @@ import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
import org.springframework.data.mongodb.core.convert.DBObjectAccessorUnitTests.NestedType;
import org.springframework.data.mongodb.core.convert.DBObjectAccessorUnitTests.ProjectingType;
import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.ClassWithMapUsingEnumAsKey.FooBarEnum;
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@@ -87,6 +84,7 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
@@ -1874,66 +1872,94 @@ public class MappingMongoConverterUnitTests {
}
/**
* @see DATAMONGO-1118
* @see DATAMONGO-1050
*/
@Test
@SuppressWarnings("unchecked")
public void convertsMapKeyUsingCustomConverterForAndBackwards() {
public void writeShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() {
MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext);
converter.setCustomConversions(new CustomConversions(Arrays.asList(new FooBarEnumToStringConverter(),
new StringToFooNumConverter())));
converter.afterPropertiesSet();
RootForClassWithExplicitlyRenamedIdField source = new RootForClassWithExplicitlyRenamedIdField();
source.id = "rootId";
source.nested = new ClassWithExplicitlyRenamedField();
source.nested.id = "nestedId";
ClassWithMapUsingEnumAsKey source = new ClassWithMapUsingEnumAsKey();
source.map = new HashMap<FooBarEnum, String>();
source.map.put(FooBarEnum.FOO, "wohoo");
DBObject sink = new BasicDBObject();
converter.write(source, sink);
DBObject target = new BasicDBObject();
converter.write(source, target);
assertThat(converter.read(ClassWithMapUsingEnumAsKey.class, target).map, is(source.map));
assertThat((String) sink.get("_id"), is("rootId"));
assertThat((DBObject) sink.get("nested"), is(new BasicDBObjectBuilder().add("id", "nestedId").get()));
}
/**
* @see DATAMONGO-1118
* @see DATAMONGO-1050
*/
@Test
public void writesMapKeyUsingCustomConverter() {
public void readShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() {
MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext);
converter.setCustomConversions(new CustomConversions(Arrays.asList(new FooBarEnumToStringConverter())));
converter.afterPropertiesSet();
DBObject source = new BasicDBObjectBuilder().add("_id", "rootId")
.add("nested", new BasicDBObject("id", "nestedId")).get();
ClassWithMapUsingEnumAsKey source = new ClassWithMapUsingEnumAsKey();
source.map = new HashMap<FooBarEnum, String>();
source.map.put(FooBarEnum.FOO, "spring");
source.map.put(FooBarEnum.BAR, "data");
RootForClassWithExplicitlyRenamedIdField sink = converter.read(RootForClassWithExplicitlyRenamedIdField.class,
source);
DBObject target = new BasicDBObject();
converter.write(source, target);
DBObject map = DBObjectTestUtils.getAsDBObject(target, "map");
assertThat(map.containsField("foo-enum-value"), is(true));
assertThat(map.containsField("bar-enum-value"), is(true));
assertThat(sink.id, is("rootId"));
assertThat(sink.nested, notNullValue());
assertThat(sink.nested.id, is("nestedId"));
}
/**
* @see DATAMONGO-1118
* @see DATAMONGO-1050
*/
@Test
public void readsMapKeyUsingCustomConverter() {
public void namedIdFieldShouldExtractValueFromUnderscoreIdField() {
MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext);
converter.setCustomConversions(new CustomConversions(Arrays.asList(new StringToFooNumConverter())));
converter.afterPropertiesSet();
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
DBObject source = new BasicDBObject("map", new BasicDBObject("foo-enum-value", "spring"));
ClassWithNamedIdField withNamedIdField = converter.read(ClassWithNamedIdField.class, dbo);
ClassWithMapUsingEnumAsKey target = converter.read(ClassWithMapUsingEnumAsKey.class, source);
assertThat(withNamedIdField.id, is("A"));
}
assertThat(target.map.get(FooBarEnum.FOO), is("spring"));
/**
* @see DATAMONGO-1050
*/
@Test
public void explicitlyRenamedIfFieldShouldExtractValueFromIdField() {
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
ClassWithExplicitlyRenamedField withExplicitlyRenamedField = converter.read(ClassWithExplicitlyRenamedField.class,
dbo);
assertThat(withExplicitlyRenamedField.id, is("B"));
}
/**
* @see DATAMONGO-1050
*/
@Test
public void annotatedIdFieldShouldExtractValueFromUnderscoreIdField() {
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
ClassWithAnnotatedIdField withAnnotatedIdField = converter.read(ClassWithAnnotatedIdField.class, dbo);
assertThat(withAnnotatedIdField.key, is("A"));
}
/**
* @see DATAMONGO-1102
*/
@Test
public void convertsJava8DateTimeTypesToDateAndBack() {
TypeWithLocalDateTime source = new TypeWithLocalDateTime();
LocalDateTime reference = source.date;
BasicDBObject result = new BasicDBObject();
converter.write(source, result);
assertThat(result.get("date"), is(instanceOf(Date.class)));
assertThat(converter.read(TypeWithLocalDateTime.class, result).date, is(reference));
}
static class GenericType<T> {
@@ -2195,50 +2221,41 @@ public class MappingMongoConverterUnitTests {
public ClassWithIntId getDbRefProperty() {
return dbRefProperty;
}
}
static class ClassWithMapUsingEnumAsKey {
static class RootForClassWithExplicitlyRenamedIdField {
static enum FooBarEnum {
FOO, BAR;
}
Map<FooBarEnum, String> map;
@Id String id;
ClassWithExplicitlyRenamedField nested;
}
@WritingConverter
static class FooBarEnumToStringConverter implements Converter<FooBarEnum, String> {
static class ClassWithExplicitlyRenamedField {
@Override
public String convert(FooBarEnum source) {
if (source == null) {
return null;
}
return FooBarEnum.FOO.equals(source) ? "foo-enum-value" : "bar-enum-value";
}
@Field("id") String id;
}
@ReadingConverter
static class StringToFooNumConverter implements Converter<String, FooBarEnum> {
static class RootForClassWithNamedIdField {
@Override
public FooBarEnum convert(String source) {
String id;
ClassWithNamedIdField nested;
}
if (source == null) {
return null;
}
static class ClassWithNamedIdField {
if (source.equals("foo-enum-value")) {
return FooBarEnum.FOO;
}
if (source.equals("bar-enum-value")) {
return FooBarEnum.BAR;
}
String id;
}
throw new ConversionNotSupportedException(source, String.class, null);
static class ClassWithAnnotatedIdField {
@Id String key;
}
static class TypeWithLocalDateTime {
LocalDateTime date;
TypeWithLocalDateTime() {
this.date = LocalDateTime.now();
}
}
}

View File

@@ -674,6 +674,34 @@ public class QueryMapperUnitTests {
assertThat(reference.getId(), is(instanceOf(ObjectId.class)));
}
/**
* @see DATAMONGO-1050
*/
@Test
public void shouldUseExplicitlySetFieldnameForIdPropertyCandidates() {
Query query = query(where("nested.id").is("bar"));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class));
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", "bar").get()));
}
/**
* @see DATAMONGO-1050
*/
@Test
public void shouldUseExplicitlySetFieldnameForIdPropertyCandidatesUsedInSortClause() {
Query query = new Query().with(new Sort("nested.id"));
DBObject dbo = mapper.getMappedSort(query.getSortObject(),
context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class));
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", 1).get()));
}
@Document
public class Foo {
@Id private ObjectId id;
@@ -755,4 +783,15 @@ public class QueryMapperUnitTests {
@Id String id;
@TextScore @Field("score") Float textScore;
}
static class RootForClassWithExplicitlyRenamedIdField {
@Id String id;
ClassWithExplicitlyRenamedField nested;
}
static class ClassWithExplicitlyRenamedField {
@Field("id") String id;
}
}

View File

@@ -20,12 +20,9 @@ import static org.hamcrest.collection.IsMapContaining.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hamcrest.Matcher;
import org.hamcrest.collection.IsIterableContainingInOrder;
@@ -42,14 +39,12 @@ import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
import org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests.ClassWithEnum.Allocation;
import org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests.ClassWithEnum.AllocationToStringConverter;
import org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests.ClassWithEnum.StringToAllocationConverter;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.Update.Position;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -299,6 +294,76 @@ public class UpdateMapperUnitTests {
assertThat(getAsDBObject(push, "type").containsField("$each"), is(true));
}
/**
* @see DATAMONGO-943
*/
@Test
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositiveIndexParameter() {
Update update = new Update().push("key").atPosition(2).each(Arrays.asList("Arya", "Arry", "Weasel"));
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
DBObject push = getAsDBObject(mappedObject, "$push");
DBObject key = getAsDBObject(push, "key");
assertThat(key.containsField("$position"), is(true));
assertThat((Integer) key.get("$position"), is(2));
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
}
/**
* @see DATAMONGO-943
*/
@Test
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionFirst() {
Update update = new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel"));
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
DBObject push = getAsDBObject(mappedObject, "$push");
DBObject key = getAsDBObject(push, "key");
assertThat(key.containsField("$position"), is(true));
assertThat((Integer) key.get("$position"), is(0));
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
}
/**
* @see DATAMONGO-943
*/
@Test
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionLast() {
Update update = new Update().push("key").atPosition(Position.LAST).each(Arrays.asList("Arya", "Arry", "Weasel"));
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
DBObject push = getAsDBObject(mappedObject, "$push");
DBObject key = getAsDBObject(push, "key");
assertThat(key.containsField("$position"), is(false));
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
}
/**
* @see DATAMONGO-943
*/
@Test
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionNull() {
Update update = new Update().push("key").atPosition(null).each(Arrays.asList("Arya", "Arry", "Weasel"));
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
DBObject push = getAsDBObject(mappedObject, "$push");
DBObject key = getAsDBObject(push, "key");
assertThat(key.containsField("$position"), is(false));
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
}
/**
* @see DATAMONGO-410
*/
@@ -364,8 +429,8 @@ public class UpdateMapperUnitTests {
public void rendersNestedDbRefCorrectly() {
Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2");
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(Wrapper.class));
DBObject mappedObject = mapper
.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Wrapper.class));
DBObject pullClause = getAsDBObject(mappedObject, "$pull");
assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true));
@@ -460,6 +525,7 @@ public class UpdateMapperUnitTests {
assertThat(((DBObject) updateValue).get("_class").toString(),
equalTo("org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests$ModelImpl"));
}
}
/**
@@ -530,210 +596,6 @@ public class UpdateMapperUnitTests {
assertThat($unset, equalTo(new BasicDBObjectBuilder().add("dbRefAnnotatedList.$", 1).get()));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void mappingEachOperatorShouldNotAddTypeInfoForNonInterfaceNonAbstractTypes() {
Update update = new Update().addToSet("nestedDocs").each(new NestedDocument("nested-1"),
new NestedDocument("nested-2"));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(DocumentWithNestedCollection.class));
assertThat(mappedUpdate, isBsonObject().notContaining("$addToSet.nestedDocs.$each.[0]._class"));
assertThat(mappedUpdate, isBsonObject().notContaining("$addToSet.nestedDocs.$each.[1]._class"));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void mappingEachOperatorShouldAddTypeHintForInterfaceTypes() {
Update update = new Update().addToSet("models").each(new ModelImpl(1), new ModelImpl(2));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(ListModelWrapper.class));
assertThat(mappedUpdate, isBsonObject().containing("$addToSet.models.$each.[0]._class", ModelImpl.class.getName()));
assertThat(mappedUpdate, isBsonObject().containing("$addToSet.models.$each.[1]._class", ModelImpl.class.getName()));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void mappingEachOperatorShouldAddTypeHintForAbstractTypes() {
Update update = new Update().addToSet("list").each(new ConcreteChildClass("foo", "one"),
new ConcreteChildClass("bar", "two"));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(ParentClass.class));
assertThat(mappedUpdate,
isBsonObject().containing("$addToSet.aliased.$each.[0]._class", ConcreteChildClass.class.getName()));
assertThat(mappedUpdate,
isBsonObject().containing("$addToSet.aliased.$each.[1]._class", ConcreteChildClass.class.getName()));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void mappingShouldOnlyRemoveTypeHintFromTopLevelTypeInCaseOfNestedDocument() {
WrapperAroundInterfaceType wait = new WrapperAroundInterfaceType();
wait.interfaceType = new ModelImpl(1);
Update update = new Update().addToSet("listHoldingConcretyTypeWithInterfaceTypeAttribute").each(wait);
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(DomainTypeWithListOfConcreteTypesHavingSingleInterfaceTypeAttribute.class));
assertThat(mappedUpdate,
isBsonObject().notContaining("$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0]._class"));
assertThat(mappedUpdate,
isBsonObject().containing(
"$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0].interfaceType._class",
ModelImpl.class.getName()));
}
/**
* @see DATAMONGO-1210
*/
@Test
public void mappingShouldRetainTypeInformationOfNestedListWhenUpdatingConcreteyParentType() {
ListModelWrapper lmw = new ListModelWrapper();
lmw.models = Collections.<Model> singletonList(new ModelImpl(1));
Update update = new Update().set("concreteTypeWithListAttributeOfInterfaceType", lmw);
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class));
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteTypeWithListAttributeOfInterfaceType._class"));
assertThat(
mappedUpdate,
isBsonObject().containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class",
ModelImpl.class.getName()));
}
/**
* @see DATAMONGO-1236
*/
@Test
public void mappingShouldRetainTypeInformationForObjectValues() {
Update update = new Update().set("value", new NestedDocument("kaladin"));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(EntityWithObject.class));
assertThat(mappedUpdate, isBsonObject().containing("$set.value.name", "kaladin"));
assertThat(mappedUpdate, isBsonObject().containing("$set.value._class", NestedDocument.class.getName()));
}
/**
* @see DATAMONGO-1236
*/
@Test
public void mappingShouldNotRetainTypeInformationForConcreteValues() {
Update update = new Update().set("concreteValue", new NestedDocument("shallan"));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(EntityWithObject.class));
assertThat(mappedUpdate, isBsonObject().containing("$set.concreteValue.name", "shallan"));
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteValue._class"));
}
/**
* @see DATAMONGO-1236
*/
@Test
public void mappingShouldRetainTypeInformationForObjectValuesWithAlias() {
Update update = new Update().set("value", new NestedDocument("adolin"));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(EntityWithAliasedObject.class));
assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value.name", "adolin"));
assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value._class", NestedDocument.class.getName()));
}
/**
* @see DATAMONGO-1236
*/
@Test
public void mappingShouldRetrainTypeInformationWhenValueTypeOfMapDoesNotMatchItsDeclaration() {
Map<Object, Object> map = Collections.<Object, Object> singletonMap("szeth", new NestedDocument("son-son-vallano"));
Update update = new Update().set("map", map);
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(EntityWithObjectMap.class));
assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth.name", "son-son-vallano"));
assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth._class", NestedDocument.class.getName()));
}
/**
* @see DATAMONGO-1236
*/
@Test
public void mappingShouldNotContainTypeInformationWhenValueTypeOfMapMatchesDeclaration() {
Map<Object, NestedDocument> map = Collections.<Object, NestedDocument> singletonMap("jasnah", new NestedDocument(
"kholin"));
Update update = new Update().set("concreteMap", map);
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(EntityWithObjectMap.class));
assertThat(mappedUpdate, isBsonObject().containing("$set.concreteMap.jasnah.name", "kholin"));
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteMap.jasnah._class"));
}
/**
* @see DATAMONGO-1250
*/
@Test
@SuppressWarnings("unchecked")
public void mapsUpdateWithBothReadingAndWritingConverterRegistered() {
CustomConversions conversions = new CustomConversions(
Arrays.asList(AllocationToStringConverter.INSTANCE, StringToAllocationConverter.INSTANCE));
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
mappingContext.afterPropertiesSet();
MappingMongoConverter converter = new MappingMongoConverter(mock(DbRefResolver.class), mappingContext);
converter.setCustomConversions(conversions);
converter.afterPropertiesSet();
UpdateMapper mapper = new UpdateMapper(converter);
Update update = new Update().set("allocation", Allocation.AVAILABLE);
DBObject result = mapper.getMappedObject(update.getUpdateObject(),
mappingContext.getPersistentEntity(ClassWithEnum.class));
assertThat(result, isBsonObject().containing("$set.allocation", Allocation.AVAILABLE.code));
}
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
}
static class DomainTypeWithListOfConcreteTypesHavingSingleInterfaceTypeAttribute {
List<WrapperAroundInterfaceType> listHoldingConcretyTypeWithInterfaceTypeAttribute;
}
static class WrapperAroundInterfaceType {
Model interfaceType;
}
@org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface")
static interface DocumentWithReferenceToInterface {
@@ -770,7 +632,7 @@ public class UpdateMapperUnitTests {
private @Id String id;
@org.springframework.data.mongodb.core.mapping.DBRef //
@org.springframework.data.mongodb.core.mapping.DBRef//
private InterfaceDocumentDefinitionWithoutId referencedDocument;
public String getId() {
@@ -831,10 +693,10 @@ public class UpdateMapperUnitTests {
String id;
@Field("aliased") //
@Field("aliased")//
List<? extends AbstractChildClass> list;
@Field //
@Field//
List<Model> listOfInterface;
public ParentClass(String id, List<? extends AbstractChildClass> list) {
@@ -867,10 +729,6 @@ public class UpdateMapperUnitTests {
static class DomainEntity {
List<NestedEntity> collectionOfNestedEntities;
public List<NestedEntity> getCollectionOfNestedEntities() {
return collectionOfNestedEntities;
}
}
static class NestedEntity {
@@ -896,10 +754,10 @@ public class UpdateMapperUnitTests {
@Id public String id;
@org.springframework.data.mongodb.core.mapping.DBRef //
@org.springframework.data.mongodb.core.mapping.DBRef//
public List<Entity> dbRefAnnotatedList;
@org.springframework.data.mongodb.core.mapping.DBRef //
@org.springframework.data.mongodb.core.mapping.DBRef//
public Entity dbRefProperty;
}
@@ -913,82 +771,4 @@ public class UpdateMapperUnitTests {
@Field("mapped") DocumentWithDBRefCollection nested;
}
static class DocumentWithNestedCollection {
List<NestedDocument> nestedDocs;
}
static class NestedDocument {
String name;
public NestedDocument(String name) {
super();
this.name = name;
}
}
static class EntityWithObject {
Object value;
NestedDocument concreteValue;
}
static class EntityWithAliasedObject {
@Field("renamed-value") Object value;
}
static class EntityWithObjectMap {
Map<Object, Object> map;
Map<Object, NestedDocument> concreteMap;
}
static class ClassWithEnum {
Allocation allocation;
static enum Allocation {
AVAILABLE("V"), ALLOCATED("A");
String code;
private Allocation(String code) {
this.code = code;
}
public static Allocation of(String code) {
for (Allocation value : values()) {
if (value.code.equals(code)) {
return value;
}
}
throw new IllegalArgumentException();
}
}
static enum AllocationToStringConverter implements Converter<Allocation, String> {
INSTANCE;
@Override
public String convert(Allocation source) {
return source.code;
}
}
static enum StringToAllocationConverter implements Converter<String, Allocation> {
INSTANCE;
@Override
public Allocation convert(String source) {
return Allocation.of(source);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
@@ -28,7 +27,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.CompoundIndexResolutionTests;
@@ -36,14 +34,15 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexRes
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.IndexResolutionTests;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.MixedIndexResolutionTests;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.TextIndexedResolutionTests;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.Language;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntityTestDummy.MongoPersistentEntityDummyBuilder;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
@@ -741,39 +740,22 @@ public class MongoPersistentEntityIndexResolverUnitTests {
* @see DATAMONGO-962
*/
@Test
@SuppressWarnings({ "rawtypes", "unchecked" })
public void shouldCatchCyclicReferenceExceptionOnRoot() {
Document documentDummy = new Document() {
@Override
public Class<? extends Annotation> annotationType() {
return Document.class;
}
@Override
public String collection() {
return null;
}
@Override
public String language() {
return null;
}
};
MongoPersistentEntity entity = new BasicMongoPersistentEntity<Object>(ClassTypeInformation.from(Object.class));
MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class);
when(propertyMock.isEntity()).thenReturn(true);
when(propertyMock.getOwner()).thenReturn(
(PersistentEntity) MongoPersistentEntityDummyBuilder.forClass(Object.class).build());
when(propertyMock.getOwner()).thenReturn(entity);
when(propertyMock.getActualType()).thenThrow(
new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar"));
MongoPersistentEntity<SelfCyclingViaCollectionType> dummy = MongoPersistentEntityDummyBuilder
.forClass(SelfCyclingViaCollectionType.class).withCollection("foo").and(propertyMock).and(documentDummy)
.build();
MongoPersistentEntity<SelfCyclingViaCollectionType> selfCyclingEntity = new BasicMongoPersistentEntity<SelfCyclingViaCollectionType>(
ClassTypeInformation.from(SelfCyclingViaCollectionType.class));
new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class))
.resolveIndexForEntity(dummy);
.resolveIndexForEntity(selfCyclingEntity);
}
/**
@@ -856,21 +838,6 @@ public class MongoPersistentEntityIndexResolverUnitTests {
assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"), equalTo("component.name"));
}
/**
* @see DATAMONGO-1121
*/
@Test
public void shouldOnlyConsiderEntitiesAsPotentialCycleCandidates() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(OuterDocumentReferingToIndexedPropertyViaDifferentNonCyclingPaths.class);
assertThat(indexDefinitions, hasSize(2));
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("path1.foo"));
assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"),
equalTo("path2.propertyWithIndexedStructure.foo"));
}
@Document
static class MixedIndexRoot {
@@ -1035,17 +1002,6 @@ public class MongoPersistentEntityIndexResolverUnitTests {
NameComponent component;
}
@Document
public static class OuterDocumentReferingToIndexedPropertyViaDifferentNonCyclingPaths {
NoCycleButIndenticallNamedPropertiesDeeplyNested path1;
AlternatePathToNoCycleButIndenticallNamedPropertiesDeeplyNestedDocument path2;
}
public static class AlternatePathToNoCycleButIndenticallNamedPropertiesDeeplyNestedDocument {
NoCycleButIndenticallNamedPropertiesDeeplyNested propertyWithIndexedStructure;
}
}
private static List<IndexDefinitionHolder> prepareMappingContextAndResolveIndexForType(Class<?> type) {

View File

@@ -143,89 +143,6 @@ public class BasicMongoPersistentEntityUnitTests {
verify(propertyMock, never()).getActualType();
}
/**
* @see DATAMONGO-1157
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test(expected = MappingException.class)
public void verifyShouldThrowErrorForLazyDBRefOnFinalClass() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
org.springframework.data.mongodb.core.mapping.DBRef.class);
when(propertyMock.isDbReference()).thenReturn(true);
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
when(dbRefMock.lazy()).thenReturn(true);
when(propertyMock.getActualType()).thenReturn((Class) Class.class);
entity.addPersistentProperty(propertyMock);
entity.verify();
}
/**
* @see DATAMONGO-1157
*/
@Test(expected = MappingException.class)
public void verifyShouldThrowErrorForLazyDBRefArray() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
org.springframework.data.mongodb.core.mapping.DBRef.class);
when(propertyMock.isDbReference()).thenReturn(true);
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
when(dbRefMock.lazy()).thenReturn(true);
when(propertyMock.isArray()).thenReturn(true);
entity.addPersistentProperty(propertyMock);
entity.verify();
}
/**
* @see DATAMONGO-1157
*/
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void verifyShouldPassForLazyDBRefOnNonArrayNonFinalClass() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
org.springframework.data.mongodb.core.mapping.DBRef.class);
when(propertyMock.isDbReference()).thenReturn(true);
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
when(dbRefMock.lazy()).thenReturn(true);
when(propertyMock.getActualType()).thenReturn((Class) Object.class);
entity.addPersistentProperty(propertyMock);
entity.verify();
verify(propertyMock, times(1)).isDbReference();
}
/**
* @see DATAMONGO-1157
*/
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void verifyShouldPassForNonLazyDBRefOnFinalClass() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
org.springframework.data.mongodb.core.mapping.DBRef.class);
when(propertyMock.isDbReference()).thenReturn(true);
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
when(dbRefMock.lazy()).thenReturn(false);
when(propertyMock.getActualType()).thenReturn((Class) Class.class);
entity.addPersistentProperty(propertyMock);
entity.verify();
verify(dbRefMock, times(1)).lazy();
}
@Document(collection = "contacts")
class Contact {

View File

@@ -130,10 +130,7 @@ public class BasicMongoPersistentPropertyUnitTests {
@Test
public void shouldDetectAnnotatedLanguagePropertyCorrectly() {
BasicMongoPersistentEntity<DocumentWithLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithLanguageProperty>(
ClassTypeInformation.from(DocumentWithLanguageProperty.class));
MongoPersistentProperty property = getPropertyFor(persistentEntity, "lang");
MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang");
assertThat(property.isLanguageProperty(), is(true));
}
@@ -143,10 +140,7 @@ public class BasicMongoPersistentPropertyUnitTests {
@Test
public void shouldDetectIplicitLanguagePropertyCorrectly() {
BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty>(
ClassTypeInformation.from(DocumentWithImplicitLanguageProperty.class));
MongoPersistentProperty property = getPropertyFor(persistentEntity, "language");
MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language");
assertThat(property.isLanguageProperty(), is(true));
}
@@ -156,10 +150,7 @@ public class BasicMongoPersistentPropertyUnitTests {
@Test
public void shouldDetectTextScorePropertyCorrectly() {
BasicMongoPersistentEntity<DocumentWithTextScoreProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithTextScoreProperty>(
ClassTypeInformation.from(DocumentWithTextScoreProperty.class));
MongoPersistentProperty property = getPropertyFor(persistentEntity, "score");
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
assertThat(property.isTextScoreProperty(), is(true));
}
@@ -169,17 +160,39 @@ public class BasicMongoPersistentPropertyUnitTests {
@Test
public void shouldDetectTextScoreAsReadOnlyProperty() {
BasicMongoPersistentEntity<DocumentWithTextScoreProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithTextScoreProperty>(
ClassTypeInformation.from(DocumentWithTextScoreProperty.class));
MongoPersistentProperty property = getPropertyFor(persistentEntity, "score");
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
assertThat(property.isWritable(), is(false));
}
/**
* @see DATAMONGO-1050
*/
@Test
public void shouldNotConsiderExplicitlyNameFieldAsIdProperty() {
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id");
assertThat(property.isIdProperty(), is(false));
}
/**
* @see DATAMONGO-1050
*/
@Test
public void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() {
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class,
"id");
assertThat(property.isIdProperty(), is(true));
}
private MongoPersistentProperty getPropertyFor(Field field) {
return getPropertyFor(entity, field);
}
private <T> MongoPersistentProperty getPropertyFor(Class<T> type, String fieldname) {
return getPropertyFor(new BasicMongoPersistentEntity<T>(ClassTypeInformation.from(type)), fieldname);
}
private MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> persistentEntity, String fieldname) {
return getPropertyFor(persistentEntity, ReflectionUtils.findField(persistentEntity.getType(), fieldname));
}
@@ -230,4 +243,14 @@ public class BasicMongoPersistentPropertyUnitTests {
static class DocumentWithTextScoreProperty {
@TextScore Float score;
}
static class DocumentWithExplicitlyRenamedIdProperty {
@org.springframework.data.mongodb.core.mapping.Field("id") String id;
}
static class DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation {
@Id @org.springframework.data.mongodb.core.mapping.Field("id") String id;
}
}

View File

@@ -1,227 +0,0 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.SimpleAssociationHandler;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.util.TypeInformation;
/**
* Trivial dummy implementation of {@link MongoPersistentEntity} to be used in tests.
*
* @author Christoph Strobl
* @param <T>
*/
public class MongoPersistentEntityTestDummy<T> implements MongoPersistentEntity<T> {
private Map<Class<?>, Annotation> annotations = new HashMap<Class<?>, Annotation>();
private Collection<MongoPersistentProperty> properties = new ArrayList<MongoPersistentProperty>();
private String collection;
private String name;
private Class<T> type;
@Override
public String getName() {
return name;
}
@Override
public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() {
return null;
}
@Override
public boolean isConstructorArgument(PersistentProperty<?> property) {
return false;
}
@Override
public boolean isIdProperty(PersistentProperty<?> property) {
return property != null ? property.isIdProperty() : false;
}
@Override
public boolean isVersionProperty(PersistentProperty<?> property) {
return property != null ? property.isIdProperty() : false;
}
@Override
public MongoPersistentProperty getIdProperty() {
return getPersistentProperty(Id.class);
}
@Override
public MongoPersistentProperty getVersionProperty() {
return getPersistentProperty(Version.class);
}
@Override
public MongoPersistentProperty getPersistentProperty(String name) {
for (MongoPersistentProperty p : this.properties) {
if (p.getName().equals(name)) {
return p;
}
}
return null;
}
@Override
public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
for (MongoPersistentProperty p : this.properties) {
if (p.isAnnotationPresent(annotationType)) {
return p;
}
}
return null;
}
@Override
public boolean hasIdProperty() {
return false;
}
@Override
public boolean hasVersionProperty() {
return getVersionProperty() != null;
}
@Override
public Class<T> getType() {
return this.type;
}
@Override
public Object getTypeAlias() {
return null;
}
@Override
public TypeInformation<T> getTypeInformation() {
return null;
}
@Override
public void doWithProperties(PropertyHandler<MongoPersistentProperty> handler) {
for (MongoPersistentProperty p : this.properties) {
handler.doWithPersistentProperty(p);
}
}
@Override
public void doWithProperties(SimplePropertyHandler handler) {
for (MongoPersistentProperty p : this.properties) {
handler.doWithPersistentProperty(p);
}
}
@Override
public void doWithAssociations(AssociationHandler<MongoPersistentProperty> handler) {
}
@Override
public void doWithAssociations(SimpleAssociationHandler handler) {
}
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
return (A) this.annotations.get(annotationType);
}
@Override
public String getCollection() {
return this.collection;
}
/**
* Simple builder to create {@link MongoPersistentEntityTestDummy} with defined properties.
*
* @author Christoph Strobl
* @param <T>
*/
public static class MongoPersistentEntityDummyBuilder<T> {
private MongoPersistentEntityTestDummy<T> instance;
private MongoPersistentEntityDummyBuilder(Class<T> type) {
this.instance = new MongoPersistentEntityTestDummy<T>();
this.instance.type = type;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <T> MongoPersistentEntityDummyBuilder<T> forClass(Class<T> type) {
return new MongoPersistentEntityDummyBuilder(type);
}
public MongoPersistentEntityDummyBuilder<T> withName(String name) {
this.instance.name = name;
return this;
}
public MongoPersistentEntityDummyBuilder<T> and(MongoPersistentProperty property) {
this.instance.properties.add(property);
return this;
}
public MongoPersistentEntityDummyBuilder<T> withCollection(String collection) {
this.instance.collection = collection;
return this;
}
public MongoPersistentEntityDummyBuilder<T> and(Annotation annotation) {
this.instance.annotations.put(annotation.annotationType(), annotation);
return this;
}
public MongoPersistentEntityTestDummy<T> build() {
return this.instance;
}
}
@Override
public String getLanguage() {
return null;
}
@Override
public MongoPersistentProperty getTextScoreProperty() {
return null;
}
@Override
public boolean hasTextScoreProperty() {
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 by the original author(s).
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,23 +16,17 @@
package org.springframework.data.mongodb.core.mapping.event;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
import com.mongodb.DBObject;
public class PersonBeforeSaveListener implements ApplicationListener<BeforeSaveEvent<PersonPojoStringId>> {
public class PersonBeforeSaveListener extends AbstractMongoEventListener<PersonPojoStringId> {
public final ArrayList<ApplicationEvent> seenEvents = new ArrayList<ApplicationEvent>();
public final List<ApplicationEvent> seenEvents = new ArrayList<ApplicationEvent>();
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeSave(java.lang.Object, com.mongodb.DBObject)
*/
@Override
public void onBeforeSave(PersonPojoStringId source, DBObject dbo) {
seenEvents.add(new BeforeSaveEvent<PersonPojoStringId>(source, dbo));
public void onApplicationEvent(BeforeSaveEvent<PersonPojoStringId> event) {
this.seenEvents.add(event);
}
}

View File

@@ -49,6 +49,7 @@ import com.mongodb.Mongo;
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -60,7 +61,6 @@ public class GroupByTests {
MongoTemplate mongoTemplate;
@Autowired
@SuppressWarnings("unchecked")
public void setMongo(Mongo mongo) throws Exception {
MongoMappingContext mappingContext = new MongoMappingContext();
@@ -116,7 +116,7 @@ public class GroupByTests {
}
@Test
public void SimpleGroup() {
public void simpleGroupFunction() {
createGroupByData();
GroupByResults<XObject> results = mongoTemplate.group(
@@ -128,7 +128,7 @@ public class GroupByTests {
}
@Test
public void SimpleGroupWithKeyFunction() {
public void simpleGroupWithKeyFunction() {
createGroupByData();
GroupByResults<XObject> results = mongoTemplate.group(
@@ -140,7 +140,7 @@ public class GroupByTests {
}
@Test
public void SimpleGroupWithFunctionsAsResources() {
public void simpleGroupWithFunctionsAsResources() {
createGroupByData();
GroupByResults<XObject> results = mongoTemplate.group(
@@ -152,7 +152,7 @@ public class GroupByTests {
}
@Test
public void SimpleGroupWithQueryAndFunctionsAsResources() {
public void simpleGroupWithQueryAndFunctionsAsResources() {
createGroupByData();
GroupByResults<XObject> results = mongoTemplate.group(

View File

@@ -420,4 +420,68 @@ public class UpdateTests {
Update update = new Update().addToSet("key", new DateTime());
assertThat(update.toString(), is(notNullValue()));
}
/**
* @see DATAMONGO-1097
*/
@Test(expected = IllegalArgumentException.class)
public void multiplyShouldThrowExceptionForNullMultiplier() {
new Update().multiply("key", null);
}
/**
* @see DATAMONGO-1097
*/
@Test
public void multiplyShouldAddMultiplierAsItsDoubleValue() {
Update update = new Update().multiply("key", 10);
assertThat(update.getUpdateObject(), equalTo(new BasicDBObjectBuilder().add("$mul", new BasicDBObject("key", 10D))
.get()));
}
/**
* @see DATAMONGO-1101
*/
@Test
public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseAnd() {
Update update = new Update().bitwise("key").and(10L);
assertThat(update.getUpdateObject(),
equalTo(new BasicDBObjectBuilder().add("$bit", new BasicDBObject("key", new BasicDBObject("and", 10L))).get()));
}
/**
* @see DATAMONGO-1101
*/
@Test
public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseOr() {
Update update = new Update().bitwise("key").or(10L);
assertThat(update.getUpdateObject(),
equalTo(new BasicDBObjectBuilder().add("$bit", new BasicDBObject("key", new BasicDBObject("or", 10L))).get()));
}
/**
* @see DATAMONGO-1101
*/
@Test
public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseXor() {
Update update = new Update().bitwise("key").xor(10L);
assertThat(update.getUpdateObject(),
equalTo(new BasicDBObjectBuilder().add("$bit", new BasicDBObject("key", new BasicDBObject("xor", 10L))).get()));
}
/**
* @see DATAMONGO-943
*/
@Test(expected = IllegalArgumentException.class)
public void pushShouldThrowExceptionWhenGivenNegativePosition() {
new Update().push("foo").atPosition(-1).each("booh");
}
}

View File

@@ -1063,6 +1063,17 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(persons, hasItem(alicia));
}
/**
* @see DATAMONGO-1105
*/
@Test
public void returnsOrderedResultsForQuerydslOrderSpecifier() {
Iterable<Person> result = repository.findAll(person.firstname.asc());
assertThat(result, contains(alicia, boyd, carter, dave, leroi, oliver, stefan));
}
/**
* @see DATAMONGO-1085
*/

View File

@@ -20,7 +20,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* Sample contact domain class.
* Sample contactt domain class.
*
* @author Oliver Gierke
*/

View File

@@ -25,7 +25,6 @@ import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
/**
* Sample domain class.
@@ -51,7 +50,7 @@ public class Person extends Contact {
@GeoSpatialIndexed private Point location;
private @Field("add") Address address;
private Address address;
private Set<Address> shippingAddresses;
@DBRef User creator;

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
@@ -100,7 +100,8 @@ public class AbstractMongoQueryUnitTests {
createQueryForMethod("deletePersonByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), Matchers.eq(Person.class),
Matchers.eq("persons"));
verify(this.mongoOperationsMock, times(0)).find(Matchers.any(Query.class), Matchers.any(Class.class),
Matchers.anyString());
}
@@ -118,8 +119,8 @@ public class AbstractMongoQueryUnitTests {
createQueryForMethod("deleteByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
verify(this.mongoOperationsMock, times(1)).findAllAndRemove(Matchers.any(Query.class), eq(Person.class),
eq("persons"));
verify(this.mongoOperationsMock, times(1)).findAllAndRemove(Matchers.any(Query.class), Matchers.eq(Person.class),
Matchers.eq("persons"));
}
/**
@@ -142,14 +143,15 @@ public class AbstractMongoQueryUnitTests {
public void testDeleteExecutionReturnsNrDocumentsDeletedFromWriteResult() {
when(writeResultMock.getN()).thenReturn(100);
when(this.mongoOperationsMock.remove(Matchers.any(Query.class), eq(Person.class), eq("persons"))).thenReturn(
writeResultMock);
when(this.mongoOperationsMock.remove(Matchers.any(Query.class), Matchers.eq(Person.class), Matchers.eq("persons")))
.thenReturn(writeResultMock);
MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class);
query.setDeleteQuery(true);
assertThat(query.execute(new Object[] { "fake" }), is((Object) 100L));
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), Matchers.eq(Person.class),
Matchers.eq("persons"));
}
/**
@@ -163,7 +165,8 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(1))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getValue().getMeta().getComment(), nullValue());
}
@@ -179,7 +182,8 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(1))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
@@ -194,7 +198,7 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(1)).count(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(1)).count(captor.capture(), Matchers.eq("persons"));
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
@@ -209,7 +213,8 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(1))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
@@ -228,7 +233,8 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(2))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getAllValues().get(0).getSkip(), is(0));
assertThat(captor.getAllValues().get(1).getSkip(), is(10));
@@ -249,7 +255,8 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(2))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getAllValues().get(0).getLimit(), is(11));
assertThat(captor.getAllValues().get(1).getLimit(), is(11));
@@ -270,7 +277,8 @@ public class AbstractMongoQueryUnitTests {
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons"));
verify(this.mongoOperationsMock, times(2))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
DBObject expectedSortObject = new BasicDBObjectBuilder().add("bar", -1).get();
assertThat(captor.getAllValues().get(0).getSortObject(), is(expectedSortObject));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2014 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,23 +36,31 @@ import org.springframework.data.mongodb.repository.support.MappingMongoEntityInf
@RunWith(MockitoJUnitRunner.class)
public class MappingMongoEntityInformationUnitTests {
@Mock
MongoPersistentEntity<Person> info;
@Mock MongoPersistentEntity<Person> info;
@Before
public void setUp() {
when(info.getType()).thenReturn(Person.class);
when(info.getCollection()).thenReturn("Person");
}
/**
* @see DATAMONGO-248
*/
@Test
public void usesEntityCollectionIfNoCustomOneGiven() {
MongoEntityInformation<Person, Long> information = new MappingMongoEntityInformation<Person, Long>(info);
assertThat(information.getCollectionName(), is("Person"));
}
/**
* @see DATAMONGO-248
*/
@Test
public void usesCustomCollectionIfGiven() {
MongoEntityInformation<Person, Long> information = new MappingMongoEntityInformation<Person, Long>(info, "foobar");
assertThat(information.getCollectionName(), is("foobar"));
}

View File

@@ -41,7 +41,6 @@ import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Field;
@@ -441,127 +440,30 @@ public class MongoQueryCreatorUnitTests {
}
/**
* @see DATAMONGO-1139
* @see DATAMONGO-1075
*/
@Test
public void createsNonShericalNearForDistanceWithDefaultMetric() {
public void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() {
Point point = new Point(1.0, 1.0);
Distance distance = new Distance(1.0);
PartTree tree = new PartTree("findByEmailAddressesNotContaining", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
PartTree tree = new PartTree("findByLocationNear", Venue.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("location").near(point).maxDistance(1.0))));
assertThat(query, is(query(where("emailAddresses").not().in("dave"))));
}
/**
* @see DATAMONGO-1229
* @see DATAMONGO-1075
*/
@Test
public void appliesIgnoreCaseToLeafProperty() {
public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() {
PartTree tree = new PartTree("findByAddressStreetIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "Street");
PartTree tree = new PartTree("findByUsernameNotContaining", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
Query query = creator.createQuery();
assertThat(new MongoQueryCreator(tree, accessor, context).createQuery(), is(notNullValue()));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void ignoreCaseShouldEscapeSource() {
PartTree tree = new PartTree("findByUsernameIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "con.flux+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("^\\Qcon.flux+\\E$", "i"))));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void ignoreCaseShouldEscapeSourceWhenUsedForStartingWith() {
PartTree tree = new PartTree("findByUsernameStartingWithIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "dawns.light+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("^\\Qdawns.light+\\E", "i"))));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void ignoreCaseShouldEscapeSourceWhenUsedForEndingWith() {
PartTree tree = new PartTree("findByUsernameEndingWithIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "new.ton+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("\\Qnew.ton+\\E$", "i"))));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void likeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex(".*\\Qfire.fight+\\E.*"))));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void likeShouldEscapeSourceWhenUsedWithLeadingWildcard() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex(".*\\Qsteel.heart+\\E"))));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void likeShouldEscapeSourceWhenUsedWithTrailingWildcard() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "cala.mity+*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("\\Qcala.mity+\\E.*"))));
}
/**
* @see DATAMONGO-1232
*/
@Test
public void likeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex(".*"))));
assertThat(query, is(query(where("username").regex(".*thew.*").not())));
}
interface PersonRepository extends Repository<Person, Long> {
@@ -576,12 +478,5 @@ public class MongoQueryCreatorUnitTests {
@DBRef User creator;
List<String> emailAddresses;
Address address;
}
class Address {
String street;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,6 @@ import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.core.RepositoryMetadata;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.util.JSONParseException;
/**
* Unit tests for {@link PartTreeMongoQuery}.
@@ -136,18 +135,6 @@ public class PartTreeMongoQueryUnitTests {
assertThat(query, isTextQuery().searchingFor("search").where(new Criteria("firstname").is("text")));
}
/**
* @see DATAMONGO-1180
*/
@Test
public void propagatesRootExceptionForInvalidQuery() {
exception.expect(IllegalStateException.class);
exception.expectCause(is(org.hamcrest.Matchers.<Throwable> instanceOf(JSONParseException.class)));
deriveQueryFromMethod("findByAge", new Object[] { 1 });
}
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object[] args) {
Class<?>[] types = new Class<?>[args.length];
@@ -192,8 +179,5 @@ public class PartTreeMongoQueryUnitTests {
Person findPersonByFirstnameAndLastname(String firstname, String lastname);
Person findPersonByFirstname(String firstname, TextCriteria fullText);
@Query(fields = "{ 'firstname }")
Person findByAge(Integer age);
}
}

View File

@@ -34,7 +34,6 @@ import org.springframework.data.repository.query.ParameterAccessor;
class StubParameterAccessor implements MongoParameterAccessor {
private final Object[] values;
private Distance distance;
/**
* Creates a new {@link ConvertingParameterAccessor} backed by a {@link StubParameterAccessor} simply returning the
@@ -49,14 +48,7 @@ class StubParameterAccessor implements MongoParameterAccessor {
}
public StubParameterAccessor(Object... values) {
this.values = values;
for (Object value : values) {
if (value instanceof Distance) {
this.distance = (Distance) value;
}
}
}
/*
@@ -96,7 +88,7 @@ class StubParameterAccessor implements MongoParameterAccessor {
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance()
*/
public Distance getMaxDistance() {
return distance;
return null;
}
/*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,16 +37,18 @@ import com.mysema.query.mongodb.MongodbQuery;
* Unit tests for {@link QuerydslRepositorySupport}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
public class QuerydslRepositorySupportUnitTests {
public class QuerydslRepositorySupportTests {
@Autowired MongoOperations operations;
Person person;
@Before
public void setUp() {
operations.remove(new Query(), Person.class);
person = new Person("Dave", "Matthews");
operations.save(person);
@@ -54,6 +56,7 @@ public class QuerydslRepositorySupportUnitTests {
@Test
public void providesMongoQuery() {
QPerson p = QPerson.person;
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {};
MongodbQuery<Person> query = support.from(p).where(p.lastname.eq("Matthews"));

View File

@@ -1,193 +0,0 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.test.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.bson.BSONObject;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.core.IsEqual;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.util.ClassUtils;
import com.mongodb.DBObject;
/**
* @author Christoph Strobl
* @param <T>
*/
public class IsBsonObject<T extends BSONObject> extends TypeSafeMatcher<T> {
private List<ExpectedBsonContent> expectations = new ArrayList<ExpectedBsonContent>();;
public static <T extends BSONObject> IsBsonObject<T> isBsonObject() {
return new IsBsonObject<T>();
}
@Override
protected void describeMismatchSafely(T item, Description mismatchDescription) {
mismatchDescription.appendText("was ").appendValue(SerializationUtils.serializeToJsonSafely(item));
}
@Override
public void describeTo(Description description) {
for (ExpectedBsonContent expectation : expectations) {
if (expectation.not) {
description.appendText(String.format("Path %s should not be present.", expectation.path));
} else if (expectation.value == null) {
description.appendText(String.format("Expected to find path %s.", expectation.path));
} else {
description.appendText(String.format("Expected to find %s for path %s.", expectation.value, expectation.path));
}
}
}
@Override
protected boolean matchesSafely(T item) {
if (expectations.isEmpty()) {
return true;
}
for (ExpectedBsonContent expectation : expectations) {
Object o = getValue(item, expectation.path);
if (o == null && expectation.not) {
return true;
}
if (o == null) {
return false;
}
if (expectation.type != null && !ClassUtils.isAssignable(expectation.type, o.getClass())) {
return false;
}
if (expectation.value != null && !new IsEqual<Object>(expectation.value).matches(o)) {
return false;
}
if (o != null && expectation.not) {
return false;
}
}
return true;
}
public IsBsonObject<T> containing(String key) {
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
this.expectations.add(expected);
return this;
}
public IsBsonObject<T> containing(String key, Class<?> type) {
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
expected.type = type;
this.expectations.add(expected);
return this;
}
public IsBsonObject<T> containing(String key, Object value) {
if (value == null) {
return notContaining(key);
}
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
expected.type = ClassUtils.getUserClass(value);
expected.value = value;
this.expectations.add(expected);
return this;
}
public IsBsonObject<T> notContaining(String key) {
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
expected.not = true;
this.expectations.add(expected);
return this;
}
static class ExpectedBsonContent {
String path;
Class<?> type;
Object value;
boolean not = false;
}
Object getValue(BSONObject source, String path) {
String[] fragments = path.split("\\.");
if (fragments.length == 1) {
return source.get(path);
}
Iterator<String> it = Arrays.asList(fragments).iterator();
Object current = source;
while (it.hasNext()) {
String key = it.next();
if (!(current instanceof BSONObject) && !key.startsWith("[")) {
return null;
}
if (key.startsWith("[")) {
String indexNumber = key.substring(1, key.indexOf("]"));
if (current instanceof List) {
current = ((List) current).get(Integer.valueOf(indexNumber));
}
if (!it.hasNext()) {
return current;
}
} else {
if (current instanceof DBObject) {
current = ((DBObject) current).get(key);
}
if (!it.hasNext()) {
return current;
}
}
}
throw new NoSuchElementException(String.format("Unable to find '%s' in %s.", path, source));
}
}

View File

@@ -4,9 +4,9 @@ Mark Pollack; Thomas Risberg; Oliver Gierke; Costin Leau; Jon Brisbin; Thomas Da
:revdate: {localdate}
:toc:
:toc-placement!:
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
:spring-data-commons-docs: https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc
(C) 2008-2015 The original authors.
(C) 2008-2014 The original authors.
NOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._
@@ -15,7 +15,6 @@ toc::[]
include::preface.adoc[]
:leveloffset: +1
include::{spring-data-commons-docs}/dependencies.adoc[]
include::{spring-data-commons-docs}/repositories.adoc[]
:leveloffset: -1
@@ -27,7 +26,6 @@ include::reference/introduction.adoc[]
include::reference/mongodb.adoc[]
include::reference/mongo-repositories.adoc[]
include::{spring-data-commons-docs}/auditing.adoc[]
include::reference/mongo-auditing.adoc[]
include::reference/mapping.adoc[]
include::reference/cross-store.adoc[]
include::reference/logging.adoc[]

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -26,30 +26,7 @@ MongoDB requires that you have an '_id' field for all documents. If you don't pr
The following outlines what field will be mapped to the '_id' document field:
* A field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the '_id' field.
* A field without an annotation but named 'id' will be mapped to the '_id' field.
* The default field name for identifiers is '_id' and can be customized via the `@Field` annotation.
[cols="1,2", options="header"]
.Examples for the translation of '_id'-field definitions
|===
| Field definition
| Resulting Id-Fieldname in MongoDB
| `String` id
| `_id`
| `@Field` `String` id
| `_id`
| `@Field('x')` `String` id
| `x`
| `@Id` `String` x
| `_id`
| `@Field('x')` `@Id` `String` x
| `_id`
|===
* A field without an annotation but named `id` will be mapped to the '_id' field.
The following outlines what type conversion, if any, will be done on the property mapped to the _id document field.

View File

@@ -1,33 +0,0 @@
[[mongo.auditing]]
== General auditing configuration
Activating auditing functionality is just a matter of adding the Spring Data Mongo `auditing` namespace element to your configuration:
.Activating auditing using XML configuration
====
[source,xml]
----
<mongo:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/>
----
====
Since Spring Data MongoDB 1.4 auditing can be enabled by annotating a configuration class with the `@EnableMongoAuditing` annotation.
.Activating auditing using JavaConfig
====
[source,java]
----
@Configuration
@EnableMongoAuditing
class Config {
@Bean
public AuditorAware<AuditableUser> myAuditorProvider() {
return new AuditorAwareImpl();
}
}
----
====
If you expose a bean of type `AuditorAware` to the `ApplicationContext`, the auditing infrastructure will pick it up automatically and use it to determine the current user to be set on domain types. If you have multiple implementations registered in the `ApplicationContext`, you can select the one to be used by explicitly setting the `auditorAwareRef` attribute of `@EnableJpaAuditing`.

View File

@@ -147,87 +147,87 @@ The first method shows a query for all people with the given lastname. The query
NOTE: Note that for version 1.0 we currently don't support referring to parameters that are mapped as `DBRef` in the domain class.
[cols="1,2,3", options="header"]
[cols="1,2,3", options="header"]
.Supported keywords for query methods
|===
| Keyword
| Sample
| Sample
| Logical result
| `GreaterThan`
| `findByAgeGreaterThan(int age)`
| `GreaterThan`
| `findByAgeGreaterThan(int age)`
| `{"age" : {"$gt" : age}}`
| `GreaterThanEqual`
| `findByAgeGreaterThanEqual(int age)`
| `GreaterThanEqual`
| `findByAgeGreaterThanEqual(int age)`
| `{"age" : {"$gte" : age}}`
| `LessThan`
| `findByAgeLessThan(int age)`
| `LessThan`
| `findByAgeLessThan(int age)`
| `{"age" : {"$lt" : age}}`
| `LessThanEqual`
| `findByAgeLessThanEqual(int age)`
| `LessThanEqual`
| `findByAgeLessThanEqual(int age)`
| `{"age" : {"$lte" : age}}`
| `Between`
| `findByAgeBetween(int from, int to)`
| `Between`
| `findByAgeBetween(int from, int to)`
| `{"age" : {"$gt" : from, "$lt" : to}}`
| `In`
| `In`
| `findByAgeIn(Collection ages)`
| `{"age" : {"$in" : [ages...]}}`
| `NotIn`
| `findByAgeNotIn(Collection ages)`
| `NotIn`
| `findByAgeNotIn(Collection ages)`
| `{"age" : {"$nin" : [ages...]}}`
| `IsNotNull, NotNull`
| `findByFirstnameNotNull()`
| `{"firstname" : {"$ne" : null}}`
| `IsNotNull, NotNull`
| `findByFirstnameNotNull()`
| `{"age" : {"$ne" : null}}`
| `IsNull, Null`
| `findByFirstnameNull()`
| `{"firstname" : null}`
| `IsNull, Null`
| `findByFirstnameNull()`
| `{"age" : null}`
| `Like`
| `Like`
| `findByFirstnameLike(String name)`
| `{"firstname" : name} ( name as regex)`
| `{"age" : age} ( age as regex)`
| `Regex`
| `findByFirstnameRegex(String firstname)`
| `Regex`
| `findByFirstnameRegex(String firstname)`
| `{"firstname" : {"$regex" : firstname }}`
| `(No keyword)`
| `(No keyword)`
| `findByFirstname(String name)`
| `{"firstname" : name}`
| `{"age" : name}`
| `Not`
| `findByFirstnameNot(String name)`
| `{"firstname" : {"$ne" : name}}`
| `Not`
| `findByFirstnameNot(String name)`
| `{"age" : {"$ne" : name}}`
| `Near`
| `findByLocationNear(Point point)`
| `Near`
| `findByLocationNear(Point point)`
| `{"location" : {"$near" : [x,y]}}`
| `Within`
| `findByLocationWithin(Circle circle)`
| `Within`
| `findByLocationWithin(Circle circle)`
| `{"location" : {"$within" : {"$center" : [ [x, y], distance]}}}`
| `Within`
| `findByLocationWithin(Box box)`
| `Within`
| `findByLocationWithin(Box box)`
| `{"location" : {"$within" : {"$box" : [ [x1, y1], x2, y2]}}}True`
| `IsTrue, True`
| `findByActiveIsTrue()`
| `IsTrue, True`
| `findByActiveIsTrue()`
| `{"active" : true}`
| `IsFalse, False`
| `findByActiveIsFalse()`
| `IsFalse, False`
| `findByActiveIsFalse()`
| `{"active" : false}`
| `Exists`
| `findByLocationExists(boolean exists)`
| `Exists`
| `findByLocationExists(boolean exists)`
| `{"location" : {"$exists" : exists }}`
|===
@@ -463,4 +463,4 @@ class RepositoryClient {
List<Person> people = repository.findAll();
}
}
----
----

View File

@@ -415,6 +415,39 @@ If you need to configure additional options on the `com.mongodb.Mongo` instance
</bean>
----
[[mongo.auditing]]
== General auditing configuration
Activating auditing functionality is just a matter of adding the Spring Data Mongo `auditing` namespace element to your configuration:
.Activating auditing using XML configuration
====
[source,xml]
----
<mongo:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/>
----
====
Since Spring Data MongoDB 1.4 auditing can be enabled by annotating a configuration class with the `@EnableMongoAuditing` annotation.
.Activating auditing using JavaConfig
====
[source,java]
----
@Configuration
@EnableMongoAuditing
class Config {
@Bean
public AuditorAware<AuditableUser> myAuditorProvider() {
return new AuditorAwareImpl();
}
}
----
====
If you expose a bean of type `AuditorAware` to the `ApplicationContext`, the auditing infrastructure will pick it up automatically and use it to determine the current user to be set on domain types. If you have multiple implementations registered in the `ApplicationContext`, you can select the one to be used by explicitly setting the `auditorAwareRef` attribute of `@EnableJpaAuditing`.
[[mongo-template]]
== Introduction to MongoTemplate
@@ -1505,8 +1538,7 @@ Note that the aggregation operations not listed here are currently not supported
[[mongo.aggregation.projection]]
=== Projection Expressions
Projection expressions are used to define the fields that are the outcome of a particular aggregation step. Projection expressions can be defined via the `project` method of the `Aggregate` class either by passing a list of `String`s or an aggregation framework `Fields` object. The projection can be extended with additional fields through a fluent API via the `and(String)` method and aliased via the `as(String)` method.
Note that one can also define fields with aliases via the static factory method `Fields.field` of the aggregation framework that can then be used to construct a new `Fields` instance.
Projection expressions are used to define the fields that are the outcome of a particular aggregation step. Projection expressions can be defined via the `project` method of the `Aggregate` class.
.Projection expression examples
====
@@ -1892,7 +1924,7 @@ class MyConverter implements Converter<Person, String> { … }
class MyConverter implements Converter<String, Person> { … }
----
In case you write a `Converter` whose source and target type are native Mongo types there's no way for us to determine whether we should consider it as reading or writing converter. Registering the converter instance as both might lead to unwanted results then. E.g. a `Converter<String, Long>` is ambiguous although it probably does not make sense to try to convert all `String` instances into `Long` instances when writing. To be generally able to force the infrastructure to register a converter for one way only we provide `@ReadingConverter` as well as `@WritingConverter` to be used at the converter implementation.
In case you write a `Converter` whose source and target type are native Mongo types there's no way for us to determine whether we should consider it as reading or writing converter. Registering the converter instance as both might lead to unwanted results then. E.g. a `Converter<String, Long>` is ambiguous although it probably does not make sense to try to convert all `String`s into `Long`s when writing. To be generally able to force the infrastructure to register a converter for one way only we provide `@ReadingConverter` as well as `@WritingConverter` to be used at the converter implementation.
[[mongo-template.index-and-collections]]
== Index and Collection management

View File

@@ -1,224 +1,6 @@
Spring Data MongoDB Changelog
=============================
Changes in version 1.6.4.RELEASE (2015-10-14)
---------------------------------------------
* DATAMONGO-1304 - Release 1.6.4 (Evans).
Changes in version 1.8.0.RELEASE (2015-09-01)
---------------------------------------------
* DATAMONGO-1282 - Release 1.8 GA (Gosling).
* DATAMONGO-1280 - Add what's new section to refrence documentation.
* DATAMONGO-1275 - Reference documentation should mention support for optimistic locking.
* DATAMONGO-1269 - QueryMapper drops numeric keys in Maps.
* DATAMONGO-1256 - Provide a collectionName in MongoMappingEvents.
Changes in version 1.8.0.RC1 (2015-08-04)
-----------------------------------------
* DATAMONGO-1268 - Release 1.8 RC1 (Gosling).
* DATAMONGO-1266 - Repository query methods returning a primitive do not detect domain type correctly.
* DATAMONGO-1260 - Prevent accidental authentication misconfiguration on SimpleMongoDbFactory.
* DATAMONGO-1257 - mongo:mongo-client namespace does not support usernames with a comma.
* DATAMONGO-1254 - Group after Project in aggregation uses incorrect field name.
* DATAMONGO-1251 - update / findAndModify throws NullPointerException.
* DATAMONGO-1250 - Custom converter implementation not used in updates.
* DATAMONGO-1244 - StringBasedMongoQuery handles complex expression parameters incorrectly.
* DATAMONGO-1242 - Update mongo-java-driver to 3.0.2 in mongo3 profile.
* DATAMONGO-1236 - MongoOperations findAndModify and updateFirst do not include the _class in Map values.
* DATAMONGO-1234 - Fix typos in JavaDoc.
* DATAMONGO-1232 - IgnoreCase should escape queries.
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
* DATAMONGO-1157 - Throw meaningful exception when @DbRef is used with unsupported types.
* DATAMONGO-1125 - Specify collection that triggers CommandFailureException.
Changes in version 1.7.2.RELEASE (2015-07-28)
---------------------------------------------
* DATAMONGO-1261 - Release 1.7.2 (Fowler).
* DATAMONGO-1260 - Prevent accidental authentication misconfiguration on SimpleMongoDbFactory.
* DATAMONGO-1257 - mongo:mongo-client namespace does not support usernames with a comma.
* DATAMONGO-1254 - Group after Project in aggregation uses incorrect field name.
* DATAMONGO-1251 - update / findAndModify throws NullPointerException.
* DATAMONGO-1250 - Custom converter implementation not used in updates.
Changes in version 1.5.6.RELEASE (2015-07-01)
---------------------------------------------
* DATAMONGO-1246 - Release 1.5.6 (Dijkstra).
* DATAMONGO-1234 - Fix typos in JavaDoc.
* DATAMONGO-1232 - IgnoreCase should escape queries.
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
* DATAMONGO-1180 - Incorrect exception message creation in PartTreeMongoQuery.
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
* DATAMONGO-1155 - Upgrade mongo-next build profiles to Java driver version 2.13.0.
Changes in version 1.6.3.RELEASE (2015-07-01)
---------------------------------------------
* DATAMONGO-1247 - Release 1.6.3 (Evans).
* DATAMONGO-1242 - Update mongo-java-driver to 3.0.2 in mongo3 profile.
* DATAMONGO-1234 - Fix typos in JavaDoc.
* DATAMONGO-1232 - IgnoreCase should escape queries.
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
* DATAMONGO-1213 - Include new section on Spring Data and Spring Framework dependencies in reference documentation.
* DATAMONGO-1210 - Inconsistent property order of _class type hint breaks document equality.
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
* DATAMONGO-1196 - Upgrade build profiles after MongoDB 3.0 Java driver release.
* DATAMONGO-1180 - Incorrect exception message creation in PartTreeMongoQuery.
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
* DATAMONGO-1157 - Throw meaningful exception when @DbRef is used with unsupported types.
* DATAMONGO-1155 - Upgrade mongo-next build profiles to Java driver version 2.13.0.
* DATAMONGO-1153 - Fix documentation build.
* DATAMONGO-1133 - Field aliasing is not honored in Aggregation operations.
* DATAMONGO-1124 - Switch log level for cyclic reference index warnings from WARN to INFO.
* DATAMONGO-1081 - Improve documentation on field mapping semantics.
Changes in version 1.7.1.RELEASE (2015-06-30)
---------------------------------------------
* DATAMONGO-1248 - Release 1.7.1 (Fowler).
* DATAMONGO-1242 - Update mongo-java-driver to 3.0.2 in mongo3 profile.
* DATAMONGO-1234 - Fix typos in JavaDoc.
* DATAMONGO-1232 - IgnoreCase should escape queries.
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
* DATAMONGO-1216 - Authentication mechanism PLAIN changes to SCRAM-SHA-1.
* DATAMONGO-1213 - Include new section on Spring Data and Spring Framework dependencies in reference documentation.
* DATAMONGO-1210 - Inconsistent property order of _class type hint breaks document equality.
* DATAMONGO-1208 - MongoTemplate.stream(…) does not consider limit, order, sort etc.
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
* DATAMONGO-1202 - Indexed annotation problems under generics.
* DATAMONGO-1196 - Upgrade build profiles after MongoDB 3.0 Java driver release.
* DATAMONGO-1193 - Prevent unnecessary database lookups when resolving DBRefs on 2.x driver.
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
* DATAMONGO-1157 - Throw meaningful exception when @DbRef is used with unsupported types.
Changes in version 1.8.0.M1 (2015-06-02)
----------------------------------------
* DATAMONGO-1228 - Release 1.8 M1 (Gosling).
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
* DATAMONGO-1218 - Deprecate non-MongoClient related configuration options in XML namespace.
* DATAMONGO-1216 - Authentication mechanism PLAIN changes to SCRAM-SHA-1.
* DATAMONGO-1213 - Include new section on Spring Data and Spring Framework dependencies in reference documentation.
* DATAMONGO-1211 - Adapt API changes in Spring Data Commons to simplify custom repository base class registration.
* DATAMONGO-1210 - Inconsistent property order of _class type hint breaks document equality.
* DATAMONGO-1208 - MongoTemplate.stream(…) does not consider limit, order, sort etc.
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
* DATAMONGO-1202 - Indexed annotation problems under generics.
* DATAMONGO-1196 - Upgrade build profiles after MongoDB 3.0 Java driver release.
* DATAMONGO-1193 - Prevent unnecessary database lookups when resolving DBRefs on 2.x driver.
* DATAMONGO-1192 - Switch back to Spring 4.1's CollectionFactory.
* DATAMONGO-1134 - Add support for $geoIntersects.
* DATAMONGO-990 - Add support for SpEL expressions in @Query.
Changes in version 1.7.0.RELEASE (2015-03-23)
---------------------------------------------
* DATAMONGO-1189 - Release 1.7 GA.
* DATAMONGO-1181 - Add Jackson Module for GeoJSON types.
* DATAMONGO-1180 - Incorrect exception message creation in PartTreeMongoQuery.
* DATAMONGO-1179 - Update reference documentation.
* DATAMONGO-1124 - Switch log level for cyclic reference index warnings from WARN to INFO.
* DATAMONGO-979 - Add support for $size expression in project and group aggregation pipeline.
Changes in version 1.7.0.RC1 (2015-03-05)
-----------------------------------------
* DATAMONGO-1173 - Release 1.7 RC1.
* DATAMONGO-1167 - Add 'findAll' method to QueryDslMongoRepository which accepts a querydsl Predicate and a Sort.
* DATAMONGO-1165 - Add support for Java 8 Stream as return type in repositories.
* DATAMONGO-1162 - Adapt test cases to semantic changes in Spring Data Commons AuditingHandler API.
* DATAMONGO-1158 - Assert compatibility with MongoDB 3.0.
* DATAMONGO-1154 - Upgrade to MongoDB Java driver 2.13.0.
* DATAMONGO-1153 - Fix documentation build.
* DATAMONGO-1148 - Use EclipseLink provided JPA API JAR.
* DATAMONGO-1147 - Remove manual array copy.
* DATAMONGO-1146 - Add 'exists' method to QueryDslMongoRepository which accepts a querydsl Predicate.
* DATAMONGO-1145 - Upgrade MongoDB Java driver to 2.12.5.
* DATAMONGO-1139 - MongoQueryCreator must not create $nearSphere query for neutral Distance.
* DATAMONGO-1136 - Use $geoWithin instead of $within for geo queries.
* DATAMONGO-1135 - Add support for $geometry to support GeoJSON queries.
* DATAMONGO-1132 - The sample does not match the logical result in the MongoDB repositories section of the documentation.
* DATAMONGO-1131 - Register converters for ThreeTen back port by default.
* DATAMONGO-1129 - Upgrade to latest MongoDB Java driver.
* DATAMONGO-1127 - Add support for geoNear queries with distance information.
* DATAMONGO-1126 - Repository keyword query findByInId with pageable not returning correctly.
* DATAMONGO-1123 - geoNear, does not return all matching elements, it returns only a max of 100 documents.
* DATAMONGO-1121 - "Cycle found" false positive.
* DATAMONGO-1120 - Pageable queries timeout or return incorrect counts.
* DATAMONGO-1118 - Custom converters not used for map keys.
* DATAMONGO-1110 - Add support for $minDistance to NearQuery.
* DATAMONGO-1082 - Improve JavaDoc and reference documentation on alias usage in aggregation framework support.
* DATAMONGO-1081 - Improve documentation on field mapping semantics.
* DATAMONGO-712 - Another round of potential performance improvements.
* DATAMONGO-479 - Support calling of MongoDB stored javascripts.
Changes in version 1.6.2.RELEASE (2015-01-28)
---------------------------------------------
* DATAMONGO-1148 - Use EclipseLink provided JPA API JAR.
* DATAMONGO-1147 - Remove manual array copy.
* DATAMONGO-1145 - Upgrade MongoDB Java driver to 2.12.5.
* DATAMONGO-1144 - Release 1.6.2.
* DATAMONGO-1139 - MongoQueryCreator must not create $nearSphere query for neutral Distance.
* DATAMONGO-1132 - The sample does not match the logical result in the MongoDB repositories section of the documentation.
* DATAMONGO-1127 - Add support for geoNear queries with distance information.
* DATAMONGO-1126 - Repository keyword query findByInId with pageable not returning correctly.
* DATAMONGO-1123 - geoNear, does not return all matching elements, it returns only a max of 100 documents.
* DATAMONGO-1121 - "Cycle found" false positive.
* DATAMONGO-1120 - Pageable queries timeout or return incorrect counts.
* DATAMONGO-1118 - Custom converters not used for map keys.
* DATAMONGO-1108 - BasicMongoPersistentEntity doesn't need to parse expression on every invocation.
* DATAMONGO-1096 - RuntimeExceptions during debug query printing in MongoTemplate.
* DATAMONGO-1094 - Wrong reference to @DocumentField in error message.
* DATAMONGO-1093 - BasicQuery missing hashCode() and equals(…) methods.
* DATAMONGO-1087 - Incorrect warning for MongoPersistentEntityIndexResolver$CyclicPropertyReferenceException: Found cycle for field...
* DATAMONGO-1085 - Sort can not use the metamodel classes generated by QueryDSL.
* DATAMONGO-1082 - Improve JavaDoc and reference documentation on alias usage in aggregation framework support.
* DATAMONGO-1078 - @Query annotated repository query fails to map complex Id structure.
* DATAMONGO-1075 - Correctly evaluate CONTAINS keyword on collection properties.
* DATAMONGO-1054 - Improve performance of saving entities by using insert(…) if possible.
* DATAMONGO-1043 - SpEL Expressions in @Document annotations are not re-evaluated for query executions.
* DATAMONGO-712 - Another round of potential performance improvements.
Changes in version 1.5.5.RELEASE (2015-01-27)
---------------------------------------------
* DATAMONGO-1148 - Use EclipseLink provided JPA API JAR.
* DATAMONGO-1147 - Remove manual array copy.
* DATAMONGO-1143 - Release 1.5.5.
* DATAMONGO-1139 - MongoQueryCreator must not create $nearSphere query for neutral Distance.
* DATAMONGO-1123 - geoNear, does not return all matching elements, it returns only a max of 100 documents.
* DATAMONGO-1121 - "Cycle found" false positive.
* DATAMONGO-1118 - Custom converters not used for map keys.
* DATAMONGO-1096 - RuntimeExceptions during debug query printing in MongoTemplate.
* DATAMONGO-1094 - Wrong reference to @DocumentField in error message.
* DATAMONGO-1087 - Incorrect warning for MongoPersistentEntityIndexResolver$CyclicPropertyReferenceException: Found cycle for field...
* DATAMONGO-1078 - @Query annotated repository query fails to map complex Id structure.
* DATAMONGO-1075 - Correctly evaluate CONTAINS keyword on collection properties.
* DATAMONGO-1072 - Query placeholders in keys no longer correctly substituted.
* DATAMONGO-1068 - elemMatch of Class Criteria fails to build special cirteria.
* DATAMONGO-1063 - IllegalStateException using any().in().
* DATAMONGO-1062 - Fix failing test in ServerAddressPropertyEditorUnitTests.
* DATAMONGO-1058 - Using @Field("foo") with @Dbref breaking behavior.
* DATAMONGO-1045 - Make sure Spring Data MongoDB can build against Spring 4.1.
* DATAMONGO-1043 - SpEL Expressions in @Document annotations are not re-evaluated for query executions.
* DATAMONGO-1040 - deleteAll repository query don't use EntityMetadata collection name.
* DATAMONGO-1039 - Polish implementation for cleaning up after tests.
* DATAMONGO-712 - Another round of potential performance improvements.
Changes in version 1.7.0.M1 (2014-12-01)
----------------------------------------
* DATAMONGO-1108 - BasicMongoPersistentEntity doesn't need to parse expression on every invocation.

View File

@@ -1,8 +1,8 @@
Spring Data MongoDB 1.6.4
Copyright (c) [2010-2015] Pivotal Software, Inc.
Spring Data MongoDB 1.7 M1
Copyright (c) [2010-2014] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
You may not use this product except in compliance with the License.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
You may not use this product except in compliance with the License.
This product may include a number of subcomponents with
separate copyright notices and license terms. Your use of the source