17
17
18
18
namespace MongoDB \Operation ;
19
19
20
+ use MongoDB \Driver \Exception \CommandException ;
20
21
use MongoDB \Driver \Exception \RuntimeException as DriverRuntimeException ;
22
+ use MongoDB \Driver \ReadConcern ;
23
+ use MongoDB \Driver \ReadPreference ;
21
24
use MongoDB \Driver \Server ;
25
+ use MongoDB \Driver \Session ;
22
26
use MongoDB \Exception \InvalidArgumentException ;
23
27
use MongoDB \Exception \UnexpectedValueException ;
24
28
use MongoDB \Exception \UnsupportedException ;
25
29
use function array_intersect_key ;
30
+ use function is_integer ;
31
+ use function MongoDB \server_supports_feature ;
26
32
27
33
/**
28
34
* Operation for obtaining an estimated count of documents in a collection
@@ -42,11 +48,15 @@ class EstimatedDocumentCount implements Executable, Explainable
42
48
/** @var array */
43
49
private $ options ;
44
50
45
- /** @var Count */
46
- private $ count ;
51
+ /** @var int */
52
+ private static $ errorCodeCollectionNotFound = 26 ;
53
+
54
+ /** @var int */
55
+ private static $ wireVersionForCollStats = 12 ;
47
56
48
57
/**
49
- * Constructs a count command.
58
+ * Constructs a command to get the estimated number of documents in a
59
+ * collection.
50
60
*
51
61
* Supported options:
52
62
*
@@ -73,9 +83,24 @@ public function __construct($databaseName, $collectionName, array $options = [])
73
83
{
74
84
$ this ->databaseName = (string ) $ databaseName ;
75
85
$ this ->collectionName = (string ) $ collectionName ;
76
- $ this ->options = array_intersect_key ($ options , ['maxTimeMS ' => 1 , 'readConcern ' => 1 , 'readPreference ' => 1 , 'session ' => 1 ]);
77
86
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 ]);
79
104
}
80
105
81
106
/**
@@ -90,18 +115,54 @@ public function __construct($databaseName, $collectionName, array $options = [])
90
115
*/
91
116
public function execute (Server $ server )
92
117
{
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 );
94
137
}
95
138
96
139
public function getCommandDocument (Server $ server )
97
140
{
98
- return $ this ->count ->getCommandDocument ($ server );
141
+ return $ this ->createCommand ( $ server ) ->getCommandDocument ($ server );
99
142
}
100
143
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
105
166
{
106
167
return new Count ($ this ->databaseName , $ this ->collectionName , [], $ this ->options );
107
168
}
0 commit comments