Completed
Push — master ( 08b9e1...e0c601 )
by Andreas
13s
created

Expr::wrapEqualityCriteria()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 10
cts 10
cp 1
rs 8.4444
c 0
b 0
f 0
cc 8
nc 5
nop 0
crap 8
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Query;
6
7
use Doctrine\ODM\MongoDB\DocumentManager;
8
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
9
use Doctrine\ODM\MongoDB\Mapping\MappingException;
10
use GeoJson\Geometry\Geometry;
11
use GeoJson\Geometry\Point;
12
use MongoDB\BSON\Binary;
13
use MongoDB\BSON\Javascript;
14
use function array_key_exists;
15
use function array_map;
16
use function array_merge;
17
use function array_values;
18
use function explode;
19
use function func_get_args;
20
use function in_array;
21
use function is_array;
22
use function is_string;
23
use function key;
24
use function sprintf;
25
use function strpos;
26
use function strtolower;
27
28
/**
29
 * Query expression builder for ODM.
30
 *
31
 */
32
class Expr
33
{
34
    /**
35
     * The query criteria array.
36
     *
37
     * @var array
38
     */
39
    private $query = [];
40
41
    /**
42
     * The "new object" array containing either a full document or a number of
43
     * atomic update operators.
44
     *
45
     * @see docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
46
     * @var array
47
     */
48
    private $newObj = [];
49
50
    /**
51
     * The current field we are operating on.
52
     *
53
     * @var string
54
     */
55
    private $currentField;
56
57
    /**
58
     * The DocumentManager instance for this query
59
     *
60
     * @var DocumentManager
61
     */
62
    private $dm;
63
64
    /**
65
     * The ClassMetadata instance for the document being queried
66
     *
67
     * @var ClassMetadata
68
     */
69
    private $class;
70
71 393
    public function __construct(DocumentManager $dm)
72
    {
73 393
        $this->dm = $dm;
74 393
    }
75
76
    /**
77
     * Add one or more $and clauses to the current query.
78
     *
79
     * @see Builder::addAnd()
80
     * @see http://docs.mongodb.org/manual/reference/operator/and/
81
     * @param array|Expr $expression
82
     * @param array|Expr ...$expressions
83
     * @return $this
84
     */
85 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...
86
    {
87 3
        if (! isset($this->query['$and'])) {
88 3
            $this->query['$and'] = [];
89
        }
90
91 3
        $this->query['$and'] = array_merge(
92 3
            $this->query['$and'],
93 3
            array_map(
94
                function ($expression) {
95 3
                    return $expression instanceof Expr ? $expression->getQuery() : $expression;
96 3
                },
97 3
                func_get_args()
98
            )
99
        );
100
101 3
        return $this;
102
    }
103
104
    /**
105
     * Add one or more $nor clauses to the current query.
106
     *
107
     * @see Builder::addNor()
108
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
109
     * @param array|Expr $expression
110
     * @param array|Expr ...$expressions
111
     * @return $this
112
     */
113 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...
114
    {
115 1
        if (! isset($this->query['$nor'])) {
116 1
            $this->query['$nor'] = [];
117
        }
118
119 1
        $this->query['$nor'] = array_merge(
120 1
            $this->query['$nor'],
121
            array_map(function ($expression) {
122 1
                return $expression instanceof Expr ? $expression->getQuery() : $expression;
123 1
            }, func_get_args())
124
        );
125
126 1
        return $this;
127
    }
128
129
    /**
130
     * Add one or more $or clauses to the current query.
131
     *
132
     * @see Builder::addOr()
133
     * @see http://docs.mongodb.org/manual/reference/operator/or/
134
     * @param array|Expr $expression
135
     * @param array|Expr ...$expressions
136
     * @return $this
137
     */
138 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...
139
    {
140 5
        if (! isset($this->query['$or'])) {
141 5
            $this->query['$or'] = [];
142
        }
143
144 5
        $this->query['$or'] = array_merge(
145 5
            $this->query['$or'],
146
            array_map(function ($expression) {
147 5
                return $expression instanceof Expr ? $expression->getQuery() : $expression;
148 5
            }, func_get_args())
149
        );
150
151 5
        return $this;
152
    }
153
154
    /**
155
     * Append one or more values to the current array field only if they do not
156
     * already exist in the array.
157
     *
158
     * If the field does not exist, it will be set to an array containing the
159
     * unique value(s) in the argument. If the field is not an array, the query
160
     * will yield an error.
161
     *
162
     * Multiple values may be specified by provided an Expr object and using
163
     * {@link Expr::each()}.
164
     *
165
     * @see Builder::addToSet()
166
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
167
     * @see http://docs.mongodb.org/manual/reference/operator/each/
168
     * @param mixed|Expr $valueOrExpression
169
     * @return $this
170
     */
171 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...
172
    {
173 5
        if ($valueOrExpression instanceof Expr) {
174 1
            $valueOrExpression = $valueOrExpression->getQuery();
175
        }
176
177 5
        $this->requiresCurrentField();
178 5
        $this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
179 5
        return $this;
180
    }
181
182
    /**
183
     * Specify $all criteria for the current field.
184
     *
185
     * @see Builder::all()
186
     * @see http://docs.mongodb.org/manual/reference/operator/all/
187
     * @param array $values
188
     * @return $this
189
     */
190 2
    public function all(array $values)
191
    {
192 2
        return $this->operator('$all', (array) $values);
193
    }
194
195
    /**
196
     * Apply a bitwise operation on the current field
197
     *
198
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
199
     * @param string $operator
200
     * @param int    $value
201
     * @return $this
202
     */
203 3
    protected function bit($operator, $value)
204
    {
205 3
        $this->requiresCurrentField();
206 3
        $this->newObj['$bit'][$this->currentField][$operator] = $value;
207 3
        return $this;
208
    }
209
210
    /**
211
     * Apply a bitwise and operation on the current field.
212
     *
213
     * @see Builder::bitAnd()
214
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
215
     * @param int $value
216
     * @return $this
217
     */
218 1
    public function bitAnd($value)
219
    {
220 1
        return $this->bit('and', $value);
221
    }
222
223
    /**
224
     * Apply a bitwise or operation on the current field.
225
     *
226
     * @see Builder::bitOr()
227
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
228
     * @param int $value
229
     * @return $this
230
     */
231 1
    public function bitOr($value)
232
    {
233 1
        return $this->bit('or', $value);
234
    }
235
236
    /**
237
     * Matches documents where all of the bit positions given by the query are
238
     * clear.
239
     *
240
     * @see Builder::bitsAllClear()
241
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
242
     * @param int|array|Binary $value
243
     * @return $this
244
     */
245
    public function bitsAllClear($value)
246
    {
247
        $this->requiresCurrentField();
248
        return $this->operator('$bitsAllClear', $value);
249
    }
250
251
    /**
252
     * Matches documents where all of the bit positions given by the query are
253
     * set.
254
     *
255
     * @see Builder::bitsAllSet()
256
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
257
     * @param int|array|Binary $value
258
     * @return $this
259
     */
260
    public function bitsAllSet($value)
261
    {
262
        $this->requiresCurrentField();
263
        return $this->operator('$bitsAllSet', $value);
264
    }
265
266
    /**
267
     * Matches documents where any of the bit positions given by the query are
268
     * clear.
269
     *
270
     * @see Builder::bitsAnyClear()
271
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
272
     * @param int|array|Binary $value
273
     * @return $this
274
     */
275
    public function bitsAnyClear($value)
276
    {
277
        $this->requiresCurrentField();
278
        return $this->operator('$bitsAnyClear', $value);
279
    }
280
281
    /**
282
     * Matches documents where any of the bit positions given by the query are
283
     * set.
284
     *
285
     * @see Builder::bitsAnySet()
286
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
287
     * @param int|array|Binary $value
288
     * @return $this
289
     */
290
    public function bitsAnySet($value)
291
    {
292
        $this->requiresCurrentField();
293
        return $this->operator('$bitsAnySet', $value);
294
    }
295
296
    /**
297
     * Apply a bitwise xor operation on the current field.
298
     *
299
     * @see Builder::bitXor()
300
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
301
     * @param int $value
302
     * @return $this
303
     */
304 1
    public function bitXor($value)
305
    {
306 1
        return $this->bit('xor', $value);
307
    }
308
309
    /**
310
     * A boolean flag to enable or disable case sensitive search for $text
311
     * criteria.
312
     *
313
     * This method must be called after text().
314
     *
315
     * @see Builder::caseSensitive()
316
     * @see http://docs.mongodb.org/manual/reference/operator/text/
317
     * @param bool $caseSensitive
318
     * @return $this
319
     * @throws \BadMethodCallException If the query does not already have $text criteria.
320
     *
321
     */
322 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...
323
    {
324 3
        if (! isset($this->query['$text'])) {
325 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
326
        }
327
328
        // Remove caseSensitive option to keep support for older database versions
329 2
        if ($caseSensitive) {
330 2
            $this->query['$text']['$caseSensitive'] = true;
331 1
        } elseif (isset($this->query['$text']['$caseSensitive'])) {
332 1
            unset($this->query['$text']['$caseSensitive']);
333
        }
334
335 2
        return $this;
336
    }
337
338
    /**
339
     * Associates a comment to any expression taking a query predicate.
340
     *
341
     * @see Builder::comment()
342
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
343
     * @param string $comment
344
     * @return $this
345
     */
346
    public function comment($comment)
347
    {
348
        $this->query['$comment'] = $comment;
349
        return $this;
350
    }
351
352
    /**
353
     * Sets the value of the current field to the current date, either as a date or a timestamp.
354
     *
355
     * @see Builder::currentDate()
356
     * @see http://docs.mongodb.org/manual/reference/operator/update/currentDate/
357
     * @param string $type
358
     * @return $this
359
     * @throws \InvalidArgumentException If an invalid type is given.
360
     */
361 3
    public function currentDate($type = 'date')
362
    {
363 3
        if (! in_array($type, ['date', 'timestamp'])) {
364 1
            throw new \InvalidArgumentException('Type for currentDate operator must be date or timestamp.');
365
        }
366
367 2
        $this->requiresCurrentField();
368 2
        $this->newObj['$currentDate'][$this->currentField]['$type'] = $type;
369 2
        return $this;
370
    }
371
372
    /**
373
     * A boolean flag to enable or disable diacritic sensitive search for $text
374
     * criteria.
375
     *
376
     * This method must be called after text().
377
     *
378
     * @see Builder::diacriticSensitive()
379
     * @see http://docs.mongodb.org/manual/reference/operator/text/
380
     * @param bool $diacriticSensitive
381
     * @return $this
382
     * @throws \BadMethodCallException If the query does not already have $text criteria.
383
     *
384
     */
385 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...
386
    {
387 3
        if (! isset($this->query['$text'])) {
388 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
389
        }
390
391
        // Remove diacriticSensitive option to keep support for older database versions
392 2
        if ($diacriticSensitive) {
393 2
            $this->query['$text']['$diacriticSensitive'] = true;
394 1
        } elseif (isset($this->query['$text']['$diacriticSensitive'])) {
395 1
            unset($this->query['$text']['$diacriticSensitive']);
396
        }
397
398 2
        return $this;
399
    }
400
401
    /**
402
     * Add $each criteria to the expression for a $push operation.
403
     *
404
     * @see Expr::push()
405
     * @see http://docs.mongodb.org/manual/reference/operator/each/
406
     * @param array $values
407
     * @return $this
408
     */
409 4
    public function each(array $values)
410
    {
411 4
        return $this->operator('$each', $values);
412
    }
413
414
    /**
415
     * Specify $elemMatch criteria for the current field.
416
     *
417
     * @see Builder::elemMatch()
418
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
419
     * @param array|Expr $expression
420
     * @return $this
421
     */
422 4
    public function elemMatch($expression)
423
    {
424 4
        return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
425
    }
426
427
    /**
428
     * Specify an equality match for the current field.
429
     *
430
     * @see Builder::equals()
431
     * @param mixed $value
432
     * @return $this
433
     */
434 102
    public function equals($value)
435
    {
436 102
        if ($this->currentField) {
437 101
            $this->query[$this->currentField] = $value;
438
        } else {
439 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...
440
        }
441 102
        return $this;
442
    }
443
444
    /**
445
     * Specify $exists criteria for the current field.
446
     *
447
     * @see Builder::exists()
448
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
449
     * @param bool $bool
450
     * @return $this
451
     */
452 5
    public function exists($bool)
453
    {
454 5
        return $this->operator('$exists', (bool) $bool);
455
    }
456
457
    /**
458
     * Set the current field for building the expression.
459
     *
460
     * @see Builder::field()
461
     * @param string $field
462
     * @return $this
463
     */
464 193
    public function field($field)
465
    {
466 193
        $this->currentField = (string) $field;
467 193
        return $this;
468
    }
469
470
    /**
471
     * Add $geoIntersects criteria with a GeoJSON geometry to the expression.
472
     *
473
     * The geometry parameter GeoJSON object or an array corresponding to the
474
     * geometry's JSON representation.
475
     *
476
     * @see Builder::geoIntersects()
477
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
478
     * @param array|Geometry $geometry
479
     * @return $this
480
     */
481 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...
482
    {
483 2
        if ($geometry instanceof Geometry) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Geometry does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
484 1
            $geometry = $geometry->jsonSerialize();
485
        }
486
487 2
        return $this->operator('$geoIntersects', ['$geometry' => $geometry]);
488
    }
489
490
    /**
491
     * Add $geoWithin criteria with a GeoJSON geometry to the expression.
492
     *
493
     * The geometry parameter GeoJSON object or an array corresponding to the
494
     * geometry's JSON representation.
495
     *
496
     * @see Builder::geoWithin()
497
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
498
     * @param array|Geometry $geometry
499
     * @return $this
500
     */
501 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...
502
    {
503 2
        if ($geometry instanceof Geometry) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Geometry does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
504 1
            $geometry = $geometry->jsonSerialize();
505
        }
506
507 2
        return $this->operator('$geoWithin', ['$geometry' => $geometry]);
508
    }
509
510
    /**
511
     * Add $geoWithin criteria with a $box shape to the expression.
512
     *
513
     * A rectangular polygon will be constructed from a pair of coordinates
514
     * corresponding to the bottom left and top right corners.
515
     *
516
     * Note: the $box operator only supports legacy coordinate pairs and 2d
517
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
518
     *
519
     * @see Builder::geoWithinBox()
520
     * @see http://docs.mongodb.org/manual/reference/operator/box/
521
     * @param float $x1
522
     * @param float $y1
523
     * @param float $x2
524
     * @param float $y2
525
     * @return $this
526
     */
527 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
528
    {
529 1
        $shape = ['$box' => [[$x1, $y1], [$x2, $y2]]];
530
531 1
        return $this->operator('$geoWithin', $shape);
532
    }
533
534
    /**
535
     * Add $geoWithin criteria with a $center shape to the expression.
536
     *
537
     * Note: the $center operator only supports legacy coordinate pairs and 2d
538
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
539
     *
540
     * @see Builider::geoWithinCenter()
541
     * @see http://docs.mongodb.org/manual/reference/operator/center/
542
     * @param float $x
543
     * @param float $y
544
     * @param float $radius
545
     * @return $this
546
     */
547 1
    public function geoWithinCenter($x, $y, $radius)
548
    {
549 1
        $shape = ['$center' => [[$x, $y], $radius]];
550
551 1
        return $this->operator('$geoWithin', $shape);
552
    }
553
554
    /**
555
     * Add $geoWithin criteria with a $centerSphere shape to the expression.
556
     *
557
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
558
     *
559
     * @see Builder::geoWithinCenterSphere()
560
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
561
     * @param float $x
562
     * @param float $y
563
     * @param float $radius
564
     * @return $this
565
     */
566 1
    public function geoWithinCenterSphere($x, $y, $radius)
567
    {
568 1
        $shape = ['$centerSphere' => [[$x, $y], $radius]];
569
570 1
        return $this->operator('$geoWithin', $shape);
571
    }
572
573
    /**
574
     * Add $geoWithin criteria with a $polygon shape to the expression.
575
     *
576
     * Point coordinates are in x, y order (easting, northing for projected
577
     * coordinates, longitude, latitude for geographic coordinates).
578
     *
579
     * The last point coordinate is implicitly connected with the first.
580
     *
581
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
582
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
583
     *
584
     * @see Builder::geoWithinPolygon()
585
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
586
     * @param array $point1    First point of the polygon
587
     * @param array $point2    Second point of the polygon
588
     * @param array $point3    Third point of the polygon
589
     * @param array ...$points Additional points of the polygon
590
     * @return $this
591
     * @throws \InvalidArgumentException If less than three points are given.
592
     */
593 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...
594
    {
595 1
        $shape = ['$polygon' => func_get_args()];
596
597 1
        return $this->operator('$geoWithin', $shape);
598
    }
599
600
    /**
601
     * Return the current field.
602
     *
603
     * @return string
604
     */
605 2
    public function getCurrentField()
606
    {
607 2
        return $this->currentField;
608
    }
609
610
    /**
611
     * Gets prepared newObj part of expression.
612
     *
613
     * @return array
614
     */
615 176
    public function getNewObj()
616
    {
617 176
        return $this->dm->getUnitOfWork()
618 176
            ->getDocumentPersister($this->class->name)
619 176
            ->prepareQueryOrNewObj($this->newObj, true);
620
    }
621
622
    /**
623
     * Gets prepared query part of expression.
624
     *
625
     * @return array
626
     */
627 252
    public function getQuery()
628
    {
629 252
        return $this->dm->getUnitOfWork()
630 252
            ->getDocumentPersister($this->class->name)
631 252
            ->prepareQueryOrNewObj($this->query);
632
    }
633
634
    /**
635
     * Specify $gt criteria for the current field.
636
     *
637
     * @see Builder::gt()
638
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
639
     * @param mixed $value
640
     * @return $this
641
     */
642 2
    public function gt($value)
643
    {
644 2
        return $this->operator('$gt', $value);
645
    }
646
647
    /**
648
     * Specify $gte criteria for the current field.
649
     *
650
     * @see Builder::gte()
651
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
652
     * @param mixed $value
653
     * @return $this
654
     */
655 2
    public function gte($value)
656
    {
657 2
        return $this->operator('$gte', $value);
658
    }
659
660
    /**
661
     * Specify $in criteria for the current field.
662
     *
663
     * @see Builder::in()
664
     * @see http://docs.mongodb.org/manual/reference/operator/in/
665
     * @param array $values
666
     * @return $this
667
     */
668 31
    public function in(array $values)
669
    {
670 31
        return $this->operator('$in', array_values($values));
671
    }
672
673
    /**
674
     * Increment the current field.
675
     *
676
     * If the field does not exist, it will be set to this value.
677
     *
678
     * @see Builder::inc()
679
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
680
     * @param float|int $value
681
     * @return $this
682
     */
683 5
    public function inc($value)
684
    {
685 5
        $this->requiresCurrentField();
686 5
        $this->newObj['$inc'][$this->currentField] = $value;
687 5
        return $this;
688
    }
689
690
    /**
691
     * Checks that the current field includes a reference to the supplied document.
692
     *
693
     * @param object $document
694
     * @return Expr
695
     */
696 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...
697
    {
698 6
        $this->requiresCurrentField();
699 6
        $mapping = $this->getReferenceMapping();
700 4
        $reference = $this->dm->createReference($document, $mapping);
701 4
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
702 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...
703
704
        switch ($storeAs) {
705 4
            case ClassMetadata::REFERENCE_STORE_AS_ID:
706 2
                $this->query[$mapping['name']] = $reference;
707 2
                return $this;
708
                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...
709
710 3
            case ClassMetadata::REFERENCE_STORE_AS_REF:
711
                $keys = ['id' => true];
712
                break;
713
714 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
715 1
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
716 3
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
717
718 3
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
719 2
                    unset($keys['$db']);
720
                }
721
722 3
                if (isset($mapping['targetDocument'])) {
723 1
                    unset($keys['$ref'], $keys['$db']);
724
                }
725 3
                break;
726
727
            default:
728
                throw new \InvalidArgumentException(sprintf('Reference type %s is invalid.', $storeAs));
729
        }
730
731 3
        foreach ($keys as $key => $value) {
732 3
            $this->query[$mapping['name']]['$elemMatch'][$key] = $reference[$key];
733
        }
734
735 3
        return $this;
736
    }
737
738
    /**
739
     * Set the $language option for $text criteria.
740
     *
741
     * This method must be called after text().
742
     *
743
     * @see Builder::language()
744
     * @see http://docs.mongodb.org/manual/reference/operator/text/
745
     * @param string $language
746
     * @return $this
747
     * @throws \BadMethodCallException If the query does not already have $text criteria.
748
     */
749 2
    public function language($language)
750
    {
751 2
        if (! isset($this->query['$text'])) {
752 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
753
        }
754
755 1
        $this->query['$text']['$language'] = (string) $language;
756
757 1
        return $this;
758
    }
759
760
    /**
761
     * Specify $lt criteria for the current field.
762
     *
763
     * @see Builder::lte()
764
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
765
     * @param mixed $value
766
     * @return $this
767
     */
768 4
    public function lt($value)
769
    {
770 4
        return $this->operator('$lt', $value);
771
    }
772
773
    /**
774
     * Specify $lte criteria for the current field.
775
     *
776
     * @see Builder::lte()
777
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
778
     * @param mixed $value
779
     * @return $this
780
     */
781 2
    public function lte($value)
782
    {
783 2
        return $this->operator('$lte', $value);
784
    }
785
786
    /**
787
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
788
     *
789
     * @see Builder::max()
790
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
791
     * @param mixed $value
792
     * @return $this
793
     */
794
    public function max($value)
795
    {
796
        $this->requiresCurrentField();
797
        $this->newObj['$max'][$this->currentField] = $value;
798
        return $this;
799
    }
800
801
    /**
802
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
803
     *
804
     * @see Builder::min()
805
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
806
     * @param mixed $value
807
     * @return $this
808
     */
809
    public function min($value)
810
    {
811
        $this->requiresCurrentField();
812
        $this->newObj['$min'][$this->currentField] = $value;
813
        return $this;
814
    }
815
816
    /**
817
     * Specify $mod criteria for the current field.
818
     *
819
     * @see Builder::mod()
820
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
821
     * @param float|int $divisor
822
     * @param float|int $remainder
823
     * @return $this
824
     */
825
    public function mod($divisor, $remainder = 0)
826
    {
827
        return $this->operator('$mod', [$divisor, $remainder]);
828
    }
829
830
    /**
831
     * Multiply the current field.
832
     *
833
     * If the field does not exist, it will be set to 0.
834
     *
835
     * @see Builder::mul()
836
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
837
     * @param float|int $value
838
     * @return $this
839
     */
840
    public function mul($value)
841
    {
842
        $this->requiresCurrentField();
843
        $this->newObj['$mul'][$this->currentField] = $value;
844
        return $this;
845
    }
846
847
    /**
848
     * Add $near criteria to the expression.
849
     *
850
     * A GeoJSON point may be provided as the first and only argument for
851
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
852
     * an array corresponding to the point's JSON representation.
853
     *
854
     * @see Builder::near()
855
     * @see http://docs.mongodb.org/manual/reference/operator/near/
856
     * @param float|array|Point $x
857
     * @param float             $y
858
     * @return $this
859
     */
860 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...
861
    {
862 3
        if ($x instanceof Point) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Point does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
863 1
            $x = $x->jsonSerialize();
864
        }
865
866 3
        if (is_array($x)) {
867 2
            return $this->operator('$near', ['$geometry' => $x]);
868
        }
869
870 1
        return $this->operator('$near', [$x, $y]);
871
    }
872
873
    /**
874
     * Add $nearSphere criteria to the expression.
875
     *
876
     * A GeoJSON point may be provided as the first and only argument for
877
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
878
     * an array corresponding to the point's JSON representation.
879
     *
880
     * @see Builder::nearSphere()
881
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
882
     * @param float|array|Point $x
883
     * @param float             $y
884
     * @return $this
885
     */
886 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...
887
    {
888 3
        if ($x instanceof Point) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Point does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
889 1
            $x = $x->jsonSerialize();
890
        }
891
892 3
        if (is_array($x)) {
893 2
            return $this->operator('$nearSphere', ['$geometry' => $x]);
894
        }
895
896 1
        return $this->operator('$nearSphere', [$x, $y]);
897
    }
898
899
    /**
900
     * Negates an expression for the current field.
901
     *
902
     * @see Builder::not()
903
     * @see http://docs.mongodb.org/manual/reference/operator/not/
904
     * @param array|Expr $expression
905
     * @return $this
906
     */
907 2
    public function not($expression)
908
    {
909 2
        return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
910
    }
911
912
    /**
913
     * Specify $ne criteria for the current field.
914
     *
915
     * @see Builder::notEqual()
916
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
917
     * @param mixed $value
918
     * @return $this
919
     */
920 6
    public function notEqual($value)
921
    {
922 6
        return $this->operator('$ne', $value);
923
    }
924
925
    /**
926
     * Specify $nin criteria for the current field.
927
     *
928
     * @see Builder::notIn()
929
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
930
     * @param array $values
931
     * @return $this
932
     */
933 6
    public function notIn(array $values)
934
    {
935 6
        return $this->operator('$nin', array_values($values));
936
    }
937
938
    /**
939
     * Defines an operator and value on the expression.
940
     *
941
     * If there is a current field, the operator will be set on it; otherwise,
942
     * the operator is set at the top level of the query.
943
     *
944
     * @param string $operator
945
     * @param mixed  $value
946
     * @return $this
947
     */
948 81
    public function operator($operator, $value)
949
    {
950 81
        $this->wrapEqualityCriteria();
951
952 81
        if ($this->currentField) {
953 56
            $this->query[$this->currentField][$operator] = $value;
954
        } else {
955 27
            $this->query[$operator] = $value;
956
        }
957 81
        return $this;
958
    }
959
960
    /**
961
     * Remove the first element from the current array field.
962
     *
963
     * @see Builder::popFirst()
964
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
965
     * @return $this
966
     */
967 1
    public function popFirst()
968
    {
969 1
        $this->requiresCurrentField();
970 1
        $this->newObj['$pop'][$this->currentField] = 1;
971 1
        return $this;
972
    }
973
974
    /**
975
     * Remove the last element from the current array field.
976
     *
977
     * @see Builder::popLast()
978
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
979
     * @return $this
980
     */
981
    public function popLast()
982
    {
983
        $this->requiresCurrentField();
984
        $this->newObj['$pop'][$this->currentField] = -1;
985
        return $this;
986
    }
987
988
    /**
989
     * Add $position criteria to the expression for a $push operation.
990
     *
991
     * This is useful in conjunction with {@link Expr::each()} for a
992
     * {@link Expr::push()} operation.
993
     *
994
     * @see http://docs.mongodb.org/manual/reference/operator/update/position/
995
     * @param int $position
996
     * @return $this
997
     */
998 1
    public function position($position)
999
    {
1000 1
        return $this->operator('$position', $position);
1001
    }
1002
1003
    /**
1004
     * Remove all elements matching the given value or expression from the
1005
     * current array field.
1006
     *
1007
     * @see Builder::pull()
1008
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1009
     * @param mixed|Expr $valueOrExpression
1010
     * @return $this
1011
     */
1012 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...
1013
    {
1014 2
        if ($valueOrExpression instanceof Expr) {
1015 1
            $valueOrExpression = $valueOrExpression->getQuery();
1016
        }
1017
1018 2
        $this->requiresCurrentField();
1019 2
        $this->newObj['$pull'][$this->currentField] = $valueOrExpression;
1020 2
        return $this;
1021
    }
1022
1023
    /**
1024
     * Remove all elements matching any of the given values from the current
1025
     * array field.
1026
     *
1027
     * @see Builder::pullAll()
1028
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1029
     * @param array $values
1030
     * @return $this
1031
     */
1032
    public function pullAll(array $values)
1033
    {
1034
        $this->requiresCurrentField();
1035
        $this->newObj['$pullAll'][$this->currentField] = $values;
1036
        return $this;
1037
    }
1038
1039
    /**
1040
     * Append one or more values to the current array field.
1041
     *
1042
     * If the field does not exist, it will be set to an array containing the
1043
     * value(s) in the argument. If the field is not an array, the query
1044
     * will yield an error.
1045
     *
1046
     * Multiple values may be specified by providing an Expr object and using
1047
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1048
     * also be used to limit and order array elements, respectively.
1049
     *
1050
     * @see Builder::push()
1051
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1052
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1053
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1054
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1055
     * @param mixed|Expr $valueOrExpression
1056
     * @return $this
1057
     */
1058 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...
1059
    {
1060 8
        if ($valueOrExpression instanceof Expr) {
1061 3
            $valueOrExpression = array_merge(
1062 3
                ['$each' => []],
1063 3
                $valueOrExpression->getQuery()
1064
            );
1065
        }
1066
1067 8
        $this->requiresCurrentField();
1068 8
        $this->newObj['$push'][$this->currentField] = $valueOrExpression;
1069 8
        return $this;
1070
    }
1071
1072
    /**
1073
     * Specify $gte and $lt criteria for the current field.
1074
     *
1075
     * This method is shorthand for specifying $gte criteria on the lower bound
1076
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1077
     *
1078
     * @see Builder::range()
1079
     * @param mixed $start
1080
     * @param mixed $end
1081
     * @return $this
1082
     */
1083 2
    public function range($start, $end)
1084
    {
1085 2
        return $this->operator('$gte', $start)->operator('$lt', $end);
1086
    }
1087
1088
    /**
1089
     * Checks that the value of the current field is a reference to the supplied document.
1090
     *
1091
     * @param object $document
1092
     * @return Expr
1093
     */
1094 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...
1095
    {
1096 13
        $this->requiresCurrentField();
1097 13
        $mapping = $this->getReferenceMapping();
1098 11
        $reference = $this->dm->createReference($document, $mapping);
1099 11
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
1100 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...
1101
1102
        switch ($storeAs) {
1103 11
            case ClassMetadata::REFERENCE_STORE_AS_ID:
1104 4
                $this->query[$mapping['name']] = $reference;
1105 4
                return $this;
1106
                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...
1107
1108 8
            case ClassMetadata::REFERENCE_STORE_AS_REF:
1109
                $keys = ['id' => true];
1110
                break;
1111
1112 8
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
1113 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
1114 8
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
1115
1116 8
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
1117 5
                    unset($keys['$db']);
1118
                }
1119
1120 8
                if (isset($mapping['targetDocument'])) {
1121 4
                    unset($keys['$ref'], $keys['$db']);
1122
                }
1123 8
                break;
1124
1125
            default:
1126
                throw new \InvalidArgumentException(sprintf('Reference type %s is invalid.', $storeAs));
1127
        }
1128
1129 8
        foreach ($keys as $key => $value) {
1130 8
            $this->query[$mapping['name'] . '.' . $key] = $reference[$key];
1131
        }
1132
1133 8
        return $this;
1134
    }
1135
1136
    /**
1137
     * Rename the current field.
1138
     *
1139
     * @see Builder::rename()
1140
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1141
     * @param string $name
1142
     * @return $this
1143
     */
1144
    public function rename($name)
1145
    {
1146
        $this->requiresCurrentField();
1147
        $this->newObj['$rename'][$this->currentField] = $name;
1148
        return $this;
1149
    }
1150
1151
    /**
1152
     * Set the current field to a value.
1153
     *
1154
     * This is only relevant for insert, update, or findAndUpdate queries. For
1155
     * update and findAndUpdate queries, the $atomic parameter will determine
1156
     * whether or not a $set operator is used.
1157
     *
1158
     * @see Builder::set()
1159
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1160
     * @param mixed $value
1161
     * @param bool  $atomic
1162
     * @return $this
1163
     */
1164 19
    public function set($value, $atomic = true)
1165
    {
1166 19
        $this->requiresCurrentField();
1167
1168 19
        if ($atomic) {
1169 16
            $this->newObj['$set'][$this->currentField] = $value;
1170 16
            return $this;
1171
        }
1172
1173 3
        if (strpos($this->currentField, '.') === false) {
1174 2
            $this->newObj[$this->currentField] = $value;
1175 2
            return $this;
1176
        }
1177
1178 2
        $keys = explode('.', $this->currentField);
1179 2
        $current = &$this->newObj;
1180 2
        foreach ($keys as $key) {
1181 2
            $current = &$current[$key];
1182
        }
1183 2
        $current = $value;
1184
1185 2
        return $this;
1186
    }
1187
1188
    /**
1189
     * Sets ClassMetadata for document being queried.
1190
     *
1191
     */
1192 391
    public function setClassMetadata(ClassMetadata $class)
1193
    {
1194 391
        $this->class = $class;
1195 391
    }
1196
1197
    /**
1198
     * Set the "new object".
1199
     *
1200
     * @see Builder::setNewObj()
1201
     * @param array $newObj
1202
     * @return $this
1203
     */
1204
    public function setNewObj(array $newObj)
1205
    {
1206
        $this->newObj = $newObj;
1207
        return $this;
1208
    }
1209
1210
    /**
1211
     * Set the current field to the value if the document is inserted in an
1212
     * upsert operation.
1213
     *
1214
     * If an update operation with upsert: true results in an insert of a
1215
     * document, then $setOnInsert assigns the specified values to the fields in
1216
     * the document. If the update operation does not result in an insert,
1217
     * $setOnInsert does nothing.
1218
     *
1219
     * @see Builder::setOnInsert()
1220
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1221
     * @param mixed $value
1222
     * @return $this
1223
     */
1224 1
    public function setOnInsert($value)
1225
    {
1226 1
        $this->requiresCurrentField();
1227 1
        $this->newObj['$setOnInsert'][$this->currentField] = $value;
1228
1229 1
        return $this;
1230
    }
1231
1232
    /**
1233
     * Set the query criteria.
1234
     *
1235
     * @see Builder::setQueryArray()
1236
     * @param array $query
1237
     * @return $this
1238
     */
1239 18
    public function setQuery(array $query)
1240
    {
1241 18
        $this->query = $query;
1242 18
        return $this;
1243
    }
1244
1245
    /**
1246
     * Specify $size criteria for the current field.
1247
     *
1248
     * @see Builder::size()
1249
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1250
     * @param int $size
1251
     * @return $this
1252
     */
1253
    public function size($size)
1254
    {
1255
        return $this->operator('$size', (int) $size);
1256
    }
1257
1258
    /**
1259
     * Add $slice criteria to the expression for a $push operation.
1260
     *
1261
     * This is useful in conjunction with {@link Expr::each()} for a
1262
     * {@link Expr::push()} operation. {@link Builder::selectSlice()} should be
1263
     * used for specifying $slice for a query projection.
1264
     *
1265
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1266
     * @param int $slice
1267
     * @return $this
1268
     */
1269 2
    public function slice($slice)
1270
    {
1271 2
        return $this->operator('$slice', $slice);
1272
    }
1273
1274
    /**
1275
     * Add $sort criteria to the expression for a $push operation.
1276
     *
1277
     * If sorting by multiple fields, the first argument should be an array of
1278
     * field name (key) and order (value) pairs.
1279
     *
1280
     * This is useful in conjunction with {@link Expr::each()} for a
1281
     * {@link Expr::push()} operation. {@link Builder::sort()} should be used to
1282
     * sort the results of a query.
1283
     *
1284
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1285
     * @param array|string $fieldName Field name or array of field/order pairs
1286
     * @param int|string   $order     Field order (if one field is specified)
1287
     * @return $this
1288
     */
1289 2
    public function sort($fieldName, $order = null)
1290
    {
1291 2
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1292
1293
        return $this->operator('$sort', array_map(function ($order) {
1294 2
            return $this->normalizeSortOrder($order);
1295 2
        }, $fields));
1296
    }
1297
1298
    /**
1299
     * Specify $text criteria for the current query.
1300
     *
1301
     * The $language option may be set with {@link Expr::language()}.
1302
     *
1303
     * @see Builder::text()
1304
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1305
     * @param string $search
1306
     * @return $this
1307
     */
1308 6
    public function text($search)
1309
    {
1310 6
        $this->query['$text'] = ['$search' => (string) $search];
1311 6
        return $this;
1312
    }
1313
1314
    /**
1315
     * Specify $type criteria for the current field.
1316
     *
1317
     * @todo Remove support for string $type argument in 2.0
1318
     * @see Builder::type()
1319
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1320
     * @param int $type
1321
     * @return $this
1322
     */
1323 1
    public function type($type)
1324
    {
1325 1
        if (is_string($type)) {
1326
            $map = [
1327 1
                'double' => 1,
1328
                'string' => 2,
1329
                'object' => 3,
1330
                'array' => 4,
1331
                'binary' => 5,
1332
                'undefined' => 6,
1333
                'objectid' => 7,
1334
                'boolean' => 8,
1335
                'date' => 9,
1336
                'null' => 10,
1337
                'regex' => 11,
1338
                'jscode' => 13,
1339
                'symbol' => 14,
1340
                'jscodewithscope' => 15,
1341
                'integer32' => 16,
1342
                'timestamp' => 17,
1343
                'integer64' => 18,
1344
                'maxkey' => 127,
1345
                'minkey' => 255,
1346
            ];
1347
1348 1
            $type = $map[$type] ?? $type;
1349
        }
1350
1351 1
        return $this->operator('$type', $type);
1352
    }
1353
1354
    /**
1355
     * Unset the current field.
1356
     *
1357
     * The field will be removed from the document (not set to null).
1358
     *
1359
     * @see Builder::unsetField()
1360
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1361
     * @return $this
1362
     */
1363 3
    public function unsetField()
1364
    {
1365 3
        $this->requiresCurrentField();
1366 3
        $this->newObj['$unset'][$this->currentField] = 1;
1367 3
        return $this;
1368
    }
1369
1370
    /**
1371
     * Specify a JavaScript expression to use for matching documents.
1372
     *
1373
     * @see Builder::where()
1374
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1375
     * @param string|Javascript $javascript
1376
     * @return $this
1377
     */
1378 3
    public function where($javascript)
1379
    {
1380 3
        $this->query['$where'] = $javascript;
1381 3
        return $this;
1382
    }
1383
1384
    /**
1385
     * Gets reference mapping for current field from current class or its descendants.
1386
     *
1387
     * @return array
1388
     * @throws MappingException
1389
     */
1390 19
    private function getReferenceMapping()
1391
    {
1392 19
        $mapping = null;
1393
        try {
1394 19
            $mapping = $this->class->getFieldMapping($this->currentField);
1395 6
        } catch (MappingException $e) {
1396 6
            if (empty($this->class->discriminatorMap)) {
1397
                throw $e;
1398
            }
1399 6
            $foundIn = null;
1400 6
            foreach ($this->class->discriminatorMap as $child) {
1401 6
                $childClass = $this->dm->getClassMetadata($child);
1402 6
                if (! $childClass->hasAssociation($this->currentField)) {
1403 4
                    continue;
1404
                }
1405
1406 4
                if ($mapping !== null && $mapping !== $childClass->getFieldMapping($this->currentField)) {
1407 2
                    throw MappingException::referenceFieldConflict($this->currentField, $foundIn->name, $childClass->name);
1408
                }
1409 4
                $mapping = $childClass->getFieldMapping($this->currentField);
1410 4
                $foundIn = $childClass;
1411
            }
1412 4
            if ($mapping === null) {
1413 2
                throw MappingException::mappingNotFoundInClassNorDescendants($this->class->name, $this->currentField);
1414
            }
1415
        }
1416 15
        return $mapping;
1417
    }
1418
1419
    /**
1420
     * @param int|string $order
1421
     *
1422
     */
1423 2
    private function normalizeSortOrder($order): int
1424
    {
1425 2
        if (is_string($order)) {
1426
            $order = strtolower($order) === 'asc' ? 1 : -1;
1427
        }
1428
1429 2
        return (int) $order;
1430
    }
1431
1432
    /**
1433
     * Ensure that a current field has been set.
1434
     *
1435
     * @throws \LogicException If a current field has not been set.
1436
     */
1437 67
    private function requiresCurrentField()
1438
    {
1439 67
        if (! $this->currentField) {
1440
            throw new \LogicException('This method requires you set a current field using field().');
1441
        }
1442 67
    }
1443
1444
    /**
1445
     * Wraps equality criteria with an operator.
1446
     *
1447
     * If equality criteria was previously specified for a field, it cannot be
1448
     * merged with other operators without first being wrapped in an operator of
1449
     * its own. Ideally, we would wrap it with $eq, but that is only available
1450
     * in MongoDB 2.8. Using a single-element $in is backwards compatible.
1451
     *
1452
     * @see Expr::operator()
1453
     */
1454 81
    private function wrapEqualityCriteria()
1455
    {
1456
        /* If the current field has no criteria yet, do nothing. This ensures
1457
         * that we do not inadvertently inject {"$in": null} into the query.
1458
         */
1459 81
        if ($this->currentField && ! isset($this->query[$this->currentField]) && ! array_key_exists($this->currentField, $this->query)) {
1460 55
            return;
1461
        }
1462
1463 31
        if ($this->currentField) {
1464 5
            $query = &$this->query[$this->currentField];
1465
        } else {
1466 27
            $query = &$this->query;
1467
        }
1468
1469
        /* If the query is an empty array, we'll assume that the user has not
1470
         * specified criteria. Otherwise, check if the array includes a query
1471
         * operator (checking the first key is sufficient). If neither of these
1472
         * conditions are met, we'll wrap the query value with $in.
1473
         */
1474 31
        if (is_array($query) && (empty($query) || strpos(key($query), '$') === 0)) {
1475 31
            return;
1476
        }
1477
1478 2
        $query = ['$in' => [$query]];
1479 2
    }
1480
}
1481