From d3b9f91478e156b11330c9d77265caf0fbefa9bd Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 6 Jul 2017 14:03:44 +0200 Subject: [PATCH] DATAMONGO-1734 - Polish MongoTemplate.exists execution. Optimize execution by using count() limited to 1 element. Original pull request: #479. --- .../data/mongodb/core/MongoTemplate.java | 30 ++++++++++++++----- .../mongodb/core/MongoTemplateUnitTests.java | 8 ++++- 2 files changed, 30 insertions(+), 8 deletions(-) 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 62b456592..31a2fbe35 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 @@ -20,6 +20,7 @@ import static org.springframework.data.mongodb.core.query.SerializationUtils.*; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import java.io.IOException; import java.util.ArrayList; @@ -131,6 +132,7 @@ import com.mongodb.client.MapReduceIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.CountOptions; import com.mongodb.client.model.CreateCollectionOptions; import com.mongodb.client.model.DeleteOptions; import com.mongodb.client.model.Filters; @@ -609,14 +611,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), getPersistentEntity(entityClass)); - FindIterable iterable = execute(collectionName, new FindCallback(mappedQuery)); - if (query.getCollation().isPresent()) { - iterable = iterable - .collation(query.getCollation().map(org.springframework.data.mongodb.core.Collation::toMongoCollation).get()); - } - - return iterable.iterator().hasNext(); + return execute(collectionName, new ExistsCallback(mappedQuery, + query.getCollation().map(org.springframework.data.mongodb.core.Collation::toMongoCollation).orElse(null))); } // Find methods that take a Query to express the query and that return a List of objects. @@ -2429,6 +2426,25 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } } + /** + * Optimized {@link CollectionCallback} that takes an already mappend query and a nullable + * {@link com.mongodb.client.model.Collation} to execute a count query limited to one element. + * + * @author Christoph Strobl + * @since 2.0 + */ + @RequiredArgsConstructor + private static class ExistsCallback implements CollectionCallback { + + private final Document mappedQuery; + private final com.mongodb.client.model.Collation collation; + + @Override + public Boolean doInCollection(MongoCollection collection) throws MongoException, DataAccessException { + return collection.count(mappedQuery, new CountOptions().limit(1).collation(collation)) > 0; + } + } + /** * Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification * {@link Document} and executes that against the {@link DBCollection}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java index 3600e097c..b9566795f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java @@ -81,6 +81,7 @@ import com.mongodb.client.MapReduceIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.CountOptions; import com.mongodb.client.model.DeleteOptions; import com.mongodb.client.model.FindOneAndDeleteOptions; import com.mongodb.client.model.FindOneAndUpdateOptions; @@ -123,6 +124,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { when(db.runCommand(Mockito.any(), Mockito.any(Class.class))).thenReturn(commandResultDocument); when(collection.find(Mockito.any(org.bson.Document.class))).thenReturn(findIterable); when(collection.mapReduce(Mockito.any(), Mockito.any())).thenReturn(mapReduceIterable); + when(collection.count(any(), any())).thenReturn(1L); when(findIterable.projection(Mockito.any())).thenReturn(findIterable); when(findIterable.sort(Mockito.any(org.bson.Document.class))).thenReturn(findIterable); when(findIterable.modifiers(Mockito.any(org.bson.Document.class))).thenReturn(findIterable); @@ -666,7 +668,11 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { template.exists(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class); - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); + ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); + verify(collection).count(any(), options.capture()); + + assertThat(options.getValue().getCollation(), + is(equalTo(com.mongodb.client.model.Collation.builder().locale("fr").build()))); } @Test // DATAMONGO-1518