diff --git a/pom.xml b/pom.xml
index c267f367b4..3dcbc3b244 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
Spring Data MongoDB Distribution
Spring Data project for MongoDB
http://www.springsource.org/spring-data/mongodb
- 1.0.2.BUILD-SNAPSHOT
+ 1.0.5.BUILD-SNAPSHOT
pom
spring-data-mongodb
@@ -151,7 +151,7 @@
version
- ${pom.version}
+ ${project.version}
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index 901c60de6a..f5db5b081e 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -4,7 +4,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.0.2.BUILD-SNAPSHOT
+ 1.0.5.BUILD-SNAPSHOT
../spring-data-mongodb-parent/pom.xml
spring-data-mongodb-cross-store
@@ -68,24 +68,6 @@
log4j
log4j
-
-
- javax.mail
- mail
-
-
- javax.jms
- jms
-
-
- com.sun.jdmk
- jmxtools
-
-
- com.sun.jmx
- jmxri
-
-
runtime
@@ -95,17 +77,6 @@
true
-
- org.mockito
- mockito-all
- test
-
-
-
- junit
- junit
-
-
org.aspectj
aspectjrt
diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml
index dc07dc40d3..c5bedea827 100644
--- a/spring-data-mongodb-log4j/pom.xml
+++ b/spring-data-mongodb-log4j/pom.xml
@@ -4,7 +4,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.0.2.BUILD-SNAPSHOT
+ 1.0.5.BUILD-SNAPSHOT
../spring-data-mongodb-parent/pom.xml
spring-data-mongodb-log4j
@@ -27,47 +27,9 @@
log4j
log4j
-
-
- javax.mail
- mail
-
-
- javax.jms
- jms
-
-
- com.sun.jdmk
- jmxtools
-
-
- com.sun.jmx
- jmxri
-
-
compile
-
-
- org.mockito
- mockito-all
- test
-
-
-
- org.hamcrest
- hamcrest-all
- 1.1
- test
-
-
-
- junit
- junit
- test
-
-
diff --git a/spring-data-mongodb-parent/pom.xml b/spring-data-mongodb-parent/pom.xml
index b4b94171ec..6c44f8a7ad 100644
--- a/spring-data-mongodb-parent/pom.xml
+++ b/spring-data-mongodb-parent/pom.xml
@@ -6,14 +6,15 @@
Spring Data MongoDB Parent
Spring Data project for MongoDB
http://www.springsource.org/spring-data/mongodb
- 1.0.2.BUILD-SNAPSHOT
+ 1.0.5.BUILD-SNAPSHOT
pom
UTF-8
- 4.8.1
- 1.2.15
+ 4.10
+ 1.2.16
1.8.4
+ 1.2.1
1.5.10
1.6.1
3.0.7.RELEASE
@@ -95,42 +96,6 @@
-
-
- strict
-
- false
-
-
-
- fast
-
- true
- true
-
-
-
- staging
-
-
- spring-site-staging
- file:///${java.io.tmpdir}/spring-data/mongodb/docs
-
-
- spring-milestone-staging
- file:///${java.io.tmpdir}/spring-data/mongodb/milestone
-
-
- spring-snapshot-staging
- file:///${java.io.tmpdir}/spring-data/mongodb/snapshot
-
-
-
-
- bootstrap
-
-
-
http://www.springsource.com/download/community
@@ -187,6 +152,11 @@
spring-tx
${org.springframework.version.range}
+
+ org.springframework
+ spring-context
+ ${org.springframework.version.range}
+
org.springframework
spring-orm
@@ -228,7 +198,7 @@
org.springframework.data
spring-data-mongodb
- 1.0.2.BUILD-SNAPSHOT
+ 1.0.5.BUILD-SNAPSHOT
@@ -254,24 +224,6 @@
log4j
log4j
${log4j.version}
-
-
- javax.mail
- mail
-
-
- javax.jms
- jms
-
-
- com.sun.jdmk
- jmxtools
-
-
- com.sun.jmx
- jmxri
-
-
runtime
@@ -284,14 +236,14 @@
org.mockito
- mockito-all
+ mockito-core
${org.mockito.version}
test
junit
- junit
+ junit-dep
${junit.version}
test
@@ -299,19 +251,34 @@
-
+
+
log4j
log4j
${log4j.version}
test
+
+
+ org.hamcrest
+ hamcrest-library
+ ${hamcrest.version}
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ junit
+ junit-dep
+ test
+
+
@@ -326,48 +293,11 @@
3.1.0.RELEASE
-
-
- ${project.basedir}/src/main/java
-
- **/*
-
-
- **/*.java
-
-
-
- ${project.basedir}/src/main/resources
-
- **/*
-
-
-
-
-
- ${project.basedir}/src/test/java
-
- **/*
-
-
- **/*.java
-
-
-
- ${project.basedir}/src/test/resources
-
- **/*
-
-
- **/*.java
-
-
-
org.apache.maven.plugins
maven-compiler-plugin
- 2.3.2
+ 2.5.1
1.5
1.5
@@ -387,13 +317,13 @@
org.apache.maven.plugins
maven-surefire-plugin
- 2.8
+ 2.10
false
**/*Tests.java
- junit:junit
+ junit:junit-dep
@@ -448,6 +378,10 @@
spring-plugins-release
http://repo.springsource.org/plugins-release
+
+ querydsl
+ http://source.mysema.com/maven2/releases
+
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 51c7d91dc9..eb923eb582 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -4,7 +4,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.0.2.BUILD-SNAPSHOT
+ 1.0.5.BUILD-SNAPSHOT
../spring-data-mongodb-parent/pom.xml
spring-data-mongodb
@@ -18,10 +18,22 @@
+
+ org.springframework
+ spring-tx
+
+
+ org.springframework
+ spring-context
+
org.springframework
spring-beans
+
+ org.springframework
+ spring-core
+
org.springframework
spring-expression
@@ -57,19 +69,6 @@
querydsl-mongodb
${querydsl.version}
true
-
-
- com.google.code.morphia
- morphia
-
-
-
-
-
- com.mysema.querydsl
- querydsl-apt
- ${querydsl.version}
- provided
@@ -79,25 +78,6 @@
-
- org.mockito
- mockito-all
- test
-
-
-
- org.hamcrest
- hamcrest-all
- 1.1
- test
-
-
-
- junit
- junit
- test
-
-
joda-time
joda-time
@@ -108,8 +88,8 @@
org.slf4j
slf4j-api
- test
+
org.slf4j
jcl-over-slf4j
@@ -138,7 +118,14 @@
com.mysema.maven
maven-apt-plugin
- 1.0.2
+ 1.0.4
+
+
+ com.mysema.querydsl
+ querydsl-apt
+ ${querydsl.version}
+
+
generate-test-sources
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
index 1cddded9c7..a38566db48 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
@@ -17,7 +17,11 @@
import java.beans.PropertyEditorSupport;
import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
import com.mongodb.ServerAddress;
@@ -30,6 +34,8 @@
*/
public class ServerAddressPropertyEditor extends PropertyEditorSupport {
+ private static final Log LOG = LogFactory.getLog(ServerAddressPropertyEditor.class);
+
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
@@ -38,21 +44,49 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport {
public void setAsText(String replicaSetString) {
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
- ServerAddress[] serverAddresses = new ServerAddress[replicaSetStringArray.length];
+ Set serverAddresses = new HashSet(replicaSetStringArray.length);
- for (int i = 0; i < replicaSetStringArray.length; i++) {
+ for (String element : replicaSetStringArray) {
- String[] hostAndPort = StringUtils.delimitedListToStringArray(replicaSetStringArray[i], ":");
+ ServerAddress address = parseServerAddress(element);
- try {
- serverAddresses[i] = new ServerAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1]));
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Could not parse port " + hostAndPort[1], e);
- } catch (UnknownHostException e) {
- throw new IllegalArgumentException("Could not parse host " + hostAndPort[0], e);
+ if (address != null) {
+ serverAddresses.add(address);
}
}
- setValue(serverAddresses);
+ if (serverAddresses.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Could not resolve at least one server of the replica set configuration! Validate your config!");
+ }
+
+ setValue(serverAddresses.toArray(new ServerAddress[serverAddresses.size()]));
+ }
+
+ /**
+ * Parses the given source into a {@link ServerAddress}.
+ *
+ * @param source
+ * @return the
+ */
+ private ServerAddress parseServerAddress(String source) {
+
+ String[] hostAndPort = StringUtils.delimitedListToStringArray(source.trim(), ":");
+
+ if (!StringUtils.hasText(source) || hostAndPort.length > 2) {
+ LOG.warn(String.format("Could not parse address source '%s'. Check your replica set configuration!", source));
+ return null;
+ }
+
+ try {
+ return hostAndPort.length == 1 ? new ServerAddress(hostAndPort[0]) : new ServerAddress(hostAndPort[0],
+ Integer.parseInt(hostAndPort[1]));
+ } catch (UnknownHostException e) {
+ LOG.warn(String.format("Could not parse host '%s'. Check your replica set configuration!", hostAndPort[0]));
+ } catch (NumberFormatException e) {
+ LOG.warn(String.format("Could not parse port '%s'. Check your replica set configuration!", hostAndPort[1]));
+ }
+
+ return null;
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java
index 6dfdc09916..679cece9c1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java
@@ -15,14 +15,15 @@
*/
package org.springframework.data.mongodb.core;
-import com.mongodb.DB;
-import com.mongodb.Mongo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
+import com.mongodb.DB;
+import com.mongodb.Mongo;
+
/**
* Helper class featuring helper methods for internal MongoDb classes.
*
@@ -78,7 +79,7 @@ public static DB doGetDB(Mongo mongo, String databaseName, String username, char
DB db = null;
if (TransactionSynchronizationManager.isSynchronizationActive() && dbHolder.doesNotHoldNonDefaultDB()) {
// Spring transaction management is active ->
- db = dbHolder.getDB();
+ db = dbHolder.getDB(databaseName);
if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
LOGGER.debug("Registering Spring transaction synchronization for existing Mongo DB");
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
@@ -96,10 +97,12 @@ public static DB doGetDB(Mongo mongo, String databaseName, String username, char
boolean credentialsGiven = username != null && password != null;
if (credentialsGiven && !db.isAuthenticated()) {
// Note, can only authenticate once against the same com.mongodb.DB object.
- if (!db.authenticate(username, password)) {
- throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName
- + "], username = [" + username + "], password = [" + new String(password) + "]", databaseName, username,
- password);
+ synchronized (db) {
+ if (!db.authenticate(username, password)) {
+ throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName
+ + "], username = [" + username + "], password = [" + new String(password) + "]", databaseName, username,
+ password);
+ }
}
}
@@ -110,9 +113,9 @@ public static DB doGetDB(Mongo mongo, String databaseName, String username, char
LOGGER.debug("Registering Spring transaction synchronization for new Hibernate Session");
DbHolder holderToUse = dbHolder;
if (holderToUse == null) {
- holderToUse = new DbHolder(db);
+ holderToUse = new DbHolder(databaseName, db);
} else {
- holderToUse.addDB(db);
+ holderToUse.addDB(databaseName, db);
}
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
holderToUse.setSynchronizedWithTransaction(true);
@@ -143,7 +146,7 @@ public static boolean isDBTransactional(DB db, Mongo mongo) {
return false;
}
DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
- return (dbHolder != null && dbHolder.containsDB(db));
+ return dbHolder != null && dbHolder.containsDB(db);
}
/**
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 0fcf67e29f..22b7ef043f 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
@@ -1,15 +1,11 @@
/*
- * Copyright 2010-2011 the original author or authors.
+ * Copyright 2010-2012 the original author or authors.
*
- * Licensed under t
-import org.springframework.util.ResourceUtils;
-
-import org.springframework.data.convert.EntityReader;
-he Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -20,6 +16,7 @@
package org.springframework.data.mongodb.core;
import static org.springframework.data.mongodb.core.query.Criteria.*;
+import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -50,6 +47,7 @@
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.convert.EntityReader;
+import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.MappingException;
@@ -108,6 +106,7 @@
* @author Graeme Rocher
* @author Mark Pollack
* @author Oliver Gierke
+ * @author Amol Nayak
*/
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -333,11 +332,12 @@ protected void executeQuery(Query query, String collectionName, DocumentCallback
Assert.notNull(query);
DBObject queryObject = query.getQueryObject();
+ DBObject sortObject = query.getSortObject();
DBObject fieldsObject = query.getFieldsObject();
if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
- + collectionName);
+ LOGGER.debug(String.format("Executing query: %s sort: %s fields: %s in collection: $s",
+ serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName));
}
this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName);
@@ -456,7 +456,7 @@ public T findOne(Query query, Class entityClass, String collectionName) {
} else {
query.limit(1);
List results = find(query, entityClass, collectionName);
- return (results.isEmpty() ? null : results.get(0));
+ return results.isEmpty() ? null : results.get(0);
}
}
@@ -732,11 +732,13 @@ public Object doInCollection(DBCollection collection) throws MongoException, Dat
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName,
entityClass, dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
+ WriteResult wr;
if (writeConcernToUse == null) {
- collection.insert(dbDoc);
+ wr = collection.insert(dbDoc);
} else {
- collection.insert(dbDoc, writeConcernToUse);
+ wr = collection.insert(dbDoc, writeConcernToUse);
}
+ handleAnyWriteResultErrors(wr, dbDoc, "insert");
return dbDoc.get(ID);
}
});
@@ -755,11 +757,13 @@ public Void doInCollection(DBCollection collection) throws MongoException, DataA
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
null, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
+ WriteResult wr;
if (writeConcernToUse == null) {
- collection.insert(dbDocList);
+ wr = collection.insert(dbDocList);
} else {
- collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
+ wr = collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
}
+ handleAnyWriteResultErrors(wr, null, "insert_list");
return null;
}
});
@@ -786,11 +790,13 @@ public Object doInCollection(DBCollection collection) throws MongoException, Dat
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
+ WriteResult wr;
if (writeConcernToUse == null) {
- collection.save(dbDoc);
+ wr = collection.save(dbDoc);
} else {
- collection.save(dbDoc, writeConcernToUse);
+ wr = collection.save(dbDoc, writeConcernToUse);
}
+ handleAnyWriteResultErrors(wr, dbDoc, "save");
return dbDoc.get(ID);
}
});
@@ -873,7 +879,7 @@ public void remove(Object object, String collection) {
return;
}
- remove(getIdQueryFor(object), collection);
+ doRemove(collection, getIdQueryFor(object), object.getClass());
}
/**
@@ -938,7 +944,7 @@ public Void doInCollection(DBCollection collection) throws MongoException, DataA
entityClass, null, queryObject);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("remove using query: " + queryObject + " in collection: " + collection.getName());
+ LOGGER.debug("remove using query: " + dboq + " in collection: " + collection.getName());
}
if (writeConcernToUse == null) {
wr = collection.remove(dboq);
@@ -996,26 +1002,15 @@ public MapReduceResults mapReduce(Query query, String inputCollectionName
LOGGER.debug("Executing MapReduce on collection [" + command.getInput() + "], mapFunction [" + mapFunc
+ "], reduceFunction [" + reduceFunc + "]");
}
- CommandResult commandResult = null;
- try {
- if (command.getOutputType() == MapReduceCommand.OutputType.INLINE) {
- commandResult = executeCommand(commandObject, getDb().getOptions());
- } else {
- commandResult = executeCommand(commandObject);
- }
- commandResult.throwOnError();
- } catch (RuntimeException ex) {
- this.potentiallyConvertRuntimeException(ex);
- }
- String error = commandResult.getErrorMessage();
- if (error != null) {
- throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = "
- + commandObject);
- }
+
+ CommandResult commandResult = command.getOutputType() == MapReduceCommand.OutputType.INLINE ? executeCommand(
+ commandObject, getDb().getOptions()) : executeCommand(commandObject);
+ handleCommandError(commandResult, commandObject);
if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("MapReduce command result = [" + commandResult + "]");
+ LOGGER.debug(String.format("MapReduce command result = [%s]", serializeToJsonSafely(commandObject)));
}
+
MapReduceOutput mapReduceOutput = new MapReduceOutput(inputCollection, commandObject, commandResult);
List mappedResults = new ArrayList();
DbObjectCallback callback = new ReadDbObjectCallback(mongoConverter, entityClass);
@@ -1040,7 +1035,7 @@ public GroupByResults group(Criteria criteria, String inputCollectionName
if (criteria == null) {
dbo.put("cond", null);
} else {
- dbo.put("cond", criteria.getCriteriaObject());
+ dbo.put("cond", mapper.getMappedObject(criteria.getCriteriaObject(), null));
}
// If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and
// convert to DBObject
@@ -1066,21 +1061,12 @@ public GroupByResults group(Criteria criteria, String inputCollectionName
DBObject commandObject = new BasicDBObject("group", dbo);
if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Executing Group with DBObject [" + commandObject.toString() + "]");
- }
- CommandResult commandResult = null;
- try {
- commandResult = executeCommand(commandObject, getDb().getOptions());
- commandResult.throwOnError();
- } catch (RuntimeException ex) {
- this.potentiallyConvertRuntimeException(ex);
- }
- String error = commandResult.getErrorMessage();
- if (error != null) {
- throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = "
- + commandObject);
+ LOGGER.debug(String.format("Executing Group with DBObject [%s]", serializeToJsonSafely(commandObject)));
}
+ CommandResult commandResult = executeCommand(commandObject, getDb().getOptions());
+ handleCommandError(commandResult, commandObject);
+
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Group command result = [" + commandResult + "]");
}
@@ -1251,11 +1237,14 @@ protected List doFind(String collectionName, DBObject query, DBObject fie
protected List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass,
CursorPreparer preparer, DbObjectCallback objectCallback) {
+
MongoPersistentEntity> entity = mappingContext.getPersistentEntity(entityClass);
+
if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + entityClass
- + " in collection: " + collectionName);
+ LOGGER.debug(String.format("find using query: %s fields: %s for class: %s in collection: %s",
+ serializeToJsonSafely(query), fields, entityClass, collectionName));
}
+
return executeFindMultiInternal(new FindCallback(mapper.getMappedObject(query, entity), fields), preparer,
objectCallback, collectionName);
}
@@ -1337,13 +1326,15 @@ protected T doFindAndModify(String collectionName, DBObject query, DBObject
updateObj.put(key, mongoConverter.convertToMongoType(updateObj.get(key)));
}
+ DBObject mappedQuery = mapper.getMappedObject(query, entity);
+
if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("findAndModify using query: " + query + " fields: " + fields + " sort: " + sort + " for class: "
- + entityClass + " and update: " + updateObj + " in collection: " + collectionName);
+ LOGGER.debug("findAndModify using query: " + mappedQuery + " fields: " + fields + " sort: " + sort
+ + " for class: " + entityClass + " and update: " + updateObj + " in collection: " + collectionName);
}
- return executeFindOneInternal(new FindAndModifyCallback(mapper.getMappedObject(query, entity), fields, sort,
- updateObj, options), new ReadDbObjectCallback(readerToUse, entityClass), collectionName);
+ return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, updateObj, options),
+ new ReadDbObjectCallback(readerToUse, entityClass), collectionName);
}
/**
@@ -1364,9 +1355,19 @@ protected void populateIdIfNecessary(Object savedObject, Object id) {
return;
}
+ ConversionService conversionService = mongoConverter.getConversionService();
+ BeanWrapper, Object> wrapper = BeanWrapper.create(savedObject, conversionService);
+
try {
- BeanWrapper.create(savedObject, mongoConverter.getConversionService()).setProperty(idProp, id);
- return;
+
+ Object idValue = wrapper.getProperty(idProp, idProp.getType(), true);
+
+ if (idValue != null) {
+ return;
+ }
+
+ wrapper.setProperty(idProp, id);
+
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
@@ -1514,9 +1515,16 @@ protected void handleAnyWriteResultErrors(WriteResult wr, DBObject query, String
String error = wr.getError();
if (error != null) {
-
- String message = String.format("Execution of %s%s failed: %s", operation, query == null ? "" : "' using '"
- + query.toString() + "' query", error);
+ String message;
+ if (operation.equals("insert") || operation.equals("save")) {
+ // assuming the insert operations will begin with insert string
+ message = String.format("Insert/Save for %s failed: %s", query, error);
+ } else if (operation.equals("insert_list")) {
+ message = String.format("Insert list failed: %s", error);
+ } else {
+ message = String.format("Execution of %s%s failed: %s", operation,
+ query == null ? "" : "' using '" + query.toString() + "' query", error);
+ }
if (WriteResultChecking.EXCEPTION == this.writeResultChecking) {
throw new DataIntegrityViolationException(message);
@@ -1539,6 +1547,27 @@ private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex)
return resolved == null ? ex : resolved;
}
+ /**
+ * 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}.
+ */
+ private void handleCommandError(CommandResult result, DBObject source) {
+
+ try {
+ result.throwOnError();
+ } catch (MongoException ex) {
+
+ String error = result.getErrorMessage();
+ error = error == null ? "NO MESSAGE" : error;
+
+ throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = "
+ + source, ex);
+ }
+ }
+
private static final MongoConverter getDefaultMongoConverter(MongoDbFactory factory) {
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext());
converter.afterPropertiesSet();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java
index cefb06821f..0aea53dd10 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java
@@ -86,6 +86,8 @@ public DBObject getMappedObject(DBObject query, MongoPersistentEntity> entity)
ids.add(convertId(id));
}
valueDbo.put(inKey, ids.toArray(new Object[ids.size()]));
+ } else if (valueDbo.containsField("$ne")) {
+ valueDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else {
value = getMappedObject((DBObject) value, null);
}
@@ -99,11 +101,9 @@ public DBObject getMappedObject(DBObject query, MongoPersistentEntity> entity)
BasicBSONList newConditions = new BasicBSONList();
Iterator> iter = conditions.iterator();
while (iter.hasNext()) {
- newConditions.add(getMappedObject((DBObject) iter.next(), null));
+ newConditions.add(getMappedObject((DBObject) iter.next(), entity));
}
value = newConditions;
- } else if (key.equals("$ne")) {
- value = convertId(value);
}
newDbo.put(newKey, convertSimpleOrDBObject(value, null));
@@ -141,7 +141,11 @@ private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity> e
*/
private boolean isIdKey(String key, MongoPersistentEntity> entity) {
- if (null != entity && entity.getIdProperty() != null) {
+ if (entity == null) {
+ return false;
+ }
+
+ if (entity.getIdProperty() != null) {
MongoPersistentProperty idProperty = entity.getIdProperty();
return idProperty.getName().equals(key) || idProperty.getFieldName().equals(key);
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java
index 71f80b8ee1..27b1720e48 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java
@@ -38,6 +38,8 @@
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
+import org.springframework.data.mongodb.core.convert.MongoConverters.StringToURLConverter;
+import org.springframework.data.mongodb.core.convert.MongoConverters.URLToStringConverter;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.util.Assert;
@@ -89,6 +91,8 @@ public CustomConversions(List> converters) {
this.converters.add(StringToBigDecimalConverter.INSTANCE);
this.converters.add(BigIntegerToStringConverter.INSTANCE);
this.converters.add(StringToBigIntegerConverter.INSTANCE);
+ this.converters.add(URLToStringConverter.INSTANCE);
+ this.converters.add(StringToURLConverter.INSTANCE);
this.converters.addAll(converters);
for (Object c : this.converters) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java
index bc1f01152c..5f7e4b8549 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java
@@ -18,6 +18,7 @@
import java.util.HashSet;
import java.util.Set;
+import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
@@ -25,6 +26,7 @@
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
+import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.TypeInformation;
@@ -37,6 +39,9 @@
*/
class MappedConstructor {
+ private static final String REJECT_CONSTRUCTOR = String.format("Entity doesn't have a usable constructor, either "
+ + "provide a custom converter or annotate a constructor with @%s!", PersistenceConstructor.class.getSimpleName());
+
private final Set parameters;
/**
@@ -44,13 +49,19 @@ class MappedConstructor {
*
* @param entity must not be {@literal null}.
* @param context must not be {@literal null}.
+ * @throws MappingException in case the {@link MongoPersistentEntity} handed in does not have a
+ * {@link PreferredConstructor}.
*/
public MappedConstructor(MongoPersistentEntity> entity,
- MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> context) {
+ MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> context) throws MappingException {
Assert.notNull(entity);
Assert.notNull(context);
+ if (entity.getPreferredConstructor() == null) {
+ throw new MappingException(REJECT_CONSTRUCTOR);
+ }
+
this.parameters = new HashSet();
for (Parameter> parameter : entity.getPreferredConstructor().getParameters()) {
@@ -83,6 +94,7 @@ public boolean isConstructorParameter(PersistentProperty> property) {
*
* @param parameter must not be {@literal null}.
* @return
+ * @throws MappingException in case no {@link MappedParameter} can be found for the given {@link Parameter}.
*/
public MappedParameter getFor(Parameter> parameter) {
@@ -92,7 +104,7 @@ public MappedParameter getFor(Parameter> parameter) {
}
}
- throw new IllegalStateException(String.format("Didn't find a MappedParameter for %s!", parameter.toString()));
+ throw new MappingException(String.format("Didn't find a MappedParameter for %s!", parameter.toString()));
}
/**
@@ -165,4 +177,4 @@ public boolean maps(PersistentProperty> property) {
return this.property.equals(property);
}
}
-}
\ No newline at end of file
+}
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 96b1c7160e..759d2f74ea 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
@@ -439,7 +439,6 @@ private static Collection> asCollection(Object source) {
*
* @param collection must not be {@literal null}.
* @param property must not be {@literal null}.
- *
* @return
*/
protected DBObject createCollection(Collection> collection, MongoPersistentProperty property) {
@@ -729,7 +728,7 @@ protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, St
* @return the converted {@link Collections}, will never be {@literal null}.
*/
@SuppressWarnings("unchecked")
- private Collection> readCollectionOrArray(TypeInformation> targetType, BasicDBList sourceValue) {
+ private Object readCollectionOrArray(TypeInformation> targetType, BasicDBList sourceValue) {
Assert.notNull(targetType);
@@ -751,7 +750,7 @@ private Collection> readCollectionOrArray(TypeInformation> targetType, Basic
}
}
- return items;
+ return getPotentiallyConvertedSimpleRead(items, targetType.getType());
}
/**
@@ -819,7 +818,7 @@ public Object convertToMongoType(Object obj) {
return null;
}
- Class> target = conversions.getCustomWriteTarget(getClass());
+ Class> target = conversions.getCustomWriteTarget(obj.getClass());
if (target != null) {
return conversionService.convert(obj, target);
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java
index 80e116ff83..07655c8440 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java
@@ -17,8 +17,12 @@
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URL;
import org.bson.types.ObjectId;
+import org.springframework.core.convert.ConversionFailedException;
+import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
@@ -119,4 +123,28 @@ public BigInteger convert(String source) {
return StringUtils.hasText(source) ? new BigInteger(source) : null;
}
}
+
+ public static enum URLToStringConverter implements Converter {
+ INSTANCE;
+
+ public String convert(URL source) {
+ return source == null ? null : source.toString();
+ }
+ }
+
+ public static enum StringToURLConverter implements Converter {
+ INSTANCE;
+
+ private static final TypeDescriptor SOURCE = TypeDescriptor.valueOf(String.class);
+ private static final TypeDescriptor TARGET = TypeDescriptor.valueOf(URL.class);
+
+ public URL convert(String source) {
+
+ try {
+ return source == null ? null : new URL(source);
+ } catch (MalformedURLException e) {
+ throw new ConversionFailedException(SOURCE, TARGET, source, e);
+ }
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java
index 19fdbd1716..b8acbf5c7d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 2011 by the original author(s).
+ * Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.springframework.data.mongodb.core.index;
+import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,14 +24,29 @@
/**
* Mark a class to use compound indexes.
*
- * @author Jon Brisbin
+ * @author Jon Brisbin
+ * @author Oliver Gierke
*/
@Target({ ElementType.TYPE })
+@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CompoundIndex {
+ /**
+ * The actual index definition in JSON format. The keys of the JSON document are the fields to be indexed, the values
+ * define the index direction (1 for ascending, -1 for descending).
+ *
+ * @return
+ */
String def();
+ /**
+ * It does not actually make sense to use that attribute as the direction has to be defined in the {@link #def()}
+ * attribute actually.
+ *
+ * @return
+ */
+ @Deprecated
IndexDirection direction() default IndexDirection.ASCENDING;
boolean unique() default false;
@@ -40,8 +55,18 @@
boolean dropDups() default false;
+ /**
+ * The name of the index to be created.
+ *
+ * @return
+ */
String name() default "";
+ /**
+ * The collection the index will be created in. Will default to the collection the annotated domain class will be
+ * stored in.
+ *
+ * @return
+ */
String collection() default "";
-
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java
index a2f739fd3b..32cb73ae73 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 2011 by the original author(s).
+ * Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.springframework.data.mongodb.core.index;
import java.lang.reflect.Field;
@@ -27,7 +26,6 @@
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.event.MappingContextEvent;
import org.springframework.data.mongodb.MongoDbFactory;
-import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
@@ -39,10 +37,10 @@
import com.mongodb.util.JSON;
/**
- * Component that inspects {@link BasicMongoPersistentEntity} instances contained in the given
- * {@link MongoMappingContext} for indexing metadata and ensures the indexes to be available.
+ * Component that inspects {@link MongoPersistentEntity} instances contained in the given {@link MongoMappingContext}
+ * for indexing metadata and ensures the indexes to be available.
*
- * @author Jon Brisbin
+ * @author Jon Brisbin
* @author Oliver Gierke
*/
public class MongoPersistentEntityIndexCreator implements
@@ -97,12 +95,12 @@ protected void checkForIndexes(final MongoPersistentEntity> entity) {
if (type.isAnnotationPresent(CompoundIndexes.class)) {
CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
for (CompoundIndex index : indexes.value()) {
- String indexColl = index.collection();
- if ("".equals(indexColl)) {
- indexColl = entity.getCollection();
- }
- ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(),
- index.sparse());
+
+ String indexColl = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection();
+ DBObject definition = (DBObject) JSON.parse(index.def());
+
+ ensureIndex(indexColl, index.name(), definition, index.unique(), index.dropDups(), index.sparse());
+
if (log.isDebugEnabled()) {
log.debug("Created compound index " + index);
}
@@ -111,10 +109,14 @@ protected void checkForIndexes(final MongoPersistentEntity> entity) {
entity.doWithProperties(new PropertyHandler() {
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
+
Field field = persistentProperty.getField();
+
if (field.isAnnotationPresent(Indexed.class)) {
+
Indexed index = field.getAnnotation(Indexed.class);
String name = index.name();
+
if (!StringUtils.hasText(name)) {
name = persistentProperty.getFieldName();
} else {
@@ -126,11 +128,17 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty)
}
}
}
+
String collection = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection();
- ensureIndex(collection, name, null, index.direction(), index.unique(), index.dropDups(), index.sparse());
+ int direction = index.direction() == IndexDirection.ASCENDING ? 1 : -1;
+ DBObject definition = new BasicDBObject(persistentProperty.getFieldName(), direction);
+
+ ensureIndex(collection, name, definition, index.unique(), index.dropDups(), index.sparse());
+
if (log.isDebugEnabled()) {
log.debug("Created property index " + index);
}
+
} else if (field.isAnnotationPresent(GeoSpatialIndexed.class)) {
GeoSpatialIndexed index = field.getAnnotation(GeoSpatialIndexed.class);
@@ -155,21 +163,15 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty)
}
}
- protected void ensureIndex(String collection, final String name, final String def, final IndexDirection direction,
- final boolean unique, final boolean dropDups, final boolean sparse) {
- DBObject defObj;
- if (null != def) {
- defObj = (DBObject) JSON.parse(def);
- } else {
- defObj = new BasicDBObject();
- defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
- }
+ protected void ensureIndex(String collection, String name, DBObject indexDefinition, boolean unique,
+ boolean dropDups, boolean sparse) {
+
DBObject opts = new BasicDBObject();
opts.put("name", name);
opts.put("dropDups", dropDups);
opts.put("sparse", sparse);
opts.put("unique", unique);
- mongoDbFactory.getDb().getCollection(collection).ensureIndex(defObj, opts);
- }
+ mongoDbFactory.getDb().getCollection(collection).ensureIndex(indexDefinition, opts);
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java
index adfbeaef96..34769d7ea4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 the original author or authors.
+ * Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,33 +26,39 @@
* Collects the results of performing a MapReduce operations.
*
* @author Mark Pollack
- *
- * @param The class in which the results are mapped onto, accessible via an interator.
+ * @author Oliver Gierke
+ * @param The class in which the results are mapped onto, accessible via an iterator.
*/
public class MapReduceResults implements Iterable {
private final List mappedResults;
-
- private DBObject rawResults;
-
- private MapReduceTiming mapReduceTiming;
-
- private MapReduceCounts mapReduceCounts;
-
- private String outputCollection;
-
+ private final DBObject rawResults;
+ private final String outputCollection;
+ private final MapReduceTiming mapReduceTiming;
+ private final MapReduceCounts mapReduceCounts;
+
+ /**
+ * Creates a new {@link MapReduceResults} from the given mapped results and the raw one.
+ *
+ * @param mappedResults must not be {@literal null}.
+ * @param rawResults must not be {@literal null}.
+ */
public MapReduceResults(List mappedResults, DBObject rawResults) {
+
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
+
this.mappedResults = mappedResults;
this.rawResults = rawResults;
- parseTiming(rawResults);
- parseCounts(rawResults);
- if (rawResults.get("result") != null) {
- this.outputCollection = (String) rawResults.get("result");
- }
+ this.mapReduceTiming = parseTiming(rawResults);
+ this.mapReduceCounts = parseCounts(rawResults);
+ this.outputCollection = parseOutputCollection(rawResults);
}
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Iterable#iterator()
+ */
public Iterator iterator() {
return mappedResults.iterator();
}
@@ -73,28 +79,71 @@ public DBObject getRawResults() {
return rawResults;
}
- protected void parseTiming(DBObject rawResults) {
+ private MapReduceTiming parseTiming(DBObject rawResults) {
+
DBObject timing = (DBObject) rawResults.get("timing");
- if (timing != null) {
- if (timing.get("mapTime") != null && timing.get("emitLoop") != null && timing.get("total") != null) {
- mapReduceTiming = new MapReduceTiming((Long) timing.get("mapTime"), (Integer) timing.get("emitLoop"),
- (Integer) timing.get("total"));
- }
- } else {
- mapReduceTiming = new MapReduceTiming(-1, -1, -1);
+
+ if (timing == null) {
+ return new MapReduceTiming(-1, -1, -1);
+ }
+
+ if (timing.get("mapTime") != null && timing.get("emitLoop") != null && timing.get("total") != null) {
+ return new MapReduceTiming(getAsLong(timing, "mapTime"), getAsLong(timing, "emitLoop"),
+ getAsLong(timing, "total"));
}
+
+ return new MapReduceTiming(-1, -1, -1);
+ }
+
+ /**
+ * Returns the value of the source's field with the given key as {@link Long}.
+ *
+ * @param source
+ * @param key
+ * @return
+ */
+ private Long getAsLong(DBObject source, String key) {
+ Object raw = source.get(key);
+ return raw instanceof Long ? (Long) raw : (Integer) raw;
}
- protected void parseCounts(DBObject rawResults) {
+ /**
+ * Parses the raw {@link DBObject} result into a {@link MapReduceCounts} value object.
+ *
+ * @param rawResults
+ * @return
+ */
+ private MapReduceCounts parseCounts(DBObject rawResults) {
+
DBObject counts = (DBObject) rawResults.get("counts");
- if (counts != null) {
- if (counts.get("input") != null && counts.get("emit") != null && counts.get("output") != null) {
- mapReduceCounts = new MapReduceCounts((Integer) counts.get("input"), (Integer) counts.get("emit"),
- (Integer) counts.get("output"));
- }
- } else {
- mapReduceCounts = new MapReduceCounts(-1, -1, -1);
+
+ if (counts == null) {
+ return new MapReduceCounts(-1, -1, -1);
+ }
+
+ if (counts.get("input") != null && counts.get("emit") != null && counts.get("output") != null) {
+ return new MapReduceCounts((Integer) counts.get("input"), (Integer) counts.get("emit"),
+ (Integer) counts.get("output"));
}
+
+ return new MapReduceCounts(-1, -1, -1);
}
+ /**
+ * Parses the output collection from the raw {@link DBObject} result.
+ *
+ * @param rawResults
+ * @return
+ */
+ private String parseOutputCollection(DBObject rawResults) {
+
+ Object resultField = rawResults.get("result");
+
+ if (resultField == null) {
+ return null;
+ }
+
+ return resultField instanceof DBObject ? ((DBObject) resultField).get("collection").toString() : resultField
+ .toString();
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
index 4ce58f14a9..e1ac523fd8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core.query;
+import static org.springframework.util.ObjectUtils.*;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -29,6 +31,7 @@
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.geo.Shape;
import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -429,11 +432,9 @@ public String getKey() {
}
/*
- * (non-Javadoc)
- *
- * @see org.springframework.datastore.document.mongodb.query.Criteria#
- * getCriteriaObject(java.lang.String)
- */
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getCriteriaObject()
+ */
public DBObject getCriteriaObject() {
if (this.criteriaChain.size() == 1) {
return criteriaChain.get(0).getSingleCriteriaObject();
@@ -496,4 +497,82 @@ private void setValue(DBObject dbo, String key, Object value) {
}
}
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || !getClass().equals(obj.getClass())) {
+ return false;
+ }
+
+ Criteria that = (Criteria) obj;
+
+ if (this.criteriaChain.size() != that.criteriaChain.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < this.criteriaChain.size(); i++) {
+
+ Criteria left = this.criteriaChain.get(i);
+ Criteria right = that.criteriaChain.get(i);
+
+ if (!simpleCriteriaEquals(left, right)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean simpleCriteriaEquals(Criteria left, Criteria right) {
+
+ boolean keyEqual = left.key == null ? right.key == null : left.key.equals(right.key);
+ boolean criteriaEqual = left.criteria.equals(right.criteria);
+ boolean valueEqual = isEqual(left.isValue, right.isValue);
+
+ return keyEqual && criteriaEqual && valueEqual;
+ }
+
+ /**
+ * Checks the given objects for equality. Handles {@link Pattern} and arrays correctly.
+ *
+ * @param left
+ * @param right
+ * @return
+ */
+ private boolean isEqual(Object left, Object right) {
+
+ if (left == null) {
+ return right == null;
+ }
+
+ if (left instanceof Pattern) {
+ return right instanceof Pattern ? ((Pattern) left).pattern().equals(((Pattern) right).pattern()) : false;
+ }
+
+ return ObjectUtils.nullSafeEquals(left, right);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+
+ int result = 17;
+
+ result += nullSafeHashCode(key);
+ result += criteria.hashCode();
+ result += nullSafeHashCode(isValue);
+
+ return result;
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
index 40565fd588..c92fa335d6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core.query;
+import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
+import static org.springframework.util.ObjectUtils.*;
+
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -140,4 +143,60 @@ public String getHint() {
protected List getCriteria() {
return new ArrayList(this.criteria.values());
}
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return String.format("Query: %s, Fields: %s, Sort: %s", serializeToJsonSafely(getQueryObject()),
+ serializeToJsonSafely(getFieldsObject()), serializeToJsonSafely(getSortObject()));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || !getClass().equals(obj.getClass())) {
+ return false;
+ }
+
+ Query that = (Query) obj;
+
+ boolean criteriaEqual = this.criteria.equals(that.criteria);
+ boolean fieldsEqual = this.fieldSpec == null ? that.fieldSpec == null : this.fieldSpec.equals(that.fieldSpec);
+ boolean sortEqual = this.sort == null ? that.sort == null : this.sort.equals(that.sort);
+ boolean hintEqual = this.hint == null ? that.hint == null : this.hint.equals(that.hint);
+ boolean skipEqual = this.skip == that.skip;
+ boolean limitEqual = this.limit == that.limit;
+
+ return criteriaEqual && fieldsEqual && sortEqual && hintEqual && skipEqual && limitEqual;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+
+ int result = 17;
+
+ result += 31 * criteria.hashCode();
+ result += 31 * nullSafeHashCode(fieldSpec);
+ result += 31 * nullSafeHashCode(sort);
+ result += 31 * nullSafeHashCode(hint);
+ result += 31 * skip;
+ result += 31 * limit;
+
+ return result;
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java
new file mode 100644
index 0000000000..b11948220c
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.query;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.springframework.core.convert.converter.Converter;
+
+import com.mongodb.DBObject;
+import com.mongodb.util.JSON;
+
+/**
+ * Utility methods for JSON serialization.
+ *
+ * @author Oliver Gierke
+ */
+public abstract class SerializationUtils {
+
+ private SerializationUtils() {
+
+ }
+
+ /**
+ * Serializes the given object into pseudo-JSON meaning it's trying to create a JSON representation as far as possible
+ * but falling back to the given object's {@link Object#toString()} method if it's not serializable. Useful for
+ * printing raw {@link DBObject}s containing complex values before actually converting them into Mongo native types.
+ *
+ * @param value
+ * @return
+ */
+ public static String serializeToJsonSafely(Object value) {
+
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ return JSON.serialize(value);
+ } catch (Exception e) {
+ if (value instanceof Collection) {
+ return toString((Collection>) value);
+ } else if (value instanceof Map) {
+ return toString((Map, ?>) value);
+ } else if (value instanceof DBObject) {
+ return toString(((DBObject) value).toMap());
+ } else {
+ return String.format("{ $java : %s }", value.toString());
+ }
+ }
+ }
+
+ private static String toString(Map, ?> source) {
+ return iterableToDelimitedString(source.entrySet(), "{ ", " }", new Converter, Object>() {
+ public Object convert(Entry, ?> source) {
+ return String.format("\"%s\" : %s", source.getKey(), serializeToJsonSafely(source.getValue()));
+ }
+ });
+ }
+
+ private static String toString(Collection> source) {
+ return iterableToDelimitedString(source, "[ ", " ]", new Converter