Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
68.75% |
11 / 16 |
CRAP | |
81.16% |
56 / 69 |
MongoPipeline | |
0.00% |
0 / 1 |
|
68.75% |
11 / 16 |
41.73 | |
81.16% |
56 / 69 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
sort | |
0.00% |
0 / 1 |
10.12 | |
89.47% |
17 / 19 |
|||
project | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
unwind | |
100.00% |
1 / 1 |
2 | |
100.00% |
7 / 7 |
|||
match | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
4 / 6 |
|||
out | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
merge | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
group | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
replaceRoot | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
toArray | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
aggregate | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
raw | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
clear | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
cleanFormat | |
0.00% |
0 / 1 |
8.83 | |
57.14% |
4 / 7 |
|||
decodeJson | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
startsWith | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
1 | <?php |
2 | |
3 | namespace Qmp\Laravel\MongoLow; |
4 | |
5 | use Illuminate\Support\Collection; |
6 | use Qmp\Laravel\MongoLow\Exceptions\MongoPipelineJsonDecodeException; |
7 | |
8 | class MongoPipeline |
9 | { |
10 | /** |
11 | * Array that holds aggregation pipeline stages informations |
12 | * |
13 | * @var array |
14 | */ |
15 | private $pipeline = []; |
16 | |
17 | /** |
18 | * Model associated with this pipeline |
19 | * |
20 | * @var Qmp\Laravel\MongoLow\Models\AbstractQmpMoloquent |
21 | */ |
22 | private $model; |
23 | |
24 | /** |
25 | * Class constructor |
26 | * |
27 | * @param \Moloquent $model |
28 | */ |
29 | public function __construct(\Moloquent $model = null) |
30 | { |
31 | $this->model = $model; |
32 | } |
33 | |
34 | /** |
35 | * Sort Stage |
36 | * |
37 | * @param string $field |
38 | * @param string $order | "asc" or "desc" |
39 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
40 | * |
41 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sort/ |
42 | * |
43 | * Future update : Sort on multiples fileds |
44 | */ |
45 | public function sort(...$params): MongoPipeline |
46 | { |
47 | $sort = collect($params)->chunk(2)->map(function ($chunk) { |
48 | $maped = $chunk->map(function ($order, $key) { |
49 | if ($key === 1) { |
50 | if (is_int($order) && ($order === 1 || $order === -1)) { |
51 | return $order; |
52 | } else if (is_string($order) && ($order === 'asc' || $order === 'desc')) { |
53 | return $order === 'asc' ? 1 : -1; |
54 | } else { |
55 | return 1; |
56 | } |
57 | } |
58 | return $order; |
59 | }); |
60 | |
61 | if ($maped->count() !== 2) { |
62 | $maped->push(1); |
63 | } |
64 | |
65 | return $maped->values(); |
66 | })->mapWithKeys(function ($item) { |
67 | return [$item[0] => $item[1]]; |
68 | }); |
69 | |
70 | $this->pipeline[] = [ |
71 | '$sort' => $sort->toArray() |
72 | ]; |
73 | return $this; |
74 | } |
75 | |
76 | /** |
77 | * Project stage |
78 | * |
79 | * @param mixed $string |
80 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
81 | * |
82 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/project/ |
83 | */ |
84 | public function project($projection): MongoPipeline |
85 | { |
86 | $this->pipeline[] = [ |
87 | '$project' => $this->cleanFormat($projection) |
88 | ]; |
89 | return $this; |
90 | } |
91 | |
92 | /** |
93 | * Unwind stage |
94 | * |
95 | * @param string $path |
96 | * @param boolean $preserveEmpty |
97 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
98 | * |
99 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/ |
100 | */ |
101 | public function unwind(string $path, bool $preserveEmpty = false): MongoPipeline |
102 | { |
103 | $path = '$' . $path; |
104 | $params = $preserveEmpty ? [ |
105 | 'path' => $path, |
106 | 'preserveNullAndEmptyArrays' => true |
107 | ] : $path; |
108 | |
109 | $this->pipeline[] = [ |
110 | '$unwind' => $params |
111 | ]; |
112 | return $this; |
113 | } |
114 | |
115 | /** |
116 | * Match stage |
117 | * |
118 | * @param string $haystack |
119 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
120 | * |
121 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/match/ |
122 | */ |
123 | public function match($haystack, $needle = null): MongoPipeline |
124 | { |
125 | if ($this->startsWith(trim($haystack), '{')) { |
126 | $this->pipeline[] = [ |
127 | '$match' => $this->decodeJson($haystack, true) |
128 | ]; |
129 | } else { |
130 | $this->pipeline[] = [ |
131 | '$match' => [$haystack => $needle] |
132 | ]; |
133 | } |
134 | |
135 | return $this; |
136 | } |
137 | |
138 | /** |
139 | * out stage |
140 | * |
141 | * @param mixed $haystack |
142 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
143 | * |
144 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/match/ |
145 | */ |
146 | public function out($out): MongoPipeline |
147 | { |
148 | $this->pipeline[] = [ |
149 | '$out' => $this->cleanFormat($out) |
150 | ]; |
151 | |
152 | return $this; |
153 | } |
154 | |
155 | /** |
156 | * merge stage |
157 | * |
158 | * @param mixed $haystack |
159 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
160 | * |
161 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/match/ |
162 | */ |
163 | public function merge($merge): MongoPipeline |
164 | { |
165 | $this->pipeline[] = [ |
166 | '$merge' => $this->cleanFormat($merge) |
167 | ]; |
168 | |
169 | return $this; |
170 | } |
171 | |
172 | /** |
173 | * Group stage |
174 | * |
175 | * @param mixed $on |
176 | * @param mixed $values |
177 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
178 | * |
179 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/group/ |
180 | */ |
181 | public function group($on, $values = []): MongoPipeline |
182 | { |
183 | $this->pipeline[] = [ |
184 | '$group' => array_merge(['_id' => $this->cleanFormat($on)], $this->cleanFormat($values)) |
185 | ]; |
186 | return $this; |
187 | } |
188 | |
189 | /** |
190 | * ReplaceRoot stage |
191 | * |
192 | * @param mixed $values |
193 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
194 | * |
195 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/ |
196 | */ |
197 | public function replaceRoot($newRoot): MongoPipeline |
198 | { |
199 | $this->pipeline[] = [ |
200 | '$replaceRoot' => [ |
201 | 'newRoot' => $this->cleanFormat($newRoot) |
202 | ] |
203 | ]; |
204 | return $this; |
205 | } |
206 | |
207 | /** |
208 | * Return pipeline array |
209 | * |
210 | * @return array |
211 | */ |
212 | public function toArray(): array |
213 | { |
214 | return $this->pipeline; |
215 | } |
216 | |
217 | /** |
218 | * Lunch aggregation on model |
219 | * |
220 | * @return Jenssegers\Mongodb\Collection |
221 | * |
222 | */ |
223 | |
224 | public function aggregate($options = []): \Illuminate\Database\Eloquent\Collection |
225 | { |
226 | return MongoAggregator::raw($this->model, $this, $options); |
227 | } |
228 | |
229 | /** |
230 | * Call a raw json pipeline |
231 | * |
232 | * @param mixed $string |
233 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
234 | */ |
235 | public function raw($string, $options = []) |
236 | { |
237 | $this->pipeline = $this->cleanFormat($string); |
238 | |
239 | return $this->aggregate($options); |
240 | } |
241 | |
242 | /** |
243 | * Clear pipeline array |
244 | * |
245 | * @return Qmp\Laravel\MongoLow\MongoPipeline |
246 | */ |
247 | public function clear(): MongoPipeline |
248 | { |
249 | $this->pipeline = []; |
250 | return $this; |
251 | } |
252 | |
253 | /** |
254 | * Format array, collection or json |
255 | * |
256 | * @param mixed $data |
257 | * @return array |
258 | */ |
259 | protected function cleanFormat($data) |
260 | { |
261 | if (is_array($data)) { |
262 | return $data; |
263 | } else if (is_string($data) && ($this->startsWith($data, '{') || $this->startsWith($data, '['))) { |
264 | return $this->decodeJson($data); |
265 | } else if ($data instanceof Collection) { |
266 | return $data->toArray(); |
267 | } |
268 | |
269 | return $data; |
270 | } |
271 | |
272 | protected function decodeJson($json) |
273 | { |
274 | $decoded = json_decode($json, true); |
275 | $error = json_last_error(); |
276 | |
277 | if ($decoded === null && $error !== JSON_ERROR_NONE) { |
278 | throw new MongoPipelineJsonDecodeException('Invalid Json', $error, $json); |
279 | } |
280 | |
281 | |
282 | |
283 | return $decoded; |
284 | } |
285 | |
286 | |
287 | protected function startsWith($haystack, $needle) |
288 | { |
289 | $length = strlen($needle); |
290 | return (substr(trim($haystack), 0, $length) === $needle); |
291 | } |
292 | } |