Completed
Push — master ( 1ec641...92e389 )
by Maciej
63:17 queued 60:26
created

Expr::type()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 30
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 5
cts 5
cp 1
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 24
nc 2
nop 1
crap 2
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\ClassMetadataInfo;
8
use Doctrine\ODM\MongoDB\Mapping\MappingException;
9
use GeoJson\Geometry\Geometry;
10
use GeoJson\Geometry\Point;
11
12
/**
13
 * Query expression builder for ODM.
14
 *
15
 * @since       1.0
16
 */
17
class Expr
18
{
19
    /**
20
     * The query criteria array.
21
     *
22
     * @var array
23
     */
24
    private $query = [];
25
26
    /**
27
     * The "new object" array containing either a full document or a number of
28
     * atomic update operators.
29
     *
30
     * @see docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
31
     * @var array
32
     */
33
    private $newObj = [];
34
35
    /**
36
     * The current field we are operating on.
37
     *
38
     * @var string
39
     */
40
    private $currentField;
41
42
    /**
43
     * The DocumentManager instance for this query
44
     *
45
     * @var DocumentManager
46
     */
47
    private $dm;
48
49
    /**
50
     * The ClassMetadata instance for the document being queried
51
     *
52
     * @var ClassMetadata
53
     */
54
    private $class;
55
56
    /**
57
     * @param DocumentManager $dm
58
     */
59 392
    public function __construct(DocumentManager $dm)
60
    {
61 392
        $this->dm = $dm;
62 392
    }
63
64
    /**
65
     * Add one or more $and clauses to the current query.
66
     *
67
     * @see Builder::addAnd()
68
     * @see http://docs.mongodb.org/manual/reference/operator/and/
69
     * @param array|Expr $expression
70
     * @return $this
71
     */
72 3 View Code Duplication
    public function addAnd($expression /*, $expression2, ... */)
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...
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
     * @return $this
98
     */
99 1 View Code Duplication
    public function addNor($expression /* , $expression2, ... */)
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...
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...
100
    {
101 1
        if (! isset($this->query['$nor'])) {
102 1
            $this->query['$nor'] = [];
103
        }
104
105 1
        $this->query['$nor'] = array_merge(
106 1
            $this->query['$nor'],
107
            array_map(function ($expression) { return $expression instanceof Expr ? $expression->getQuery() : $expression; }, func_get_args())
108
        );
109
110 1
        return $this;
111
    }
112
113
    /**
114
     * Add one or more $or clauses to the current query.
115
     *
116
     * @see Builder::addOr()
117
     * @see http://docs.mongodb.org/manual/reference/operator/or/
118
     * @param array|Expr $expression
119
     * @return $this
120
     */
121 5 View Code Duplication
    public function addOr($expression /* , $expression2, ... */)
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...
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...
122
    {
123 5
        if (! isset($this->query['$or'])) {
124 5
            $this->query['$or'] = [];
125
        }
126
127 5
        $this->query['$or'] = array_merge(
128 5
            $this->query['$or'],
129
            array_map(function ($expression) { return $expression instanceof Expr ? $expression->getQuery() : $expression; }, func_get_args())
130
        );
131
132 5
        return $this;
133
    }
134
135
    /**
136
     * Append one or more values to the current array field only if they do not
137
     * already exist in the array.
138
     *
139
     * If the field does not exist, it will be set to an array containing the
140
     * unique value(s) in the argument. If the field is not an array, the query
141
     * will yield an error.
142
     *
143
     * Multiple values may be specified by provided an Expr object and using
144
     * {@link Expr::each()}.
145
     *
146
     * @see Builder::addToSet()
147
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
148
     * @see http://docs.mongodb.org/manual/reference/operator/each/
149
     * @param mixed|Expr $valueOrExpression
150
     * @return $this
151
     */
152 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...
153
    {
154 5
        if ($valueOrExpression instanceof Expr) {
155 1
            $valueOrExpression = $valueOrExpression->getQuery();
156
        }
157
158 5
        $this->requiresCurrentField();
159 5
        $this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
160 5
        return $this;
161
    }
162
163
    /**
164
     * Specify $all criteria for the current field.
165
     *
166
     * @see Builder::all()
167
     * @see http://docs.mongodb.org/manual/reference/operator/all/
168
     * @param array $values
169
     * @return $this
170
     */
171 2
    public function all(array $values)
172
    {
173 2
        return $this->operator('$all', (array) $values);
174
    }
175
176
    /**
177
     * Apply a bitwise operation on the current field
178
     *
179
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
180
     * @param string $operator
181
     * @param int $value
182
     * @return $this
183
     */
184 3
    protected function bit($operator, $value)
185
    {
186 3
        $this->requiresCurrentField();
187 3
        $this->newObj['$bit'][$this->currentField][$operator] = $value;
188 3
        return $this;
189
    }
190
191
    /**
192
     * Apply a bitwise and operation on the current field.
193
     *
194
     * @see Builder::bitAnd()
195
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
196
     * @param int $value
197
     * @return $this
198
     */
199 1
    public function bitAnd($value)
200
    {
201 1
        return $this->bit('and', $value);
202
    }
203
204
    /**
205
     * Apply a bitwise or operation on the current field.
206
     *
207
     * @see Builder::bitOr()
208
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
209
     * @param int $value
210
     * @return $this
211
     */
212 1
    public function bitOr($value)
213
    {
214 1
        return $this->bit('or', $value);
215
    }
216
217
    /**
218
     * Matches documents where all of the bit positions given by the query are
219
     * clear.
220
     *
221
     * @see Builder::bitsAllClear()
222
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
223
     * @param int|array|\MongoBinData $value
224
     * @return $this
225
     */
226
    public function bitsAllClear($value)
227
    {
228
        $this->requiresCurrentField();
229
        return $this->operator('$bitsAllClear', $value);
230
    }
231
232
    /**
233
     * Matches documents where all of the bit positions given by the query are
234
     * set.
235
     *
236
     * @see Builder::bitsAllSet()
237
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
238
     * @param int|array|\MongoBinData $value
239
     * @return $this
240
     */
241
    public function bitsAllSet($value)
242
    {
243
        $this->requiresCurrentField();
244
        return $this->operator('$bitsAllSet', $value);
245
    }
246
247
    /**
248
     * Matches documents where any of the bit positions given by the query are
249
     * clear.
250
     *
251
     * @see Builder::bitsAnyClear()
252
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
253
     * @param int|array|\MongoBinData $value
254
     * @return $this
255
     */
256
    public function bitsAnyClear($value)
257
    {
258
        $this->requiresCurrentField();
259
        return $this->operator('$bitsAnyClear', $value);
260
    }
261
262
    /**
263
     * Matches documents where any of the bit positions given by the query are
264
     * set.
265
     *
266
     * @see Builder::bitsAnySet()
267
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
268
     * @param int|array|\MongoBinData $value
269
     * @return $this
270
     */
271
    public function bitsAnySet($value)
272
    {
273
        $this->requiresCurrentField();
274
        return $this->operator('$bitsAnySet', $value);
275
    }
276
277
    /**
278
     * Apply a bitwise xor operation on the current field.
279
     *
280
     * @see Builder::bitXor()
281
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
282
     * @param int $value
283
     * @return $this
284
     */
285 1
    public function bitXor($value)
286
    {
287 1
        return $this->bit('xor', $value);
288
    }
289
290
    /**
291
     * A boolean flag to enable or disable case sensitive search for $text
292
     * criteria.
293
     *
294
     * This method must be called after text().
295
     *
296
     * @see Builder::caseSensitive()
297
     * @see http://docs.mongodb.org/manual/reference/operator/text/
298
     * @param bool $caseSensitive
299
     * @return $this
300
     * @throws \BadMethodCallException if the query does not already have $text criteria
301
     *
302
     * @since 1.3
303
     */
304 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...
305
    {
306 3
        if ( ! isset($this->query['$text'])) {
307 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
308
        }
309
310
        // Remove caseSensitive option to keep support for older database versions
311 2
        if ($caseSensitive) {
312 2
            $this->query['$text']['$caseSensitive'] = true;
313 1
        } elseif (isset($this->query['$text']['$caseSensitive'])) {
314 1
            unset($this->query['$text']['$caseSensitive']);
315
        }
316
317 2
        return $this;
318
    }
319
320
    /**
321
     * Associates a comment to any expression taking a query predicate.
322
     *
323
     * @see Builder::comment()
324
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
325
     * @param string $comment
326
     * @return $this
327
     */
328
    public function comment($comment)
329
    {
330
        $this->query['$comment'] = $comment;
331
        return $this;
332
    }
333
334
    /**
335
     * Sets the value of the current field to the current date, either as a date or a timestamp.
336
     *
337
     * @see Builder::currentDate()
338
     * @see http://docs.mongodb.org/manual/reference/operator/update/currentDate/
339
     * @param string $type
340
     * @return $this
341
     * @throws \InvalidArgumentException if an invalid type is given
342
     */
343 3
    public function currentDate($type = 'date')
344
    {
345 3
        if (! in_array($type, ['date', 'timestamp'])) {
346 1
            throw new \InvalidArgumentException('Type for currentDate operator must be date or timestamp.');
347
        }
348
349 2
        $this->requiresCurrentField();
350 2
        $this->newObj['$currentDate'][$this->currentField]['$type'] = $type;
351 2
        return $this;
352
    }
353
354
    /**
355
     * A boolean flag to enable or disable diacritic sensitive search for $text
356
     * criteria.
357
     *
358
     * This method must be called after text().
359
     *
360
     * @see Builder::diacriticSensitive()
361
     * @see http://docs.mongodb.org/manual/reference/operator/text/
362
     * @param bool $diacriticSensitive
363
     * @return $this
364
     * @throws \BadMethodCallException if the query does not already have $text criteria
365
     *
366
     * @since 1.3
367
     */
368 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...
369
    {
370 3
        if ( ! isset($this->query['$text'])) {
371 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
372
        }
373
374
        // Remove diacriticSensitive option to keep support for older database versions
375 2
        if ($diacriticSensitive) {
376 2
            $this->query['$text']['$diacriticSensitive'] = true;
377 1
        } elseif (isset($this->query['$text']['$diacriticSensitive'])) {
378 1
            unset($this->query['$text']['$diacriticSensitive']);
379
        }
380
381 2
        return $this;
382
    }
383
384
    /**
385
     * Add $each criteria to the expression for a $push operation.
386
     *
387
     * @see Expr::push()
388
     * @see http://docs.mongodb.org/manual/reference/operator/each/
389
     * @param array $values
390
     * @return $this
391
     */
392 4
    public function each(array $values)
393
    {
394 4
        return $this->operator('$each', $values);
395
    }
396
397
    /**
398
     * Specify $elemMatch criteria for the current field.
399
     *
400
     * @see Builder::elemMatch()
401
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
402
     * @param array|Expr $expression
403
     * @return $this
404
     */
405 4
    public function elemMatch($expression)
406
    {
407 4
        return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
408
    }
409
410
    /**
411
     * Specify an equality match for the current field.
412
     *
413
     * @see Builder::equals()
414
     * @param mixed $value
415
     * @return $this
416
     */
417 100
    public function equals($value)
418
    {
419 100
        if ($this->currentField) {
420 99
            $this->query[$this->currentField] = $value;
421
        } else {
422 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...
423
        }
424 100
        return $this;
425
    }
426
427
    /**
428
     * Specify $exists criteria for the current field.
429
     *
430
     * @see Builder::exists()
431
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
432
     * @param boolean $bool
433
     * @return $this
434
     */
435 5
    public function exists($bool)
436
    {
437 5
        return $this->operator('$exists', (boolean) $bool);
438
    }
439
440
    /**
441
     * Set the current field for building the expression.
442
     *
443
     * @see Builder::field()
444
     * @param string $field
445
     * @return $this
446
     */
447 191
    public function field($field)
448
    {
449 191
        $this->currentField = (string) $field;
450 191
        return $this;
451
    }
452
453
    /**
454
     * Add $geoIntersects criteria with a GeoJSON geometry to the expression.
455
     *
456
     * The geometry parameter GeoJSON object or an array corresponding to the
457
     * geometry's JSON representation.
458
     *
459
     * @see Builder::geoIntersects()
460
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
461
     * @param array|Geometry $geometry
462
     * @return $this
463
     */
464 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...
465
    {
466 2
        if ($geometry instanceof Geometry) {
467 1
            $geometry = $geometry->jsonSerialize();
468
        }
469
470 2
        return $this->operator('$geoIntersects', ['$geometry' => $geometry]);
471
    }
472
473
    /**
474
     * Add $geoWithin criteria with a GeoJSON geometry to the expression.
475
     *
476
     * The geometry parameter GeoJSON object or an array corresponding to the
477
     * geometry's JSON representation.
478
     *
479
     * @see Builder::geoWithin()
480
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
481
     * @param array|Geometry $geometry
482
     * @return $this
483
     */
484 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...
485
    {
486 2
        if ($geometry instanceof Geometry) {
487 1
            $geometry = $geometry->jsonSerialize();
488
        }
489
490 2
        return $this->operator('$geoWithin', ['$geometry' => $geometry]);
491
    }
492
493
    /**
494
     * Add $geoWithin criteria with a $box shape to the expression.
495
     *
496
     * A rectangular polygon will be constructed from a pair of coordinates
497
     * corresponding to the bottom left and top right corners.
498
     *
499
     * Note: the $box operator only supports legacy coordinate pairs and 2d
500
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
501
     *
502
     * @see Builder::geoWithinBox()
503
     * @see http://docs.mongodb.org/manual/reference/operator/box/
504
     * @param float $x1
505
     * @param float $y1
506
     * @param float $x2
507
     * @param float $y2
508
     * @return $this
509
     */
510 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
511
    {
512 1
        $shape = ['$box' => [[$x1, $y1], [$x2, $y2]]];
513
514 1
        return $this->operator('$geoWithin', $shape);
515
    }
516
517
    /**
518
     * Add $geoWithin criteria with a $center shape to the expression.
519
     *
520
     * Note: the $center operator only supports legacy coordinate pairs and 2d
521
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
522
     *
523
     * @see Builider::geoWithinCenter()
524
     * @see http://docs.mongodb.org/manual/reference/operator/center/
525
     * @param float $x
526
     * @param float $y
527
     * @param float $radius
528
     * @return $this
529
     */
530 1
    public function geoWithinCenter($x, $y, $radius)
531
    {
532 1
        $shape = ['$center' => [[$x, $y], $radius]];
533
534 1
        return $this->operator('$geoWithin', $shape);
535
    }
536
537
    /**
538
     * Add $geoWithin criteria with a $centerSphere shape to the expression.
539
     *
540
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
541
     *
542
     * @see Builder::geoWithinCenterSphere()
543
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
544
     * @param float $x
545
     * @param float $y
546
     * @param float $radius
547
     * @return $this
548
     */
549 1
    public function geoWithinCenterSphere($x, $y, $radius)
550
    {
551 1
        $shape = ['$centerSphere' => [[$x, $y], $radius]];
552
553 1
        return $this->operator('$geoWithin', $shape);
554
    }
555
556
    /**
557
     * Add $geoWithin criteria with a $polygon shape to the expression.
558
     *
559
     * Point coordinates are in x, y order (easting, northing for projected
560
     * coordinates, longitude, latitude for geographic coordinates).
561
     *
562
     * The last point coordinate is implicitly connected with the first.
563
     *
564
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
565
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
566
     *
567
     * @see Builder::geoWithinPolygon()
568
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
569
     * @param array $point,... Three or more point coordinate tuples
0 ignored issues
show
Bug introduced by
There is no parameter named $point,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
570
     * @return $this
571
     * @throws \InvalidArgumentException if less than three points are given
572
     */
573 2
    public function geoWithinPolygon(/* array($x1, $y1), ... */)
574
    {
575 2
        if (func_num_args() < 3) {
576 1
            throw new \InvalidArgumentException('Polygon must be defined by three or more points.');
577
        }
578
579 1
        $shape = ['$polygon' => func_get_args()];
580
581 1
        return $this->operator('$geoWithin', $shape);
582
    }
583
584
    /**
585
     * Return the current field.
586
     *
587
     * @return string
588
     */
589 2
    public function getCurrentField()
590
    {
591 2
        return $this->currentField;
592
    }
593
594
    /**
595
     * Gets prepared newObj part of expression.
596
     *
597
     * @return array
598
     */
599 174
    public function getNewObj()
600
    {
601 174
        return $this->dm->getUnitOfWork()
602 174
            ->getDocumentPersister($this->class->name)
603 174
            ->prepareQueryOrNewObj($this->newObj, true);
604
    }
605
606
    /**
607
     * Gets prepared query part of expression.
608
     *
609
     * @return array
610
     */
611 250
    public function getQuery()
612
    {
613 250
        return $this->dm->getUnitOfWork()
614 250
            ->getDocumentPersister($this->class->name)
615 250
            ->prepareQueryOrNewObj($this->query);
616
    }
617
618
    /**
619
     * Specify $gt criteria for the current field.
620
     *
621
     * @see Builder::gt()
622
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
623
     * @param mixed $value
624
     * @return $this
625
     */
626 2
    public function gt($value)
627
    {
628 2
        return $this->operator('$gt', $value);
629
    }
630
631
    /**
632
     * Specify $gte criteria for the current field.
633
     *
634
     * @see Builder::gte()
635
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
636
     * @param mixed $value
637
     * @return $this
638
     */
639 2
    public function gte($value)
640
    {
641 2
        return $this->operator('$gte', $value);
642
    }
643
644
    /**
645
     * Specify $in criteria for the current field.
646
     *
647
     * @see Builder::in()
648
     * @see http://docs.mongodb.org/manual/reference/operator/in/
649
     * @param array $values
650
     * @return $this
651
     */
652 31
    public function in(array $values)
653
    {
654 31
        return $this->operator('$in', array_values($values));
655
    }
656
657
    /**
658
     * Increment the current field.
659
     *
660
     * If the field does not exist, it will be set to this value.
661
     *
662
     * @see Builder::inc()
663
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
664
     * @param float|integer $value
665
     * @return $this
666
     */
667 5
    public function inc($value)
668
    {
669 5
        $this->requiresCurrentField();
670 5
        $this->newObj['$inc'][$this->currentField] = $value;
671 5
        return $this;
672
    }
673
674
    /**
675
     * Checks that the current field includes a reference to the supplied document.
676
     *
677
     * @param object $document
678
     * @return Expr
679
     */
680 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...
681
    {
682 6
        $this->requiresCurrentField();
683 6
        $mapping = $this->getReferenceMapping();
684 4
        $reference = $this->dm->createReference($document, $mapping);
685 4
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
686
687
        switch ($storeAs) {
688 4
            case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
689 2
                $this->query[$mapping['name']] = $reference;
690 2
                return $this;
691
                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...
692
693 3
            case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
694
                $keys = ['id' => true];
695
                break;
696
697 3
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF:
698 1
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB:
699 3
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
700
701 3
                if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF) {
702 2
                    unset($keys['$db']);
703
                }
704
705 3
                if (isset($mapping['targetDocument'])) {
706 1
                    unset($keys['$ref'], $keys['$db']);
707
                }
708 3
                break;
709
710
            default:
711
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
712
        }
713
714 3
        foreach ($keys as $key => $value) {
715 3
            $this->query[$mapping['name']]['$elemMatch'][$key] = $reference[$key];
716
        }
717
718 3
        return $this;
719
    }
720
721
    /**
722
     * Set the $language option for $text criteria.
723
     *
724
     * This method must be called after text().
725
     *
726
     * @see Builder::language()
727
     * @see http://docs.mongodb.org/manual/reference/operator/text/
728
     * @param string $language
729
     * @return $this
730
     * @throws \BadMethodCallException if the query does not already have $text criteria
731
     */
732 2
    public function language($language)
733
    {
734 2
        if ( ! isset($this->query['$text'])) {
735 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
736
        }
737
738 1
        $this->query['$text']['$language'] = (string) $language;
739
740 1
        return $this;
741
    }
742
743
    /**
744
     * Specify $lt criteria for the current field.
745
     *
746
     * @see Builder::lte()
747
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
748
     * @param mixed $value
749
     * @return $this
750
     */
751 4
    public function lt($value)
752
    {
753 4
        return $this->operator('$lt', $value);
754
    }
755
756
    /**
757
     * Specify $lte criteria for the current field.
758
     *
759
     * @see Builder::lte()
760
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
761
     * @param mixed $value
762
     * @return $this
763
     */
764 2
    public function lte($value)
765
    {
766 2
        return $this->operator('$lte', $value);
767
    }
768
769
    /**
770
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
771
     *
772
     * @see Builder::max()
773
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
774
     * @param mixed $value
775
     * @return $this
776
     */
777
    public function max($value)
778
    {
779
        $this->requiresCurrentField();
780
        $this->newObj['$max'][$this->currentField] = $value;
781
        return $this;
782
    }
783
784
    /**
785
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
786
     *
787
     * @see Builder::min()
788
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
789
     * @param mixed $value
790
     * @return $this
791
     */
792
    public function min($value)
793
    {
794
        $this->requiresCurrentField();
795
        $this->newObj['$min'][$this->currentField] = $value;
796
        return $this;
797
    }
798
799
    /**
800
     * Specify $mod criteria for the current field.
801
     *
802
     * @see Builder::mod()
803
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
804
     * @param float|integer $divisor
805
     * @param float|integer $remainder
806
     * @return $this
807
     */
808
    public function mod($divisor, $remainder = 0)
809
    {
810
        return $this->operator('$mod', [$divisor, $remainder]);
811
    }
812
813
    /**
814
     * Multiply the current field.
815
     *
816
     * If the field does not exist, it will be set to 0.
817
     *
818
     * @see Builder::mul()
819
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
820
     * @param float|integer $value
821
     * @return $this
822
     */
823
    public function mul($value)
824
    {
825
        $this->requiresCurrentField();
826
        $this->newObj['$mul'][$this->currentField] = $value;
827
        return $this;
828
    }
829
830
    /**
831
     * Add $near criteria to the expression.
832
     *
833
     * A GeoJSON point may be provided as the first and only argument for
834
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
835
     * an array corresponding to the point's JSON representation.
836
     *
837
     * @see Builder::near()
838
     * @see http://docs.mongodb.org/manual/reference/operator/near/
839
     * @param float|array|Point $x
840
     * @param float $y
841
     * @return $this
842
     */
843 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...
844
    {
845 3
        if ($x instanceof Point) {
846 1
            $x = $x->jsonSerialize();
847
        }
848
849 3
        if (is_array($x)) {
850 2
            return $this->operator('$near', ['$geometry' => $x]);
851
        }
852
853 1
        return $this->operator('$near', [$x, $y]);
854
    }
855
856
    /**
857
     * Add $nearSphere criteria to the expression.
858
     *
859
     * A GeoJSON point may be provided as the first and only argument for
860
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
861
     * an array corresponding to the point's JSON representation.
862
     *
863
     * @see Builder::nearSphere()
864
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
865
     * @param float|array|Point $x
866
     * @param float $y
867
     * @return $this
868
     */
869 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...
870
    {
871 3
        if ($x instanceof Point) {
872 1
            $x = $x->jsonSerialize();
873
        }
874
875 3
        if (is_array($x)) {
876 2
            return $this->operator('$nearSphere', ['$geometry' => $x]);
877
        }
878
879 1
        return $this->operator('$nearSphere', [$x, $y]);
880
    }
881
882
    /**
883
     * Negates an expression for the current field.
884
     *
885
     * @see Builder::not()
886
     * @see http://docs.mongodb.org/manual/reference/operator/not/
887
     * @param array|Expr $expression
888
     * @return $this
889
     */
890 2
    public function not($expression)
891
    {
892 2
        return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
893
    }
894
895
    /**
896
     * Specify $ne criteria for the current field.
897
     *
898
     * @see Builder::notEqual()
899
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
900
     * @param mixed $value
901
     * @return $this
902
     */
903 6
    public function notEqual($value)
904
    {
905 6
        return $this->operator('$ne', $value);
906
    }
907
908
    /**
909
     * Specify $nin criteria for the current field.
910
     *
911
     * @see Builder::notIn()
912
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
913
     * @param array $values
914
     * @return $this
915
     */
916 6
    public function notIn(array $values)
917
    {
918 6
        return $this->operator('$nin', array_values($values));
919
    }
920
921
    /**
922
     * Defines an operator and value on the expression.
923
     *
924
     * If there is a current field, the operator will be set on it; otherwise,
925
     * the operator is set at the top level of the query.
926
     *
927
     * @param string $operator
928
     * @param mixed $value
929
     * @return $this
930
     */
931 81
    public function operator($operator, $value)
932
    {
933 81
        $this->wrapEqualityCriteria();
934
935 81
        if ($this->currentField) {
936 56
            $this->query[$this->currentField][$operator] = $value;
937
        } else {
938 27
            $this->query[$operator] = $value;
939
        }
940 81
        return $this;
941
    }
942
943
    /**
944
     * Remove the first element from the current array field.
945
     *
946
     * @see Builder::popFirst()
947
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
948
     * @return $this
949
     */
950 1
    public function popFirst()
951
    {
952 1
        $this->requiresCurrentField();
953 1
        $this->newObj['$pop'][$this->currentField] = 1;
954 1
        return $this;
955
    }
956
957
    /**
958
     * Remove the last element from the current array field.
959
     *
960
     * @see Builder::popLast()
961
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
962
     * @return $this
963
     */
964
    public function popLast()
965
    {
966
        $this->requiresCurrentField();
967
        $this->newObj['$pop'][$this->currentField] = -1;
968
        return $this;
969
    }
970
971
    /**
972
     * Add $position criteria to the expression for a $push operation.
973
     *
974
     * This is useful in conjunction with {@link Expr::each()} for a
975
     * {@link Expr::push()} operation.
976
     *
977
     * @see http://docs.mongodb.org/manual/reference/operator/update/position/
978
     * @param integer $position
979
     * @return $this
980
     */
981 1
    public function position($position)
982
    {
983 1
        return $this->operator('$position', $position);
984
    }
985
986
    /**
987
     * Remove all elements matching the given value or expression from the
988
     * current array field.
989
     *
990
     * @see Builder::pull()
991
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
992
     * @param mixed|Expr $valueOrExpression
993
     * @return $this
994
     */
995 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...
996
    {
997 2
        if ($valueOrExpression instanceof Expr) {
998 1
            $valueOrExpression = $valueOrExpression->getQuery();
999
        }
1000
1001 2
        $this->requiresCurrentField();
1002 2
        $this->newObj['$pull'][$this->currentField] = $valueOrExpression;
1003 2
        return $this;
1004
    }
1005
1006
    /**
1007
     * Remove all elements matching any of the given values from the current
1008
     * array field.
1009
     *
1010
     * @see Builder::pullAll()
1011
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1012
     * @param array $values
1013
     * @return $this
1014
     */
1015
    public function pullAll(array $values)
1016
    {
1017
        $this->requiresCurrentField();
1018
        $this->newObj['$pullAll'][$this->currentField] = $values;
1019
        return $this;
1020
    }
1021
1022
    /**
1023
     * Append one or more values to the current array field.
1024
     *
1025
     * If the field does not exist, it will be set to an array containing the
1026
     * value(s) in the argument. If the field is not an array, the query
1027
     * will yield an error.
1028
     *
1029
     * Multiple values may be specified by providing an Expr object and using
1030
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1031
     * also be used to limit and order array elements, respectively.
1032
     *
1033
     * @see Builder::push()
1034
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1035
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1036
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1037
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1038
     * @param mixed|Expr $valueOrExpression
1039
     * @return $this
1040
     */
1041 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...
1042
    {
1043 8
        if ($valueOrExpression instanceof Expr) {
1044 3
            $valueOrExpression = array_merge(
1045 3
                ['$each' => []],
1046 3
                $valueOrExpression->getQuery()
1047
            );
1048
        }
1049
1050 8
        $this->requiresCurrentField();
1051 8
        $this->newObj['$push'][$this->currentField] = $valueOrExpression;
1052 8
        return $this;
1053
    }
1054
1055
    /**
1056
     * Specify $gte and $lt criteria for the current field.
1057
     *
1058
     * This method is shorthand for specifying $gte criteria on the lower bound
1059
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1060
     *
1061
     * @see Builder::range()
1062
     * @param mixed $start
1063
     * @param mixed $end
1064
     * @return $this
1065
     */
1066 2
    public function range($start, $end)
1067
    {
1068 2
        return $this->operator('$gte', $start)->operator('$lt', $end);
1069
    }
1070
1071
    /**
1072
     * Checks that the value of the current field is a reference to the supplied document.
1073
     *
1074
     * @param object $document
1075
     * @return Expr
1076
     */
1077 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...
1078
    {
1079 13
        $this->requiresCurrentField();
1080 13
        $mapping = $this->getReferenceMapping();
1081 11
        $reference = $this->dm->createReference($document, $mapping);
1082 11
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
1083
1084
        switch ($storeAs) {
1085 11
            case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
1086 4
                $this->query[$mapping['name']] = $reference;
1087 4
                return $this;
1088
                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...
1089
1090 8
            case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
1091
                $keys = ['id' => true];
1092
                break;
1093
1094 8
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF:
1095 3
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB:
1096 8
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
1097
1098 8
                if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF) {
1099 5
                    unset($keys['$db']);
1100
                }
1101
1102 8
                if (isset($mapping['targetDocument'])) {
1103 4
                    unset($keys['$ref'], $keys['$db']);
1104
                }
1105 8
                break;
1106
1107
            default:
1108
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
1109
        }
1110
1111 8
        foreach ($keys as $key => $value) {
1112 8
            $this->query[$mapping['name'] . '.' . $key] = $reference[$key];
1113
        }
1114
1115 8
        return $this;
1116
    }
1117
1118
    /**
1119
     * Rename the current field.
1120
     *
1121
     * @see Builder::rename()
1122
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1123
     * @param string $name
1124
     * @return $this
1125
     */
1126
    public function rename($name)
1127
    {
1128
        $this->requiresCurrentField();
1129
        $this->newObj['$rename'][$this->currentField] = $name;
1130
        return $this;
1131
    }
1132
1133
    /**
1134
     * Set the current field to a value.
1135
     *
1136
     * This is only relevant for insert, update, or findAndUpdate queries. For
1137
     * update and findAndUpdate queries, the $atomic parameter will determine
1138
     * whether or not a $set operator is used.
1139
     *
1140
     * @see Builder::set()
1141
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1142
     * @param mixed $value
1143
     * @param boolean $atomic
1144
     * @return $this
1145
     */
1146 19
    public function set($value, $atomic = true)
1147
    {
1148 19
        $this->requiresCurrentField();
1149
1150 19
        if ($atomic) {
1151 16
            $this->newObj['$set'][$this->currentField] = $value;
1152 16
            return $this;
1153
        }
1154
1155 3
        if (strpos($this->currentField, '.') === false) {
1156 2
            $this->newObj[$this->currentField] = $value;
1157 2
            return $this;
1158
        }
1159
1160 2
        $keys = explode('.', $this->currentField);
1161 2
        $current = &$this->newObj;
1162 2
        foreach ($keys as $key) {
1163 2
            $current = &$current[$key];
1164
        }
1165 2
        $current = $value;
1166
1167 2
        return $this;
1168
    }
1169
1170
    /**
1171
     * Sets ClassMetadata for document being queried.
1172
     *
1173
     * @param ClassMetadata $class
1174
     */
1175 390
    public function setClassMetadata(ClassMetadata $class)
1176
    {
1177 390
        $this->class = $class;
1178 390
    }
1179
1180
    /**
1181
     * Set the "new object".
1182
     *
1183
     * @see Builder::setNewObj()
1184
     * @param array $newObj
1185
     * @return $this
1186
     */
1187
    public function setNewObj(array $newObj)
1188
    {
1189
        $this->newObj = $newObj;
1190
        return $this;
1191
    }
1192
1193
    /**
1194
     * Set the current field to the value if the document is inserted in an
1195
     * upsert operation.
1196
     *
1197
     * If an update operation with upsert: true results in an insert of a
1198
     * document, then $setOnInsert assigns the specified values to the fields in
1199
     * the document. If the update operation does not result in an insert,
1200
     * $setOnInsert does nothing.
1201
     *
1202
     * @see Builder::setOnInsert()
1203
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1204
     * @param mixed $value
1205
     * @return $this
1206
     */
1207 1
    public function setOnInsert($value)
1208
    {
1209 1
        $this->requiresCurrentField();
1210 1
        $this->newObj['$setOnInsert'][$this->currentField] = $value;
1211
1212 1
        return $this;
1213
    }
1214
1215
    /**
1216
     * Set the query criteria.
1217
     *
1218
     * @see Builder::setQueryArray()
1219
     * @param array $query
1220
     * @return $this
1221
     */
1222 18
    public function setQuery(array $query)
1223
    {
1224 18
        $this->query = $query;
1225 18
        return $this;
1226
    }
1227
1228
    /**
1229
     * Specify $size criteria for the current field.
1230
     *
1231
     * @see Builder::size()
1232
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1233
     * @param integer $size
1234
     * @return $this
1235
     */
1236
    public function size($size)
1237
    {
1238
        return $this->operator('$size', (integer) $size);
1239
    }
1240
1241
    /**
1242
     * Add $slice criteria to the expression for a $push operation.
1243
     *
1244
     * This is useful in conjunction with {@link Expr::each()} for a
1245
     * {@link Expr::push()} operation. {@link Builder::selectSlice()} should be
1246
     * used for specifying $slice for a query projection.
1247
     *
1248
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1249
     * @param integer $slice
1250
     * @return $this
1251
     */
1252 2
    public function slice($slice)
1253
    {
1254 2
        return $this->operator('$slice', $slice);
1255
    }
1256
1257
    /**
1258
     * Add $sort criteria to the expression for a $push operation.
1259
     *
1260
     * If sorting by multiple fields, the first argument should be an array of
1261
     * field name (key) and order (value) pairs.
1262
     *
1263
     * This is useful in conjunction with {@link Expr::each()} for a
1264
     * {@link Expr::push()} operation. {@link Builder::sort()} should be used to
1265
     * sort the results of a query.
1266
     *
1267
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1268
     * @param array|string $fieldName Field name or array of field/order pairs
1269
     * @param int|string $order       Field order (if one field is specified)
1270
     * @return $this
1271
     */
1272 2
    public function sort($fieldName, $order = null)
1273
    {
1274 2
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1275
1276 2
        return $this->operator('$sort', array_map([$this, 'normalizeSortOrder'], $fields));
1277
    }
1278
1279
    /**
1280
     * Specify $text criteria for the current query.
1281
     *
1282
     * The $language option may be set with {@link Expr::language()}.
1283
     *
1284
     * @see Builder::text()
1285
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1286
     * @param string $search
1287
     * @return $this
1288
     */
1289 6
    public function text($search)
1290
    {
1291 6
        $this->query['$text'] = ['$search' => (string) $search];
1292 6
        return $this;
1293
    }
1294
1295
    /**
1296
     * Specify $type criteria for the current field.
1297
     *
1298
     * @todo Remove support for string $type argument in 2.0
1299
     * @see Builder::type()
1300
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1301
     * @param integer $type
1302
     * @return $this
1303
     */
1304 1
    public function type($type)
1305
    {
1306 1
        if (is_string($type)) {
1307
            $map = [
1308 1
                'double' => 1,
1309
                'string' => 2,
1310
                'object' => 3,
1311
                'array' => 4,
1312
                'binary' => 5,
1313
                'undefined' => 6,
1314
                'objectid' => 7,
1315
                'boolean' => 8,
1316
                'date' => 9,
1317
                'null' => 10,
1318
                'regex' => 11,
1319
                'jscode' => 13,
1320
                'symbol' => 14,
1321
                'jscodewithscope' => 15,
1322
                'integer32' => 16,
1323
                'timestamp' => 17,
1324
                'integer64' => 18,
1325
                'maxkey' => 127,
1326
                'minkey' => 255,
1327
            ];
1328
1329 1
            $type = $map[$type] ?? $type;
1330
        }
1331
1332 1
        return $this->operator('$type', $type);
1333
    }
1334
1335
    /**
1336
     * Unset the current field.
1337
     *
1338
     * The field will be removed from the document (not set to null).
1339
     *
1340
     * @see Builder::unsetField()
1341
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1342
     * @return $this
1343
     */
1344 3
    public function unsetField()
1345
    {
1346 3
        $this->requiresCurrentField();
1347 3
        $this->newObj['$unset'][$this->currentField] = 1;
1348 3
        return $this;
1349
    }
1350
1351
    /**
1352
     * Specify a JavaScript expression to use for matching documents.
1353
     *
1354
     * @see Builder::where()
1355
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1356
     * @param string|\MongoCode $javascript
1357
     * @return $this
1358
     */
1359 3
    public function where($javascript)
1360
    {
1361 3
        $this->query['$where'] = $javascript;
1362 3
        return $this;
1363
    }
1364
1365
    /**
1366
     * Gets reference mapping for current field from current class or its descendants.
1367
     *
1368
     * @return array
1369
     * @throws MappingException
1370
     */
1371 19
    private function getReferenceMapping()
1372
    {
1373 19
        $mapping = null;
1374
        try {
1375 19
            $mapping = $this->class->getFieldMapping($this->currentField);
1376 6
        } catch (MappingException $e) {
1377 6
            if (empty($this->class->discriminatorMap)) {
1378
                throw $e;
1379
            }
1380 6
            $foundIn = null;
1381 6
            foreach ($this->class->discriminatorMap as $child) {
1382 6
                $childClass = $this->dm->getClassMetadata($child);
1383 6
                if ($childClass->hasAssociation($this->currentField)) {
1384 4
                    if ($mapping !== null && $mapping !== $childClass->getFieldMapping($this->currentField)) {
1385 2
                        throw MappingException::referenceFieldConflict($this->currentField, $foundIn->name, $childClass->name);
1386
                    }
1387 4
                    $mapping = $childClass->getFieldMapping($this->currentField);
1388 6
                    $foundIn = $childClass;
1389
                }
1390
            }
1391 4
            if ($mapping === null) {
1392 2
                throw MappingException::mappingNotFoundInClassNorDescendants($this->class->name, $this->currentField);
1393
            }
1394
        }
1395 15
        return $mapping;
1396
    }
1397
1398
    /**
1399
     * @param int|string $order
1400
     *
1401
     * @return int
1402
     */
1403 2
    private function normalizeSortOrder($order): int
1404
    {
1405 2
        if (is_string($order)) {
1406
            $order = strtolower($order) === 'asc' ? 1 : -1;
1407
        }
1408
1409 2
        return (int) $order;
1410
    }
1411
1412
    /**
1413
     * Ensure that a current field has been set.
1414
     *
1415
     * @throws \LogicException if a current field has not been set
1416
     */
1417 67
    private function requiresCurrentField()
1418
    {
1419 67
        if ( ! $this->currentField) {
1420
            throw new \LogicException('This method requires you set a current field using field().');
1421
        }
1422 67
    }
1423
1424
    /**
1425
     * Wraps equality criteria with an operator.
1426
     *
1427
     * If equality criteria was previously specified for a field, it cannot be
1428
     * merged with other operators without first being wrapped in an operator of
1429
     * its own. Ideally, we would wrap it with $eq, but that is only available
1430
     * in MongoDB 2.8. Using a single-element $in is backwards compatible.
1431
     *
1432
     * @see Expr::operator()
1433
     */
1434 81
    private function wrapEqualityCriteria()
1435
    {
1436
        /* If the current field has no criteria yet, do nothing. This ensures
1437
         * that we do not inadvertently inject {"$in": null} into the query.
1438
         */
1439 81
        if ($this->currentField && ! isset($this->query[$this->currentField]) && ! array_key_exists($this->currentField, $this->query)) {
1440 55
            return;
1441
        }
1442
1443 31
        if ($this->currentField) {
1444 5
            $query = &$this->query[$this->currentField];
1445
        } else {
1446 27
            $query = &$this->query;
1447
        }
1448
1449
        /* If the query is an empty array, we'll assume that the user has not
1450
         * specified criteria. Otherwise, check if the array includes a query
1451
         * operator (checking the first key is sufficient). If neither of these
1452
         * conditions are met, we'll wrap the query value with $in.
1453
         */
1454 31
        if (is_array($query) && (empty($query) || strpos(key($query), '$') === 0)) {
1455 31
            return;
1456
        }
1457
1458 2
        $query = ['$in' => [$query]];
1459 2
    }
1460
}
1461