diff --git a/.travis.yml b/.travis.yml index ed1d39166..a1a6c58e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ before_script: env: matrix: - PROFILE=ci - - PROFILE=mongo-next - PROFILE=mongo3 - PROFILE=mongo3-next - PROFILE=mongo31 diff --git a/pom.xml b/pom.xml index c86d0db97..f8b1965de 100644 --- a/pom.xml +++ b/pom.xml @@ -113,22 +113,6 @@ - - - mongo-next - - 2.15.0-SNAPSHOT - - - - - mongo-snapshots - https://oss.sonatype.org/content/repositories/snapshots - - - - - mongo3 diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 273e96109..40f9225c2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -128,7 +128,7 @@ import com.mongodb.util.JSONParseException; /** * Primary implementation of {@link MongoOperations}. - * + * * @author Thomas Risberg * @author Graeme Rocher * @author Mark Pollack @@ -181,7 +181,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Constructor used for a basic template configuration - * + * * @param mongo must not be {@literal null}. * @param databaseName must not be {@literal null} or empty. */ @@ -192,7 +192,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Constructor used for a template configuration with user credentials in the form of * {@link org.springframework.data.authentication.UserCredentials} - * + * * @param mongo must not be {@literal null}. * @param databaseName must not be {@literal null} or empty. * @param userCredentials @@ -203,7 +203,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Constructor used for a basic template configuration. - * + * * @param mongoDbFactory must not be {@literal null}. */ public MongoTemplate(MongoDbFactory mongoDbFactory) { @@ -212,7 +212,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Constructor used for a basic template configuration. - * + * * @param mongoDbFactory must not be {@literal null}. * @param mongoConverter */ @@ -241,7 +241,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Configures the {@link WriteResultChecking} to be used with the template. Setting {@literal null} will reset the * default of {@value #DEFAULT_WRITE_RESULT_CHECKING}. - * + * * @param resultChecking */ public void setWriteResultChecking(WriteResultChecking resultChecking) { @@ -252,7 +252,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * Configures the {@link WriteConcern} to be used with the template. If none is configured the {@link WriteConcern} * configured on the {@link MongoDbFactory} will apply. If you configured a {@link Mongo} instance no * {@link WriteConcern} will be used. - * + * * @param writeConcern */ public void setWriteConcern(WriteConcern writeConcern) { @@ -261,7 +261,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Configures the {@link WriteConcernResolver} to be used with the template. - * + * * @param writeConcernResolver */ public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) { @@ -271,7 +271,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are * performed. - * + * * @param readPreference */ public void setReadPreference(ReadPreference readPreference) { @@ -298,7 +298,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * they were registered for the current {@link MappingContext}. If no creator for the current {@link MappingContext} * can be found we manually add the internally created one as {@link ApplicationListener} to make sure indexes get * created appropriately for entity types persisted through this {@link MongoTemplate} instance. - * + * * @param context must not be {@literal null}. */ private void prepareIndexCreator(ApplicationContext context) { @@ -319,14 +319,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Returns the default {@link org.springframework.data.mongodb.core.core.convert.MongoConverter}. - * + * * @return */ public MongoConverter getConverter() { return this.mongoConverter; } - /* + /* * (non-Javadoc) * @see org.springframework.data.mongodb.core.MongoOperations#executeAsStream(org.springframework.data.mongodb.core.query.Query, java.lang.Class) */ @@ -437,7 +437,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Execute a MongoDB query and iterate over the query results on a per-document basis with a * {@link DocumentCallbackHandler} using the provided CursorPreparer. - * + * * @param query the query class that specifies the criteria used to find a record and also an optional fields * specification, must not be {@literal null}. * @param collectionName name of the collection to retrieve the objects from @@ -709,7 +709,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /* * As MongoDB currently (2.4.4) doesn't support the skipping of elements in near queries * we skip the elements ourselves to avoid at least the document 2 object mapping overhead. - * + * * @see https://jira.mongodb.org/browse/SERVER-3925 */ if (index >= elementsToSkip) { @@ -814,7 +814,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Prepare the collection before any processing is done using it. This allows a convenient way to apply settings like * slaveOk() etc. Can be overridden in sub-classes. - * + * * @param collection */ protected MongoCollection prepareCollection(MongoCollection collection) { @@ -830,7 +830,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * settings in sub-classes.
* In case of using MongoDB Java driver version 3 the returned {@link WriteConcern} will be defaulted to * {@link WriteConcern#ACKNOWLEDGED} when {@link WriteResultChecking} is set to {@link WriteResultChecking#EXCEPTION}. - * + * * @param writeConcern any WriteConcern already configured or null * @return The prepared WriteConcern or null */ @@ -1225,19 +1225,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { UpdateOptions opts = new UpdateOptions(); opts.upsert(upsert); - // TODO hack - split up update and replaces - boolean useUpdate = false; - - for (String s : updateObj.keySet()) { - if (s.startsWith("$")) { - useUpdate = true; - break; - } - } - collection = writeConcernToUse != null ? collection.withWriteConcern(writeConcernToUse) : collection; - if (!useUpdate) { + if (!UpdateMapper.isUpdateObject(updateObj)) { return collection.replaceOne(queryObj, updateObj, opts); } else { if (multi) { @@ -1292,7 +1282,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Returns {@link Entry} containing the field name of the id property as {@link Entry#getKey()} and the {@link Id}s * property value as its {@link Entry#getValue()}. - * + * * @param object * @return */ @@ -1319,7 +1309,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Returns a {@link Query} for the given entity by its id. - * + * * @param object must not be {@literal null}. * @return */ @@ -1331,7 +1321,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Returns a {@link Query} for the given entities by their ids. - * + * * @param objects must not be {@literal null} or {@literal empty}. * @return */ @@ -1623,7 +1613,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * Retrieve and remove all documents matching the given {@code query} by calling {@link #find(Query, Class, String)} * and {@link #remove(Query, Class, String)}, whereas the {@link Query} for {@link #remove(Query, Class, String)} is * constructed out of the find result. - * + * * @param collectionName * @param query * @param entityClass @@ -1661,8 +1651,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { } /** - * Returns the potentially mapped results of the given {@commandResult} contained some. - * + * Returns the potentially mapped results of the given {@code commandResult}. + * * @param outputType * @param commandResult * @return @@ -1739,7 +1729,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Create the specified collection using the provided options - * + * * @param collectionName * @param collectionOptions * @return the collection that was created @@ -1776,7 +1766,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter. * The query document is specified as a standard {@link Document} and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from. * @param query the query document that specifies the criteria used to find a record. * @param fields the document that specifies the fields to be returned. @@ -1801,7 +1791,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Map the results of an ad-hoc query on the default MongoDB collection to a List using the template's converter. The * query document is specified as a standard Document and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1817,7 +1807,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * Map the results of an ad-hoc query on the default MongoDB collection to a List of the specified type. The object is * converted from the MongoDB native representation using an instance of {@see MongoConverter}. The query document is * specified as a standard Document and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from. * @param query the query document that specifies the criteria used to find a record. * @param fields the document that specifies the fields to be returned. @@ -1870,7 +1860,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { * The first document that matches the query is returned and also removed from the collection in the database. *

* The query document is specified as a standard Document and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param entityClass the parameterized type of the returned list. @@ -1921,7 +1911,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Populates the id property of the saved object, if it's not set already. - * + * * @param savedObject * @param id */ @@ -1971,7 +1961,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { *

  • Execute the given {@link ConnectionCallback} for a {@link Document}.
  • *
  • Apply the given {@link DbObjectCallback} to each of the {@link Document}s to obtain the result.
  • *
      - * + * * @param * @param collectionCallback the callback to retrieve the {@link Document} with * @param objectCallback the {@link DbObjectCallback} to transform {@link Document}s into the actual domain type @@ -2000,7 +1990,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { *
    1. Iterate over the {@link DBCursor} and applies the given {@link DbObjectCallback} to each of the * {@link Document}s collecting the actual result {@link List}.
    2. *
        - * + * * @param * @param collectionCallback the callback to retrieve the {@link DBCursor} with * @param preparer the {@link CursorPreparer} to potentially modify the {@link DBCursor} before ireating over it @@ -2113,7 +2103,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Handles {@link WriteResult} errors based on the configured {@link WriteResultChecking}. - * + * * @param writeResult * @param query * @param operation @@ -2157,7 +2147,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Inspects the given {@link CommandResult} for erros and potentially throws an * {@link InvalidDataAccessApiUsageException} for that error. - * + * * @param result must not be {@literal null}. * @param source must not be {@literal null}. */ @@ -2195,12 +2185,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original * exception if the conversation failed. Thus allows safe re-throwing of the return value. - * + * * @param ex the exception to translate * @param exceptionTranslator the {@link PersistenceExceptionTranslator} to be used for translation * @return */ - private static RuntimeException potentiallyConvertRuntimeException(RuntimeException ex, + static RuntimeException potentiallyConvertRuntimeException(RuntimeException ex, PersistenceExceptionTranslator exceptionTranslator) { RuntimeException resolved = exceptionTranslator.translateExceptionIfPossible(ex); return resolved == null ? ex : resolved; @@ -2234,7 +2224,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification * {@link Document} and executes that against the {@link DBCollection}. - * + * * @author Oliver Gierke * @author Thomas Risberg */ @@ -2268,7 +2258,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification * {@link Document} and executes that against the {@link DBCollection}. - * + * * @author Oliver Gierke * @author Thomas Risberg */ @@ -2300,7 +2290,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification * {@link Document} and executes that against the {@link DBCollection}. - * + * * @author Thomas Risberg */ private static class FindAndRemoveCallback implements CollectionCallback { @@ -2360,7 +2350,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Simple internal callback to allow operations on a {@link Document}. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -2373,7 +2363,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Simple {@link DbObjectCallback} that will transform {@link Document} into the given target type using the given * {@link MongoReader}. - * + * * @author Oliver Gierke * @author Christoph Strobl */ @@ -2513,7 +2503,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to * a delegate and creates a {@link GeoResult} from the result. - * + * * @author Oliver Gierke */ static class GeoNearResultDbObjectCallback implements DbObjectCallback> { @@ -2524,7 +2514,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Creates a new {@link GeoNearResultDbObjectCallback} using the given {@link DbObjectCallback} delegate for * {@link GeoResult} content unmarshalling. - * + * * @param delegate must not be {@literal null}. */ public GeoNearResultDbObjectCallback(DbObjectCallback delegate, Metric metric) { @@ -2546,7 +2536,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * A {@link CloseableIterator} that is backed by a MongoDB {@link Cursor}. - * + * * @since 1.7 * @author Thomas Darimont */ @@ -2565,7 +2555,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { /** * Creates a new {@link CloseableIterableCursorAdapter} backed by the given {@link Cursor}. - * + * * @param cursor * @param exceptionTranslator * @param objectReadCallback diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 418c41643..44fafb120 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -1114,7 +1114,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } if (obj instanceof Map) { - + Document result = new Document(); for (Map.Entry entry : ((Map) obj).entrySet()) { result.put(entry.getKey().toString(), convertToMongoType(entry.getValue(), typeHint)); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java index c64a3f0ba..23275337b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.convert; import java.util.Map.Entry; import org.bson.Document; +import org.bson.conversions.Bson; import org.springframework.core.convert.converter.Converter; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.context.MappingContext; @@ -35,6 +36,7 @@ import org.springframework.data.util.TypeInformation; * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch */ public class UpdateMapper extends QueryMapper { @@ -51,6 +53,70 @@ public class UpdateMapper extends QueryMapper { this.converter = converter; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.convert.QueryMapper#getMappedObject(Bson, MongoPersistentEntity) + */ + @Override + public Document getMappedObject(Bson query, MongoPersistentEntity entity) { + + Document document = super.getMappedObject(query, entity); + + boolean hasOperators = false; + boolean hasFields = false; + + Document set = null; + for (String s : document.keySet()) { + if (s.startsWith("$")) { + + if(s.equals("$set")){ + set = document.get(s, Document.class); + } + hasOperators = true; + } else { + hasFields = true; + } + } + + if (hasOperators && hasFields) { + + Document updateObject = new Document(); + Document fieldsToSet = set == null ? new Document() : set; + + for (String s : document.keySet()) { + if (s.startsWith("$")) { + updateObject.put(s, document.get(s)); + } else { + fieldsToSet.put(s, document.get(s)); + } + } + updateObject.put("$set", fieldsToSet); + + return updateObject; + } + return document; + } + + /** + * Returns {@literal true} if the given {@link Document} is an update object that uses update operators. + * @param updateObj + * @return {@literal true} if the given {@link Document} is an update object. + */ + public static boolean isUpdateObject(Document updateObj) { + + if (updateObj == null) { + return false; + } + + for (String s : updateObj.keySet()) { + if (s.startsWith("$")) { + return true; + } + } + + return false; + } + /** * Converts the given source object to a mongo type retaining the original type information of the source type on the * mongo type. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index c335945f4..814efc7f1 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -360,18 +360,12 @@ public class MongoTemplateTests { assertThat(indexInfo.size(), is(2)); Object indexKey = null; boolean unique = false; - boolean dropDupes = false; for (org.bson.Document ix : indexInfo) { if ("age_-1".equals(ix.get("name"))) { indexKey = ix.get("key"); unique = (Boolean) ix.get("unique"); - if (mongoVersion.isLessThan(TWO_DOT_EIGHT)) { - dropDupes = (Boolean) ix.get("dropDups"); - assertThat(dropDupes, is(true)); - } else { - assertThat(ix.get("dropDups"), is(nullValue())); - } + assertThat(ix.get("dropDups"), is(nullValue())); } } assertThat(((org.bson.Document) indexKey), IsMapContaining. hasEntry("age", -1)); @@ -382,13 +376,7 @@ public class MongoTemplateTests { assertThat(indexInfoList.size(), is(2)); IndexInfo ii = indexInfoList.get(1); assertThat(ii.isUnique(), is(true)); - - if (mongoVersion.isLessThan(TWO_DOT_EIGHT)) { - assertThat(ii.isDropDuplicates(), is(true)); - } else { - assertThat(ii.isDropDuplicates(), is(false)); - } - + assertThat(ii.isDropDuplicates(), is(false)); assertThat(ii.isSparse(), is(false)); List indexFields = ii.getIndexFields(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java index 7ff4f55b8..8d94dc943 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java @@ -44,9 +44,6 @@ 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; @@ -105,7 +102,7 @@ public class UpdateMapperUnitTests { Document push = getAsDocument(mappedObject, "$push"); Document list = getAsDocument(push, "aliased"); - assertThat(list.get("_class"), is((Object) ConcreteChildClass.class.getName())); + assertThat(list.get("_class"), is(ConcreteChildClass.class.getName())); } /** @@ -171,7 +168,7 @@ public class UpdateMapperUnitTests { Document set = getAsDocument(mappedObject, "$set"); Document modelDbObject = getAsDocument(set, "aliased.$"); - assertThat(modelDbObject.get("_class"), is((Object) ConcreteChildClass.class.getName())); + assertThat(modelDbObject.get("_class"), is(ConcreteChildClass.class.getName())); } /** @@ -187,8 +184,8 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(ParentClass.class)); Document set = getAsDocument(mappedObject, "$set"); - assertThat(set.get("aliased.$.value"), is((Object) "foo")); - assertThat(set.get("aliased.$.otherValue"), is((Object) "bar")); + assertThat(set.get("aliased.$.value"), is("foo")); + assertThat(set.get("aliased.$.otherValue"), is("bar")); } /** @@ -204,12 +201,12 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(ParentClass.class)); Document dbo = getAsDocument(mappedObject, "$set"); - assertThat(dbo.get("aliased.$.value"), is((Object) "foo")); + assertThat(dbo.get("aliased.$.value"), is("foo")); Document someObject = getAsDocument(dbo, "aliased.$.someObject"); assertThat(someObject, is(notNullValue())); - assertThat(someObject.get("_class"), is((Object) ConcreteChildClass.class.getName())); - assertThat(someObject.get("value"), is((Object) "bubu")); + assertThat(someObject.get("_class"), is(ConcreteChildClass.class.getName())); + assertThat(someObject.get("value"), is("bubu")); } /** @@ -229,7 +226,7 @@ public class UpdateMapperUnitTests { assertThat(push.get("_class"), nullValue()); assertThat(values.get("_class"), nullValue()); - assertThat(each, IsIterableContainingInOrder. contains("spring", "data", "mongodb")); + assertThat(each, IsIterableContainingInOrder.contains("spring", "data", "mongodb")); } /** @@ -264,7 +261,7 @@ public class UpdateMapperUnitTests { List each = getAsDBList(model, "$each"); List values = getAsDBList((Document) each.get(0), "values"); - assertThat(values, IsIterableContainingInOrder. contains("spring", "data", "mongodb")); + assertThat(values, IsIterableContainingInOrder.contains("spring", "data", "mongodb")); } /** @@ -312,7 +309,7 @@ public class UpdateMapperUnitTests { Document key = getAsDocument(push, "key"); assertThat(key.containsKey("$position"), is(true)); - assertThat((Integer) key.get("$position"), is(2)); + assertThat(key.get("$position"), is(2)); assertThat(getAsDocument(push, "key").containsKey("$each"), is(true)); } @@ -330,7 +327,7 @@ public class UpdateMapperUnitTests { Document key = getAsDocument(push, "key"); assertThat(key.containsKey("$position"), is(true)); - assertThat((Integer) key.get("$position"), is(0)); + assertThat(key.get("$position"), is(0)); assertThat(getAsDocument(push, "key").containsKey("$each"), is(true)); } @@ -382,7 +379,7 @@ public class UpdateMapperUnitTests { Document key = getAsDocument(push, "key"); assertThat(key.containsKey("$slice"), is(true)); - assertThat((Integer) key.get("$slice"), is(5)); + assertThat(key.get("$slice"), is(5)); assertThat(key.containsKey("$each"), is(true)); } @@ -401,13 +398,13 @@ public class UpdateMapperUnitTests { Document key = getAsDocument(push, "key"); assertThat(key.containsKey("$slice"), is(true)); - assertThat((Integer) key.get("$slice"), is(5)); + assertThat(key.get("$slice"), is(5)); assertThat(key.containsKey("$each"), is(true)); Document key2 = getAsDocument(push, "key-2"); assertThat(key2.containsKey("$slice"), is(true)); - assertThat((Integer) key2.get("$slice"), is(-2)); + assertThat(key2.get("$slice"), is(-2)); assertThat(key2.containsKey("$each"), is(true)); } @@ -439,7 +436,7 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(DocumentWithDBRefCollection.class)); Document pullClause = getAsDocument(mappedObject, "$pull"); - assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef("entity", "2"))); + assertThat(pullClause.get("dbRefAnnotatedList"), is(new DBRef("entity", "2"))); } /** @@ -456,7 +453,7 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(DocumentWithDBRefCollection.class)); Document pullClause = getAsDocument(mappedObject, "$pull"); - assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef("entity", entity.id))); + assertThat(pullClause.get("dbRefAnnotatedList"), is(new DBRef("entity", entity.id))); } /** @@ -497,7 +494,7 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(DocumentWithDBRefCollection.class)); Document setClause = getAsDocument(mappedObject, "$set"); - assertThat(setClause.get("dbRefProperty"), is((Object) new DBRef("entity", entity.id))); + assertThat(setClause.get("dbRefProperty"), is(new DBRef("entity", entity.id))); } /** @@ -532,7 +529,7 @@ public class UpdateMapperUnitTests { Document idClause = getAsDocument(options, "_id"); List inClause = getAsDBList(idClause, "$in"); - assertThat(inClause, IsIterableContainingInOrder. contains(1L, 2L)); + assertThat(inClause, IsIterableContainingInOrder.contains(1L, 2L)); } /** @@ -550,7 +547,7 @@ public class UpdateMapperUnitTests { Document values = getAsDocument(addToSet, "values"); List each = getAsDBList(values, "$each"); - assertThat(each, IsIterableContainingInOrder. contains("spring", "data", "mongodb")); + assertThat(each, IsIterableContainingInOrder.contains("spring", "data", "mongodb")); } /** @@ -588,7 +585,7 @@ public class UpdateMapperUnitTests { Object model = $set.get("referencedDocument"); DBRef expectedDBRef = new DBRef("interfaceDocumentDefinitionImpl", "1"); - assertThat(model, allOf(instanceOf(DBRef.class), IsEqual. equalTo(expectedDBRef))); + assertThat(model, allOf(instanceOf(DBRef.class), IsEqual.equalTo(expectedDBRef))); } /** @@ -606,7 +603,7 @@ public class UpdateMapperUnitTests { Document value = DBObjectTestUtils.getAsDocument(list, "value"); List $in = DBObjectTestUtils.getAsDBList(value, "$in"); - assertThat($in, IsIterableContainingInOrder. contains("foo", "bar")); + assertThat($in, IsIterableContainingInOrder.contains("foo", "bar")); } /** @@ -719,7 +716,7 @@ public class UpdateMapperUnitTests { public void mappingShouldRetainTypeInformationOfNestedListWhenUpdatingConcreteyParentType() { ListModelWrapper lmw = new ListModelWrapper(); - lmw.models = Collections. singletonList(new ModelImpl(1)); + lmw.models = Collections.singletonList(new ModelImpl(1)); Update update = new Update().set("concreteTypeWithListAttributeOfInterfaceType", lmw); Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), @@ -778,7 +775,7 @@ public class UpdateMapperUnitTests { @Test public void mappingShouldRetrainTypeInformationWhenValueTypeOfMapDoesNotMatchItsDeclaration() { - Map map = Collections. singletonMap("szeth", new NestedDocument("son-son-vallano")); + Map map = Collections.singletonMap("szeth", new NestedDocument("son-son-vallano")); Update update = new Update().set("map", map); Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), @@ -794,7 +791,7 @@ public class UpdateMapperUnitTests { @Test public void mappingShouldNotContainTypeInformationWhenValueTypeOfMapMatchesDeclaration() { - Map map = Collections. singletonMap("jasnah", + Map map = Collections.singletonMap("jasnah", new NestedDocument("kholin")); Update update = new Update().set("concreteMap", map); @@ -813,7 +810,7 @@ public class UpdateMapperUnitTests { public void mapsUpdateWithBothReadingAndWritingConverterRegistered() { CustomConversions conversions = new CustomConversions( - Arrays.asList(AllocationToStringConverter.INSTANCE, StringToAllocationConverter.INSTANCE)); + Arrays.asList(ClassWithEnum.AllocationToStringConverter.INSTANCE, ClassWithEnum.StringToAllocationConverter.INSTANCE)); MongoMappingContext mappingContext = new MongoMappingContext(); mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); @@ -825,11 +822,11 @@ public class UpdateMapperUnitTests { UpdateMapper mapper = new UpdateMapper(converter); - Update update = new Update().set("allocation", Allocation.AVAILABLE); + Update update = new Update().set("allocation", ClassWithEnum.Allocation.AVAILABLE); Document result = mapper.getMappedObject(update.getUpdateObject(), mappingContext.getPersistentEntity(ClassWithEnum.class)); - assertThat(result, isBsonObject().containing("$set.allocation", Allocation.AVAILABLE.code)); + assertThat(result, isBsonObject().containing("$set.allocation", ClassWithEnum.Allocation.AVAILABLE.code)); } /** @@ -907,7 +904,7 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(SimpleValueHolder.class)); Document $set = DBObjectTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set.get("intValue"), Is. is(10)); + assertThat($set.get("intValue"), Is.is(10)); } /** @@ -921,7 +918,7 @@ public class UpdateMapperUnitTests { context.getPersistentEntity(SimpleValueHolder.class)); Document $set = DBObjectTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set.get("primIntValue"), Is. is(10)); + assertThat($set.get("primIntValue"), Is.is(10)); } /** @@ -958,7 +955,7 @@ public class UpdateMapperUnitTests { public void mappingShouldConsiderCustomConvertersForEnumMapKeys() { CustomConversions conversions = new CustomConversions( - Arrays.asList(AllocationToStringConverter.INSTANCE, StringToAllocationConverter.INSTANCE)); + Arrays.asList(ClassWithEnum.AllocationToStringConverter.INSTANCE, ClassWithEnum.StringToAllocationConverter.INSTANCE)); MongoMappingContext mappingContext = new MongoMappingContext(); mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); @@ -970,7 +967,7 @@ public class UpdateMapperUnitTests { UpdateMapper mapper = new UpdateMapper(converter); - Update update = new Update().set("enumAsMapKey", Collections.singletonMap(Allocation.AVAILABLE, 100)); + Update update = new Update().set("enumAsMapKey", Collections.singletonMap(ClassWithEnum.Allocation.AVAILABLE, 100)); Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), mappingContext.getPersistentEntity(ClassWithEnum.class)); @@ -981,6 +978,51 @@ public class UpdateMapperUnitTests { assertThat(enumAsMapKey.get("AVAILABLE"), is(100)); } + /** + * @see DATAMONGO-1176 + */ + @Test + public void mappingShouldPrepareUpdateObjectForMixedOperatorsAndFields() { + + Document document = new Document("key", "value").append("$set", new Document("a", "b").append("x", "y")); + + Document mappedObject = mapper.getMappedObject(document, context.getPersistentEntity(SimpleValueHolder.class)); + + assertThat(mappedObject.get("$set"), is(equalTo(new Document("a", "b").append("x", "y").append("key", "value")))); + assertThat(mappedObject.size(), is(1)); + } + + /** + * @see DATAMONGO-1176 + */ + @Test + public void mappingShouldReturnReplaceObject() { + + Document document = new Document("key", "value").append("a", "b").append("x", "y"); + + Document mappedObject = mapper.getMappedObject(document, context.getPersistentEntity(SimpleValueHolder.class)); + + assertThat(mappedObject.get("key"), is(equalTo("value"))); + assertThat(mappedObject.get("a"), is(equalTo("b"))); + assertThat(mappedObject.get("x"), is(equalTo("y"))); + assertThat(mappedObject.size(), is(3)); + } + + /** + * @see DATAMONGO-1176 + */ + @Test + public void mappingShouldReturnUpdateObject() { + + Document document = new Document("$push", new Document("x", "y")).append("$set", new Document("a", "b")); + + Document mappedObject = mapper.getMappedObject(document, context.getPersistentEntity(SimpleValueHolder.class)); + + assertThat(mappedObject.get("$push"), is(equalTo(new Document("x", "y")))); + assertThat(mappedObject.get("$set"), is(equalTo(new Document("a", "b")))); + assertThat(mappedObject.size(), is(2)); + } + /** * @see DATAMONGO-1486 */ @@ -1011,7 +1053,7 @@ public class UpdateMapperUnitTests { } @org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface") - static interface DocumentWithReferenceToInterface { + interface DocumentWithReferenceToInterface { String getId(); @@ -1019,7 +1061,7 @@ public class UpdateMapperUnitTests { } - static interface InterfaceDocumentDefinitionWithoutId { + interface InterfaceDocumentDefinitionWithoutId { String getValue(); } @@ -1068,7 +1110,7 @@ public class UpdateMapperUnitTests { } - static interface Model {} + interface Model {} static class ModelImpl implements Model { public int value; @@ -1226,13 +1268,13 @@ public class UpdateMapperUnitTests { Allocation allocation; Map enumAsMapKey; - static enum Allocation { + enum Allocation { AVAILABLE("V"), ALLOCATED("A"); String code; - private Allocation(String code) { + Allocation(String code) { this.code = code; } @@ -1248,7 +1290,7 @@ public class UpdateMapperUnitTests { } } - static enum AllocationToStringConverter implements Converter { + enum AllocationToStringConverter implements Converter { INSTANCE; @@ -1258,7 +1300,7 @@ public class UpdateMapperUnitTests { } } - static enum StringToAllocationConverter implements Converter { + enum StringToAllocationConverter implements Converter { INSTANCE;