1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the PHPMongo package. |
5
|
|
|
* |
6
|
|
|
* (c) Dmytro Sokil <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Sokil\Mongo; |
13
|
|
|
|
14
|
|
|
use Sokil\Mongo\Pipeline\GroupStage; |
15
|
|
|
use Sokil\Mongo\Structure\Arrayable; |
16
|
|
|
|
17
|
|
|
class Pipeline implements Arrayable, \JsonSerializable |
18
|
|
|
{ |
19
|
|
|
|
20
|
|
|
private $stages = array(); |
21
|
|
|
|
22
|
|
|
private $options = array(); |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var \Sokil\Mongo\Collection |
26
|
|
|
*/ |
27
|
|
|
private $collection; |
28
|
|
|
|
29
|
|
|
public function __construct(Collection $collection) |
30
|
|
|
{ |
31
|
|
|
$this->collection = $collection; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
private function addStage($operator, $stage) |
35
|
|
|
{ |
36
|
|
|
$lastIndex = count($this->stages) - 1; |
37
|
|
|
|
38
|
|
|
if (!$this->stages || |
|
|
|
|
39
|
|
|
!isset($this->stages[$lastIndex][$operator]) || |
40
|
|
|
in_array($operator, array('$group', '$unwind')) |
41
|
|
|
) { |
42
|
|
|
$this->stages[] = array($operator => $stage); |
43
|
|
|
} else { |
44
|
|
|
$this->stages[$lastIndex][$operator] = array_merge($this->stages[$lastIndex][$operator], $stage); |
45
|
|
|
} |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Filter documents by expression |
50
|
|
|
* |
51
|
|
|
* @param array|\Sokil\Mongo\Expression $expression |
52
|
|
|
* @return \Sokil\Mongo\Pipeline |
53
|
|
|
* @throws \Sokil\Mongo\Exception |
54
|
|
|
*/ |
55
|
|
View Code Duplication |
public function match($expression) |
|
|
|
|
56
|
|
|
{ |
57
|
|
|
if (is_callable($expression)) { |
58
|
|
|
$expressionConfigurator = $expression; |
59
|
|
|
$expression = new Expression(); |
60
|
|
|
call_user_func($expressionConfigurator, $expression); |
61
|
|
|
$expression = $expression->toArray(); |
62
|
|
|
} elseif (!is_array($expression)) { |
63
|
|
|
throw new Exception('Must be array or instance of Expression'); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
$this->addStage('$match', $expression); |
67
|
|
|
return $this; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Passes along the documents with only the specified fields to the next |
72
|
|
|
* stage in the pipeline. The specified fields can be existing fields |
73
|
|
|
* from the input documents or newly computed fields. |
74
|
|
|
* |
75
|
|
|
* @param array $pipeline |
76
|
|
|
* @return \Sokil\Mongo\Pipeline |
77
|
|
|
*/ |
78
|
|
|
public function project(array $pipeline) |
79
|
|
|
{ |
80
|
|
|
$this->addStage('$project', $pipeline); |
81
|
|
|
return $this; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Groups documents by some specified expression and outputs to the next |
86
|
|
|
* stage a document for each distinct grouping. The output documents |
87
|
|
|
* contain an _id field which contains the distinct group by key. The |
88
|
|
|
* output documents can also contain computed fields that hold the values |
89
|
|
|
* of some accumulator expression grouped by the $group‘s _id field. $group |
90
|
|
|
* does not order its output documents. |
91
|
|
|
* |
92
|
|
|
* @link http://docs.mongodb.org/manual/reference/operator/aggregation/group/ |
93
|
|
|
* |
94
|
|
|
* @param array|callable $stage |
95
|
|
|
* @return \Sokil\Mongo\Pipeline |
96
|
|
|
* @throws \Sokil\Mongo\Exception |
97
|
|
|
*/ |
98
|
|
|
public function group($stage) |
99
|
|
|
{ |
100
|
|
|
if (is_callable($stage)) { |
101
|
|
|
$configurator = $stage; |
102
|
|
|
$stage = new GroupStage(); |
103
|
|
|
call_user_func($configurator, $stage); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
if($stage instanceof GroupStage) { |
107
|
|
|
$stage = $stage->toArray(); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
if(!is_array($stage)) { |
111
|
|
|
throw new Exception('Group stage must be array or instance of Sokil\Mongo\Pipeline\GroupStage or callabe'); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
if (!isset($stage['_id'])) { |
115
|
|
|
throw new Exception('Group field in _id key must be specified'); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
$this->addStage('$group', $stage); |
119
|
|
|
return $this; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Deconstructs an array field from the input documents to output a document for each element. |
124
|
|
|
* Each output document is the input document with the value of the array field replaced by the element. |
125
|
|
|
* @link http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/ |
126
|
|
|
* |
127
|
|
|
* @param string $path path to field |
128
|
|
|
* @return \Sokil\Mongo\Pipeline |
129
|
|
|
*/ |
130
|
|
|
public function unwind($path) |
131
|
|
|
{ |
132
|
|
|
$this->addStage('$unwind', $path); |
133
|
|
|
return $this; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
public function sort(array $sortFields) |
137
|
|
|
{ |
138
|
|
|
$this->addStage('$sort', $sortFields); |
139
|
|
|
return $this; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
public function limit($limit) |
143
|
|
|
{ |
144
|
|
|
$this->addStage('$limit', (int) $limit); |
145
|
|
|
return $this; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
public function skip($skip) |
149
|
|
|
{ |
150
|
|
|
$this->addStage('$skip', (int) $skip); |
151
|
|
|
return $this; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
public function aggregate(array $options = array()) { |
155
|
|
|
return $this->collection->aggregate($this, $options, false); |
|
|
|
|
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
public function aggregateCursor(array $options = array()) |
159
|
|
|
{ |
160
|
|
|
return $this->collection->aggregate($this, $options, true); |
|
|
|
|
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function toArray() |
164
|
|
|
{ |
165
|
|
|
return $this->stages; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
public function jsonSerialize() |
169
|
|
|
{ |
170
|
|
|
return $this->stages; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function __toString() |
174
|
|
|
{ |
175
|
|
|
return json_encode($this->stages); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
public function explain($allow = true) |
179
|
|
|
{ |
180
|
|
|
$this->options['explain'] = (bool) $allow; |
181
|
|
|
return $this; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
public function allowDiskUse($allow = true) |
185
|
|
|
{ |
186
|
|
|
$this->options['allowDiskUse'] = (bool) $allow; |
187
|
|
|
return $this; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
public function setBatchSize($batchSize) |
191
|
|
|
{ |
192
|
|
|
$this->options['cursor']['batchSize'] = (int) $batchSize; |
193
|
|
|
return $this; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
public function getOptions() |
197
|
|
|
{ |
198
|
|
|
return $this->options; |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.