Completed
Pull Request — master (#1714)
by Gabriel
09:27
created

Expr::includesReferenceTo()   D

Complexity

Conditions 9
Paths 40

Size

Total Lines 41
Code Lines 27

Duplication

Lines 41
Ratio 100 %

Code Coverage

Tests 21
CRAP Score 9.1582

Importance

Changes 0
Metric Value
dl 41
loc 41
ccs 21
cts 24
cp 0.875
rs 4.909
c 0
b 0
f 0
cc 9
eloc 27
nc 40
nop 1
crap 9.1582
1
<?php
2
3
namespace Doctrine\ODM\MongoDB\Query;
4
5
use Doctrine\ODM\MongoDB\DocumentManager;
6
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
7
use Doctrine\ODM\MongoDB\Mapping\MappingException;
8
use GeoJson\Geometry\Geometry;
9
use GeoJson\Geometry\Point;
10
11
/**
12
 * Query expression builder for ODM.
13
 *
14
 * @since       1.0
15
 */
16
class Expr
17
{
18
    /**
19
     * The query criteria array.
20
     *
21
     * @var array
22
     */
23
    private $query = [];
24
25
    /**
26
     * The "new object" array containing either a full document or a number of
27
     * atomic update operators.
28
     *
29
     * @see docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
30
     * @var array
31
     */
32
    private $newObj = [];
33
34
    /**
35
     * The current field we are operating on.
36
     *
37
     * @var string
38
     */
39
    private $currentField;
40
41
    /**
42
     * The DocumentManager instance for this query
43
     *
44
     * @var DocumentManager
45
     */
46
    private $dm;
47
48
    /**
49
     * The ClassMetadata instance for the document being queried
50
     *
51
     * @var ClassMetadata
52
     */
53
    private $class;
54
55
    /**
56
     * @param DocumentManager $dm
57
     */
58 391
    public function __construct(DocumentManager $dm)
59
    {
60 391
        $this->dm = $dm;
61 391
    }
62
63
    /**
64
     * Add one or more $and clauses to the current query.
65
     *
66
     * @see Builder::addAnd()
67
     * @see http://docs.mongodb.org/manual/reference/operator/and/
68
     * @param array|Expr $expression
69
     * @param array|Expr ...$expressions
70
     * @return $this
71
     */
72 3 View Code Duplication
    public function addAnd($expression, ...$expressions)
0 ignored issues
show
Unused Code introduced by
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73
    {
74 3
        if (! isset($this->query['$and'])) {
75 3
            $this->query['$and'] = [];
76
        }
77
78 3
        $this->query['$and'] = array_merge(
79 3
            $this->query['$and'],
80 3
            array_map(
81 3
                function ($expression) {
82 3
                    return $expression instanceof Expr ? $expression->getQuery() : $expression;
83 3
                },
84 3
                func_get_args()
85
            )
86
        );
87
88 3
        return $this;
89
    }
90
91
    /**
92
     * Add one or more $nor clauses to the current query.
93
     *
94
     * @see Builder::addNor()
95
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
96
     * @param array|Expr $expression
97
     * @param array|Expr ...$expressions
98
     * @return $this
99
     */
100 1 View Code Duplication
    public function addNor($expression, ...$expressions)
0 ignored issues
show
Unused Code introduced by
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
    {
102 1
        if (! isset($this->query['$nor'])) {
103 1
            $this->query['$nor'] = [];
104
        }
105
106 1
        $this->query['$nor'] = array_merge(
107 1
            $this->query['$nor'],
108
            array_map(function ($expression) { return $expression instanceof Expr ? $expression->getQuery() : $expression; }, func_get_args())
109
        );
110
111 1
        return $this;
112
    }
113
114
    /**
115
     * Add one or more $or clauses to the current query.
116
     *
117
     * @see Builder::addOr()
118
     * @see http://docs.mongodb.org/manual/reference/operator/or/
119
     * @param array|Expr $expression
120
     * @param array|Expr ...$expressions
121
     * @return $this
122
     */
123 5 View Code Duplication
    public function addOr($expression, ...$expressions)
0 ignored issues
show
Unused Code introduced by
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
    {
125 5
        if (! isset($this->query['$or'])) {
126 5
            $this->query['$or'] = [];
127
        }
128
129 5
        $this->query['$or'] = array_merge(
130 5
            $this->query['$or'],
131
            array_map(function ($expression) { return $expression instanceof Expr ? $expression->getQuery() : $expression; }, func_get_args())
132
        );
133
134 5
        return $this;
135
    }
136
137
    /**
138
     * Append one or more values to the current array field only if they do not
139
     * already exist in the array.
140
     *
141
     * If the field does not exist, it will be set to an array containing the
142
     * unique value(s) in the argument. If the field is not an array, the query
143
     * will yield an error.
144
     *
145
     * Multiple values may be specified by provided an Expr object and using
146
     * {@link Expr::each()}.
147
     *
148
     * @see Builder::addToSet()
149
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
150
     * @see http://docs.mongodb.org/manual/reference/operator/each/
151
     * @param mixed|Expr $valueOrExpression
152
     * @return $this
153
     */
154 5 View Code Duplication
    public function addToSet($valueOrExpression)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
    {
156 5
        if ($valueOrExpression instanceof Expr) {
157 1
            $valueOrExpression = $valueOrExpression->getQuery();
158
        }
159
160 5
        $this->requiresCurrentField();
161 5
        $this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
162 5
        return $this;
163
    }
164
165
    /**
166
     * Specify $all criteria for the current field.
167
     *
168
     * @see Builder::all()
169
     * @see http://docs.mongodb.org/manual/reference/operator/all/
170
     * @param array $values
171
     * @return $this
172
     */
173 2
    public function all(array $values)
174
    {
175 2
        return $this->operator('$all', (array) $values);
176
    }
177
178
    /**
179
     * Apply a bitwise operation on the current field
180
     *
181
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
182
     * @param string $operator
183
     * @param int $value
184
     * @return $this
185
     */
186 3
    protected function bit($operator, $value)
187
    {
188 3
        $this->requiresCurrentField();
189 3
        $this->newObj['$bit'][$this->currentField][$operator] = $value;
190 3
        return $this;
191
    }
192
193
    /**
194
     * Apply a bitwise and operation on the current field.
195
     *
196
     * @see Builder::bitAnd()
197
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
198
     * @param int $value
199
     * @return $this
200
     */
201 1
    public function bitAnd($value)
202
    {
203 1
        return $this->bit('and', $value);
204
    }
205
206
    /**
207
     * Apply a bitwise or operation on the current field.
208
     *
209
     * @see Builder::bitOr()
210
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
211
     * @param int $value
212
     * @return $this
213
     */
214 1
    public function bitOr($value)
215
    {
216 1
        return $this->bit('or', $value);
217
    }
218
219
    /**
220
     * Matches documents where all of the bit positions given by the query are
221
     * clear.
222
     *
223
     * @see Builder::bitsAllClear()
224
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
225
     * @param int|array|\MongoDB\BSON\Binary $value
226
     * @return $this
227
     */
228
    public function bitsAllClear($value)
229
    {
230
        $this->requiresCurrentField();
231
        return $this->operator('$bitsAllClear', $value);
232
    }
233
234
    /**
235
     * Matches documents where all of the bit positions given by the query are
236
     * set.
237
     *
238
     * @see Builder::bitsAllSet()
239
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
240
     * @param int|array|\MongoDB\BSON\Binary $value
241
     * @return $this
242
     */
243
    public function bitsAllSet($value)
244
    {
245
        $this->requiresCurrentField();
246
        return $this->operator('$bitsAllSet', $value);
247
    }
248
249
    /**
250
     * Matches documents where any of the bit positions given by the query are
251
     * clear.
252
     *
253
     * @see Builder::bitsAnyClear()
254
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
255
     * @param int|array|\MongoDB\BSON\Binary $value
256
     * @return $this
257
     */
258
    public function bitsAnyClear($value)
259
    {
260
        $this->requiresCurrentField();
261
        return $this->operator('$bitsAnyClear', $value);
262
    }
263
264
    /**
265
     * Matches documents where any of the bit positions given by the query are
266
     * set.
267
     *
268
     * @see Builder::bitsAnySet()
269
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
270
     * @param int|array|\MongoDB\BSON\Binary $value
271
     * @return $this
272
     */
273
    public function bitsAnySet($value)
274
    {
275
        $this->requiresCurrentField();
276
        return $this->operator('$bitsAnySet', $value);
277
    }
278
279
    /**
280
     * Apply a bitwise xor operation on the current field.
281
     *
282
     * @see Builder::bitXor()
283
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
284
     * @param int $value
285
     * @return $this
286
     */
287 1
    public function bitXor($value)
288
    {
289 1
        return $this->bit('xor', $value);
290
    }
291
292
    /**
293
     * A boolean flag to enable or disable case sensitive search for $text
294
     * criteria.
295
     *
296
     * This method must be called after text().
297
     *
298
     * @see Builder::caseSensitive()
299
     * @see http://docs.mongodb.org/manual/reference/operator/text/
300
     * @param bool $caseSensitive
301
     * @return $this
302
     * @throws \BadMethodCallException if the query does not already have $text criteria
303
     *
304
     * @since 1.3
305
     */
306 3 View Code Duplication
    public function caseSensitive($caseSensitive)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307
    {
308 3
        if ( ! isset($this->query['$text'])) {
309 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
310
        }
311
312
        // Remove caseSensitive option to keep support for older database versions
313 2
        if ($caseSensitive) {
314 2
            $this->query['$text']['$caseSensitive'] = true;
315 1
        } elseif (isset($this->query['$text']['$caseSensitive'])) {
316 1
            unset($this->query['$text']['$caseSensitive']);
317
        }
318
319 2
        return $this;
320
    }
321
322
    /**
323
     * Associates a comment to any expression taking a query predicate.
324
     *
325
     * @see Builder::comment()
326
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
327
     * @param string $comment
328
     * @return $this
329
     */
330
    public function comment($comment)
331
    {
332
        $this->query['$comment'] = $comment;
333
        return $this;
334
    }
335
336
    /**
337
     * Sets the value of the current field to the current date, either as a date or a timestamp.
338
     *
339
     * @see Builder::currentDate()
340
     * @see http://docs.mongodb.org/manual/reference/operator/update/currentDate/
341
     * @param string $type
342
     * @return $this
343
     * @throws \InvalidArgumentException if an invalid type is given
344
     */
345 3
    public function currentDate($type = 'date')
346
    {
347 3
        if (! in_array($type, ['date', 'timestamp'])) {
348 1
            throw new \InvalidArgumentException('Type for currentDate operator must be date or timestamp.');
349
        }
350
351 2
        $this->requiresCurrentField();
352 2
        $this->newObj['$currentDate'][$this->currentField]['$type'] = $type;
353 2
        return $this;
354
    }
355
356
    /**
357
     * A boolean flag to enable or disable diacritic sensitive search for $text
358
     * criteria.
359
     *
360
     * This method must be called after text().
361
     *
362
     * @see Builder::diacriticSensitive()
363
     * @see http://docs.mongodb.org/manual/reference/operator/text/
364
     * @param bool $diacriticSensitive
365
     * @return $this
366
     * @throws \BadMethodCallException if the query does not already have $text criteria
367
     *
368
     * @since 1.3
369
     */
370 3 View Code Duplication
    public function diacriticSensitive($diacriticSensitive)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
    {
372 3
        if ( ! isset($this->query['$text'])) {
373 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
374
        }
375
376
        // Remove diacriticSensitive option to keep support for older database versions
377 2
        if ($diacriticSensitive) {
378 2
            $this->query['$text']['$diacriticSensitive'] = true;
379 1
        } elseif (isset($this->query['$text']['$diacriticSensitive'])) {
380 1
            unset($this->query['$text']['$diacriticSensitive']);
381
        }
382
383 2
        return $this;
384
    }
385
386
    /**
387
     * Add $each criteria to the expression for a $push operation.
388
     *
389
     * @see Expr::push()
390
     * @see http://docs.mongodb.org/manual/reference/operator/each/
391
     * @param array $values
392
     * @return $this
393
     */
394 4
    public function each(array $values)
395
    {
396 4
        return $this->operator('$each', $values);
397
    }
398
399
    /**
400
     * Specify $elemMatch criteria for the current field.
401
     *
402
     * @see Builder::elemMatch()
403
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
404
     * @param array|Expr $expression
405
     * @return $this
406
     */
407 4
    public function elemMatch($expression)
408
    {
409 4
        return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
410
    }
411
412
    /**
413
     * Specify an equality match for the current field.
414
     *
415
     * @see Builder::equals()
416
     * @param mixed $value
417
     * @return $this
418
     */
419 100
    public function equals($value)
420
    {
421 100
        if ($this->currentField) {
422 99
            $this->query[$this->currentField] = $value;
423
        } else {
424 1
            $this->query = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value of type * is incompatible with the declared type array of property $query.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
425
        }
426 100
        return $this;
427
    }
428
429
    /**
430
     * Specify $exists criteria for the current field.
431
     *
432
     * @see Builder::exists()
433
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
434
     * @param boolean $bool
435
     * @return $this
436
     */
437 5
    public function exists($bool)
438
    {
439 5
        return $this->operator('$exists', (boolean) $bool);
440
    }
441
442
    /**
443
     * Set the current field for building the expression.
444
     *
445
     * @see Builder::field()
446
     * @param string $field
447
     * @return $this
448
     */
449 191
    public function field($field)
450
    {
451 191
        $this->currentField = (string) $field;
452 191
        return $this;
453
    }
454
455
    /**
456
     * Add $geoIntersects criteria with a GeoJSON geometry to the expression.
457
     *
458
     * The geometry parameter GeoJSON object or an array corresponding to the
459
     * geometry's JSON representation.
460
     *
461
     * @see Builder::geoIntersects()
462
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
463
     * @param array|Geometry $geometry
464
     * @return $this
465
     */
466 2 View Code Duplication
    public function geoIntersects($geometry)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
467
    {
468 2
        if ($geometry instanceof Geometry) {
469 1
            $geometry = $geometry->jsonSerialize();
470
        }
471
472 2
        return $this->operator('$geoIntersects', ['$geometry' => $geometry]);
473
    }
474
475
    /**
476
     * Add $geoWithin criteria with a GeoJSON geometry to the expression.
477
     *
478
     * The geometry parameter GeoJSON object or an array corresponding to the
479
     * geometry's JSON representation.
480
     *
481
     * @see Builder::geoWithin()
482
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
483
     * @param array|Geometry $geometry
484
     * @return $this
485
     */
486 2 View Code Duplication
    public function geoWithin($geometry)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
487
    {
488 2
        if ($geometry instanceof Geometry) {
489 1
            $geometry = $geometry->jsonSerialize();
490
        }
491
492 2
        return $this->operator('$geoWithin', ['$geometry' => $geometry]);
493
    }
494
495
    /**
496
     * Add $geoWithin criteria with a $box shape to the expression.
497
     *
498
     * A rectangular polygon will be constructed from a pair of coordinates
499
     * corresponding to the bottom left and top right corners.
500
     *
501
     * Note: the $box operator only supports legacy coordinate pairs and 2d
502
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
503
     *
504
     * @see Builder::geoWithinBox()
505
     * @see http://docs.mongodb.org/manual/reference/operator/box/
506
     * @param float $x1
507
     * @param float $y1
508
     * @param float $x2
509
     * @param float $y2
510
     * @return $this
511
     */
512 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
513
    {
514 1
        $shape = ['$box' => [[$x1, $y1], [$x2, $y2]]];
515
516 1
        return $this->operator('$geoWithin', $shape);
517
    }
518
519
    /**
520
     * Add $geoWithin criteria with a $center shape to the expression.
521
     *
522
     * Note: the $center operator only supports legacy coordinate pairs and 2d
523
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
524
     *
525
     * @see Builider::geoWithinCenter()
526
     * @see http://docs.mongodb.org/manual/reference/operator/center/
527
     * @param float $x
528
     * @param float $y
529
     * @param float $radius
530
     * @return $this
531
     */
532 1
    public function geoWithinCenter($x, $y, $radius)
533
    {
534 1
        $shape = ['$center' => [[$x, $y], $radius]];
535
536 1
        return $this->operator('$geoWithin', $shape);
537
    }
538
539
    /**
540
     * Add $geoWithin criteria with a $centerSphere shape to the expression.
541
     *
542
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
543
     *
544
     * @see Builder::geoWithinCenterSphere()
545
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
546
     * @param float $x
547
     * @param float $y
548
     * @param float $radius
549
     * @return $this
550
     */
551 1
    public function geoWithinCenterSphere($x, $y, $radius)
552
    {
553 1
        $shape = ['$centerSphere' => [[$x, $y], $radius]];
554
555 1
        return $this->operator('$geoWithin', $shape);
556
    }
557
558
    /**
559
     * Add $geoWithin criteria with a $polygon shape to the expression.
560
     *
561
     * Point coordinates are in x, y order (easting, northing for projected
562
     * coordinates, longitude, latitude for geographic coordinates).
563
     *
564
     * The last point coordinate is implicitly connected with the first.
565
     *
566
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
567
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
568
     *
569
     * @see Builder::geoWithinPolygon()
570
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
571
     * @param array $point1 First point of the polygon
572
     * @param array $point2 Second point of the polygon
573
     * @param array $point3 Third point of the polygon
574
     * @param array ...$points Additional points of the polygon
575
     * @return $this
576
     * @throws \InvalidArgumentException if less than three points are given
577
     */
578 1
    public function geoWithinPolygon($point1, $point2, $point3, ...$points)
0 ignored issues
show
Unused Code introduced by
The parameter $point1 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $point2 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $point3 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $points is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
579
    {
580 1
        $shape = ['$polygon' => func_get_args()];
581
582 1
        return $this->operator('$geoWithin', $shape);
583
    }
584
585
    /**
586
     * Return the current field.
587
     *
588
     * @return string
589
     */
590 2
    public function getCurrentField()
591
    {
592 2
        return $this->currentField;
593
    }
594
595
    /**
596
     * Gets prepared newObj part of expression.
597
     *
598
     * @return array
599
     */
600 174
    public function getNewObj()
601
    {
602 174
        return $this->dm->getUnitOfWork()
603 174
            ->getDocumentPersister($this->class->name)
604 174
            ->prepareQueryOrNewObj($this->newObj, true);
605
    }
606
607
    /**
608
     * Gets prepared query part of expression.
609
     *
610
     * @return array
611
     */
612 250
    public function getQuery()
613
    {
614 250
        return $this->dm->getUnitOfWork()
615 250
            ->getDocumentPersister($this->class->name)
616 250
            ->prepareQueryOrNewObj($this->query);
617
    }
618
619
    /**
620
     * Specify $gt criteria for the current field.
621
     *
622
     * @see Builder::gt()
623
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
624
     * @param mixed $value
625
     * @return $this
626
     */
627 2
    public function gt($value)
628
    {
629 2
        return $this->operator('$gt', $value);
630
    }
631
632
    /**
633
     * Specify $gte criteria for the current field.
634
     *
635
     * @see Builder::gte()
636
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
637
     * @param mixed $value
638
     * @return $this
639
     */
640 2
    public function gte($value)
641
    {
642 2
        return $this->operator('$gte', $value);
643
    }
644
645
    /**
646
     * Specify $in criteria for the current field.
647
     *
648
     * @see Builder::in()
649
     * @see http://docs.mongodb.org/manual/reference/operator/in/
650
     * @param array $values
651
     * @return $this
652
     */
653 31
    public function in(array $values)
654
    {
655 31
        return $this->operator('$in', array_values($values));
656
    }
657
658
    /**
659
     * Increment the current field.
660
     *
661
     * If the field does not exist, it will be set to this value.
662
     *
663
     * @see Builder::inc()
664
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
665
     * @param float|integer $value
666
     * @return $this
667
     */
668 5
    public function inc($value)
669
    {
670 5
        $this->requiresCurrentField();
671 5
        $this->newObj['$inc'][$this->currentField] = $value;
672 5
        return $this;
673
    }
674
675
    /**
676
     * Checks that the current field includes a reference to the supplied document.
677
     *
678
     * @param object $document
679
     * @return Expr
680
     */
681 6 View Code Duplication
    public function includesReferenceTo($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
682
    {
683 6
        $this->requiresCurrentField();
684 6
        $mapping = $this->getReferenceMapping();
685 4
        $reference = $this->dm->createReference($document, $mapping);
686 4
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
687 4
        $keys = [];
0 ignored issues
show
Unused Code introduced by
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
688
689
        switch ($storeAs) {
690 4
            case ClassMetadata::REFERENCE_STORE_AS_ID:
691 2
                $this->query[$mapping['name']] = $reference;
692 2
                return $this;
693
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
694
695 3
            case ClassMetadata::REFERENCE_STORE_AS_REF:
696
                $keys = ['id' => true];
697
                break;
698
699 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
700 1
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
701 3
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
702
703 3
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
704 2
                    unset($keys['$db']);
705
                }
706
707 3
                if (isset($mapping['targetDocument'])) {
708 1
                    unset($keys['$ref'], $keys['$db']);
709
                }
710 3
                break;
711
712
            default:
713
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
714
        }
715
716 3
        foreach ($keys as $key => $value) {
717 3
            $this->query[$mapping['name']]['$elemMatch'][$key] = $reference[$key];
718
        }
719
720 3
        return $this;
721
    }
722
723
    /**
724
     * Set the $language option for $text criteria.
725
     *
726
     * This method must be called after text().
727
     *
728
     * @see Builder::language()
729
     * @see http://docs.mongodb.org/manual/reference/operator/text/
730
     * @param string $language
731
     * @return $this
732
     * @throws \BadMethodCallException if the query does not already have $text criteria
733
     */
734 2
    public function language($language)
735
    {
736 2
        if ( ! isset($this->query['$text'])) {
737 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
738
        }
739
740 1
        $this->query['$text']['$language'] = (string) $language;
741
742 1
        return $this;
743
    }
744
745
    /**
746
     * Specify $lt criteria for the current field.
747
     *
748
     * @see Builder::lte()
749
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
750
     * @param mixed $value
751
     * @return $this
752
     */
753 4
    public function lt($value)
754
    {
755 4
        return $this->operator('$lt', $value);
756
    }
757
758
    /**
759
     * Specify $lte criteria for the current field.
760
     *
761
     * @see Builder::lte()
762
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
763
     * @param mixed $value
764
     * @return $this
765
     */
766 2
    public function lte($value)
767
    {
768 2
        return $this->operator('$lte', $value);
769
    }
770
771
    /**
772
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
773
     *
774
     * @see Builder::max()
775
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
776
     * @param mixed $value
777
     * @return $this
778
     */
779
    public function max($value)
780
    {
781
        $this->requiresCurrentField();
782
        $this->newObj['$max'][$this->currentField] = $value;
783
        return $this;
784
    }
785
786
    /**
787
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
788
     *
789
     * @see Builder::min()
790
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
791
     * @param mixed $value
792
     * @return $this
793
     */
794
    public function min($value)
795
    {
796
        $this->requiresCurrentField();
797
        $this->newObj['$min'][$this->currentField] = $value;
798
        return $this;
799
    }
800
801
    /**
802
     * Specify $mod criteria for the current field.
803
     *
804
     * @see Builder::mod()
805
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
806
     * @param float|integer $divisor
807
     * @param float|integer $remainder
808
     * @return $this
809
     */
810
    public function mod($divisor, $remainder = 0)
811
    {
812
        return $this->operator('$mod', [$divisor, $remainder]);
813
    }
814
815
    /**
816
     * Multiply the current field.
817
     *
818
     * If the field does not exist, it will be set to 0.
819
     *
820
     * @see Builder::mul()
821
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
822
     * @param float|integer $value
823
     * @return $this
824
     */
825
    public function mul($value)
826
    {
827
        $this->requiresCurrentField();
828
        $this->newObj['$mul'][$this->currentField] = $value;
829
        return $this;
830
    }
831
832
    /**
833
     * Add $near criteria to the expression.
834
     *
835
     * A GeoJSON point may be provided as the first and only argument for
836
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
837
     * an array corresponding to the point's JSON representation.
838
     *
839
     * @see Builder::near()
840
     * @see http://docs.mongodb.org/manual/reference/operator/near/
841
     * @param float|array|Point $x
842
     * @param float $y
843
     * @return $this
844
     */
845 3 View Code Duplication
    public function near($x, $y = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
846
    {
847 3
        if ($x instanceof Point) {
848 1
            $x = $x->jsonSerialize();
849
        }
850
851 3
        if (is_array($x)) {
852 2
            return $this->operator('$near', ['$geometry' => $x]);
853
        }
854
855 1
        return $this->operator('$near', [$x, $y]);
856
    }
857
858
    /**
859
     * Add $nearSphere criteria to the expression.
860
     *
861
     * A GeoJSON point may be provided as the first and only argument for
862
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
863
     * an array corresponding to the point's JSON representation.
864
     *
865
     * @see Builder::nearSphere()
866
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
867
     * @param float|array|Point $x
868
     * @param float $y
869
     * @return $this
870
     */
871 3 View Code Duplication
    public function nearSphere($x, $y = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
872
    {
873 3
        if ($x instanceof Point) {
874 1
            $x = $x->jsonSerialize();
875
        }
876
877 3
        if (is_array($x)) {
878 2
            return $this->operator('$nearSphere', ['$geometry' => $x]);
879
        }
880
881 1
        return $this->operator('$nearSphere', [$x, $y]);
882
    }
883
884
    /**
885
     * Negates an expression for the current field.
886
     *
887
     * @see Builder::not()
888
     * @see http://docs.mongodb.org/manual/reference/operator/not/
889
     * @param array|Expr $expression
890
     * @return $this
891
     */
892 2
    public function not($expression)
893
    {
894 2
        return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
895
    }
896
897
    /**
898
     * Specify $ne criteria for the current field.
899
     *
900
     * @see Builder::notEqual()
901
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
902
     * @param mixed $value
903
     * @return $this
904
     */
905 6
    public function notEqual($value)
906
    {
907 6
        return $this->operator('$ne', $value);
908
    }
909
910
    /**
911
     * Specify $nin criteria for the current field.
912
     *
913
     * @see Builder::notIn()
914
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
915
     * @param array $values
916
     * @return $this
917
     */
918 6
    public function notIn(array $values)
919
    {
920 6
        return $this->operator('$nin', array_values($values));
921
    }
922
923
    /**
924
     * Defines an operator and value on the expression.
925
     *
926
     * If there is a current field, the operator will be set on it; otherwise,
927
     * the operator is set at the top level of the query.
928
     *
929
     * @param string $operator
930
     * @param mixed $value
931
     * @return $this
932
     */
933 81
    public function operator($operator, $value)
934
    {
935 81
        $this->wrapEqualityCriteria();
936
937 81
        if ($this->currentField) {
938 56
            $this->query[$this->currentField][$operator] = $value;
939
        } else {
940 27
            $this->query[$operator] = $value;
941
        }
942 81
        return $this;
943
    }
944
945
    /**
946
     * Remove the first element from the current array field.
947
     *
948
     * @see Builder::popFirst()
949
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
950
     * @return $this
951
     */
952 1
    public function popFirst()
953
    {
954 1
        $this->requiresCurrentField();
955 1
        $this->newObj['$pop'][$this->currentField] = 1;
956 1
        return $this;
957
    }
958
959
    /**
960
     * Remove the last element from the current array field.
961
     *
962
     * @see Builder::popLast()
963
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
964
     * @return $this
965
     */
966
    public function popLast()
967
    {
968
        $this->requiresCurrentField();
969
        $this->newObj['$pop'][$this->currentField] = -1;
970
        return $this;
971
    }
972
973
    /**
974
     * Add $position criteria to the expression for a $push operation.
975
     *
976
     * This is useful in conjunction with {@link Expr::each()} for a
977
     * {@link Expr::push()} operation.
978
     *
979
     * @see http://docs.mongodb.org/manual/reference/operator/update/position/
980
     * @param integer $position
981
     * @return $this
982
     */
983 1
    public function position($position)
984
    {
985 1
        return $this->operator('$position', $position);
986
    }
987
988
    /**
989
     * Remove all elements matching the given value or expression from the
990
     * current array field.
991
     *
992
     * @see Builder::pull()
993
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
994
     * @param mixed|Expr $valueOrExpression
995
     * @return $this
996
     */
997 2 View Code Duplication
    public function pull($valueOrExpression)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
998
    {
999 2
        if ($valueOrExpression instanceof Expr) {
1000 1
            $valueOrExpression = $valueOrExpression->getQuery();
1001
        }
1002
1003 2
        $this->requiresCurrentField();
1004 2
        $this->newObj['$pull'][$this->currentField] = $valueOrExpression;
1005 2
        return $this;
1006
    }
1007
1008
    /**
1009
     * Remove all elements matching any of the given values from the current
1010
     * array field.
1011
     *
1012
     * @see Builder::pullAll()
1013
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1014
     * @param array $values
1015
     * @return $this
1016
     */
1017
    public function pullAll(array $values)
1018
    {
1019
        $this->requiresCurrentField();
1020
        $this->newObj['$pullAll'][$this->currentField] = $values;
1021
        return $this;
1022
    }
1023
1024
    /**
1025
     * Append one or more values to the current array field.
1026
     *
1027
     * If the field does not exist, it will be set to an array containing the
1028
     * value(s) in the argument. If the field is not an array, the query
1029
     * will yield an error.
1030
     *
1031
     * Multiple values may be specified by providing an Expr object and using
1032
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1033
     * also be used to limit and order array elements, respectively.
1034
     *
1035
     * @see Builder::push()
1036
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1037
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1038
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1039
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1040
     * @param mixed|Expr $valueOrExpression
1041
     * @return $this
1042
     */
1043 8 View Code Duplication
    public function push($valueOrExpression)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1044
    {
1045 8
        if ($valueOrExpression instanceof Expr) {
1046 3
            $valueOrExpression = array_merge(
1047 3
                ['$each' => []],
1048 3
                $valueOrExpression->getQuery()
1049
            );
1050
        }
1051
1052 8
        $this->requiresCurrentField();
1053 8
        $this->newObj['$push'][$this->currentField] = $valueOrExpression;
1054 8
        return $this;
1055
    }
1056
1057
    /**
1058
     * Specify $gte and $lt criteria for the current field.
1059
     *
1060
     * This method is shorthand for specifying $gte criteria on the lower bound
1061
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1062
     *
1063
     * @see Builder::range()
1064
     * @param mixed $start
1065
     * @param mixed $end
1066
     * @return $this
1067
     */
1068 2
    public function range($start, $end)
1069
    {
1070 2
        return $this->operator('$gte', $start)->operator('$lt', $end);
1071
    }
1072
1073
    /**
1074
     * Checks that the value of the current field is a reference to the supplied document.
1075
     *
1076
     * @param object $document
1077
     * @return Expr
1078
     */
1079 13 View Code Duplication
    public function references($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1080
    {
1081 13
        $this->requiresCurrentField();
1082 13
        $mapping = $this->getReferenceMapping();
1083 11
        $reference = $this->dm->createReference($document, $mapping);
1084 11
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
1085 11
        $keys = [];
0 ignored issues
show
Unused Code introduced by
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1086
1087
        switch ($storeAs) {
1088 11
            case ClassMetadata::REFERENCE_STORE_AS_ID:
1089 4
                $this->query[$mapping['name']] = $reference;
1090 4
                return $this;
1091
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1092
1093 8
            case ClassMetadata::REFERENCE_STORE_AS_REF:
1094
                $keys = ['id' => true];
1095
                break;
1096
1097 8
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
1098 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
1099 8
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
1100
1101 8
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
1102 5
                    unset($keys['$db']);
1103
                }
1104
1105 8
                if (isset($mapping['targetDocument'])) {
1106 4
                    unset($keys['$ref'], $keys['$db']);
1107
                }
1108 8
                break;
1109
1110
            default:
1111
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
1112
        }
1113
1114 8
        foreach ($keys as $key => $value) {
1115 8
            $this->query[$mapping['name'] . '.' . $key] = $reference[$key];
1116
        }
1117
1118 8
        return $this;
1119
    }
1120
1121
    /**
1122
     * Rename the current field.
1123
     *
1124
     * @see Builder::rename()
1125
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1126
     * @param string $name
1127
     * @return $this
1128
     */
1129
    public function rename($name)
1130
    {
1131
        $this->requiresCurrentField();
1132
        $this->newObj['$rename'][$this->currentField] = $name;
1133
        return $this;
1134
    }
1135
1136
    /**
1137
     * Set the current field to a value.
1138
     *
1139
     * This is only relevant for insert, update, or findAndUpdate queries. For
1140
     * update and findAndUpdate queries, the $atomic parameter will determine
1141
     * whether or not a $set operator is used.
1142
     *
1143
     * @see Builder::set()
1144
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1145
     * @param mixed $value
1146
     * @param boolean $atomic
1147
     * @return $this
1148
     */
1149 19
    public function set($value, $atomic = true)
1150
    {
1151 19
        $this->requiresCurrentField();
1152
1153 19
        if ($atomic) {
1154 16
            $this->newObj['$set'][$this->currentField] = $value;
1155 16
            return $this;
1156
        }
1157
1158 3
        if (strpos($this->currentField, '.') === false) {
1159 2
            $this->newObj[$this->currentField] = $value;
1160 2
            return $this;
1161
        }
1162
1163 2
        $keys = explode('.', $this->currentField);
1164 2
        $current = &$this->newObj;
1165 2
        foreach ($keys as $key) {
1166 2
            $current = &$current[$key];
1167
        }
1168 2
        $current = $value;
1169
1170 2
        return $this;
1171
    }
1172
1173
    /**
1174
     * Sets ClassMetadata for document being queried.
1175
     *
1176
     * @param ClassMetadata $class
1177
     */
1178 389
    public function setClassMetadata(ClassMetadata $class)
1179
    {
1180 389
        $this->class = $class;
1181 389
    }
1182
1183
    /**
1184
     * Set the "new object".
1185
     *
1186
     * @see Builder::setNewObj()
1187
     * @param array $newObj
1188
     * @return $this
1189
     */
1190
    public function setNewObj(array $newObj)
1191
    {
1192
        $this->newObj = $newObj;
1193
        return $this;
1194
    }
1195
1196
    /**
1197
     * Set the current field to the value if the document is inserted in an
1198
     * upsert operation.
1199
     *
1200
     * If an update operation with upsert: true results in an insert of a
1201
     * document, then $setOnInsert assigns the specified values to the fields in
1202
     * the document. If the update operation does not result in an insert,
1203
     * $setOnInsert does nothing.
1204
     *
1205
     * @see Builder::setOnInsert()
1206
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1207
     * @param mixed $value
1208
     * @return $this
1209
     */
1210 1
    public function setOnInsert($value)
1211
    {
1212 1
        $this->requiresCurrentField();
1213 1
        $this->newObj['$setOnInsert'][$this->currentField] = $value;
1214
1215 1
        return $this;
1216
    }
1217
1218
    /**
1219
     * Set the query criteria.
1220
     *
1221
     * @see Builder::setQueryArray()
1222
     * @param array $query
1223
     * @return $this
1224
     */
1225 18
    public function setQuery(array $query)
1226
    {
1227 18
        $this->query = $query;
1228 18
        return $this;
1229
    }
1230
1231
    /**
1232
     * Specify $size criteria for the current field.
1233
     *
1234
     * @see Builder::size()
1235
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1236
     * @param integer $size
1237
     * @return $this
1238
     */
1239
    public function size($size)
1240
    {
1241
        return $this->operator('$size', (integer) $size);
1242
    }
1243
1244
    /**
1245
     * Add $slice criteria to the expression for a $push operation.
1246
     *
1247
     * This is useful in conjunction with {@link Expr::each()} for a
1248
     * {@link Expr::push()} operation. {@link Builder::selectSlice()} should be
1249
     * used for specifying $slice for a query projection.
1250
     *
1251
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1252
     * @param integer $slice
1253
     * @return $this
1254
     */
1255 2
    public function slice($slice)
1256
    {
1257 2
        return $this->operator('$slice', $slice);
1258
    }
1259
1260
    /**
1261
     * Add $sort criteria to the expression for a $push operation.
1262
     *
1263
     * If sorting by multiple fields, the first argument should be an array of
1264
     * field name (key) and order (value) pairs.
1265
     *
1266
     * This is useful in conjunction with {@link Expr::each()} for a
1267
     * {@link Expr::push()} operation. {@link Builder::sort()} should be used to
1268
     * sort the results of a query.
1269
     *
1270
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1271
     * @param array|string $fieldName Field name or array of field/order pairs
1272
     * @param int|string $order       Field order (if one field is specified)
1273
     * @return $this
1274
     */
1275 2
    public function sort($fieldName, $order = null)
1276
    {
1277 2
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1278
1279 2
        return $this->operator('$sort', array_map([$this, 'normalizeSortOrder'], $fields));
1280
    }
1281
1282
    /**
1283
     * Specify $text criteria for the current query.
1284
     *
1285
     * The $language option may be set with {@link Expr::language()}.
1286
     *
1287
     * @see Builder::text()
1288
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1289
     * @param string $search
1290
     * @return $this
1291
     */
1292 6
    public function text($search)
1293
    {
1294 6
        $this->query['$text'] = ['$search' => (string) $search];
1295 6
        return $this;
1296
    }
1297
1298
    /**
1299
     * Specify $type criteria for the current field.
1300
     *
1301
     * @todo Remove support for string $type argument in 2.0
1302
     * @see Builder::type()
1303
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1304
     * @param integer $type
1305
     * @return $this
1306
     */
1307 1
    public function type($type)
1308
    {
1309 1
        if (is_string($type)) {
1310
            $map = [
1311 1
                'double' => 1,
1312
                'string' => 2,
1313
                'object' => 3,
1314
                'array' => 4,
1315
                'binary' => 5,
1316
                'undefined' => 6,
1317
                'objectid' => 7,
1318
                'boolean' => 8,
1319
                'date' => 9,
1320
                'null' => 10,
1321
                'regex' => 11,
1322
                'jscode' => 13,
1323
                'symbol' => 14,
1324
                'jscodewithscope' => 15,
1325
                'integer32' => 16,
1326
                'timestamp' => 17,
1327
                'integer64' => 18,
1328
                'maxkey' => 127,
1329
                'minkey' => 255,
1330
            ];
1331
1332 1
            $type = $map[$type] ?? $type;
1333
        }
1334
1335 1
        return $this->operator('$type', $type);
1336
    }
1337
1338
    /**
1339
     * Unset the current field.
1340
     *
1341
     * The field will be removed from the document (not set to null).
1342
     *
1343
     * @see Builder::unsetField()
1344
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1345
     * @return $this
1346
     */
1347 3
    public function unsetField()
1348
    {
1349 3
        $this->requiresCurrentField();
1350 3
        $this->newObj['$unset'][$this->currentField] = 1;
1351 3
        return $this;
1352
    }
1353
1354
    /**
1355
     * Specify a JavaScript expression to use for matching documents.
1356
     *
1357
     * @see Builder::where()
1358
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1359
     * @param string|\MongoDB\BSON\Javascript $javascript
1360
     * @return $this
1361
     */
1362 3
    public function where($javascript)
1363
    {
1364 3
        $this->query['$where'] = $javascript;
1365 3
        return $this;
1366
    }
1367
1368
    /**
1369
     * Gets reference mapping for current field from current class or its descendants.
1370
     *
1371
     * @return array
1372
     * @throws MappingException
1373
     */
1374 19
    private function getReferenceMapping()
1375
    {
1376 19
        $mapping = null;
1377
        try {
1378 19
            $mapping = $this->class->getFieldMapping($this->currentField);
1379 6
        } catch (MappingException $e) {
1380 6
            if (empty($this->class->discriminatorMap)) {
1381
                throw $e;
1382
            }
1383 6
            $foundIn = null;
1384 6
            foreach ($this->class->discriminatorMap as $child) {
1385 6
                $childClass = $this->dm->getClassMetadata($child);
1386 6
                if ($childClass->hasAssociation($this->currentField)) {
1387 4
                    if ($mapping !== null && $mapping !== $childClass->getFieldMapping($this->currentField)) {
1388 2
                        throw MappingException::referenceFieldConflict($this->currentField, $foundIn->name, $childClass->name);
1389
                    }
1390 4
                    $mapping = $childClass->getFieldMapping($this->currentField);
1391 6
                    $foundIn = $childClass;
1392
                }
1393
            }
1394 4
            if ($mapping === null) {
1395 2
                throw MappingException::mappingNotFoundInClassNorDescendants($this->class->name, $this->currentField);
1396
            }
1397
        }
1398 15
        return $mapping;
1399
    }
1400
1401
    /**
1402
     * @param int|string $order
1403
     *
1404
     * @return int
1405
     */
1406 2
    private function normalizeSortOrder($order): int
1407
    {
1408 2
        if (is_string($order)) {
1409
            $order = strtolower($order) === 'asc' ? 1 : -1;
1410
        }
1411
1412 2
        return (int) $order;
1413
    }
1414
1415
    /**
1416
     * Ensure that a current field has been set.
1417
     *
1418
     * @throws \LogicException if a current field has not been set
1419
     */
1420 67
    private function requiresCurrentField()
1421
    {
1422 67
        if ( ! $this->currentField) {
1423
            throw new \LogicException('This method requires you set a current field using field().');
1424
        }
1425 67
    }
1426
1427
    /**
1428
     * Wraps equality criteria with an operator.
1429
     *
1430
     * If equality criteria was previously specified for a field, it cannot be
1431
     * merged with other operators without first being wrapped in an operator of
1432
     * its own. Ideally, we would wrap it with $eq, but that is only available
1433
     * in MongoDB 2.8. Using a single-element $in is backwards compatible.
1434
     *
1435
     * @see Expr::operator()
1436
     */
1437 81
    private function wrapEqualityCriteria()
1438
    {
1439
        /* If the current field has no criteria yet, do nothing. This ensures
1440
         * that we do not inadvertently inject {"$in": null} into the query.
1441
         */
1442 81
        if ($this->currentField && ! isset($this->query[$this->currentField]) && ! array_key_exists($this->currentField, $this->query)) {
1443 55
            return;
1444
        }
1445
1446 31
        if ($this->currentField) {
1447 5
            $query = &$this->query[$this->currentField];
1448
        } else {
1449 27
            $query = &$this->query;
1450
        }
1451
1452
        /* If the query is an empty array, we'll assume that the user has not
1453
         * specified criteria. Otherwise, check if the array includes a query
1454
         * operator (checking the first key is sufficient). If neither of these
1455
         * conditions are met, we'll wrap the query value with $in.
1456
         */
1457 31
        if (is_array($query) && (empty($query) || strpos(key($query), '$') === 0)) {
1458 31
            return;
1459
        }
1460
1461 2
        $query = ['$in' => [$query]];
1462 2
    }
1463
}
1464