|
22 | 22 | import java.math.RoundingMode;
|
23 | 23 | import java.util.*;
|
24 | 24 | import java.util.concurrent.TimeUnit;
|
| 25 | +import java.util.function.BiPredicate; |
25 | 26 | import java.util.stream.Collectors;
|
26 | 27 |
|
27 | 28 | import org.apache.commons.logging.Log;
|
28 | 29 | import org.apache.commons.logging.LogFactory;
|
29 | 30 | import org.bson.Document;
|
30 | 31 | import org.bson.conversions.Bson;
|
31 |
| - |
32 | 32 | import org.springframework.beans.BeansException;
|
33 | 33 | import org.springframework.context.ApplicationContext;
|
34 | 34 | import org.springframework.context.ApplicationContextAware;
|
@@ -188,6 +188,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
188 | 188 |
|
189 | 189 | private SessionSynchronization sessionSynchronization = SessionSynchronization.ON_ACTUAL_TRANSACTION;
|
190 | 190 |
|
| 191 | + private CountExecution countExecution = (collectionName, filter, options) -> { |
| 192 | + return execute(collectionName, |
| 193 | + collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options)); |
| 194 | + }; |
| 195 | + |
191 | 196 | /**
|
192 | 197 | * Constructor used for a basic template configuration.
|
193 | 198 | *
|
@@ -345,6 +350,51 @@ public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
|
345 | 350 | this.entityCallbacks = entityCallbacks;
|
346 | 351 | }
|
347 | 352 |
|
| 353 | + /** |
| 354 | + * En-/Disable usage of estimated count. |
| 355 | + * |
| 356 | + * @param enabled if {@literal true} {@link MongoCollection#estimatedDocumentCount()} ()} will we used for unpaged, |
| 357 | + * empty {@link Query queries}. |
| 358 | + * @since 3.4 |
| 359 | + */ |
| 360 | + public void useEstimatedCount(boolean enabled) { |
| 361 | + useEstimatedCount(enabled, this::countCanBeEstimated); |
| 362 | + } |
| 363 | + |
| 364 | + /** |
| 365 | + * En-/Disable usage of estimated count based on the given {@link BiPredicate estimationFilter}. |
| 366 | + * |
| 367 | + * @param enabled if {@literal true} {@link MongoCollection#estimatedDocumentCount()} will we used for {@link Document |
| 368 | + * filter queries} that pass the given {@link BiPredicate estimationFilter}. |
| 369 | + * @param estimationFilter the {@link BiPredicate filter}. |
| 370 | + * @since 3.4 |
| 371 | + */ |
| 372 | + public void useEstimatedCount(boolean enabled, BiPredicate<Document, CountOptions> estimationFilter) { |
| 373 | + |
| 374 | + if (enabled) { |
| 375 | + |
| 376 | + this.countExecution = (collectionName, filter, options) -> { |
| 377 | + |
| 378 | + if (!estimationFilter.test(filter, options)) { |
| 379 | + return execute(collectionName, |
| 380 | + collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options)); |
| 381 | + } |
| 382 | + |
| 383 | + EstimatedDocumentCountOptions estimatedDocumentCountOptions = new EstimatedDocumentCountOptions(); |
| 384 | + if (options.getMaxTime(TimeUnit.MILLISECONDS) > 0) { |
| 385 | + estimatedDocumentCountOptions.maxTime(options.getMaxTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); |
| 386 | + } |
| 387 | + |
| 388 | + return doEstimatedCount(collectionName, estimatedDocumentCountOptions); |
| 389 | + }; |
| 390 | + } else { |
| 391 | + this.countExecution = (collectionName, filter, options) -> { |
| 392 | + return execute(collectionName, |
| 393 | + collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options)); |
| 394 | + }; |
| 395 | + } |
| 396 | + } |
| 397 | + |
348 | 398 | /**
|
349 | 399 | * Inspects the given {@link ApplicationContext} for {@link MongoPersistentEntityIndexCreator} and those in turn if
|
350 | 400 | * they were registered for the current {@link MappingContext}. If no creator for the current {@link MappingContext}
|
@@ -1131,8 +1181,22 @@ protected long doCount(String collectionName, Document filter, CountOptions opti
|
1131 | 1181 | .debug(String.format("Executing count: %s in collection: %s", serializeToJsonSafely(filter), collectionName));
|
1132 | 1182 | }
|
1133 | 1183 |
|
1134 |
| - return execute(collectionName, |
1135 |
| - collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options)); |
| 1184 | + return countExecution.countDocuments(collectionName, filter, options); |
| 1185 | + } |
| 1186 | + |
| 1187 | + protected boolean countCanBeEstimated(Document filter, CountOptions options) { |
| 1188 | + |
| 1189 | + return |
| 1190 | + // only empty filter for estimatedCount |
| 1191 | + filter.isEmpty() && |
| 1192 | + // no skip, no limit,... |
| 1193 | + isEmptyOptions(options) && |
| 1194 | + // transaction active? |
| 1195 | + !MongoDatabaseUtils.isTransactionActive(getMongoDatabaseFactory()); |
| 1196 | + } |
| 1197 | + |
| 1198 | + private boolean isEmptyOptions(CountOptions options) { |
| 1199 | + return options.getLimit() <= 0 && options.getSkip() <= 0; |
1136 | 1200 | }
|
1137 | 1201 |
|
1138 | 1202 | /*
|
@@ -3569,5 +3633,15 @@ public MongoDatabase getDb() {
|
3569 | 3633 | // native MongoDB objects that offer methods with ClientSession must not be proxied.
|
3570 | 3634 | return delegate.getDb();
|
3571 | 3635 | }
|
| 3636 | + |
| 3637 | + @Override |
| 3638 | + protected boolean countCanBeEstimated(Document filter, CountOptions options) { |
| 3639 | + return false; |
| 3640 | + } |
| 3641 | + } |
| 3642 | + |
| 3643 | + @FunctionalInterface |
| 3644 | + interface CountExecution { |
| 3645 | + long countDocuments(String collection, Document filter, CountOptions options); |
3572 | 3646 | }
|
3573 | 3647 | }
|
0 commit comments