Compare commits

..

25 Commits

Author SHA1 Message Date
Oliver Gierke
a5148f89c1 DATAMONGO-1522 - Release version 1.9.6 (Hopper SR6). 2016-12-21 19:04:46 +01:00
Oliver Gierke
995a680823 DATAMONGO-1522 - Prepare 1.9.6 (Hopper SR6). 2016-12-21 19:03:39 +01:00
Oliver Gierke
9f0abb69fd DATAMONGO-1522 - Updated changelog. 2016-12-21 19:03:32 +01:00
Oliver Gierke
d65eebe9c3 DATAMONGO-1469 - Updated changelog. 2016-12-21 18:42:59 +01:00
Oliver Gierke
ca4f1f1b7c DATAMONGO-1467 - Polishing.
Original pull request: #431.
2016-12-19 19:45:17 +01:00
Christoph Strobl
46b119ce71 DATAMONGO-1467 - Add support for MongoDB 3.2 partialFilterExpression for index creation.
We now support partial filter expression on indexes via Index.partial(…). This allows to create partial indexes that only index the documents in a collection that meet a specified filter expression. 

new Index().named("idx").on("k3y", ASC).partial(filter(where("age").gte(10)))

The filter expression can be set via a plain DBObject or a CriteriaDefinition and is mapped against the associated domain type.

Original pull request: #431.
2016-12-19 19:45:14 +01:00
Oliver Gierke
fc0dd7d094 DATAMONGO-1565 - Polishing.
Formatting in ExpressionEvaluatingParameterBinder and StringBasedMongoQueryUnitTests. Turned Placeholder into value object.
2016-12-19 18:08:36 +01:00
Mark Paluch
712d8be7bb DATAMONGO-1565 - Polishing.
Consider quoted/unquoted parameter use with the same parameter reference. Extend date range in license headers.
2016-12-19 18:03:41 +01:00
Christoph Strobl
536dcc14ca DATAMONGO-1565 - Ignore placeholder pattern in replacement values for annotated queries.
We now make sure to quote single and double ticks in the replacement values before actually appending them to the query. We also replace single ticks around parameters in the actual raw annotated query by double quotes to make sure they are treated as a single string parameter.
2016-12-19 14:41:26 +01:00
Oliver Gierke
dc44c3a455 DATAMONGO-1525 - Improved creation of empty collections, esp. EnumSet.
We now use more type information to create a better empty collection in the first place. The previous algorithm always used an empty HashSet plus a subsequent conversion using the raw collection type. Especially the latter caused problems for EnumSets as the conversion into one requires the presence of component type information.

We now use Spring's collection factory and more available type information to create a proper collection in the first place and only rely on a subsequent conversion for arrays.
2016-12-01 20:18:06 +01:00
Christoph Strobl
8e90366712 DATAMONGO-1534 - Fix bulk operations missing to write type info.
We now correctly convert entities into their MongoDB representation including type information via _class property.

Original pull request: #415.
2016-11-28 09:05:46 +01:00
Oliver Gierke
ed36fd7260 DATAMONGO-1527 - Updated changelog. 2016-11-23 13:52:45 +01:00
Oliver Gierke
31a6a74743 DATAMONGO-1502 - After release cleanups. 2016-11-03 18:20:12 +01:00
Oliver Gierke
001ff508b5 DATAMONGO-1502 - Prepare next development iteration. 2016-11-03 18:20:10 +01:00
Oliver Gierke
6882fa9d10 DATAMONGO-1502 - Release version 1.9.5 (Hopper SR5). 2016-11-03 17:56:32 +01:00
Oliver Gierke
91eaae0ef6 DATAMONGO-1502 - Prepare 1.9.5 (Hopper SR5). 2016-11-03 17:56:00 +01:00
Oliver Gierke
785dc6ab78 DATAMONGO-1502 - Updated changelog. 2016-11-03 17:55:53 +01:00
Oliver Gierke
f011a9a4ee DATAMONGO-1521 - Added Aggregation.skip(…) overload to support longs.
Deprecated the one taking an int.
2016-11-03 15:04:07 +01:00
Christoph Strobl
c6c58050e7 DATAMONGO-1500 - Fix JSON serialization error in derived queries with field spec.
We now make sure not to eagerly attempt to convert given query parameters into a mongo specific format by calling toString() the query object, but rather delegate this to another step later in the chain.

Original pull request: #404.
2016-11-03 09:36:50 +01:00
Christoph Strobl
5fce8bcac6 DATAMONGO-1504 - Assert compatibility with MongoDB 3.4.
We now make sure to comply to the API requirements of mongo-java-driver 3.4 (in current beta1) by using empty DBObjects instead of null, ignoring non appropriate replication settings and cleaning up tests after execution.

Original pull request: #394.
2016-11-03 09:33:11 +01:00
Oliver Gierke
2f522bae5c DATAMONGO-1513 - Fixed identifier population for event listener generated, non-ObjectId on batch inserts.
The methods in MongoTemplate inserting a batch of documents previously only returned database generated identifiers, more especially ObjectId ones. This caused non-ObjectId identifiers potentially generated by other parties — i.e. an event listener reacting to a BeforeSaveEvent — not being considered for source object identifier population.

This commit adds a workaround augmenting the list of database generated identifiers with the ones actually present in the documents to be inserted. A follow-up ticket DATAMONGO-1519 was created to track the removal of the workaround in favor of a proper fix unfortunately requiring a change in public API (so a 2.0 candidate only).

Related tickets: DATAMONGO-1519.
2016-11-02 09:44:36 +01:00
Oliver Gierke
2e6f91924d DATAMONGO-1514 - Polishing.
Extended license years in copyright header.

Original pull request: #401.
2016-10-27 14:32:55 +02:00
Martin Macko
15f7a9c74a DATAMONGO-1514 - SpringDataMongodbQuery needs to be public.
SpringDataMongodbQuery is exposed publicly in QuerydslRepositorySupport, that's we've got to make it public to make sure class to the exposed methods from outside the package actually compile.

Original pull request: #401.
2016-10-27 14:32:55 +02:00
Oliver Gierke
49f52f0258 DATAMONGO-1495 - After release cleanups. 2016-09-29 14:20:12 +02:00
Oliver Gierke
396ea471fb DATAMONGO-1495 - Prepare next development iteration. 2016-09-29 14:20:08 +02:00
33 changed files with 998 additions and 165 deletions

View File

@@ -15,6 +15,7 @@ env:
- PROFILE=mongo31
- PROFILE=mongo32
- PROFILE=mongo33
- PROFILE=mongo34-next
# Current MongoDB version is 2.4.2 as of 2016-04, see https://github.com/travis-ci/travis-ci/issues/3694
# apt-get starts a MongoDB instance so it's not started using before_script

22
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.4.RELEASE</version>
<version>1.9.6.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.8.4.RELEASE</version>
<version>1.8.6.RELEASE</version>
</parent>
<modules>
@@ -28,7 +28,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>1.12.4.RELEASE</springdata.commons>
<springdata.commons>1.12.6.RELEASE</springdata.commons>
<mongo>2.14.3</mongo>
<mongo.osgi>2.13.0</mongo.osgi>
</properties>
@@ -178,6 +178,22 @@
</profile>
<profile>
<id>mongo34-next</id>
<properties>
<mongo>3.4.0-SNAPSHOT</mongo>
</properties>
<repositories>
<repository>
<id>mongo-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
</profile>
<profile>
<id>release</id>
<build>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.4.RELEASE</version>
<version>1.9.6.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -48,7 +48,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.9.4.RELEASE</version>
<version>1.9.6.RELEASE</version>
</dependency>
<dependency>

View File

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

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.4.RELEASE</version>
<version>1.9.6.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
/**
@@ -37,6 +38,7 @@ import com.mongodb.WriteConcern;
*
* @author Jon Brisbin
* @author Oliver Gierke
* @auhtor Christoph Strobl
*/
public class MongoLog4jAppender extends AppenderSkeleton {
@@ -58,8 +60,8 @@ public class MongoLog4jAppender extends AppenderSkeleton {
protected String collectionPattern = "%c";
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
protected String applicationId = System.getProperty("APPLICATION_ID", null);
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.SAFE;
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL;
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.ACKNOWLEDGED;
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.UNACKNOWLEDGED;
protected Mongo mongo;
protected DB db;
@@ -128,7 +130,7 @@ public class MongoLog4jAppender extends AppenderSkeleton {
}
protected void connectToMongo() throws UnknownHostException {
this.mongo = new Mongo(host, port);
this.mongo = new MongoClient(host, port);
this.db = mongo.getDB(database);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,37 +22,44 @@ import java.util.Calendar;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
/**
* Integration tests for {@link MongoLog4jAppender}.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class MongoLog4jAppenderIntegrationTests {
static final String NAME = MongoLog4jAppenderIntegrationTests.class.getName();
private static final Logger log = Logger.getLogger(NAME);
Mongo mongo;
MongoClient mongo;
DB db;
String collection;
@Before
public void setUp() throws Exception {
mongo = new Mongo("localhost", 27017);
mongo = new MongoClient("localhost", 27017);
db = mongo.getDB("logs");
Calendar now = Calendar.getInstance();
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
db.getCollection(collection).drop();
}
@After
public void tearDown() {
db.getCollection(collection).remove(new BasicDBObject());
}
@Test
@@ -64,7 +71,6 @@ public class MongoLog4jAppenderIntegrationTests {
log.error("ERROR message");
DBCursor msgs = db.getCollection(collection).find();
assertThat(msgs.count(), is(4));
}

View File

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

View File

@@ -25,6 +25,7 @@ import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.BulkWriteException;
import com.mongodb.BulkWriteOperation;
import com.mongodb.BulkWriteRequestBuilder;
@@ -38,6 +39,7 @@ import com.mongodb.WriteConcern;
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
@@ -117,7 +119,15 @@ class DefaultBulkOperations implements BulkOperations {
Assert.notNull(document, "Document must not be null!");
bulk.insert((DBObject) mongoOperations.getConverter().convertToMongoType(document));
if (document instanceof DBObject) {
bulk.insert((DBObject) document);
return this;
}
DBObject sink = new BasicDBObject();
mongoOperations.getConverter().write(document, sink);
bulk.insert(sink);
return this;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,17 +15,15 @@
*/
package org.springframework.data.mongodb.core;
import static org.springframework.data.domain.Sort.Direction.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
@@ -42,12 +40,12 @@ import com.mongodb.MongoException;
*/
public class DefaultIndexOperations implements IndexOperations {
private static final Double ONE = Double.valueOf(1);
private static final Double MINUS_ONE = Double.valueOf(-1);
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
private static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression";
private final MongoOperations mongoOperations;
private final String collectionName;
private final QueryMapper mapper;
private final Class<?> type;
/**
* Creates a new {@link DefaultIndexOperations}.
@@ -56,12 +54,26 @@ public class DefaultIndexOperations implements IndexOperations {
* @param collectionName must not be {@literal null}.
*/
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
this(mongoOperations, collectionName, null);
}
/**
* Creates a new {@link DefaultIndexOperations}.
*
* @param mongoOperations must not be {@literal null}.
* @param collectionName must not be {@literal null}.
* @param type Type used for mapping potential partial index filter expression. Can be {@literal null}.
* @since 1.10
*/
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName, Class<?> type) {
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
Assert.notNull(collectionName, "Collection name can not be null!");
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.mapper = new QueryMapper(mongoOperations.getConverter());
this.type = type;
}
/*
@@ -69,9 +81,20 @@ public class DefaultIndexOperations implements IndexOperations {
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
*/
public void ensureIndex(final IndexDefinition indexDefinition) {
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject indexOptions = indexDefinition.getIndexOptions();
if (indexOptions != null && indexOptions.containsField(PARTIAL_FILTER_EXPRESSION_KEY)) {
Assert.isInstanceOf(DBObject.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
indexOptions.put(PARTIAL_FILTER_EXPRESSION_KEY,
mapper.getMappedObject((DBObject) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
lookupPersistentEntity(type, collectionName)));
}
if (indexOptions != null) {
collection.createIndex(indexDefinition.getIndexKeys(), indexOptions);
} else {
@@ -79,6 +102,24 @@ public class DefaultIndexOperations implements IndexOperations {
}
return null;
}
private MongoPersistentEntity<?> lookupPersistentEntity(Class<?> entityType, String collection) {
if (entityType != null) {
return mongoOperations.getConverter().getMappingContext().getPersistentEntity(entityType);
}
Collection<? extends MongoPersistentEntity<?>> entities = mongoOperations.getConverter().getMappingContext()
.getPersistentEntities();
for (MongoPersistentEntity<?> entity : entities) {
if (entity.getCollection().equals(collection)) {
return entity;
}
}
return null;
}
});
}
@@ -126,7 +167,9 @@ public class DefaultIndexOperations implements IndexOperations {
public List<IndexInfo> getIndexInfo() {
return mongoOperations.execute(collectionName, new CollectionCallback<List<IndexInfo>>() {
public List<IndexInfo> doInCollection(DBCollection collection) throws MongoException, DataAccessException {
List<DBObject> dbObjectList = collection.getIndexInfo();
return getIndexData(dbObjectList);
}
@@ -136,44 +179,7 @@ public class DefaultIndexOperations implements IndexOperations {
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
for (DBObject ix : dbObjectList) {
DBObject keyDbObject = (DBObject) ix.get("key");
int numberOfElements = keyDbObject.keySet().size();
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
for (String key : keyDbObject.keySet()) {
Object value = keyDbObject.get(key);
if (TWO_D_IDENTIFIERS.contains(value)) {
indexFields.add(IndexField.geo(key));
} else if ("text".equals(value)) {
DBObject weights = (DBObject) ix.get("weights");
for (String fieldName : weights.keySet()) {
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
}
} else {
Double keyValue = new Double(value.toString());
if (ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, ASC));
} else if (MINUS_ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, DESC));
}
}
}
String name = ix.get("name").toString();
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
indexInfoList.add(IndexInfo.indexInfoOf(ix));
}
return indexInfoList;

View File

@@ -541,7 +541,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public IndexOperations indexOps(Class<?> entityClass) {
return new DefaultIndexOperations(this, determineCollectionName(entityClass));
return new DefaultIndexOperations(this, determineCollectionName(entityClass), entityClass);
}
public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) {
@@ -932,7 +932,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
maybeEmitEvent(new BeforeSaveEvent<T>(o, dbDoc, collectionName));
dbObjectList.add(dbDoc);
}
List<ObjectId> ids = insertDBObjectList(collectionName, dbObjectList);
List<Object> ids = consolidateIdentifiers(insertDBObjectList(collectionName, dbObjectList), dbObjectList);
int i = 0;
for (T obj : batchToSave) {
if (i < ids.size()) {
@@ -1037,6 +1039,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
});
}
// TODO: 2.0 - Change method signature to return List<Object> and return all identifiers (DATAMONGO-1513,
// DATAMONGO-1519)
protected List<ObjectId> insertDBObjectList(final String collectionName, final List<DBObject> dbDocList) {
if (dbDocList.isEmpty()) {
return Collections.emptyList();
@@ -2115,6 +2119,28 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return resolved == null ? ex : resolved;
}
/**
* Returns all identifiers for the given documents. Will augment the given identifiers and fill in only the ones that
* are {@literal null} currently. This would've been better solved in {@link #insertDBObjectList(String, List)}
* directly but would require a signature change of that method.
*
* @param ids
* @param documents
* @return TODO: Remove for 2.0 and change method signature of {@link #insertDBObjectList(String, List)}.
*/
private static List<Object> consolidateIdentifiers(List<ObjectId> ids, List<DBObject> documents) {
List<Object> result = new ArrayList<Object>(ids.size());
for (int i = 0; i < ids.size(); i++) {
ObjectId objectId = ids.get(i);
result.add(objectId == null ? documents.get(i).get(ID_FIELD) : objectId);
}
return result;
}
// Callback implementations
/**
@@ -2168,7 +2194,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public FindCallback(DBObject query, DBObject fields) {
this.query = query;
this.query = query == null ? new BasicDBObject() : query;
this.fields = fields;
}

View File

@@ -248,11 +248,22 @@ public class Aggregation {
*
* @param elementsToSkip must not be less than zero.
* @return
* @deprecated prepare to get this one removed in favor of {@link #skip(long)}.
*/
public static SkipOperation skip(int elementsToSkip) {
return new SkipOperation(elementsToSkip);
}
/**
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param elementsToSkip must not be less than zero.
* @return
*/
public static SkipOperation skip(long elementsToSkip) {
return new SkipOperation(elementsToSkip);
}
/**
* Creates a new {@link LimitOperation} limiting the result to the given number of elements.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public class SkipOperation implements AggregationOperation {
/**
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param skipCount number of documents to skip.
* @param skipCount number of documents to skip, must not be less than zero.
*/
public SkipOperation(long skipCount) {

View File

@@ -886,10 +886,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Class<?> collectionType = targetType.getType();
if (sourceValue.isEmpty()) {
return getPotentiallyConvertedSimpleRead(new HashSet<Object>(), collectionType);
}
TypeInformation<?> componentType = targetType.getComponentType();
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
@@ -897,6 +893,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
: CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size());
if (sourceValue.isEmpty()) {
return getPotentiallyConvertedSimpleRead(items, collectionType);
}
for (Object dbObjItem : sourceValue) {
if (dbObjItem instanceof DBRef) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ public class GeospatialIndex implements IndexDefinition {
private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D;
private Double bucketSize = 1.0;
private String additionalField;
private IndexFilter filter;
/**
* Creates a new {@link GeospatialIndex} for the given field.
@@ -119,6 +120,22 @@ public class GeospatialIndex implements IndexDefinition {
return this;
}
/**
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
*
* @param filter can be {@literal null}.
* @return
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 1.10
*/
public GeospatialIndex partial(IndexFilter filter) {
this.filter = filter;
return this;
}
public DBObject getIndexKeys() {
DBObject dbo = new BasicDBObject();
@@ -186,6 +203,10 @@ public class GeospatialIndex implements IndexDefinition {
break;
}
if (filter != null) {
dbo.put("partialFilterExpression", filter.getFilterObject());
}
return dbo;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,6 +63,8 @@ public class Index implements IndexDefinition {
private long expire = -1;
private IndexFilter filter;
public Index() {}
public Index(String key, Direction direction) {
@@ -176,6 +178,21 @@ public class Index implements IndexDefinition {
return unique();
}
/**
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
*
* @param filter can be {@literal null}.
* @return
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 1.10
*/
public Index partial(IndexFilter filter) {
this.filter = filter;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexKeys()
@@ -213,6 +230,9 @@ public class Index implements IndexDefinition {
dbo.put("expireAfterSeconds", expire);
}
if (filter != null) {
dbo.put("partialFilterExpression", filter.getFilterObject());
}
return dbo;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.index;
import com.mongodb.DBObject;
/**
* Use {@link IndexFilter} to create the partial filter expression used when creating
* <a href="https://docs.mongodb.com/manual/core/index-partial/">Partial Indexes</a>.
*
* @author Christoph Strobl
* @since 1.10
*/
public interface IndexFilter {
/**
* Get the raw (unmapped) filter expression.
*
* @return
*/
DBObject getFilterObject();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,10 @@
*/
package org.springframework.data.mongodb.core.index;
import static org.springframework.data.domain.Sort.Direction.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -23,6 +26,8 @@ import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.DBObject;
/**
* @author Mark Pollack
* @author Oliver Gierke
@@ -30,6 +35,10 @@ import org.springframework.util.ObjectUtils;
*/
public class IndexInfo {
private static final Double ONE = Double.valueOf(1);
private static final Double MINUS_ONE = Double.valueOf(-1);
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
private final List<IndexField> indexFields;
private final String name;
@@ -37,6 +46,7 @@ public class IndexInfo {
private final boolean dropDuplicates;
private final boolean sparse;
private final String language;
private String partialFilterExpression;
/**
* @deprecated Will be removed in 1.7. Please use {@link #IndexInfo(List, String, boolean, boolean, boolean, String)}
@@ -62,6 +72,64 @@ public class IndexInfo {
this.language = language;
}
/**
* Creates new {@link IndexInfo} parsing required properties from the given {@literal sourceDocument}.
*
* @param sourceDocument
* @return
* @since 1.10
*/
public static IndexInfo indexInfoOf(DBObject sourceDocument) {
DBObject keyDbObject = (DBObject) sourceDocument.get("key");
int numberOfElements = keyDbObject.keySet().size();
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
for (String key : keyDbObject.keySet()) {
Object value = keyDbObject.get(key);
if (TWO_D_IDENTIFIERS.contains(value)) {
indexFields.add(IndexField.geo(key));
} else if ("text".equals(value)) {
DBObject weights = (DBObject) sourceDocument.get("weights");
for (String fieldName : weights.keySet()) {
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
}
} else {
Double keyValue = new Double(value.toString());
if (ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, ASC));
} else if (MINUS_ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, DESC));
}
}
}
String name = sourceDocument.get("name").toString();
boolean unique = sourceDocument.containsField("unique") ? (Boolean) sourceDocument.get("unique") : false;
boolean dropDuplicates = sourceDocument.containsField("dropDups") ? (Boolean) sourceDocument.get("dropDups")
: false;
boolean sparse = sourceDocument.containsField("sparse") ? (Boolean) sourceDocument.get("sparse") : false;
String language = sourceDocument.containsField("default_language") ? (String) sourceDocument.get("default_language")
: "";
String partialFilter = sourceDocument.containsField("partialFilterExpression")
? sourceDocument.get("partialFilterExpression").toString() : "";
IndexInfo info = new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language);
info.partialFilterExpression = partialFilter;
return info;
}
/**
* Returns the individual index fields of the index.
*
@@ -113,10 +181,19 @@ public class IndexInfo {
return language;
}
/**
* @return
* @since 1.0
*/
public String getPartialFilterExpression() {
return partialFilterExpression;
}
@Override
public String toString() {
return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", dropDuplicates="
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + "]";
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + ", partialFilterExpression="
+ partialFilterExpression + "]";
}
@Override
@@ -130,6 +207,7 @@ public class IndexInfo {
result = prime * result + (sparse ? 1231 : 1237);
result = prime * result + (unique ? 1231 : 1237);
result = prime * result + ObjectUtils.nullSafeHashCode(language);
result = prime * result + ObjectUtils.nullSafeHashCode(partialFilterExpression);
return result;
}
@@ -171,6 +249,9 @@ public class IndexInfo {
if (!ObjectUtils.nullSafeEquals(language, other.language)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(partialFilterExpression, other.partialFilterExpression)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2016. the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.index;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import com.mongodb.DBObject;
/**
* {@link IndexFilter} implementation for usage with plain {@link DBObject} as well as {@link CriteriaDefinition} filter
* expressions.
*
* @author Christoph Strobl
* @since 1.10
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class PartialIndexFilter implements IndexFilter {
private final @NonNull Object filterExpression;
/**
* Create new {@link PartialIndexFilter} for given {@link DBObject filter expression}.
*
* @param where must not be {@literal null}.
* @return
*/
public static PartialIndexFilter of(DBObject where) {
return new PartialIndexFilter(where);
}
/**
* Create new {@link PartialIndexFilter} for given {@link CriteriaDefinition filter expression}.
*
* @param where must not be {@literal null}.
* @return
*/
public static PartialIndexFilter of(CriteriaDefinition where) {
return new PartialIndexFilter(where);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.IndexFilter#getFilterObject()
*/
public DBObject getFilterObject() {
if (filterExpression instanceof DBObject) {
return (DBObject) filterExpression;
}
if (filterExpression instanceof CriteriaDefinition) {
return ((CriteriaDefinition) filterExpression).getCriteriaObject();
}
throw new IllegalArgumentException(
String.format("Unknown type %s used as filter expression.", filterExpression.getClass()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ public class TextIndexDefinition implements IndexDefinition {
private Set<TextIndexedFieldSpec> fieldSpecs;
private String defaultLanguage;
private String languageOverride;
private IndexFilter filter;
TextIndexDefinition() {
fieldSpecs = new LinkedHashSet<TextIndexedFieldSpec>();
@@ -129,6 +130,10 @@ public class TextIndexDefinition implements IndexDefinition {
options.put("language_override", languageOverride);
}
if (filter != null) {
options.put("partialFilterExpression", filter.getFilterObject());
}
return options;
}
@@ -288,8 +293,8 @@ public class TextIndexDefinition implements IndexDefinition {
public TextIndexDefinitionBuilder onField(String fieldname, Float weight) {
if (this.instance.fieldSpecs.contains(ALL_FIELDS)) {
throw new InvalidDataAccessApiUsageException(String.format("Cannot add %s to field spec for all fields.",
fieldname));
throw new InvalidDataAccessApiUsageException(
String.format("Cannot add %s to field spec for all fields.", fieldname));
}
this.instance.fieldSpecs.add(new TextIndexedFieldSpec(fieldname, weight));
@@ -318,15 +323,30 @@ public class TextIndexDefinition implements IndexDefinition {
public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) {
if (StringUtils.hasText(this.instance.languageOverride)) {
throw new InvalidDataAccessApiUsageException(String.format(
"Cannot set language override on %s as it is already defined on %s.", fieldname,
this.instance.languageOverride));
throw new InvalidDataAccessApiUsageException(
String.format("Cannot set language override on %s as it is already defined on %s.", fieldname,
this.instance.languageOverride));
}
this.instance.languageOverride = fieldname;
return this;
}
/**
* Only index the documents that meet the specified {@link IndexFilter filter expression}.
*
* @param filter can be {@literal null}.
* @return
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 1.10
*/
public TextIndexDefinitionBuilder partial(IndexFilter filter) {
this.instance.filter = filter;
return this;
}
public TextIndexDefinition build() {
return this.instance;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,6 +45,7 @@ import com.mongodb.gridfs.GridFSInputFile;
* @author Philipp Schneider
* @author Thomas Darimont
* @author Martin Baumgartner
* @author Christoph Strobl
*/
public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver {
@@ -182,7 +183,7 @@ public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver
public List<GridFSDBFile> find(Query query) {
if (query == null) {
return getGridFs().find((DBObject) null);
return getGridFs().find(new BasicDBObject());
}
DBObject queryObject = getMappedQuery(query.getQueryObject());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,15 @@
*/
package org.springframework.data.mongodb.repository.query;
import java.util.Collections;
import lombok.Value;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;
@@ -39,6 +46,7 @@ import com.mongodb.util.JSON;
* @author Christoph Strobl
* @author Thomas Darimont
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.9
*/
class ExpressionEvaluatingParameterBinder {
@@ -85,7 +93,7 @@ class ExpressionEvaluatingParameterBinder {
*
* @param input must not be {@literal null} or empty.
* @param accessor must not be {@literal null}.
* @param bindings must not be {@literal null}.
* @param bindingContext must not be {@literal null}.
* @return
*/
private String replacePlaceholders(String input, MongoParameterAccessor accessor, BindingContext bindingContext) {
@@ -94,47 +102,72 @@ class ExpressionEvaluatingParameterBinder {
return input;
}
boolean isCompletlyParameterizedQuery = input.matches("^\\?\\d+$");
StringBuilder result = new StringBuilder(input);
for (ParameterBinding binding : bindingContext.getBindings()) {
String parameter = binding.getParameter();
int idx = result.indexOf(parameter);
if (idx == -1) {
continue;
}
String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding);
int start = idx;
int end = idx + parameter.length();
// If the value to bind is an object literal we need to remove the quoting around the expression insertion point.
if (valueForBinding.startsWith("{") && !isCompletlyParameterizedQuery) {
// Is the insertion point actually surrounded by quotes?
char beforeStart = result.charAt(start - 1);
char afterEnd = result.charAt(end);
if ((beforeStart == '\'' || beforeStart == '"') && (afterEnd == '\'' || afterEnd == '"')) {
// Skip preceding and following quote
start -= 1;
end += 1;
}
}
result.replace(start, end, valueForBinding);
if (input.matches("^\\?\\d+$")) {
return getParameterValueForBinding(accessor, bindingContext.getParameters(),
bindingContext.getBindings().iterator().next());
}
return result.toString();
Matcher matcher = createReplacementPattern(bindingContext.getBindings()).matcher(input);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
ParameterBinding binding = bindingContext.getBindingFor(extractPlaceholder(matcher.group()));
String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding);
// appendReplacement does not like unescaped $ sign and others, so we need to quote that stuff first
matcher.appendReplacement(buffer, Matcher.quoteReplacement(valueForBinding));
if (binding.isQuoted()) {
postProcessQuotedBinding(buffer, valueForBinding);
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
/**
* Sanitize String binding by replacing single quoted values with double quotes which prevents potential single quotes
* contained in replacement to interfere with the Json parsing. Also take care of complex objects by removing the
* quotation entirely.
*
* @param buffer the {@link StringBuffer} to operate upon.
* @param valueForBinding the actual binding value.
*/
private void postProcessQuotedBinding(StringBuffer buffer, String valueForBinding) {
int quotationMarkIndex = buffer.length() - valueForBinding.length() - 1;
char quotationMark = buffer.charAt(quotationMarkIndex);
while (quotationMark != '\'' && quotationMark != '"') {
quotationMarkIndex--;
if (quotationMarkIndex < 0) {
throw new IllegalArgumentException("Could not find opening quotes for quoted parameter");
}
quotationMark = buffer.charAt(quotationMarkIndex);
}
if (valueForBinding.startsWith("{")) { // remove quotation char before the complex object string
buffer.deleteCharAt(quotationMarkIndex);
} else {
if (quotationMark == '\'') {
buffer.replace(quotationMarkIndex, quotationMarkIndex + 1, "\"");
}
buffer.append("\"");
}
}
/**
* Returns the serialized value to be used for the given {@link ParameterBinding}.
*
*
* @param accessor must not be {@literal null}.
* @param parameters
* @param binding must not be {@literal null}.
@@ -148,7 +181,7 @@ class ExpressionEvaluatingParameterBinder {
: accessor.getBindableValue(binding.getParameterIndex());
if (value instanceof String && binding.isQuoted()) {
return (String) value;
return ((String) value).startsWith("{") ? (String) value : ((String) value).replace("\"", "\\\"");
}
if (value instanceof byte[]) {
@@ -167,7 +200,7 @@ class ExpressionEvaluatingParameterBinder {
/**
* Evaluates the given {@code expressionString}.
*
*
* @param expressionString must not be {@literal null} or empty.
* @param parameters must not be {@literal null}.
* @param parameterValues must not be {@literal null}.
@@ -181,25 +214,61 @@ class ExpressionEvaluatingParameterBinder {
return expression.getValue(evaluationContext, Object.class);
}
/**
* Creates a replacement {@link Pattern} for all {@link ParameterBinding#getParameter() binding parameters} including
* a potentially trailing quotation mark.
*
* @param bindings
* @return
*/
private Pattern createReplacementPattern(List<ParameterBinding> bindings) {
StringBuilder regex = new StringBuilder();
for (ParameterBinding binding : bindings) {
regex.append("|");
regex.append(Pattern.quote(binding.getParameter()));
regex.append("['\"]?"); // potential quotation char (as in { foo : '?0' }).
}
return Pattern.compile(regex.substring(1));
}
/**
* Extract the placeholder stripping any trailing trailing quotation mark that might have resulted from the
* {@link #createReplacementPattern(List) pattern} used.
*
* @param groupName The actual {@link Matcher#group() group}.
* @return
*/
private Placeholder extractPlaceholder(String groupName) {
return !groupName.endsWith("'") && !groupName.endsWith("\"") ? //
Placeholder.of(groupName, false) : //
Placeholder.of(groupName.substring(0, groupName.length() - 1), true);
}
/**
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.9
*/
static class BindingContext {
final MongoParameters parameters;
final List<ParameterBinding> bindings;
final Map<Placeholder, ParameterBinding> bindings;
/**
* Creates new {@link BindingContext}.
*
*
* @param parameters
* @param bindings
*/
public BindingContext(MongoParameters parameters, List<ParameterBinding> bindings) {
this.parameters = parameters;
this.bindings = bindings;
this.bindings = mapBindings(bindings);
}
/**
@@ -211,21 +280,70 @@ class ExpressionEvaluatingParameterBinder {
/**
* Get unmodifiable list of {@link ParameterBinding}s.
*
*
* @return never {@literal null}.
*/
public List<ParameterBinding> getBindings() {
return Collections.unmodifiableList(bindings);
return new ArrayList<ParameterBinding>(bindings.values());
}
/**
* Get the concrete {@link ParameterBinding} for a given {@literal placeholder}.
*
* @param placeholder must not be {@literal null}.
* @return
* @throws java.util.NoSuchElementException
* @since 1.10
*/
ParameterBinding getBindingFor(Placeholder placeholder) {
if (!bindings.containsKey(placeholder)) {
throw new NoSuchElementException(String.format("Could not to find binding for placeholder '%s'.", placeholder));
}
return bindings.get(placeholder);
}
/**
* Get the associated {@link MongoParameters}.
*
*
* @return
*/
public MongoParameters getParameters() {
return parameters;
}
private static Map<Placeholder, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
Map<Placeholder, ParameterBinding> map = new LinkedHashMap<Placeholder, ParameterBinding>(bindings.size(), 1);
for (ParameterBinding binding : bindings) {
map.put(Placeholder.of(binding.getParameter(), binding.isQuoted()), binding);
}
return map;
}
}
/**
* Encapsulates a quoted/unquoted parameter placeholder.
*
* @author Mark Paluch
* @since 1.9
*/
@Value(staticConstructor = "of")
static class Placeholder {
private final String parameter;
private final boolean quoted;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return quoted ? String.format("'%s'", parameter) : parameter;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.repository.query;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -110,7 +112,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
try {
BasicQuery result = new BasicQuery(query.getQueryObject().toString(), fieldSpec);
BasicQuery result = new BasicQuery(query.getQueryObject(), (DBObject) JSON.parse(fieldSpec));
result.setSortObject(query.getSortObject());
return result;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import com.querydsl.mongodb.AbstractMongodbQuery;
*
* @author Oliver Gierke
*/
class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
public class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
private final MongoOperations operations;

View File

@@ -46,6 +46,7 @@ import com.mongodb.WriteConcern;
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -270,6 +271,25 @@ public class DefaultBulkOperationsIntegrationTests {
assertThat(result.getRemovedCount(), is(1));
}
/**
* @see DATAMONGO-1534
*/
@Test
public void insertShouldConsiderInheritance() {
SpecialDoc specialDoc = new SpecialDoc();
specialDoc.id = "id-special";
specialDoc.value = "normal-value";
specialDoc.specialValue = "special-value";
createBulkOps(BulkMode.ORDERED).insert(Arrays.asList(specialDoc)).execute();
BaseDoc doc = operations.findOne(where("_id", specialDoc.id), BaseDoc.class, COLLECTION_NAME);
assertThat(doc, notNullValue());
assertThat(doc, instanceOf(SpecialDoc.class));
}
private void testUpdate(BulkMode mode, boolean multi, int expectedUpdates) {
BulkOperations bulkOps = createBulkOps(mode);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,18 +17,28 @@ package org.springframework.data.mongodb.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.data.mongodb.core.ReflectiveDBCollectionInvoker.*;
import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.util.Version;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
@@ -42,6 +52,9 @@ import com.mongodb.DBObject;
@ContextConfiguration("classpath:infrastructure.xml")
public class DefaultIndexOperationsIntegrationTests {
private static final Version THREE_DOT_TWO = new Version(3, 2);
private static Version mongoVersion;
static final DBObject GEO_SPHERE_2D = new BasicDBObject("loaction", "2dsphere");
@Autowired MongoTemplate template;
@@ -51,6 +64,7 @@ public class DefaultIndexOperationsIntegrationTests {
@Before
public void setUp() {
queryMongoVersionIfNecessary();
String collectionName = this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class);
this.collection = this.template.getDb().getCollection(collectionName);
@@ -59,6 +73,14 @@ public class DefaultIndexOperationsIntegrationTests {
this.indexOps = new DefaultIndexOperations(template, collectionName);
}
private void queryMongoVersionIfNecessary() {
if (mongoVersion == null) {
CommandResult result = template.executeCommand("{ buildInfo: 1 }");
mongoVersion = Version.parse(result.get("version").toString());
}
}
/**
* @see DATAMONGO-1008
*/
@@ -71,6 +93,78 @@ public class DefaultIndexOperationsIntegrationTests {
assertThat(info.getIndexFields().get(0).isGeo(), is(true));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldApplyPartialFilterCorrectly() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC)
.partial(of(where("q-t-y").gte(10)));
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-criteria");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"q-t-y\" : { \"$gte\" : 10}}")));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldApplyPartialFilterWithMappedPropertyCorrectly() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC)
.partial(of(where("quantity").gte(10)));
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-mapped-criteria");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"qty\" : { \"$gte\" : 10}}")));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldApplyPartialDBOFilterCorrectly() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC)
.partial(of(new BasicDBObject("qty", new BasicDBObject("$gte", 10))));
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-dbo");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"qty\" : { \"$gte\" : 10}}")));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldFavorExplicitMappingHintViaClass() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC)
.partial(of(where("age").gte(10)));
indexOps = new DefaultIndexOperations(template,
this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class),
MappingToSameCollection.class);
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-inheritance");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"a_g_e\" : { \"$gte\" : 10}}")));
}
private IndexInfo findAndReturnIndexInfo(DBObject keys) {
return findAndReturnIndexInfo(indexOps.getIndexInfo(), keys);
}
@@ -89,5 +183,15 @@ public class DefaultIndexOperationsIntegrationTests {
throw new AssertionError(String.format("Index with %s was not found", name));
}
static class DefaultIndexOperationsIntegrationTestsSample {}
@Document(collection = "default-index-operations-tests")
static class DefaultIndexOperationsIntegrationTestsSample {
@Field("qty") Integer quantity;
}
@Document(collection = "default-index-operations-tests")
static class MappingToSameCollection extends DefaultIndexOperationsIntegrationTestsSample {
@Field("a_g_e") Integer age;
}
}

View File

@@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
@@ -43,6 +44,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
@@ -70,11 +72,14 @@ import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.mongodb.core.query.BasicQuery;
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.util.CloseableIterator;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;
@@ -114,6 +119,7 @@ public class MongoTemplateTests {
@Autowired MongoTemplate template;
@Autowired MongoDbFactory factory;
@Autowired ConfigurableApplicationContext context;
MongoTemplate mappingTemplate;
org.springframework.data.util.Version mongoVersion;
@@ -3164,6 +3170,28 @@ public class MongoTemplateTests {
assertThat(template.findOne(query(where("id").is(wgj.id)), WithGeoJson.class).point, is(equalTo(wgj.point)));
}
/**
* @see DATAMONGO-1513
*/
@Test
@DirtiesContext
public void populatesIdsAddedByEventListener() {
context.addApplicationListener(new AbstractMongoEventListener<Document>() {
@Override
public void onBeforeSave(BeforeSaveEvent<Document> event) {
event.getDBObject().put("_id", UUID.randomUUID().toString());
}
});
Document document = new Document();
template.insertAll(Arrays.asList(document));
assertThat(document.id, is(notNullValue()));
}
static class DoucmentWithNamedIdField {
@Id String someIdKey;

View File

@@ -2086,6 +2086,17 @@ public class MappingMongoConverterUnitTests {
assertThat(result.sample, is("value"));
}
/**
* @see DATAMONGO-1525
*/
@Test
public void readsEmptyEnumSet() {
DBObject source = new BasicDBObject("enumSet", new BasicDBList());
assertThat(converter.read(ClassWithEnumProperty.class, source).enumSet, is(EnumSet.noneOf(SampleEnum.class)));
}
static class GenericType<T> {
T content;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,7 +74,7 @@ public class PerformanceTests {
private static final int ITERATIONS = 50;
private static final StopWatch watch = new StopWatch();
private static final Collection<String> IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE",
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED");
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED", "W2", "W3");
private static final int COLLECTION_SIZE = 1024 * 1024 * 256; // 256 MB
private static final Collection<String> COLLECTION_NAMES = Arrays.asList("template", "driver", "person");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.query.IsTextQuery.*;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
@@ -40,6 +41,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Person;
import org.springframework.data.mongodb.repository.Person.Sex;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -192,6 +194,18 @@ public class PartTreeMongoQueryUnitTests {
assertThat(fields.get("age"), is((Object) 1));
}
/**
* @see DATAMONGO-1500
*/
@Test
public void shouldLeaveParameterConversionToQueryMapper() {
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findBySex", Sex.FEMALE);
assertThat(query.getQueryObject().get("sex"), is((Object) Sex.FEMALE));
assertThat(query.getFieldsObject().get("firstname"), is((Object) 1));
}
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object... args) {
Class<?>[] types = new Class<?>[args.length];
@@ -249,6 +263,9 @@ public class PartTreeMongoQueryUnitTests {
PersonDto findPersonDtoByAge(Integer age);
<T> T findDynamicallyProjectedBy(Class<T> type);
@Query(fields = "{ 'firstname' : 1 }")
List<Person> findBySex(Sex sex);
}
interface PersonProjection {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,10 +50,12 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.mongodb.util.JSON;
/**
* Unit tests for {@link StringBasedMongoQuery}.
@@ -84,9 +86,9 @@ public class StringBasedMongoQueryUnitTests {
public void bindsSimplePropertyCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -98,13 +100,13 @@ public class StringBasedMongoQueryUnitTests {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class);
Address address = new Address("Foo", "0123", "Bar");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address);
DBObject dbObject = new BasicDBObject();
converter.write(address, dbObject);
dbObject.removeField(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
BasicDBObject queryObject = new BasicDBObject("address", dbObject);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
@@ -117,7 +119,7 @@ public class StringBasedMongoQueryUnitTests {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAndAddress", String.class, Address.class);
Address address = new Address("Foo", "0123", "Bar");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
DBObject addressDbObject = new BasicDBObject();
converter.write(address, addressDbObject);
@@ -126,7 +128,7 @@ public class StringBasedMongoQueryUnitTests {
DBObject reference = new BasicDBObject("address", addressDbObject);
reference.put("lastname", "Matthews");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is(reference));
}
@@ -178,8 +180,8 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception {
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1) });
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1));
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", DBObject.class,
Map.class);
@@ -227,10 +229,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -242,10 +244,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -258,9 +260,9 @@ public class StringBasedMongoQueryUnitTests {
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -303,10 +305,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueries() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -318,11 +320,11 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class,
String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -334,11 +336,11 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
boolean.class, String.class, String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
"{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}");
@@ -352,16 +354,126 @@ public class StringBasedMongoQueryUnitTests {
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
byte[] binaryData = "Matthews".getBytes("UTF-8");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, binaryData);
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
+ DatatypeConverter.printBase64Binary(binaryData) + "', '$type' : " + BSON.B_GENERAL + "}}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void bindsPropertyReferenceMultipleTimesCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
BasicDBList or = new BasicDBList();
or.add(new BasicDBObject("age", 3));
or.add(new BasicDBObject("displayAge", "3"));
BasicDBObject queryObject = new BasicDBObject("$or", or);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldIgnorePlaceholderPatternInReplacementValue() throws Exception {
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
"nothing-special");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is(JSON.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteStringReplacementCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews', password: 'foo");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
assertThat(query.getQueryObject(),
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews', password: 'foo")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteStringReplacementContainingQuotesCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews\", password: \"foo");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
assertThat(query.getQueryObject(),
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews\", password: \"foo")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteStringReplacementWithQuotationsCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
"\"Dave Matthews\", password: 'foo");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
assertThat(query.getQueryObject(),
is((DBObject) new BasicDBObject("lastname", "\"Dave Matthews\", password: 'foo")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteComplexQueryStringCorreclty() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "{ $ne : \"calamity\" }");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
assertThat(query.getQueryObject(),
is((DBObject) new BasicDBObject("lastname", new BasicDBObject("$ne", "calamity"))));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuotationInQuotedComplexQueryString() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
"{ $ne : \"\\\"calamity\\\"\" }");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
assertThat(query.getQueryObject(),
is((DBObject) new BasicDBObject("lastname", new BasicDBObject("$ne", "\"calamity\""))));
}
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
Method method = SampleRepository.class.getMethod(name, parameters);
@@ -420,5 +532,11 @@ public class StringBasedMongoQueryUnitTests {
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
boolean findByAgeQuotedAndUnquoted(int age);
@Query("{ 'arg0' : ?0, 'arg1' : ?1 }")
List<Person> findByStringWithWildcardChar(String arg0, String arg1);
}
}

View File

@@ -1,6 +1,90 @@
Spring Data MongoDB Changelog
=============================
Changes in version 1.9.6.RELEASE (2016-12-21)
---------------------------------------------
* DATAMONGO-1565 - Placeholders in manually defined queries not escaped properly.
* DATAMONGO-1534 - Type hint is missing when using BulkOperations.insert.
* DATAMONGO-1525 - Reading empty EnumSet fails.
* DATAMONGO-1522 - Release 1.9.6 (Hopper SR6).
Changes in version 1.10.0.RC1 (2016-12-21)
------------------------------------------
* DATAMONGO-1567 - Upgrade to a newer JDK version on TravisCI.
* DATAMONGO-1566 - Adapt API in RepositoryFactoryBeanSupport implementation.
* DATAMONGO-1565 - Placeholders in manually defined queries not escaped properly.
* DATAMONGO-1564 - Split up AggregationExpressions.
* DATAMONGO-1558 - Upgrade travis-ci profile to MongoDB 3.4.
* DATAMONGO-1552 - Add $facet, $bucket and $bucketAuto aggregation stages.
* DATAMONGO-1551 - Add $graphLookup aggregation stage.
* DATAMONGO-1550 - Add $replaceRoot aggregation stage.
* DATAMONGO-1549 - Add $count aggregation stage.
* DATAMONGO-1548 - Add new MongoDB 3.4 aggregation operators.
* DATAMONGO-1547 - Register repository factory in spring.factories for multi-store support.
* DATAMONGO-1546 - Switch to new way of registering custom Jackson modules.
* DATAMONGO-1542 - Refactor CondOperator and IfNullOperator to children of AggregationExpressions.
* DATAMONGO-1540 - Add support for $map to aggregation.
* DATAMONGO-1539 - Add dedicated annotations for manually declared count and delete queries.
* DATAMONGO-1538 - Add support for $let to aggregation.
* DATAMONGO-1536 - Add missing aggregation operators.
* DATAMONGO-1534 - Type hint is missing when using BulkOperations.insert.
* DATAMONGO-1533 - Add support for SpEL in GroupOperations (aggregation).
* DATAMONGO-1530 - Support missing aggregation pipeline operators in expression support.
* DATAMONGO-1525 - Reading empty EnumSet fails.
* DATAMONGO-1521 - Aggregation.skip(...) expects int but new SkipOperation(...) supports long.
* DATAMONGO-1520 - Aggregation.match should accept CriteriaDefinition.
* DATAMONGO-1514 - SpringDataMongodbQuery should be public.
* DATAMONGO-1513 - Non-ObjectId identifiers generated by event listeners are not populated if documents are inserted as batch.
* DATAMONGO-1504 - Assert compatibility with MongoDB 3.4 server and driver.
* DATAMONGO-1500 - RuntimeException for query methods with fields declaration and Pageable parameters.
* DATAMONGO-1498 - MongoMappingContext doesn't know about types usually auto-detected (JodaTime, JDK 8 date time types).
* DATAMONGO-1493 - Typos in reference documentation.
* DATAMONGO-1492 - Interface AggregationExpression in package org.springframework.data.mongodb.core.aggregation should be public.
* DATAMONGO-1491 - Add support for $filter to aggregation.
* DATAMONGO-1490 - Change the XML data type of boolean flags to String.
* DATAMONGO-1486 - Changes to MappingMongoConverter Result in Class Cast Exception.
* DATAMONGO-1485 - Querydsl MongodbSerializer does not take registered converters for Enums into account.
* DATAMONGO-1480 - Add support for noCursorTimeout in Query.
* DATAMONGO-1479 - MappingMongoConverter.convertToMongoType causes StackOverflowError for parameterized map value types.
* DATAMONGO-1476 - New stream method only partially makes use of collection name.
* DATAMONGO-1471 - MappingMongoConverter attempts to set null value on potentially primitive identifier.
* DATAMONGO-1470 - AbstractMongoConfiguraton should allow multiple base package for @Document scanning.
* DATAMONGO-1469 - Release 1.10 RC1 (Ingalls).
* DATAMONGO-1467 - Support partial filter expressions for indexing introduced in MongoDB 3.2.
* DATAMONGO-1465 - String arguments passed to DefaultScriptOperations.execute() appear quoted in script.
* DATAMONGO-1454 - Add support for exists projection in repository query derivation.
* DATAMONGO-1406 - Query mapper does not use @Field field name when querying nested fields in combination with nested keywords.
* DATAMONGO-1328 - Add support for mongodb 3.2 specific arithmetic operators to aggregation.
* DATAMONGO-1327 - Add support for $stdDevSamp and $stdDevPop to aggregation ($group stage).
* DATAMONGO-1299 - Add support for date aggregations.
* DATAMONGO-1141 - Add support for $push $sort in Update.
* DATAMONGO-861 - Add support for $cond and $ifNull operators in aggregation operation.
* DATAMONGO-784 - Add support for $cmp in group or project aggregation.
Changes in version 2.0.0.M1 (2016-11-23)
----------------------------------------
* DATAMONGO-1527 - Release 2.0 M1 (Kay).
* DATAMONGO-1509 - Inconsistent type alias placement in list of classes.
* DATAMONGO-1461 - Upgrade Hibernate/JPA dependencies to match Spring 5 baseline.
* DATAMONGO-1448 - Set up 2.0 development.
* DATAMONGO-1444 - Reactive support in Spring Data MongoDB.
* DATAMONGO-1176 - Use org.bson types instead of com.mongodb.
* DATAMONGO-563 - Upgrade to MongoDB driver 2.9.2 as it fixes a serious regression introduced in 2.9.0.
* DATAMONGO-562 - Cannot create entity with OptimisticLocking (@Version) and initial id.
Changes in version 1.9.5.RELEASE (2016-11-03)
---------------------------------------------
* DATAMONGO-1521 - Aggregation.skip(...) expects int but new SkipOperation(...) supports long.
* DATAMONGO-1514 - SpringDataMongodbQuery should be public.
* DATAMONGO-1513 - Non-ObjectId identifiers generated by event listeners are not populated if documents are inserted as batch.
* DATAMONGO-1504 - Assert compatibility with MongoDB 3.4 server and driver.
* DATAMONGO-1502 - Release 1.9.5 (Hopper SR5).
* DATAMONGO-1500 - RuntimeException for query methods with fields declaration and Pageable parameters.
Changes in version 1.9.4.RELEASE (2016-09-29)
---------------------------------------------
* DATAMONGO-1498 - MongoMappingContext doesn't know about types usually auto-detected (JodaTime, JDK 8 date time types).

View File

@@ -1,4 +1,4 @@
Spring Data MongoDB 1.9.4
Spring Data MongoDB 1.9.6
Copyright (c) [2010-2015] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").