Skip to content

Commit 8fea8b4

Browse files
committed
Extract Collection::count() to an operation class
1 parent 252c54e commit 8fea8b4

File tree

2 files changed

+150
-54
lines changed

2 files changed

+150
-54
lines changed

src/Collection.php

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use MongoDB\Model\IndexInput;
1818
use MongoDB\Operation\Aggregate;
1919
use MongoDB\Operation\CreateIndexes;
20+
use MongoDB\Operation\Count;
2021
use MongoDB\Operation\Distinct;
2122
use Traversable;
2223

@@ -236,28 +237,19 @@ public function bulkWrite(array $ops, array $options = array())
236237
}
237238

238239
/**
239-
* Counts all documents matching $filter
240-
* If no $filter provided, returns the numbers of documents in the collection
240+
* Gets the number of documents matching the filter.
241241
*
242-
* @see http://docs.mongodb.org/manual/reference/command/count/
243-
* @see Collection::getCountOptions() for supported $options
244-
*
245-
* @param array $filter The find query to execute
246-
* @param array $options Additional options
242+
* @see Count::__construct() for supported options
243+
* @param array $filter Query by which to filter documents
244+
* @param array $options Command options
247245
* @return integer
248246
*/
249247
public function count(array $filter = array(), array $options = array())
250248
{
251-
$cmd = array(
252-
"count" => $this->collname,
253-
"query" => (object) $filter,
254-
) + $options;
249+
$operation = new Count($this->dbname, $this->collname, $filter, $options);
250+
$server = $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY));
255251

256-
$doc = current($this->_runCommand($this->dbname, $cmd)->toArray());
257-
if ($doc["ok"]) {
258-
return (integer) $doc["n"];
259-
}
260-
throw $this->_generateCommandException($doc);
252+
return $operation->execute($server);
261253
}
262254

263255
/**
@@ -581,44 +573,6 @@ public function getCollectionName()
581573
return $this->collname;
582574
}
583575

584-
/**
585-
* Retrieves all count options with their default values.
586-
*
587-
* @return array of Collection::count() options
588-
*/
589-
public function getCountOptions()
590-
{
591-
return array(
592-
/**
593-
* The index to use.
594-
*
595-
* @see http://docs.mongodb.org/manual/reference/command/count/
596-
*/
597-
"hint" => "", // string or document
598-
599-
/**
600-
* The maximum number of documents to count.
601-
*
602-
* @see http://docs.mongodb.org/manual/reference/command/count/
603-
*/
604-
"limit" => 0,
605-
606-
/**
607-
* The maximum amount of time to allow the query to run.
608-
*
609-
* @see http://docs.mongodb.org/manual/reference/command/count/
610-
*/
611-
"maxTimeMS" => 0,
612-
613-
/**
614-
* The number of documents to skip before returning the documents.
615-
*
616-
* @see http://docs.mongodb.org/manual/reference/command/count/
617-
*/
618-
"skip" => 0,
619-
);
620-
}
621-
622576
/**
623577
* Return the database name.
624578
*

src/Operation/Count.php

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
3+
namespace MongoDB\Operation;
4+
5+
use MongoDB\Driver\Command;
6+
use MongoDB\Driver\Server;
7+
use MongoDB\Exception\InvalidArgumentException;
8+
use MongoDB\Exception\InvalidArgumentTypeException;
9+
use MongoDB\Exception\RuntimeException;
10+
use MongoDB\Exception\UnexpectedValueException;
11+
12+
/**
13+
* Operation for the count command.
14+
*
15+
* @api
16+
* @see MongoDB\Collection::count()
17+
* @see http://docs.mongodb.org/manual/reference/command/count/
18+
*/
19+
class Count implements Executable
20+
{
21+
private $databaseName;
22+
private $collectionName;
23+
private $filter;
24+
private $options;
25+
26+
/**
27+
* Constructs a count command.
28+
*
29+
* Supported options:
30+
*
31+
* * hint (string|document): The index to use. If a document, it will be
32+
* interpretted as an index specification and a name will be generated.
33+
*
34+
* * limit (integer): The maximum number of documents to count.
35+
*
36+
* * maxTimeMS (integer): The maximum amount of time to allow the query to
37+
* run.
38+
*
39+
* * skip (integer): The number of documents to skip before returning the
40+
* documents.
41+
*
42+
* @param string $databaseName Database name
43+
* @param string $collectionName Collection name
44+
* @param array $filter Query by which to filter documents
45+
* @param array $options Command options
46+
* @throws InvalidArgumentException
47+
*/
48+
public function __construct($databaseName, $collectionName, array $filter = array(), array $options = array())
49+
{
50+
if (isset($options['hint'])) {
51+
if (is_array($options['hint']) || is_object($options['hint'])) {
52+
$options['hint'] = $this->generateIndexName($options['hint']);
53+
}
54+
55+
if ( ! is_string($options['hint'])) {
56+
throw new InvalidArgumentTypeException('hint option', $options['hint'], 'string or array or object');
57+
}
58+
}
59+
60+
if (isset($options['limit']) && ! is_integer($options['limit'])) {
61+
throw new InvalidArgumentTypeException('limit option', $options['limit'], 'integer');
62+
}
63+
64+
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
65+
throw new InvalidArgumentTypeException('maxTimeMS option', $options['maxTimeMS'], 'integer');
66+
}
67+
68+
if (isset($options['skip']) && ! is_integer($options['skip'])) {
69+
throw new InvalidArgumentTypeException('skip option', $options['skip'], 'integer');
70+
}
71+
72+
$this->databaseName = (string) $databaseName;
73+
$this->collectionName = (string) $collectionName;
74+
$this->filter = $filter;
75+
$this->options = $options;
76+
}
77+
78+
/**
79+
* Execute the operation.
80+
*
81+
* @see Executable::execute()
82+
* @param Server $server
83+
* @return integer
84+
*/
85+
public function execute(Server $server)
86+
{
87+
$cursor = $server->executeCommand($this->databaseName, $this->createCommand());
88+
$result = current($cursor->toArray());
89+
90+
if (empty($result['ok'])) {
91+
throw new RuntimeException(isset($result['errmsg']) ? $result['errmsg'] : 'Unknown error');
92+
}
93+
94+
if ( ! isset($result['n']) || ! is_integer($result['n'])) {
95+
throw new UnexpectedValueException('count command did not return an "n" integer');
96+
}
97+
98+
return $result['n'];
99+
}
100+
101+
/**
102+
* Create the count command.
103+
*
104+
* @return Command
105+
*/
106+
private function createCommand()
107+
{
108+
$cmd = array(
109+
'count' => $this->collectionName,
110+
);
111+
112+
if ( ! empty($this->filter)) {
113+
$cmd['query'] = (object) $this->filter;
114+
}
115+
116+
foreach (array('hint', 'limit', 'maxTimeMS', 'skip') as $option) {
117+
if (isset($this->options[$option])) {
118+
$cmd[$option] = $this->options[$option];
119+
}
120+
}
121+
122+
return new Command($cmd);
123+
}
124+
125+
/**
126+
* Generates an index name from its key specification.
127+
*
128+
* @param array|object $key Document containing fields mapped to values,
129+
* which denote order or an index type
130+
* @return string
131+
*/
132+
private function generateIndexName($key)
133+
{
134+
$name = '';
135+
136+
foreach ($key as $field => $type) {
137+
$name .= ($name != '' ? '_' : '') . $field . '_' . $type;
138+
}
139+
140+
return $name;
141+
}
142+
}

0 commit comments

Comments
 (0)