Completed
Push — master ( a74607...8291b9 )
by Maciej
10:41
created

Expr::diacriticSensitive()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 15
loc 15
ccs 8
cts 8
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 1
crap 4
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 strpos;
25
use function strtolower;
26
27
/**
28
 * Query expression builder for ODM.
29
 *
30
 */
31
class Expr
32
{
33
    /**
34
     * The query criteria array.
35
     *
36
     * @var array
37
     */
38
    private $query = [];
39
40
    /**
41
     * The "new object" array containing either a full document or a number of
42
     * atomic update operators.
43
     *
44
     * @see docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
45
     * @var array
46
     */
47
    private $newObj = [];
48
49
    /**
50
     * The current field we are operating on.
51
     *
52
     * @var string
53
     */
54
    private $currentField;
55
56
    /**
57
     * The DocumentManager instance for this query
58
     *
59
     * @var DocumentManager
60
     */
61
    private $dm;
62
63
    /**
64
     * The ClassMetadata instance for the document being queried
65
     *
66
     * @var ClassMetadata
67
     */
68
    private $class;
69
70 391
    public function __construct(DocumentManager $dm)
71
    {
72 391
        $this->dm = $dm;
73 391
    }
74
75
    /**
76
     * Add one or more $and clauses to the current query.
77
     *
78
     * @see Builder::addAnd()
79
     * @see http://docs.mongodb.org/manual/reference/operator/and/
80
     * @param array|Expr $expression
81
     * @param array|Expr ...$expressions
82
     * @return $this
83
     */
84 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...
85
    {
86 3
        if (! isset($this->query['$and'])) {
87 3
            $this->query['$and'] = [];
88
        }
89
90 3
        $this->query['$and'] = array_merge(
91 3
            $this->query['$and'],
92 3
            array_map(
93 3
                function ($expression) {
94 3
                    return $expression instanceof Expr ? $expression->getQuery() : $expression;
95 3
                },
96 3
                func_get_args()
97
            )
98
        );
99
100 3
        return $this;
101
    }
102
103
    /**
104
     * Add one or more $nor clauses to the current query.
105
     *
106
     * @see Builder::addNor()
107
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
108
     * @param array|Expr $expression
109
     * @param array|Expr ...$expressions
110
     * @return $this
111
     */
112 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...
113
    {
114 1
        if (! isset($this->query['$nor'])) {
115 1
            $this->query['$nor'] = [];
116
        }
117
118 1
        $this->query['$nor'] = array_merge(
119 1
            $this->query['$nor'],
120 1
            array_map(function ($expression) {
121 1
                return $expression instanceof Expr ? $expression->getQuery() : $expression;
122 1
            }, func_get_args())
123
        );
124
125 1
        return $this;
126
    }
127
128
    /**
129
     * Add one or more $or clauses to the current query.
130
     *
131
     * @see Builder::addOr()
132
     * @see http://docs.mongodb.org/manual/reference/operator/or/
133
     * @param array|Expr $expression
134
     * @param array|Expr ...$expressions
135
     * @return $this
136
     */
137 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...
138
    {
139 5
        if (! isset($this->query['$or'])) {
140 5
            $this->query['$or'] = [];
141
        }
142
143 5
        $this->query['$or'] = array_merge(
144 5
            $this->query['$or'],
145 5
            array_map(function ($expression) {
146 5
                return $expression instanceof Expr ? $expression->getQuery() : $expression;
147 5
            }, func_get_args())
148
        );
149
150 5
        return $this;
151
    }
152
153
    /**
154
     * Append one or more values to the current array field only if they do not
155
     * already exist in the array.
156
     *
157
     * If the field does not exist, it will be set to an array containing the
158
     * unique value(s) in the argument. If the field is not an array, the query
159
     * will yield an error.
160
     *
161
     * Multiple values may be specified by provided an Expr object and using
162
     * {@link Expr::each()}.
163
     *
164
     * @see Builder::addToSet()
165
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
166
     * @see http://docs.mongodb.org/manual/reference/operator/each/
167
     * @param mixed|Expr $valueOrExpression
168
     * @return $this
169
     */
170 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...
171
    {
172 5
        if ($valueOrExpression instanceof Expr) {
173 1
            $valueOrExpression = $valueOrExpression->getQuery();
174
        }
175
176 5
        $this->requiresCurrentField();
177 5
        $this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
178 5
        return $this;
179
    }
180
181
    /**
182
     * Specify $all criteria for the current field.
183
     *
184
     * @see Builder::all()
185
     * @see http://docs.mongodb.org/manual/reference/operator/all/
186
     * @param array $values
187
     * @return $this
188
     */
189 2
    public function all(array $values)
190
    {
191 2
        return $this->operator('$all', (array) $values);
192
    }
193
194
    /**
195
     * Apply a bitwise operation on the current field
196
     *
197
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
198
     * @param string $operator
199
     * @param int    $value
200
     * @return $this
201
     */
202 3
    protected function bit($operator, $value)
203
    {
204 3
        $this->requiresCurrentField();
205 3
        $this->newObj['$bit'][$this->currentField][$operator] = $value;
206 3
        return $this;
207
    }
208
209
    /**
210
     * Apply a bitwise and operation on the current field.
211
     *
212
     * @see Builder::bitAnd()
213
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
214
     * @param int $value
215
     * @return $this
216
     */
217 1
    public function bitAnd($value)
218
    {
219 1
        return $this->bit('and', $value);
220
    }
221
222
    /**
223
     * Apply a bitwise or operation on the current field.
224
     *
225
     * @see Builder::bitOr()
226
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
227
     * @param int $value
228
     * @return $this
229
     */
230 1
    public function bitOr($value)
231
    {
232 1
        return $this->bit('or', $value);
233
    }
234
235
    /**
236
     * Matches documents where all of the bit positions given by the query are
237
     * clear.
238
     *
239
     * @see Builder::bitsAllClear()
240
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
241
     * @param int|array|Binary $value
242
     * @return $this
243
     */
244
    public function bitsAllClear($value)
245
    {
246
        $this->requiresCurrentField();
247
        return $this->operator('$bitsAllClear', $value);
248
    }
249
250
    /**
251
     * Matches documents where all of the bit positions given by the query are
252
     * set.
253
     *
254
     * @see Builder::bitsAllSet()
255
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
256
     * @param int|array|Binary $value
257
     * @return $this
258
     */
259
    public function bitsAllSet($value)
260
    {
261
        $this->requiresCurrentField();
262
        return $this->operator('$bitsAllSet', $value);
263
    }
264
265
    /**
266
     * Matches documents where any of the bit positions given by the query are
267
     * clear.
268
     *
269
     * @see Builder::bitsAnyClear()
270
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
271
     * @param int|array|Binary $value
272
     * @return $this
273
     */
274
    public function bitsAnyClear($value)
275
    {
276
        $this->requiresCurrentField();
277
        return $this->operator('$bitsAnyClear', $value);
278
    }
279
280
    /**
281
     * Matches documents where any of the bit positions given by the query are
282
     * set.
283
     *
284
     * @see Builder::bitsAnySet()
285
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
286
     * @param int|array|Binary $value
287
     * @return $this
288
     */
289
    public function bitsAnySet($value)
290
    {
291
        $this->requiresCurrentField();
292
        return $this->operator('$bitsAnySet', $value);
293
    }
294
295
    /**
296
     * Apply a bitwise xor operation on the current field.
297
     *
298
     * @see Builder::bitXor()
299
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
300
     * @param int $value
301
     * @return $this
302
     */
303 1
    public function bitXor($value)
304
    {
305 1
        return $this->bit('xor', $value);
306
    }
307
308
    /**
309
     * A boolean flag to enable or disable case sensitive search for $text
310
     * criteria.
311
     *
312
     * This method must be called after text().
313
     *
314
     * @see Builder::caseSensitive()
315
     * @see http://docs.mongodb.org/manual/reference/operator/text/
316
     * @param bool $caseSensitive
317
     * @return $this
318
     * @throws \BadMethodCallException If the query does not already have $text criteria.
319
     *
320
     */
321 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...
322
    {
323 3
        if (! isset($this->query['$text'])) {
324 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
325
        }
326
327
        // Remove caseSensitive option to keep support for older database versions
328 2
        if ($caseSensitive) {
329 2
            $this->query['$text']['$caseSensitive'] = true;
330 1
        } elseif (isset($this->query['$text']['$caseSensitive'])) {
331 1
            unset($this->query['$text']['$caseSensitive']);
332
        }
333
334 2
        return $this;
335
    }
336
337
    /**
338
     * Associates a comment to any expression taking a query predicate.
339
     *
340
     * @see Builder::comment()
341
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
342
     * @param string $comment
343
     * @return $this
344
     */
345
    public function comment($comment)
346
    {
347
        $this->query['$comment'] = $comment;
348
        return $this;
349
    }
350
351
    /**
352
     * Sets the value of the current field to the current date, either as a date or a timestamp.
353
     *
354
     * @see Builder::currentDate()
355
     * @see http://docs.mongodb.org/manual/reference/operator/update/currentDate/
356
     * @param string $type
357
     * @return $this
358
     * @throws \InvalidArgumentException If an invalid type is given.
359
     */
360 3
    public function currentDate($type = 'date')
361
    {
362 3
        if (! in_array($type, ['date', 'timestamp'])) {
363 1
            throw new \InvalidArgumentException('Type for currentDate operator must be date or timestamp.');
364
        }
365
366 2
        $this->requiresCurrentField();
367 2
        $this->newObj['$currentDate'][$this->currentField]['$type'] = $type;
368 2
        return $this;
369
    }
370
371
    /**
372
     * A boolean flag to enable or disable diacritic sensitive search for $text
373
     * criteria.
374
     *
375
     * This method must be called after text().
376
     *
377
     * @see Builder::diacriticSensitive()
378
     * @see http://docs.mongodb.org/manual/reference/operator/text/
379
     * @param bool $diacriticSensitive
380
     * @return $this
381
     * @throws \BadMethodCallException If the query does not already have $text criteria.
382
     *
383
     */
384 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...
385
    {
386 3
        if (! isset($this->query['$text'])) {
387 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
388
        }
389
390
        // Remove diacriticSensitive option to keep support for older database versions
391 2
        if ($diacriticSensitive) {
392 2
            $this->query['$text']['$diacriticSensitive'] = true;
393 1
        } elseif (isset($this->query['$text']['$diacriticSensitive'])) {
394 1
            unset($this->query['$text']['$diacriticSensitive']);
395
        }
396
397 2
        return $this;
398
    }
399
400
    /**
401
     * Add $each criteria to the expression for a $push operation.
402
     *
403
     * @see Expr::push()
404
     * @see http://docs.mongodb.org/manual/reference/operator/each/
405
     * @param array $values
406
     * @return $this
407
     */
408 4
    public function each(array $values)
409
    {
410 4
        return $this->operator('$each', $values);
411
    }
412
413
    /**
414
     * Specify $elemMatch criteria for the current field.
415
     *
416
     * @see Builder::elemMatch()
417
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
418
     * @param array|Expr $expression
419
     * @return $this
420
     */
421 4
    public function elemMatch($expression)
422
    {
423 4
        return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
424
    }
425
426
    /**
427
     * Specify an equality match for the current field.
428
     *
429
     * @see Builder::equals()
430
     * @param mixed $value
431
     * @return $this
432
     */
433 100
    public function equals($value)
434
    {
435 100
        if ($this->currentField) {
436 99
            $this->query[$this->currentField] = $value;
437
        } else {
438 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...
439
        }
440 100
        return $this;
441
    }
442
443
    /**
444
     * Specify $exists criteria for the current field.
445
     *
446
     * @see Builder::exists()
447
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
448
     * @param bool $bool
449
     * @return $this
450
     */
451 5
    public function exists($bool)
452
    {
453 5
        return $this->operator('$exists', (bool) $bool);
454
    }
455
456
    /**
457
     * Set the current field for building the expression.
458
     *
459
     * @see Builder::field()
460
     * @param string $field
461
     * @return $this
462
     */
463 191
    public function field($field)
464
    {
465 191
        $this->currentField = (string) $field;
466 191
        return $this;
467
    }
468
469
    /**
470
     * Add $geoIntersects criteria with a GeoJSON geometry to the expression.
471
     *
472
     * The geometry parameter GeoJSON object or an array corresponding to the
473
     * geometry's JSON representation.
474
     *
475
     * @see Builder::geoIntersects()
476
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
477
     * @param array|Geometry $geometry
478
     * @return $this
479
     */
480 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...
481
    {
482 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...
483 1
            $geometry = $geometry->jsonSerialize();
484
        }
485
486 2
        return $this->operator('$geoIntersects', ['$geometry' => $geometry]);
487
    }
488
489
    /**
490
     * Add $geoWithin criteria with a GeoJSON geometry to the expression.
491
     *
492
     * The geometry parameter GeoJSON object or an array corresponding to the
493
     * geometry's JSON representation.
494
     *
495
     * @see Builder::geoWithin()
496
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
497
     * @param array|Geometry $geometry
498
     * @return $this
499
     */
500 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...
501
    {
502 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...
503 1
            $geometry = $geometry->jsonSerialize();
504
        }
505
506 2
        return $this->operator('$geoWithin', ['$geometry' => $geometry]);
507
    }
508
509
    /**
510
     * Add $geoWithin criteria with a $box shape to the expression.
511
     *
512
     * A rectangular polygon will be constructed from a pair of coordinates
513
     * corresponding to the bottom left and top right corners.
514
     *
515
     * Note: the $box operator only supports legacy coordinate pairs and 2d
516
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
517
     *
518
     * @see Builder::geoWithinBox()
519
     * @see http://docs.mongodb.org/manual/reference/operator/box/
520
     * @param float $x1
521
     * @param float $y1
522
     * @param float $x2
523
     * @param float $y2
524
     * @return $this
525
     */
526 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
527
    {
528 1
        $shape = ['$box' => [[$x1, $y1], [$x2, $y2]]];
529
530 1
        return $this->operator('$geoWithin', $shape);
531
    }
532
533
    /**
534
     * Add $geoWithin criteria with a $center shape to the expression.
535
     *
536
     * Note: the $center operator only supports legacy coordinate pairs and 2d
537
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
538
     *
539
     * @see Builider::geoWithinCenter()
540
     * @see http://docs.mongodb.org/manual/reference/operator/center/
541
     * @param float $x
542
     * @param float $y
543
     * @param float $radius
544
     * @return $this
545
     */
546 1
    public function geoWithinCenter($x, $y, $radius)
547
    {
548 1
        $shape = ['$center' => [[$x, $y], $radius]];
549
550 1
        return $this->operator('$geoWithin', $shape);
551
    }
552
553
    /**
554
     * Add $geoWithin criteria with a $centerSphere shape to the expression.
555
     *
556
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
557
     *
558
     * @see Builder::geoWithinCenterSphere()
559
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
560
     * @param float $x
561
     * @param float $y
562
     * @param float $radius
563
     * @return $this
564
     */
565 1
    public function geoWithinCenterSphere($x, $y, $radius)
566
    {
567 1
        $shape = ['$centerSphere' => [[$x, $y], $radius]];
568
569 1
        return $this->operator('$geoWithin', $shape);
570
    }
571
572
    /**
573
     * Add $geoWithin criteria with a $polygon shape to the expression.
574
     *
575
     * Point coordinates are in x, y order (easting, northing for projected
576
     * coordinates, longitude, latitude for geographic coordinates).
577
     *
578
     * The last point coordinate is implicitly connected with the first.
579
     *
580
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
581
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
582
     *
583
     * @see Builder::geoWithinPolygon()
584
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
585
     * @param array $point1    First point of the polygon
586
     * @param array $point2    Second point of the polygon
587
     * @param array $point3    Third point of the polygon
588
     * @param array ...$points Additional points of the polygon
589
     * @return $this
590
     * @throws \InvalidArgumentException If less than three points are given.
591
     */
592 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...
593
    {
594 1
        $shape = ['$polygon' => func_get_args()];
595
596 1
        return $this->operator('$geoWithin', $shape);
597
    }
598
599
    /**
600
     * Return the current field.
601
     *
602
     * @return string
603
     */
604 2
    public function getCurrentField()
605
    {
606 2
        return $this->currentField;
607
    }
608
609
    /**
610
     * Gets prepared newObj part of expression.
611
     *
612
     * @return array
613
     */
614 174
    public function getNewObj()
615
    {
616 174
        return $this->dm->getUnitOfWork()
617 174
            ->getDocumentPersister($this->class->name)
618 174
            ->prepareQueryOrNewObj($this->newObj, true);
619
    }
620
621
    /**
622
     * Gets prepared query part of expression.
623
     *
624
     * @return array
625
     */
626 250
    public function getQuery()
627
    {
628 250
        return $this->dm->getUnitOfWork()
629 250
            ->getDocumentPersister($this->class->name)
630 250
            ->prepareQueryOrNewObj($this->query);
631
    }
632
633
    /**
634
     * Specify $gt criteria for the current field.
635
     *
636
     * @see Builder::gt()
637
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
638
     * @param mixed $value
639
     * @return $this
640
     */
641 2
    public function gt($value)
642
    {
643 2
        return $this->operator('$gt', $value);
644
    }
645
646
    /**
647
     * Specify $gte criteria for the current field.
648
     *
649
     * @see Builder::gte()
650
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
651
     * @param mixed $value
652
     * @return $this
653
     */
654 2
    public function gte($value)
655
    {
656 2
        return $this->operator('$gte', $value);
657
    }
658
659
    /**
660
     * Specify $in criteria for the current field.
661
     *
662
     * @see Builder::in()
663
     * @see http://docs.mongodb.org/manual/reference/operator/in/
664
     * @param array $values
665
     * @return $this
666
     */
667 31
    public function in(array $values)
668
    {
669 31
        return $this->operator('$in', array_values($values));
670
    }
671
672
    /**
673
     * Increment the current field.
674
     *
675
     * If the field does not exist, it will be set to this value.
676
     *
677
     * @see Builder::inc()
678
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
679
     * @param float|int $value
680
     * @return $this
681
     */
682 5
    public function inc($value)
683
    {
684 5
        $this->requiresCurrentField();
685 5
        $this->newObj['$inc'][$this->currentField] = $value;
686 5
        return $this;
687
    }
688
689
    /**
690
     * Checks that the current field includes a reference to the supplied document.
691
     *
692
     * @param object $document
693
     * @return Expr
694
     */
695 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...
696
    {
697 6
        $this->requiresCurrentField();
698 6
        $mapping = $this->getReferenceMapping();
699 4
        $reference = $this->dm->createReference($document, $mapping);
700 4
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
701 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...
702
703
        switch ($storeAs) {
704 4
            case ClassMetadata::REFERENCE_STORE_AS_ID:
705 2
                $this->query[$mapping['name']] = $reference;
706 2
                return $this;
707
                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...
708
709 3
            case ClassMetadata::REFERENCE_STORE_AS_REF:
710
                $keys = ['id' => true];
711
                break;
712
713 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
714 1
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
715 3
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
716
717 3
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
718 2
                    unset($keys['$db']);
719
                }
720
721 3
                if (isset($mapping['targetDocument'])) {
722 1
                    unset($keys['$ref'], $keys['$db']);
723
                }
724 3
                break;
725
726
            default:
727
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
728
        }
729
730 3
        foreach ($keys as $key => $value) {
731 3
            $this->query[$mapping['name']]['$elemMatch'][$key] = $reference[$key];
732
        }
733
734 3
        return $this;
735
    }
736
737
    /**
738
     * Set the $language option for $text criteria.
739
     *
740
     * This method must be called after text().
741
     *
742
     * @see Builder::language()
743
     * @see http://docs.mongodb.org/manual/reference/operator/text/
744
     * @param string $language
745
     * @return $this
746
     * @throws \BadMethodCallException If the query does not already have $text criteria.
747
     */
748 2
    public function language($language)
749
    {
750 2
        if (! isset($this->query['$text'])) {
751 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
752
        }
753
754 1
        $this->query['$text']['$language'] = (string) $language;
755
756 1
        return $this;
757
    }
758
759
    /**
760
     * Specify $lt criteria for the current field.
761
     *
762
     * @see Builder::lte()
763
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
764
     * @param mixed $value
765
     * @return $this
766
     */
767 4
    public function lt($value)
768
    {
769 4
        return $this->operator('$lt', $value);
770
    }
771
772
    /**
773
     * Specify $lte criteria for the current field.
774
     *
775
     * @see Builder::lte()
776
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
777
     * @param mixed $value
778
     * @return $this
779
     */
780 2
    public function lte($value)
781
    {
782 2
        return $this->operator('$lte', $value);
783
    }
784
785
    /**
786
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
787
     *
788
     * @see Builder::max()
789
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
790
     * @param mixed $value
791
     * @return $this
792
     */
793
    public function max($value)
794
    {
795
        $this->requiresCurrentField();
796
        $this->newObj['$max'][$this->currentField] = $value;
797
        return $this;
798
    }
799
800
    /**
801
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
802
     *
803
     * @see Builder::min()
804
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
805
     * @param mixed $value
806
     * @return $this
807
     */
808
    public function min($value)
809
    {
810
        $this->requiresCurrentField();
811
        $this->newObj['$min'][$this->currentField] = $value;
812
        return $this;
813
    }
814
815
    /**
816
     * Specify $mod criteria for the current field.
817
     *
818
     * @see Builder::mod()
819
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
820
     * @param float|int $divisor
821
     * @param float|int $remainder
822
     * @return $this
823
     */
824
    public function mod($divisor, $remainder = 0)
825
    {
826
        return $this->operator('$mod', [$divisor, $remainder]);
827
    }
828
829
    /**
830
     * Multiply the current field.
831
     *
832
     * If the field does not exist, it will be set to 0.
833
     *
834
     * @see Builder::mul()
835
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
836
     * @param float|int $value
837
     * @return $this
838
     */
839
    public function mul($value)
840
    {
841
        $this->requiresCurrentField();
842
        $this->newObj['$mul'][$this->currentField] = $value;
843
        return $this;
844
    }
845
846
    /**
847
     * Add $near criteria to the expression.
848
     *
849
     * A GeoJSON point may be provided as the first and only argument for
850
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
851
     * an array corresponding to the point's JSON representation.
852
     *
853
     * @see Builder::near()
854
     * @see http://docs.mongodb.org/manual/reference/operator/near/
855
     * @param float|array|Point $x
856
     * @param float             $y
857
     * @return $this
858
     */
859 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...
860
    {
861 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...
862 1
            $x = $x->jsonSerialize();
863
        }
864
865 3
        if (is_array($x)) {
866 2
            return $this->operator('$near', ['$geometry' => $x]);
867
        }
868
869 1
        return $this->operator('$near', [$x, $y]);
870
    }
871
872
    /**
873
     * Add $nearSphere criteria to the expression.
874
     *
875
     * A GeoJSON point may be provided as the first and only argument for
876
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
877
     * an array corresponding to the point's JSON representation.
878
     *
879
     * @see Builder::nearSphere()
880
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
881
     * @param float|array|Point $x
882
     * @param float             $y
883
     * @return $this
884
     */
885 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...
886
    {
887 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...
888 1
            $x = $x->jsonSerialize();
889
        }
890
891 3
        if (is_array($x)) {
892 2
            return $this->operator('$nearSphere', ['$geometry' => $x]);
893
        }
894
895 1
        return $this->operator('$nearSphere', [$x, $y]);
896
    }
897
898
    /**
899
     * Negates an expression for the current field.
900
     *
901
     * @see Builder::not()
902
     * @see http://docs.mongodb.org/manual/reference/operator/not/
903
     * @param array|Expr $expression
904
     * @return $this
905
     */
906 2
    public function not($expression)
907
    {
908 2
        return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
909
    }
910
911
    /**
912
     * Specify $ne criteria for the current field.
913
     *
914
     * @see Builder::notEqual()
915
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
916
     * @param mixed $value
917
     * @return $this
918
     */
919 6
    public function notEqual($value)
920
    {
921 6
        return $this->operator('$ne', $value);
922
    }
923
924
    /**
925
     * Specify $nin criteria for the current field.
926
     *
927
     * @see Builder::notIn()
928
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
929
     * @param array $values
930
     * @return $this
931
     */
932 6
    public function notIn(array $values)
933
    {
934 6
        return $this->operator('$nin', array_values($values));
935
    }
936
937
    /**
938
     * Defines an operator and value on the expression.
939
     *
940
     * If there is a current field, the operator will be set on it; otherwise,
941
     * the operator is set at the top level of the query.
942
     *
943
     * @param string $operator
944
     * @param mixed  $value
945
     * @return $this
946
     */
947 81
    public function operator($operator, $value)
948
    {
949 81
        $this->wrapEqualityCriteria();
950
951 81
        if ($this->currentField) {
952 56
            $this->query[$this->currentField][$operator] = $value;
953
        } else {
954 27
            $this->query[$operator] = $value;
955
        }
956 81
        return $this;
957
    }
958
959
    /**
960
     * Remove the first element from the current array field.
961
     *
962
     * @see Builder::popFirst()
963
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
964
     * @return $this
965
     */
966 1
    public function popFirst()
967
    {
968 1
        $this->requiresCurrentField();
969 1
        $this->newObj['$pop'][$this->currentField] = 1;
970 1
        return $this;
971
    }
972
973
    /**
974
     * Remove the last element from the current array field.
975
     *
976
     * @see Builder::popLast()
977
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
978
     * @return $this
979
     */
980
    public function popLast()
981
    {
982
        $this->requiresCurrentField();
983
        $this->newObj['$pop'][$this->currentField] = -1;
984
        return $this;
985
    }
986
987
    /**
988
     * Add $position criteria to the expression for a $push operation.
989
     *
990
     * This is useful in conjunction with {@link Expr::each()} for a
991
     * {@link Expr::push()} operation.
992
     *
993
     * @see http://docs.mongodb.org/manual/reference/operator/update/position/
994
     * @param int $position
995
     * @return $this
996
     */
997 1
    public function position($position)
998
    {
999 1
        return $this->operator('$position', $position);
1000
    }
1001
1002
    /**
1003
     * Remove all elements matching the given value or expression from the
1004
     * current array field.
1005
     *
1006
     * @see Builder::pull()
1007
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1008
     * @param mixed|Expr $valueOrExpression
1009
     * @return $this
1010
     */
1011 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...
1012
    {
1013 2
        if ($valueOrExpression instanceof Expr) {
1014 1
            $valueOrExpression = $valueOrExpression->getQuery();
1015
        }
1016
1017 2
        $this->requiresCurrentField();
1018 2
        $this->newObj['$pull'][$this->currentField] = $valueOrExpression;
1019 2
        return $this;
1020
    }
1021
1022
    /**
1023
     * Remove all elements matching any of the given values from the current
1024
     * array field.
1025
     *
1026
     * @see Builder::pullAll()
1027
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1028
     * @param array $values
1029
     * @return $this
1030
     */
1031
    public function pullAll(array $values)
1032
    {
1033
        $this->requiresCurrentField();
1034
        $this->newObj['$pullAll'][$this->currentField] = $values;
1035
        return $this;
1036
    }
1037
1038
    /**
1039
     * Append one or more values to the current array field.
1040
     *
1041
     * If the field does not exist, it will be set to an array containing the
1042
     * value(s) in the argument. If the field is not an array, the query
1043
     * will yield an error.
1044
     *
1045
     * Multiple values may be specified by providing an Expr object and using
1046
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1047
     * also be used to limit and order array elements, respectively.
1048
     *
1049
     * @see Builder::push()
1050
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1051
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1052
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1053
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1054
     * @param mixed|Expr $valueOrExpression
1055
     * @return $this
1056
     */
1057 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...
1058
    {
1059 8
        if ($valueOrExpression instanceof Expr) {
1060 3
            $valueOrExpression = array_merge(
1061 3
                ['$each' => []],
1062 3
                $valueOrExpression->getQuery()
1063
            );
1064
        }
1065
1066 8
        $this->requiresCurrentField();
1067 8
        $this->newObj['$push'][$this->currentField] = $valueOrExpression;
1068 8
        return $this;
1069
    }
1070
1071
    /**
1072
     * Specify $gte and $lt criteria for the current field.
1073
     *
1074
     * This method is shorthand for specifying $gte criteria on the lower bound
1075
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1076
     *
1077
     * @see Builder::range()
1078
     * @param mixed $start
1079
     * @param mixed $end
1080
     * @return $this
1081
     */
1082 2
    public function range($start, $end)
1083
    {
1084 2
        return $this->operator('$gte', $start)->operator('$lt', $end);
1085
    }
1086
1087
    /**
1088
     * Checks that the value of the current field is a reference to the supplied document.
1089
     *
1090
     * @param object $document
1091
     * @return Expr
1092
     */
1093 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...
1094
    {
1095 13
        $this->requiresCurrentField();
1096 13
        $mapping = $this->getReferenceMapping();
1097 11
        $reference = $this->dm->createReference($document, $mapping);
1098 11
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
1099 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...
1100
1101
        switch ($storeAs) {
1102 11
            case ClassMetadata::REFERENCE_STORE_AS_ID:
1103 4
                $this->query[$mapping['name']] = $reference;
1104 4
                return $this;
1105
                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...
1106
1107 8
            case ClassMetadata::REFERENCE_STORE_AS_REF:
1108
                $keys = ['id' => true];
1109
                break;
1110
1111 8
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
1112 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
1113 8
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
1114
1115 8
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
1116 5
                    unset($keys['$db']);
1117
                }
1118
1119 8
                if (isset($mapping['targetDocument'])) {
1120 4
                    unset($keys['$ref'], $keys['$db']);
1121
                }
1122 8
                break;
1123
1124
            default:
1125
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
1126
        }
1127
1128 8
        foreach ($keys as $key => $value) {
1129 8
            $this->query[$mapping['name'] . '.' . $key] = $reference[$key];
1130
        }
1131
1132 8
        return $this;
1133
    }
1134
1135
    /**
1136
     * Rename the current field.
1137
     *
1138
     * @see Builder::rename()
1139
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1140
     * @param string $name
1141
     * @return $this
1142
     */
1143
    public function rename($name)
1144
    {
1145
        $this->requiresCurrentField();
1146
        $this->newObj['$rename'][$this->currentField] = $name;
1147
        return $this;
1148
    }
1149
1150
    /**
1151
     * Set the current field to a value.
1152
     *
1153
     * This is only relevant for insert, update, or findAndUpdate queries. For
1154
     * update and findAndUpdate queries, the $atomic parameter will determine
1155
     * whether or not a $set operator is used.
1156
     *
1157
     * @see Builder::set()
1158
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1159
     * @param mixed $value
1160
     * @param bool  $atomic
1161
     * @return $this
1162
     */
1163 19
    public function set($value, $atomic = true)
1164
    {
1165 19
        $this->requiresCurrentField();
1166
1167 19
        if ($atomic) {
1168 16
            $this->newObj['$set'][$this->currentField] = $value;
1169 16
            return $this;
1170
        }
1171
1172 3
        if (strpos($this->currentField, '.') === false) {
1173 2
            $this->newObj[$this->currentField] = $value;
1174 2
            return $this;
1175
        }
1176
1177 2
        $keys = explode('.', $this->currentField);
1178 2
        $current = &$this->newObj;
1179 2
        foreach ($keys as $key) {
1180 2
            $current = &$current[$key];
1181
        }
1182 2
        $current = $value;
1183
1184 2
        return $this;
1185
    }
1186
1187
    /**
1188
     * Sets ClassMetadata for document being queried.
1189
     *
1190
     */
1191 389
    public function setClassMetadata(ClassMetadata $class)
1192
    {
1193 389
        $this->class = $class;
1194 389
    }
1195
1196
    /**
1197
     * Set the "new object".
1198
     *
1199
     * @see Builder::setNewObj()
1200
     * @param array $newObj
1201
     * @return $this
1202
     */
1203
    public function setNewObj(array $newObj)
1204
    {
1205
        $this->newObj = $newObj;
1206
        return $this;
1207
    }
1208
1209
    /**
1210
     * Set the current field to the value if the document is inserted in an
1211
     * upsert operation.
1212
     *
1213
     * If an update operation with upsert: true results in an insert of a
1214
     * document, then $setOnInsert assigns the specified values to the fields in
1215
     * the document. If the update operation does not result in an insert,
1216
     * $setOnInsert does nothing.
1217
     *
1218
     * @see Builder::setOnInsert()
1219
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1220
     * @param mixed $value
1221
     * @return $this
1222
     */
1223 1
    public function setOnInsert($value)
1224
    {
1225 1
        $this->requiresCurrentField();
1226 1
        $this->newObj['$setOnInsert'][$this->currentField] = $value;
1227
1228 1
        return $this;
1229
    }
1230
1231
    /**
1232
     * Set the query criteria.
1233
     *
1234
     * @see Builder::setQueryArray()
1235
     * @param array $query
1236
     * @return $this
1237
     */
1238 18
    public function setQuery(array $query)
1239
    {
1240 18
        $this->query = $query;
1241 18
        return $this;
1242
    }
1243
1244
    /**
1245
     * Specify $size criteria for the current field.
1246
     *
1247
     * @see Builder::size()
1248
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1249
     * @param int $size
1250
     * @return $this
1251
     */
1252
    public function size($size)
1253
    {
1254
        return $this->operator('$size', (int) $size);
1255
    }
1256
1257
    /**
1258
     * Add $slice criteria to the expression for a $push operation.
1259
     *
1260
     * This is useful in conjunction with {@link Expr::each()} for a
1261
     * {@link Expr::push()} operation. {@link Builder::selectSlice()} should be
1262
     * used for specifying $slice for a query projection.
1263
     *
1264
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1265
     * @param int $slice
1266
     * @return $this
1267
     */
1268 2
    public function slice($slice)
1269
    {
1270 2
        return $this->operator('$slice', $slice);
1271
    }
1272
1273
    /**
1274
     * Add $sort criteria to the expression for a $push operation.
1275
     *
1276
     * If sorting by multiple fields, the first argument should be an array of
1277
     * field name (key) and order (value) pairs.
1278
     *
1279
     * This is useful in conjunction with {@link Expr::each()} for a
1280
     * {@link Expr::push()} operation. {@link Builder::sort()} should be used to
1281
     * sort the results of a query.
1282
     *
1283
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1284
     * @param array|string $fieldName Field name or array of field/order pairs
1285
     * @param int|string   $order     Field order (if one field is specified)
1286
     * @return $this
1287
     */
1288 2
    public function sort($fieldName, $order = null)
1289
    {
1290 2
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1291
1292 2
        return $this->operator('$sort', array_map(function ($order) {
1293 2
            return $this->normalizeSortOrder($order);
1294 2
        }, $fields));
1295
    }
1296
1297
    /**
1298
     * Specify $text criteria for the current query.
1299
     *
1300
     * The $language option may be set with {@link Expr::language()}.
1301
     *
1302
     * @see Builder::text()
1303
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1304
     * @param string $search
1305
     * @return $this
1306
     */
1307 6
    public function text($search)
1308
    {
1309 6
        $this->query['$text'] = ['$search' => (string) $search];
1310 6
        return $this;
1311
    }
1312
1313
    /**
1314
     * Specify $type criteria for the current field.
1315
     *
1316
     * @todo Remove support for string $type argument in 2.0
1317
     * @see Builder::type()
1318
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1319
     * @param int $type
1320
     * @return $this
1321
     */
1322 1
    public function type($type)
1323
    {
1324 1
        if (is_string($type)) {
1325
            $map = [
1326 1
                'double' => 1,
1327
                'string' => 2,
1328
                'object' => 3,
1329
                'array' => 4,
1330
                'binary' => 5,
1331
                'undefined' => 6,
1332
                'objectid' => 7,
1333
                'boolean' => 8,
1334
                'date' => 9,
1335
                'null' => 10,
1336
                'regex' => 11,
1337
                'jscode' => 13,
1338
                'symbol' => 14,
1339
                'jscodewithscope' => 15,
1340
                'integer32' => 16,
1341
                'timestamp' => 17,
1342
                'integer64' => 18,
1343
                'maxkey' => 127,
1344
                'minkey' => 255,
1345
            ];
1346
1347 1
            $type = $map[$type] ?? $type;
1348
        }
1349
1350 1
        return $this->operator('$type', $type);
1351
    }
1352
1353
    /**
1354
     * Unset the current field.
1355
     *
1356
     * The field will be removed from the document (not set to null).
1357
     *
1358
     * @see Builder::unsetField()
1359
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1360
     * @return $this
1361
     */
1362 3
    public function unsetField()
1363
    {
1364 3
        $this->requiresCurrentField();
1365 3
        $this->newObj['$unset'][$this->currentField] = 1;
1366 3
        return $this;
1367
    }
1368
1369
    /**
1370
     * Specify a JavaScript expression to use for matching documents.
1371
     *
1372
     * @see Builder::where()
1373
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1374
     * @param string|Javascript $javascript
1375
     * @return $this
1376
     */
1377 3
    public function where($javascript)
1378
    {
1379 3
        $this->query['$where'] = $javascript;
1380 3
        return $this;
1381
    }
1382
1383
    /**
1384
     * Gets reference mapping for current field from current class or its descendants.
1385
     *
1386
     * @return array
1387
     * @throws MappingException
1388
     */
1389 19
    private function getReferenceMapping()
1390
    {
1391 19
        $mapping = null;
1392
        try {
1393 19
            $mapping = $this->class->getFieldMapping($this->currentField);
1394 6
        } catch (MappingException $e) {
1395 6
            if (empty($this->class->discriminatorMap)) {
1396
                throw $e;
1397
            }
1398 6
            $foundIn = null;
1399 6
            foreach ($this->class->discriminatorMap as $child) {
1400 6
                $childClass = $this->dm->getClassMetadata($child);
1401 6
                if ($childClass->hasAssociation($this->currentField)) {
1402 4
                    if ($mapping !== null && $mapping !== $childClass->getFieldMapping($this->currentField)) {
1403 2
                        throw MappingException::referenceFieldConflict($this->currentField, $foundIn->name, $childClass->name);
1404
                    }
1405 4
                    $mapping = $childClass->getFieldMapping($this->currentField);
1406 6
                    $foundIn = $childClass;
1407
                }
1408
            }
1409 4
            if ($mapping === null) {
1410 2
                throw MappingException::mappingNotFoundInClassNorDescendants($this->class->name, $this->currentField);
1411
            }
1412
        }
1413 15
        return $mapping;
1414
    }
1415
1416
    /**
1417
     * @param int|string $order
1418
     *
1419
     */
1420 2
    private function normalizeSortOrder($order): int
1421
    {
1422 2
        if (is_string($order)) {
1423
            $order = strtolower($order) === 'asc' ? 1 : -1;
1424
        }
1425
1426 2
        return (int) $order;
1427
    }
1428
1429
    /**
1430
     * Ensure that a current field has been set.
1431
     *
1432
     * @throws \LogicException If a current field has not been set.
1433
     */
1434 67
    private function requiresCurrentField()
1435
    {
1436 67
        if (! $this->currentField) {
1437
            throw new \LogicException('This method requires you set a current field using field().');
1438
        }
1439 67
    }
1440
1441
    /**
1442
     * Wraps equality criteria with an operator.
1443
     *
1444
     * If equality criteria was previously specified for a field, it cannot be
1445
     * merged with other operators without first being wrapped in an operator of
1446
     * its own. Ideally, we would wrap it with $eq, but that is only available
1447
     * in MongoDB 2.8. Using a single-element $in is backwards compatible.
1448
     *
1449
     * @see Expr::operator()
1450
     */
1451 81
    private function wrapEqualityCriteria()
1452
    {
1453
        /* If the current field has no criteria yet, do nothing. This ensures
1454
         * that we do not inadvertently inject {"$in": null} into the query.
1455
         */
1456 81
        if ($this->currentField && ! isset($this->query[$this->currentField]) && ! array_key_exists($this->currentField, $this->query)) {
1457 55
            return;
1458
        }
1459
1460 31
        if ($this->currentField) {
1461 5
            $query = &$this->query[$this->currentField];
1462
        } else {
1463 27
            $query = &$this->query;
1464
        }
1465
1466
        /* If the query is an empty array, we'll assume that the user has not
1467
         * specified criteria. Otherwise, check if the array includes a query
1468
         * operator (checking the first key is sufficient). If neither of these
1469
         * conditions are met, we'll wrap the query value with $in.
1470
         */
1471 31
        if (is_array($query) && (empty($query) || strpos(key($query), '$') === 0)) {
1472 31
            return;
1473
        }
1474
1475 2
        $query = ['$in' => [$query]];
1476 2
    }
1477
}
1478