1717
1818namespace MongoDB \Operation ;
1919
20+ use MongoDB \Driver \Exception \CommandException ;
2021use MongoDB \Driver \Exception \RuntimeException as DriverRuntimeException ;
22+ use MongoDB \Driver \ReadConcern ;
23+ use MongoDB \Driver \ReadPreference ;
2124use MongoDB \Driver \Server ;
25+ use MongoDB \Driver \Session ;
2226use MongoDB \Exception \InvalidArgumentException ;
2327use MongoDB \Exception \UnexpectedValueException ;
2428use MongoDB \Exception \UnsupportedException ;
2529use function array_intersect_key ;
30+ use function is_integer ;
31+ use function MongoDB \server_supports_feature ;
2632
2733/**
2834 * Operation for obtaining an estimated count of documents in a collection
@@ -42,11 +48,15 @@ class EstimatedDocumentCount implements Executable, Explainable
4248 /** @var array */
4349 private $ options ;
4450
45- /** @var Count */
46- private $ count ;
51+ /** @var int */
52+ private static $ errorCodeCollectionNotFound = 26 ;
53+
54+ /** @var int */
55+ private static $ wireVersionForCollStats = 12 ;
4756
4857 /**
49- * Constructs a count command.
58+ * Constructs a command to get the estimated number of documents in a
59+ * collection.
5060 *
5161 * Supported options:
5262 *
@@ -73,9 +83,24 @@ public function __construct($databaseName, $collectionName, array $options = [])
7383 {
7484 $ this ->databaseName = (string ) $ databaseName ;
7585 $ this ->collectionName = (string ) $ collectionName ;
76- $ this ->options = array_intersect_key ($ options , ['maxTimeMS ' => 1 , 'readConcern ' => 1 , 'readPreference ' => 1 , 'session ' => 1 ]);
7786
78- $ this ->count = $ this ->createCount ();
87+ if (isset ($ options ['maxTimeMS ' ]) && ! is_integer ($ options ['maxTimeMS ' ])) {
88+ throw InvalidArgumentException::invalidType ('"maxTimeMS" option ' , $ options ['maxTimeMS ' ], 'integer ' );
89+ }
90+
91+ if (isset ($ options ['readConcern ' ]) && ! $ options ['readConcern ' ] instanceof ReadConcern) {
92+ throw InvalidArgumentException::invalidType ('"readConcern" option ' , $ options ['readConcern ' ], ReadConcern::class);
93+ }
94+
95+ if (isset ($ options ['readPreference ' ]) && ! $ options ['readPreference ' ] instanceof ReadPreference) {
96+ throw InvalidArgumentException::invalidType ('"readPreference" option ' , $ options ['readPreference ' ], ReadPreference::class);
97+ }
98+
99+ if (isset ($ options ['session ' ]) && ! $ options ['session ' ] instanceof Session) {
100+ throw InvalidArgumentException::invalidType ('"session" option ' , $ options ['session ' ], Session::class);
101+ }
102+
103+ $ this ->options = array_intersect_key ($ options , ['maxTimeMS ' => 1 , 'readConcern ' => 1 , 'readPreference ' => 1 , 'session ' => 1 ]);
79104 }
80105
81106 /**
@@ -90,18 +115,54 @@ public function __construct($databaseName, $collectionName, array $options = [])
90115 */
91116 public function execute (Server $ server )
92117 {
93- return $ this ->count ->execute ($ server );
118+ $ command = $ this ->createCommand ($ server );
119+
120+ if ($ command instanceof Aggregate) {
121+ try {
122+ $ cursor = $ command ->execute ($ server );
123+ } catch (CommandException $ e ) {
124+ if ($ e ->getCode () == self ::$ errorCodeCollectionNotFound ) {
125+ return 0 ;
126+ }
127+
128+ throw $ e ;
129+ }
130+
131+ $ cursor ->rewind ();
132+
133+ return $ cursor ->current ()->n ;
134+ }
135+
136+ return $ command ->execute ($ server );
94137 }
95138
96139 public function getCommandDocument (Server $ server )
97140 {
98- return $ this ->count ->getCommandDocument ($ server );
141+ return $ this ->createCommand ( $ server ) ->getCommandDocument ($ server );
99142 }
100143
101- /**
102- * @return Count
103- */
104- private function createCount ()
144+ private function createAggregate () : Aggregate
145+ {
146+ return new Aggregate (
147+ $ this ->databaseName ,
148+ $ this ->collectionName ,
149+ [
150+ ['$collStats ' => ['count ' => (object ) []]],
151+ ['$group ' => ['_id ' => 1 , 'n ' => ['$sum ' => '$count ' ]]],
152+ ],
153+ $ this ->options
154+ );
155+ }
156+
157+ /** @return Aggregate|Count */
158+ private function createCommand (Server $ server )
159+ {
160+ return server_supports_feature ($ server , self ::$ wireVersionForCollStats )
161+ ? $ this ->createAggregate ()
162+ : $ this ->createCount ();
163+ }
164+
165+ private function createCount () : Count
105166 {
106167 return new Count ($ this ->databaseName , $ this ->collectionName , [], $ this ->options );
107168 }
0 commit comments