diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java index 8bbb651f8..d9b440e24 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java @@ -396,6 +396,85 @@ public interface MongoOperations { */ List find(String collectionName, Query query, Class targetClass, CursorPreparer preparer); + /** + * Map the results of an ad-hoc query on the default MongoDB collection to a single instance of an object + * of the specified type. The first document that matches the query is returned and also removed from the + * collection in the database. + *

+ * The object is converted from the MongoDB native representation using an instance of + * {@see MongoConverter}. Unless configured otherwise, an + * instance of SimpleMongoConverter will be used. + *

+ * The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more + * feature rich {@link Query}. + * + * @param query the query class that specifies the criteria used to find a record and also an optional fields specification + * @param targetClass the parameterized type of the returned list. + * @return the converted object + */ + T findAndRemove(Query query, Class targetClass); + + /** + * Map the results of an ad-hoc query on the default MongoDB collection to a single instance of an object + * of the specified type. The first document that matches the query is returned and also removed from the + * collection in the database. + *

+ * The object is converted from the MongoDB native representation using an instance of + * {@see MongoConverter}. Unless configured otherwise, an + * instance of SimpleMongoConverter will be used. + *

+ * The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more + * feature rich {@link Query}. + * + * @param query the query class that specifies the criteria used to find a record and also an optional fields specification + * @param targetClass the parameterized type of the returned list. + * @param reader the MongoReader to convert from DBObject to an object. + * @return the converted object + */ + T findAndRemove(Query query, Class targetClass, + MongoReader reader); + + /** + * Map the results of an ad-hoc query on the specified collection to a single instance of an object + * of the specified type. The first document that matches the query is returned and also removed from the + * collection in the database. + *

+ * The object is converted from the MongoDB native representation using an instance of + * {@see MongoConverter}. Unless configured otherwise, an + * instance of SimpleMongoConverter will be used. + *

+ * The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more + * feature rich {@link Query}. + * + * @param collectionName name of the collection to retrieve the objects from + * @param query the query class that specifies the criteria used to find a record and also an optional fields specification + * @param targetClass the parameterized type of the returned list. + * @return the converted object + */ + T findAndRemove(String collectionName, Query query, + Class targetClass); + + /** + * Map the results of an ad-hoc query on the specified collection to a single instance of an object + * of the specified type. The first document that matches the query is returned and also removed from the + * collection in the database. + *

+ * The object is converted from the MongoDB native representation using an instance of + * {@see MongoConverter}. Unless configured otherwise, an + * instance of SimpleMongoConverter will be used. + *

+ * The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more + * feature rich {@link Query}. + * + * @param collectionName name of the collection to retrieve the objects from + * @param query the query class that specifies the criteria used to find a record and also an optional fields specification + * @param targetClass the parameterized type of the returned list. + * @param reader the MongoReader to convert from DBObject to an object. + * @return the converted object + */ + T findAndRemove(String collectionName, Query query, + Class targetClass, MongoReader reader); + /** * Insert the object into the default collection. *

diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java index e3350d3e3..cb412641b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java @@ -548,6 +548,28 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), targetClass, preparer); } + // Find methods that take a Query to express the query and that return a single object that is + // also removed from the collection in the database. + + public T findAndRemove(Query query, Class targetClass) { + return findAndRemove(getDefaultCollectionName(), query, targetClass); + } + + public T findAndRemove(Query query, Class targetClass, + MongoReader reader) { + return findAndRemove(getDefaultCollectionName(), query, targetClass, reader); + } + + public T findAndRemove(String collectionName, Query query, + Class targetClass) { + return findAndRemove(collectionName, query, targetClass, null); + } + + public T findAndRemove(String collectionName, Query query, + Class targetClass, MongoReader reader) { + return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(), query.getSortObject(), targetClass, reader); + } + /* (non-Javadoc) * @see org.springframework.data.document.mongodb.MongoOperations#insert(java.lang.Object) */ @@ -947,6 +969,28 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica return dbo; } + /** + * Map the results of an ad-hoc query on the default MongoDB collection to an object using the provided MongoReader + * The first document that matches the query is returned and also removed from the collection in the database. + *

+ * The query document is specified as a standard DBObject and so is the fields specification. + * + * @param collectionName name of the collection to retrieve the objects from + * @param query the query document that specifies the criteria used to find a record + * @param targetClass the parameterized type of the returned list. + * @param reader the MongoReader to convert from DBObject to an object. + * @return the List of converted objects. + */ + protected T doFindAndRemove(String collectionName, DBObject query, DBObject fields, DBObject sort, Class targetClass, MongoReader reader) { + MongoReader readerToUse = reader; + if (readerToUse == null) { + readerToUse = this.mongoConverter; + } + substituteMappedIdIfNecessary(query, targetClass, readerToUse); + return execute(new FindAndRemoveCallback(query, fields, sort), new ReadDbObjectCallback(readerToUse, targetClass), + collectionName); + } + /** * Populates the id property of the saved object, if it's not set already. * @@ -1175,6 +1219,31 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica } } + /** + * Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification + * {@link DBObject} and executes that against the {@link DBCollection}. + * + * @author Thomas Risberg + */ + private static class FindAndRemoveCallback implements CollectionCallback { + + private final DBObject query; + + private final DBObject fields; + + private final DBObject sort; + + public FindAndRemoveCallback(DBObject query, DBObject fields, DBObject sort) { + this.query = query; + this.fields = fields; + this.sort = sort; + } + + public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException { + return collection.findAndModify(query, fields, sort, true, null, false, false); + } + } + /** * Simple internal callback to allow operations on a {@link DBObject}. * diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Message.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Message.java new file mode 100644 index 000000000..09fdd7583 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/Message.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010-2011 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.document.mongodb; + +import java.util.Date; + +import org.bson.types.ObjectId; + +public class Message { + + private ObjectId id; + + private String text; + + private Date timestamp; + + public Message() { + } + + public Message(String text) { + super(); + this.text = text; + this.timestamp = new Date(); + } + + public Message(String text, Date timestamp) { + super(); + this.text = text; + this.timestamp = timestamp; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Date getTimestamp() { + return timestamp; + } + + public void setTimestamp(Date timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return "Message [id=" + id + ", text=" + text + ", timestamp=" + timestamp + + "]"; + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateTests.java index abd65a5c7..9a37460e3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateTests.java @@ -225,4 +225,21 @@ public class MongoTemplateTests { assertThat(p8q.get_id(), is(p8.get_id())); } + @Test + public void testFindAndRemove() throws Exception { + + Message m1 = new Message("Hello Spring"); + template.insert(m1); + Message m2 = new Message("Hello Mongo"); + template.insert(m2); + + Query q = new Query(Criteria.where("text").regex("^Hello.*")); + Message found1 = template.findAndRemove(q, Message.class); + Message found2 = template.findAndRemove(q, Message.class); +// Message notFound = template.findAndRemove(q, Message.class); + DBObject notFound = template.getCollection("").findAndRemove(q.getQueryObject()); + assertThat(found1, notNullValue()); + assertThat(found2, notNullValue()); + assertThat(notFound, nullValue()); + } }