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
import java .util .stream .Stream ;
27
28
@@ -185,6 +186,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
185
186
186
187
private SessionSynchronization sessionSynchronization = SessionSynchronization .ON_ACTUAL_TRANSACTION ;
187
188
189
+ private CountExecution countExecution = this ::doExactCount ;
190
+
188
191
/**
189
192
* Constructor used for a basic template configuration.
190
193
*
@@ -338,6 +341,47 @@ public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
338
341
this .entityCallbacks = entityCallbacks ;
339
342
}
340
343
344
+ /**
345
+ * En-/Disable usage of estimated count.
346
+ *
347
+ * @param enabled if {@literal true} {@link MongoCollection#estimatedDocumentCount()} ()} will we used for unpaged,
348
+ * empty {@link Query queries}.
349
+ * @since 3.4
350
+ */
351
+ public void useEstimatedCount (boolean enabled ) {
352
+ useEstimatedCount (enabled , this ::countCanBeEstimated );
353
+ }
354
+
355
+ /**
356
+ * En-/Disable usage of estimated count based on the given {@link BiPredicate estimationFilter}.
357
+ *
358
+ * @param enabled if {@literal true} {@link MongoCollection#estimatedDocumentCount()} will we used for {@link Document
359
+ * filter queries} that pass the given {@link BiPredicate estimationFilter}.
360
+ * @param estimationFilter the {@link BiPredicate filter}.
361
+ * @since 3.4
362
+ */
363
+ private void useEstimatedCount (boolean enabled , BiPredicate <Document , CountOptions > estimationFilter ) {
364
+
365
+ if (enabled ) {
366
+
367
+ this .countExecution = (collectionName , filter , options ) -> {
368
+
369
+ if (!estimationFilter .test (filter , options )) {
370
+ return doExactCount (collectionName , filter , options );
371
+ }
372
+
373
+ EstimatedDocumentCountOptions estimatedDocumentCountOptions = new EstimatedDocumentCountOptions ();
374
+ if (options .getMaxTime (TimeUnit .MILLISECONDS ) > 0 ) {
375
+ estimatedDocumentCountOptions .maxTime (options .getMaxTime (TimeUnit .MILLISECONDS ), TimeUnit .MILLISECONDS );
376
+ }
377
+
378
+ return doEstimatedCount (collectionName , estimatedDocumentCountOptions );
379
+ };
380
+ } else {
381
+ this .countExecution = this ::doExactCount ;
382
+ }
383
+ }
384
+
341
385
/**
342
386
* Inspects the given {@link ApplicationContext} for {@link MongoPersistentEntityIndexCreator} and those in turn if
343
387
* they were registered for the current {@link MappingContext}. If no creator for the current {@link MappingContext}
@@ -969,6 +1013,21 @@ public long count(Query query, String collectionName) {
969
1013
return count (query , null , collectionName );
970
1014
}
971
1015
1016
+ @ Override
1017
+ public long exactCount (Query query , @ Nullable Class <?> entityClass , String collectionName ) {
1018
+
1019
+ CountContext countContext = queryOperations .countQueryContext (query );
1020
+
1021
+ CountOptions options = countContext .getCountOptions (entityClass );
1022
+ Document mappedQuery = countContext .getMappedQuery (entityClass , mappingContext ::getPersistentEntity );
1023
+
1024
+ return doExactCount (collectionName , mappedQuery , options );
1025
+ }
1026
+
1027
+ /*
1028
+ * (non-Javadoc)
1029
+ * @see org.springframework.data.mongodb.core.MongoOperations#count(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
1030
+ */
972
1031
public long count (Query query , @ Nullable Class <?> entityClass , String collectionName ) {
973
1032
974
1033
Assert .notNull (query , "Query must not be null!" );
@@ -990,10 +1049,33 @@ protected long doCount(String collectionName, Document filter, CountOptions opti
990
1049
.debug (String .format ("Executing count: %s in collection: %s" , serializeToJsonSafely (filter ), collectionName ));
991
1050
}
992
1051
1052
+ return countExecution .countDocuments (collectionName , filter , options );
1053
+ }
1054
+
1055
+ protected long doExactCount (String collectionName , Document filter , CountOptions options ) {
993
1056
return execute (collectionName ,
994
1057
collection -> collection .countDocuments (CountQuery .of (filter ).toQueryDocument (), options ));
995
1058
}
996
1059
1060
+ protected boolean countCanBeEstimated (Document filter , CountOptions options ) {
1061
+
1062
+ return
1063
+ // only empty filter for estimatedCount
1064
+ filter .isEmpty () &&
1065
+ // no skip, no limit,...
1066
+ isEmptyOptions (options ) &&
1067
+ // transaction active?
1068
+ !MongoDatabaseUtils .isTransactionActive (getMongoDatabaseFactory ());
1069
+ }
1070
+
1071
+ private boolean isEmptyOptions (CountOptions options ) {
1072
+ return options .getLimit () <= 0 && options .getSkip () <= 0 ;
1073
+ }
1074
+
1075
+ /*
1076
+ * (non-Javadoc)
1077
+ * @see org.springframework.data.mongodb.core.MongoOperations#estimatedCount(java.lang.String)
1078
+ */
997
1079
@ Override
998
1080
public long estimatedCount (String collectionName ) {
999
1081
return doEstimatedCount (collectionName , new EstimatedDocumentCountOptions ());
@@ -3225,5 +3307,15 @@ public MongoDatabase getDb() {
3225
3307
// native MongoDB objects that offer methods with ClientSession must not be proxied.
3226
3308
return delegate .getDb ();
3227
3309
}
3310
+
3311
+ @ Override
3312
+ protected boolean countCanBeEstimated (Document filter , CountOptions options ) {
3313
+ return false ;
3314
+ }
3315
+ }
3316
+
3317
+ @ FunctionalInterface
3318
+ interface CountExecution {
3319
+ long countDocuments (String collection , Document filter , CountOptions options );
3228
3320
}
3229
3321
}
0 commit comments