Completed
Pull Request — master (#1787)
by Stefano
21:31
created

Builder::nearSphere()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
475
     * @return $this
476
     */
477 6 View Code Duplication
    public function exclude($fieldName = 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...
478
    {
479 6
        if (! isset($this->query['select'])) {
480 6
            $this->query['select'] = [];
481
        }
482
483 6
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
484
485 6
        foreach ($fieldNames as $fieldName) {
486 4
            $this->query['select'][$fieldName] = 0;
487
        }
488
489 6
        return $this;
490
    }
491
492
    /**
493
     * Specify $exists criteria for the current field.
494
     *
495
     * @see Expr::exists()
496
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
497
     * @param bool $bool
498
     * @return $this
499
     */
500 5
    public function exists($bool)
501
    {
502 5
        $this->expr->exists((bool) $bool);
503 5
        return $this;
504
    }
505
506
    /**
507
     * Create a new Expr instance that can be used as an expression with the Builder
508
     *
509
     * @return Expr $expr
510
     */
511 26
    public function expr()
512
    {
513 26
        $expr = new Expr($this->dm);
514 26
        $expr->setClassMetadata($this->class);
515
516 26
        return $expr;
517
    }
518
519
    /**
520
     * Set the current field to operate on.
521
     *
522
     * @param string $field
523
     * @return $this
524
     */
525 146
    public function field($field)
526
    {
527 146
        $this->currentField = $field;
528 146
        $this->expr->field((string) $field);
529
530 146
        return $this;
531
    }
532
533
    /**
534
     * Set the "finalize" option for a mapReduce or group command.
535
     *
536
     * @param string|Javascript $finalize
537
     * @return $this
538
     * @throws \BadMethodCallException If the query is not a mapReduce or group command.
539
     */
540 2 View Code Duplication
    public function finalize($finalize)
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...
541
    {
542 2
        switch ($this->query['type']) {
543
            case Query::TYPE_MAP_REDUCE:
544 1
                $this->query['mapReduce']['options']['finalize'] = $finalize;
545 1
                break;
546
547
            case Query::TYPE_GROUP:
548
                $this->query['group']['options']['finalize'] = $finalize;
549
                break;
550
551
            default:
552 1
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before finalize()');
553
        }
554
555 1
        return $this;
556
    }
557
558
    /**
559
     * Change the query type to find and optionally set and change the class being queried.
560
     *
561
     * @param string $documentName
562
     * @return $this
563
     */
564 13
    public function find($documentName = null)
565
    {
566 13
        $this->setDocumentName($documentName);
567 13
        $this->query['type'] = Query::TYPE_FIND;
568
569 13
        return $this;
570
    }
571
572
    /**
573
     * @param string $documentName
574
     * @return $this
575
     */
576 1
    public function findAndRemove($documentName = null)
577
    {
578 1
        $this->setDocumentName($documentName);
579 1
        $this->query['type'] = Query::TYPE_FIND_AND_REMOVE;
580
581 1
        return $this;
582
    }
583
584
    /**
585
     * @param string $documentName
586
     * @return $this
587
     */
588 13
    public function findAndUpdate($documentName = null)
589
    {
590 13
        $this->setDocumentName($documentName);
591 13
        $this->query['type'] = Query::TYPE_FIND_AND_UPDATE;
592
593 13
        return $this;
594
    }
595
596
    /**
597
     * Add $geoIntersects criteria with a GeoJSON geometry to the query.
598
     *
599
     * The geometry parameter GeoJSON object or an array corresponding to the
600
     * geometry's JSON representation.
601
     *
602
     * @see Expr::geoIntersects()
603
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
604
     * @param array|Geometry $geometry
605
     * @return $this
606
     */
607 1
    public function geoIntersects($geometry)
608
    {
609 1
        $this->expr->geoIntersects($geometry);
610 1
        return $this;
611
    }
612
613
    /**
614
     * Add $geoWithin criteria with a GeoJSON geometry to the query.
615
     *
616
     * The geometry parameter GeoJSON object or an array corresponding to the
617
     * geometry's JSON representation.
618
     *
619
     * @see Expr::geoWithin()
620
     * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
621
     * @param array|Geometry $geometry
622
     * @return $this
623
     */
624 1
    public function geoWithin($geometry)
625
    {
626 1
        $this->expr->geoWithin($geometry);
627 1
        return $this;
628
    }
629
630
    /**
631
     * Add $geoWithin criteria with a $box shape to the query.
632
     *
633
     * A rectangular polygon will be constructed from a pair of coordinates
634
     * corresponding to the bottom left and top right corners.
635
     *
636
     * Note: the $box operator only supports legacy coordinate pairs and 2d
637
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
638
     *
639
     * @see Expr::geoWithinBox()
640
     * @see http://docs.mongodb.org/manual/reference/operator/box/
641
     * @param float $x1
642
     * @param float $y1
643
     * @param float $x2
644
     * @param float $y2
645
     * @return $this
646
     */
647 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
648
    {
649 1
        $this->expr->geoWithinBox($x1, $y1, $x2, $y2);
650 1
        return $this;
651
    }
652
653
    /**
654
     * Add $geoWithin criteria with a $center shape to the query.
655
     *
656
     * Note: the $center operator only supports legacy coordinate pairs and 2d
657
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
658
     *
659
     * @see Expr::geoWithinCenter()
660
     * @see http://docs.mongodb.org/manual/reference/operator/center/
661
     * @param float $x
662
     * @param float $y
663
     * @param float $radius
664
     * @return $this
665
     */
666 1
    public function geoWithinCenter($x, $y, $radius)
667
    {
668 1
        $this->expr->geoWithinCenter($x, $y, $radius);
669 1
        return $this;
670
    }
671
672
    /**
673
     * Add $geoWithin criteria with a $centerSphere shape to the query.
674
     *
675
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
676
     *
677
     * @see Expr::geoWithinCenterSphere()
678
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
679
     * @param float $x
680
     * @param float $y
681
     * @param float $radius
682
     * @return $this
683
     */
684 1
    public function geoWithinCenterSphere($x, $y, $radius)
685
    {
686 1
        $this->expr->geoWithinCenterSphere($x, $y, $radius);
687 1
        return $this;
688
    }
689
690
    /**
691
     * Add $geoWithin criteria with a $polygon shape to the query.
692
     *
693
     * Point coordinates are in x, y order (easting, northing for projected
694
     * coordinates, longitude, latitude for geographic coordinates).
695
     *
696
     * The last point coordinate is implicitly connected with the first.
697
     *
698
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
699
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
700
     *
701
     * @see Expr::geoWithinPolygon()
702
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
703
     * @param array $point1    First point of the polygon
704
     * @param array $point2    Second point of the polygon
705
     * @param array $point3    Third point of the polygon
706
     * @param array ...$points Additional points of the polygon
707
     * @return $this
708
     */
709 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...
710
    {
711 1
        $this->expr->geoWithinPolygon(...func_get_args());
712 1
        return $this;
713
    }
714
715
    /**
716
     * Return the expression's "new object".
717
     *
718
     * @see Expr::getNewObj()
719
     * @return array
720
     */
721 13
    public function getNewObj()
722
    {
723 13
        return $this->expr->getNewObj();
724
    }
725
726
    /**
727
     * Gets the Query executable.
728
     *
729
     * @param array $options
730
     * @return Query $query
731
     */
732 152
    public function getQuery(array $options = [])
733
    {
734 152
        if ($this->query['type'] === Query::TYPE_MAP_REDUCE) {
735
            $this->hydrate = false;
736
        }
737
738 152
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
739
740 152
        $query = $this->query;
741
742 152
        $query['query'] = $this->expr->getQuery();
743 152
        $query['query'] = $documentPersister->addDiscriminatorToPreparedQuery($query['query']);
744 152
        $query['query'] = $documentPersister->addFilterToPreparedQuery($query['query']);
745
746 152
        $query['newObj'] = $this->expr->getNewObj();
747
748 152
        if (isset($query['distinct'])) {
749 2
            $query['distinct'] = $documentPersister->prepareFieldName($query['distinct']);
0 ignored issues
show
Documentation introduced by
$query['distinct'] is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
750
        }
751
752 152
        if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION && ! empty($query['upsert']) &&
753 152
            (empty($query['query'][$this->class->discriminatorField]) || is_array($query['query'][$this->class->discriminatorField]))) {
754 1
            throw new \InvalidArgumentException('Upsert query that is to be performed on discriminated document does not have single ' .
755 1
                'discriminator. Either not use base class or set \'' . $this->class->discriminatorField . '\' field manually.');
756
        }
757
758 151
        if (! empty($query['select'])) {
759 14
            $query['select'] = $documentPersister->prepareProjection($query['select']);
760 14
            if ($this->hydrate && $this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION
761 14
                && ! isset($query['select'][$this->class->discriminatorField])) {
762
                $includeMode = 0 < count(array_filter($query['select'], function ($mode) {
763 2
                    return $mode === 1;
764 2
                }));
765 2
                if ($includeMode && ! isset($query['select'][$this->class->discriminatorField])) {
766 1
                    $query['select'][$this->class->discriminatorField] = 1;
767
                }
768
            }
769
        }
770
771 151
        if (isset($query['sort'])) {
772 23
            $query['sort'] = $documentPersister->prepareSort($query['sort']);
773
        }
774
775 151
        if ($this->class->readPreference && ! array_key_exists('readPreference', $query)) {
776 1
            $query['readPreference'] = new ReadPreference($this->class->readPreference, $this->class->readPreferenceTags);
777
        }
778
779 151
        return new Query(
780 151
            $this->dm,
781 151
            $this->class,
782 151
            $this->collection,
783 151
            $query,
784 151
            $options,
785 151
            $this->hydrate,
786 151
            $this->refresh,
787 151
            $this->primers,
788 151
            $this->readOnly
789
        );
790
    }
791
792
    /**
793
     * Return the expression's query criteria.
794
     *
795
     * @see Expr::getQuery()
796
     * @return array
797
     */
798 33
    public function getQueryArray()
799
    {
800 33
        return $this->expr->getQuery();
801
    }
802
803
    /**
804
     * Get the type of this query.
805
     *
806
     * @return int $type
807
     */
808 2
    public function getType()
809
    {
810 2
        return $this->query['type'];
811
    }
812
813
    /**
814
     * Specify $gt criteria for the current field.
815
     *
816
     * @see Expr::gt()
817
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
818
     * @param mixed $value
819
     * @return $this
820
     */
821 2
    public function gt($value)
822
    {
823 2
        $this->expr->gt($value);
824 2
        return $this;
825
    }
826
827
    /**
828
     * Specify $gte criteria for the current field.
829
     *
830
     * @see Expr::gte()
831
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
832
     * @param mixed $value
833
     * @return $this
834
     */
835 2
    public function gte($value)
836
    {
837 2
        $this->expr->gte($value);
838 2
        return $this;
839
    }
840
841
    /**
842
     * Set the index hint for the query.
843
     *
844
     * @param array|string $index
845
     * @return $this
846
     */
847
    public function hint($index)
848
    {
849
        $this->query['hint'] = $index;
850
        return $this;
851
    }
852
853
    /**
854
     * @param bool $bool
855
     * @return $this
856
     */
857 17
    public function hydrate($bool = true)
858
    {
859 17
        $this->hydrate = $bool;
860 17
        return $this;
861
    }
862
863
    /**
864
     * Set the immortal cursor flag.
865
     *
866
     * @param bool $bool
867
     * @return $this
868
     */
869
    public function immortal($bool = true)
870
    {
871
        $this->query['immortal'] = (bool) $bool;
872
        return $this;
873
    }
874
875
    /**
876
     * Specify $in criteria for the current field.
877
     *
878
     * @see Expr::in()
879
     * @see http://docs.mongodb.org/manual/reference/operator/in/
880
     * @param array $values
881
     * @return $this
882
     */
883 24
    public function in(array $values)
884
    {
885 24
        $this->expr->in($values);
886 24
        return $this;
887
    }
888
889
    /**
890
     * Increment the current field.
891
     *
892
     * If the field does not exist, it will be set to this value.
893
     *
894
     * @see Expr::inc()
895
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
896
     * @param float|int $value
897
     * @return $this
898
     */
899 6
    public function inc($value)
900
    {
901 6
        $this->expr->inc($value);
902 6
        return $this;
903
    }
904
905
    /**
906
     * @param object $document
907
     * @return $this
908
     */
909 6
    public function includesReferenceTo($document)
910
    {
911 6
        $this->expr->includesReferenceTo($document);
912 4
        return $this;
913
    }
914
915
    /**
916
     * @param string $documentName
917
     * @return $this
918
     */
919 1
    public function insert($documentName = null)
920
    {
921 1
        $this->setDocumentName($documentName);
922 1
        $this->query['type'] = Query::TYPE_INSERT;
923
924 1
        return $this;
925
    }
926
927
    /**
928
     * Set the $language option for $text criteria.
929
     *
930
     * This method must be called after text().
931
     *
932
     * @see Expr::language()
933
     * @see http://docs.mongodb.org/manual/reference/operator/text/
934
     * @param string $language
935
     * @return $this
936
     */
937 1
    public function language($language)
938
    {
939 1
        $this->expr->language($language);
940 1
        return $this;
941
    }
942
943
    /**
944
     * Set the limit for the query.
945
     *
946
     * This is only relevant for find queries and geoNear and mapReduce
947
     * commands.
948
     *
949
     * @see Query::prepareCursor()
950
     * @param int $limit
951
     * @return $this
952
     */
953 2
    public function limit($limit)
954
    {
955 2
        $this->query['limit'] = (int) $limit;
956 2
        return $this;
957
    }
958
959
    /**
960
     * Specify $lt criteria for the current field.
961
     *
962
     * @see Expr::lte()
963
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
964
     * @param mixed $value
965
     * @return $this
966
     */
967
    public function lt($value)
968
    {
969
        $this->expr->lt($value);
970
        return $this;
971
    }
972
973
    /**
974
     * Specify $lte criteria for the current field.
975
     *
976
     * @see Expr::lte()
977
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
978
     * @param mixed $value
979
     * @return $this
980
     */
981
    public function lte($value)
982
    {
983
        $this->expr->lte($value);
984
        return $this;
985
    }
986
987
    /**
988
     * Change the query type to a mapReduce command.
989
     *
990
     * The "reduce" option is not specified when calling this method; it must
991
     * be set with the {@link Builder::reduce()} method.
992
     *
993
     * The "out" option defaults to inline, like {@link Builder::mapReduce()}.
994
     *
995
     * @see http://docs.mongodb.org/manual/reference/command/mapReduce/
996
     * @param string|Javascript $map
997
     * @return $this
998
     */
999 1
    public function map($map)
1000
    {
1001 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
1002 1
        $this->query['mapReduce'] = [
1003 1
            'map' => $map,
1004
            'reduce' => null,
1005
            'out' => ['inline' => true],
1006
            'options' => [],
1007
        ];
1008 1
        return $this;
1009
    }
1010
1011
    /**
1012
     * Change the query type to a mapReduce command.
1013
     *
1014
     * @see http://docs.mongodb.org/manual/reference/command/mapReduce/
1015
     * @param string|Javascript $map
1016
     * @param string|Javascript $reduce
1017
     * @param array|string      $out
1018
     * @param array             $options
1019
     * @return $this
1020
     */
1021 1
    public function mapReduce($map, $reduce, $out = ['inline' => true], array $options = [])
1022
    {
1023 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
1024 1
        $this->query['mapReduce'] = [
1025 1
            'map' => $map,
1026 1
            'reduce' => $reduce,
1027 1
            'out' => $out,
1028 1
            'options' => $options,
1029
        ];
1030 1
        return $this;
1031
    }
1032
1033
    /**
1034
     * Set additional options for a mapReduce command.
1035
     *
1036
     * @param array $options
1037
     * @return $this
1038
     * @throws \BadMethodCallException If the query is not a mapReduce command.
1039
     */
1040 1 View Code Duplication
    public function mapReduceOptions(array $options)
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...
1041
    {
1042 1
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1043 1
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1044
        }
1045
1046
        $this->query['mapReduce']['options'] = $options;
1047
        return $this;
1048
    }
1049
1050
    /**
1051
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
1052
     *
1053
     * @see Expr::max()
1054
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
1055
     * @param mixed $value
1056
     * @return $this
1057
     */
1058 1
    public function max($value)
1059
    {
1060 1
        $this->expr->max($value);
1061 1
        return $this;
1062
    }
1063
1064
    /**
1065
     * Specifies a cumulative time limit in milliseconds for processing operations on a cursor.
1066
     *
1067
     * @param int $ms
1068
     * @return $this
1069
     */
1070
    public function maxTimeMS($ms)
1071
    {
1072
        $this->query['maxTimeMS'] = $ms;
1073
        return $this;
1074
    }
1075
1076
    /**
1077
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
1078
     *
1079
     * @see Expr::min()
1080
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
1081
     * @param mixed $value
1082
     * @return $this
1083
     */
1084 1
    public function min($value)
1085
    {
1086 1
        $this->expr->min($value);
1087 1
        return $this;
1088
    }
1089
1090
    /**
1091
     * Specify $mod criteria for the current field.
1092
     *
1093
     * @see Expr::mod()
1094
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
1095
     * @param float|int $divisor
1096
     * @param float|int $remainder
1097
     * @return $this
1098
     */
1099 1
    public function mod($divisor, $remainder = 0)
1100
    {
1101 1
        $this->expr->mod($divisor, $remainder);
1102 1
        return $this;
1103
    }
1104
1105
    /**
1106
     * Multiply the current field.
1107
     *
1108
     * If the field does not exist, it will be set to 0.
1109
     *
1110
     * @see Expr::mul()
1111
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
1112
     * @param float|int $value
1113
     * @return $this
1114
     */
1115 1
    public function mul($value)
1116
    {
1117 1
        $this->expr->mul($value);
1118 1
        return $this;
1119
    }
1120
1121
    /**
1122
     * Add $near criteria to the query.
1123
     *
1124
     * A GeoJSON point may be provided as the first and only argument for
1125
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1126
     * an array corresponding to the point's JSON representation.
1127
     *
1128
     * @see Expr::near()
1129
     * @see http://docs.mongodb.org/manual/reference/operator/near/
1130
     * @param float|array|Point $x
1131
     * @param float             $y
1132
     * @return $this
1133
     */
1134 1
    public function near($x, $y = null)
1135
    {
1136 1
        $this->expr->near($x, $y);
1137 1
        return $this;
1138
    }
1139
1140
    /**
1141
     * Add $nearSphere criteria to the query.
1142
     *
1143
     * A GeoJSON point may be provided as the first and only argument for
1144
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1145
     * an array corresponding to the point's JSON representation.
1146
     *
1147
     * @see Expr::nearSphere()
1148
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
1149
     * @param float|array|Point $x
1150
     * @param float             $y
1151
     * @return $this
1152
     */
1153 1
    public function nearSphere($x, $y = null)
1154
    {
1155 1
        $this->expr->nearSphere($x, $y);
1156 1
        return $this;
1157
    }
1158
1159
    /**
1160
     * Negates an expression for the current field.
1161
     *
1162
     * You can create a new expression using the {@link Builder::expr()} method.
1163
     *
1164
     * @see Expr::not()
1165
     * @see http://docs.mongodb.org/manual/reference/operator/not/
1166
     * @param array|Expr $expression
1167
     * @return $this
1168
     */
1169 3
    public function not($expression)
1170
    {
1171 3
        $this->expr->not($expression);
1172 3
        return $this;
1173
    }
1174
1175
    /**
1176
     * Specify $ne criteria for the current field.
1177
     *
1178
     * @see Expr::notEqual()
1179
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
1180
     * @param mixed $value
1181
     * @return $this
1182
     */
1183 4
    public function notEqual($value)
1184
    {
1185 4
        $this->expr->notEqual($value);
1186 4
        return $this;
1187
    }
1188
1189
    /**
1190
     * Specify $nin criteria for the current field.
1191
     *
1192
     * @see Expr::notIn()
1193
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
1194
     * @param array $values
1195
     * @return $this
1196
     */
1197 4
    public function notIn(array $values)
1198
    {
1199 4
        $this->expr->notIn($values);
1200 4
        return $this;
1201
    }
1202
1203
    /**
1204
     * Set the "out" option for a mapReduce command.
1205
     *
1206
     * @param array|string $out
1207
     * @return $this
1208
     * @throws \BadMethodCallException If the query is not a mapReduce command.
1209
     */
1210 1 View Code Duplication
    public function out($out)
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...
1211
    {
1212 1
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1213 1
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1214
        }
1215
1216
        $this->query['mapReduce']['out'] = $out;
1217
        return $this;
1218
    }
1219
1220
    /**
1221
     * Remove the first element from the current array field.
1222
     *
1223
     * @see Expr::popFirst()
1224
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1225
     * @return $this
1226
     */
1227 2
    public function popFirst()
1228
    {
1229 2
        $this->expr->popFirst();
1230 2
        return $this;
1231
    }
1232
1233
    /**
1234
     * Remove the last element from the current array field.
1235
     *
1236
     * @see Expr::popLast()
1237
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1238
     * @return $this
1239
     */
1240 1
    public function popLast()
1241
    {
1242 1
        $this->expr->popLast();
1243 1
        return $this;
1244
    }
1245
1246
    /**
1247
     * Use a primer to eagerly load all references in the current field.
1248
     *
1249
     * If $primer is true or a callable is provided, referenced documents for
1250
     * this field will loaded into UnitOfWork immediately after the query is
1251
     * executed. This will avoid multiple queries due to lazy initialization of
1252
     * Proxy objects.
1253
     *
1254
     * If $primer is false, no priming will take place. That is also the default
1255
     * behavior.
1256
     *
1257
     * If a custom callable is used, its signature should conform to the default
1258
     * Closure defined in {@link ReferencePrimer::__construct()}.
1259
     *
1260
     * @param bool|callable $primer
1261
     * @return $this
1262
     * @throws \InvalidArgumentException If $primer is not boolean or callable.
1263
     */
1264 25
    public function prime($primer = true)
1265
    {
1266 25
        if (! is_bool($primer) && ! is_callable($primer)) {
1267 1
            throw new \InvalidArgumentException('$primer is not a boolean or callable');
1268
        }
1269
1270 24
        if ($primer === false) {
1271
            unset($this->primers[$this->currentField]);
1272
1273
            return $this;
1274
        }
1275
1276 24
        if (array_key_exists('eagerCursor', $this->query) && ! $this->query['eagerCursor']) {
1277 1
            throw new \BadMethodCallException("Can't call prime() when setting eagerCursor to false");
1278
        }
1279
1280 23
        $this->primers[$this->currentField] = $primer;
1281 23
        return $this;
1282
    }
1283
1284
    /**
1285
     * Remove all elements matching the given value or expression from the
1286
     * current array field.
1287
     *
1288
     * @see Expr::pull()
1289
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1290
     * @param mixed|Expr $valueOrExpression
1291
     * @return $this
1292
     */
1293 1
    public function pull($valueOrExpression)
1294
    {
1295 1
        $this->expr->pull($valueOrExpression);
1296 1
        return $this;
1297
    }
1298
1299
    /**
1300
     * Remove all elements matching any of the given values from the current
1301
     * array field.
1302
     *
1303
     * @see Expr::pullAll()
1304
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1305
     * @param array $values
1306
     * @return $this
1307
     */
1308 1
    public function pullAll(array $values)
1309
    {
1310 1
        $this->expr->pullAll($values);
1311 1
        return $this;
1312
    }
1313
1314
    /**
1315
     * Append one or more values to the current array field.
1316
     *
1317
     * If the field does not exist, it will be set to an array containing the
1318
     * value(s) in the argument. If the field is not an array, the query
1319
     * will yield an error.
1320
     *
1321
     * Multiple values may be specified by providing an Expr object and using
1322
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1323
     * also be used to limit and order array elements, respectively.
1324
     *
1325
     * @see Expr::push()
1326
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1327
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1328
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1329
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1330
     * @param mixed|Expr $valueOrExpression
1331
     * @return $this
1332
     */
1333 6
    public function push($valueOrExpression)
1334
    {
1335 6
        $this->expr->push($valueOrExpression);
1336 6
        return $this;
1337
    }
1338
1339
    /**
1340
     * Specify $gte and $lt criteria for the current field.
1341
     *
1342
     * This method is shorthand for specifying $gte criteria on the lower bound
1343
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1344
     *
1345
     * @see Expr::range()
1346
     * @param mixed $start
1347
     * @param mixed $end
1348
     * @return $this
1349
     */
1350 3
    public function range($start, $end)
1351
    {
1352 3
        $this->expr->range($start, $end);
1353 3
        return $this;
1354
    }
1355
1356
    /**
1357
     * @param bool $bool
1358
     * @return $this
1359
     */
1360 2
    public function readOnly($bool = true)
1361
    {
1362 2
        $this->readOnly = $bool;
1363 2
        return $this;
1364
    }
1365
1366
    /**
1367
     * Set the "reduce" option for a mapReduce or group command.
1368
     *
1369
     * @param string|Javascript $reduce
1370
     * @return $this
1371
     * @throws \BadMethodCallException If the query is not a mapReduce or group command.
1372
     */
1373 2 View Code Duplication
    public function reduce($reduce)
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...
1374
    {
1375 2
        switch ($this->query['type']) {
1376
            case Query::TYPE_MAP_REDUCE:
1377 1
                $this->query['mapReduce']['reduce'] = $reduce;
1378 1
                break;
1379
1380
            case Query::TYPE_GROUP:
1381
                $this->query['group']['reduce'] = $reduce;
1382
                break;
1383
1384
            default:
1385 1
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()');
1386
        }
1387
1388 1
        return $this;
1389
    }
1390
1391
    /**
1392
     * @param object $document
1393
     * @return $this
1394
     */
1395 10
    public function references($document)
1396
    {
1397 10
        $this->expr->references($document);
1398 8
        return $this;
1399
    }
1400
1401
    /**
1402
     * @param bool $bool
1403
     * @return $this
1404
     */
1405 5
    public function refresh($bool = true)
1406
    {
1407 5
        $this->refresh = $bool;
1408 5
        return $this;
1409
    }
1410
1411
    /**
1412
     * @param string $documentName
1413
     * @return $this
1414
     */
1415 1
    public function remove($documentName = null)
1416
    {
1417 1
        $this->setDocumentName($documentName);
1418 1
        $this->query['type'] = Query::TYPE_REMOVE;
1419
1420 1
        return $this;
1421
    }
1422
1423
    /**
1424
     * Rename the current field.
1425
     *
1426
     * @see Expr::rename()
1427
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1428
     * @param string $name
1429
     * @return $this
1430
     */
1431
    public function rename($name)
1432
    {
1433
        $this->expr->rename($name);
1434
        return $this;
1435
    }
1436
1437
    /**
1438
     * @param bool $bool
1439
     * @return $this
1440
     */
1441 4
    public function returnNew($bool = true)
1442
    {
1443 4
        $this->refresh(true);
1444 4
        $this->query['new'] = (bool) $bool;
1445
1446 4
        return $this;
1447
    }
1448
1449
    /**
1450
     * Set one or more fields to be included in the query projection.
1451
     *
1452
     * @param array|string $fieldName,...
0 ignored issues
show
Documentation introduced by
There is no parameter named $fieldName,.... Did you maybe mean $fieldName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
1453
     * @return $this
1454
     */
1455 19 View Code Duplication
    public function select($fieldName = 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...
1456
    {
1457 19
        if (! isset($this->query['select'])) {
1458 18
            $this->query['select'] = [];
1459
        }
1460
1461 19
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
1462
1463 19
        foreach ($fieldNames as $fieldName) {
1464 16
            $this->query['select'][$fieldName] = 1;
1465
        }
1466
1467 19
        return $this;
1468
    }
1469
1470
    /**
1471
     * Select only matching embedded documents in an array field for the query
1472
     * projection.
1473
     *
1474
     * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
1475
     * @param string     $fieldName
1476
     * @param array|Expr $expression
1477
     * @return $this
1478
     */
1479 2
    public function selectElemMatch($fieldName, $expression)
1480
    {
1481 2
        if ($expression instanceof Expr) {
1482 1
            $expression = $expression->getQuery();
1483
        }
1484 2
        $this->query['select'][$fieldName] = ['$elemMatch' => $expression];
1485 2
        return $this;
1486
    }
1487
1488
    /**
1489
     * Select a metadata field for the query projection.
1490
     *
1491
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/
1492
     * @param string $fieldName
1493
     * @param string $metaDataKeyword
1494
     * @return $this
1495
     */
1496 2
    public function selectMeta($fieldName, $metaDataKeyword)
1497
    {
1498 2
        $this->query['select'][$fieldName] = ['$meta' => $metaDataKeyword];
1499 2
        return $this;
1500
    }
1501
1502
    /**
1503
     * Select a slice of an array field for the query projection.
1504
     *
1505
     * The $countOrSkip parameter has two very different meanings, depending on
1506
     * whether or not $limit is provided. See the MongoDB documentation for more
1507
     * information.
1508
     *
1509
     * @see http://docs.mongodb.org/manual/reference/projection/slice/
1510
     * @param string $fieldName
1511
     * @param int    $countOrSkip Count parameter, or skip if limit is specified
1512
     * @param int    $limit       Limit parameter used in conjunction with skip
1513
     * @return $this
1514
     */
1515 3
    public function selectSlice($fieldName, $countOrSkip, $limit = null)
1516
    {
1517 3
        $slice = $countOrSkip;
1518 3
        if ($limit !== null) {
1519 2
            $slice = [$slice, $limit];
1520
        }
1521 3
        $this->query['select'][$fieldName] = ['$slice' => $slice];
1522 3
        return $this;
1523
    }
1524
1525
    /**
1526
     * Set the current field to a value.
1527
     *
1528
     * This is only relevant for insert, update, or findAndUpdate queries. For
1529
     * update and findAndUpdate queries, the $atomic parameter will determine
1530
     * whether or not a $set operator is used.
1531
     *
1532
     * @see Expr::set()
1533
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1534
     * @param mixed $value
1535
     * @param bool  $atomic
1536
     * @return $this
1537
     */
1538 16
    public function set($value, $atomic = true)
1539
    {
1540 16
        $this->expr->set($value, $atomic && $this->query['type'] !== Query::TYPE_INSERT);
1541 16
        return $this;
1542
    }
1543
1544
    /**
1545
     * Set the expression's "new object".
1546
     *
1547
     * @see Expr::setNewObj()
1548
     * @param array $newObj
1549
     * @return $this
1550
     */
1551
    public function setNewObj(array $newObj)
1552
    {
1553
        $this->expr->setNewObj($newObj);
1554
        return $this;
1555
    }
1556
1557
    /**
1558
     * Set the current field to the value if the document is inserted in an
1559
     * upsert operation.
1560
     *
1561
     * If an update operation with upsert: true results in an insert of a
1562
     * document, then $setOnInsert assigns the specified values to the fields in
1563
     * the document. If the update operation does not result in an insert,
1564
     * $setOnInsert does nothing.
1565
     *
1566
     * @see Expr::setOnInsert()
1567
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1568
     * @param mixed $value
1569
     * @return $this
1570
     */
1571 2
    public function setOnInsert($value)
1572
    {
1573 2
        $this->expr->setOnInsert($value);
1574 2
        return $this;
1575
    }
1576
1577
    /**
1578
     * Set the read preference for the query.
1579
     *
1580
     * This is only relevant for read-only queries and commands.
1581
     *
1582
     * @see http://docs.mongodb.org/manual/core/read-preference/
1583
     * @return $this
1584
     */
1585 6
    public function setReadPreference(ReadPreference $readPreference)
1586
    {
1587 6
        $this->query['readPreference'] = $readPreference;
1588 6
        return $this;
1589
    }
1590
1591
    /**
1592
     * Set the expression's query criteria.
1593
     *
1594
     * @see Expr::setQuery()
1595
     * @param array $query
1596
     * @return $this
1597
     */
1598 18
    public function setQueryArray(array $query)
1599
    {
1600 18
        $this->expr->setQuery($query);
1601 18
        return $this;
1602
    }
1603
1604
    /**
1605
     * Specify $size criteria for the current field.
1606
     *
1607
     * @see Expr::size()
1608
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1609
     * @param int $size
1610
     * @return $this
1611
     */
1612 1
    public function size($size)
1613
    {
1614 1
        $this->expr->size((int) $size);
1615 1
        return $this;
1616
    }
1617
1618
    /**
1619
     * Set the skip for the query cursor.
1620
     *
1621
     * This is only relevant for find queries, or mapReduce queries that store
1622
     * results in an output collecton and return a cursor.
1623
     *
1624
     * @see Query::prepareCursor()
1625
     * @param int $skip
1626
     * @return $this
1627
     */
1628
    public function skip($skip)
1629
    {
1630
        $this->query['skip'] = (int) $skip;
1631
        return $this;
1632
    }
1633
1634
    /**
1635
     * Set the snapshot cursor flag.
1636
     *
1637
     * @param bool $bool
1638
     * @return $this
1639
     */
1640
    public function snapshot($bool = true)
1641
    {
1642
        $this->query['snapshot'] = (bool) $bool;
1643
        return $this;
1644
    }
1645
1646
    /**
1647
     * Set one or more field/order pairs on which to sort the query.
1648
     *
1649
     * If sorting by multiple fields, the first argument should be an array of
1650
     * field name (key) and order (value) pairs.
1651
     *
1652
     * @param array|string $fieldName Field name or array of field/order pairs
1653
     * @param int|string   $order     Field order (if one field is specified)
1654
     * @return $this
1655
     */
1656 31
    public function sort($fieldName, $order = 1)
1657
    {
1658 31
        if (! isset($this->query['sort'])) {
1659 31
            $this->query['sort'] = [];
1660
        }
1661
1662 31
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1663
1664 31
        foreach ($fields as $fieldName => $order) {
1665 14
            if (is_string($order)) {
1666 9
                $order = strtolower($order) === 'asc' ? 1 : -1;
1667
            }
1668 14
            $this->query['sort'][$fieldName] = (int) $order;
1669
        }
1670
1671 31
        return $this;
1672
    }
1673
1674
    /**
1675
     * Specify a projected metadata field on which to sort the query.
1676
     *
1677
     * Sort order is not configurable for metadata fields. Sorting by a metadata
1678
     * field requires the same field and $meta expression to exist in the
1679
     * projection document. This method will call {@link Builder::selectMeta()}
1680
     * if the field is not already set in the projection.
1681
     *
1682
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/#sort
1683
     * @param string $fieldName       Field name of the projected metadata
1684
     * @param string $metaDataKeyword
1685
     * @return $this
1686
     */
1687 2
    public function sortMeta($fieldName, $metaDataKeyword)
1688
    {
1689
        /* It's possible that the field is already projected without the $meta
1690
         * operator. We'll assume that the user knows what they're doing in that
1691
         * case and will not attempt to override the projection.
1692
         */
1693 2
        if (! isset($this->query['select'][$fieldName])) {
1694 1
            $this->selectMeta($fieldName, $metaDataKeyword);
1695
        }
1696
1697 2
        $this->query['sort'][$fieldName] = ['$meta' => $metaDataKeyword];
1698
1699 2
        return $this;
1700
    }
1701
1702
    /**
1703
     * Specify $text criteria for the current field.
1704
     *
1705
     * The $language option may be set with {@link Builder::language()}.
1706
     *
1707
     * @see Expr::text()
1708
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1709
     * @param string $search
1710
     * @return $this
1711
     */
1712 1
    public function text($search)
1713
    {
1714 1
        $this->expr->text($search);
1715 1
        return $this;
1716
    }
1717
1718
    /**
1719
     * Specify $type criteria for the current field.
1720
     *
1721
     * @see Expr::type()
1722
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1723
     * @param int $type
1724
     * @return $this
1725
     */
1726 2
    public function type($type)
1727
    {
1728 2
        $this->expr->type($type);
1729 2
        return $this;
1730
    }
1731
1732
    /**
1733
     * Unset the current field.
1734
     *
1735
     * The field will be removed from the document (not set to null).
1736
     *
1737
     * @see Expr::unsetField()
1738
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1739
     * @return $this
1740
     */
1741 4
    public function unsetField()
1742
    {
1743 4
        $this->expr->unsetField();
1744 4
        return $this;
1745
    }
1746
1747
    /**
1748
     * @param string $documentName
1749
     * @return $this
1750
     */
1751 21 View Code Duplication
    public function updateOne($documentName = 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...
1752
    {
1753 21
        $this->setDocumentName($documentName);
1754 21
        $this->query['type'] = Query::TYPE_UPDATE;
1755 21
        $this->query['multiple'] = false;
1756
1757 21
        return $this;
1758
    }
1759
1760
    /**
1761
     * @param string $documentName
1762
     * @return $this
1763
     */
1764 3 View Code Duplication
    public function updateMany($documentName = 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...
1765
    {
1766 3
        $this->setDocumentName($documentName);
1767 3
        $this->query['type'] = Query::TYPE_UPDATE;
1768 3
        $this->query['multiple'] = true;
1769
1770 3
        return $this;
1771
    }
1772
1773
    /**
1774
     * Set the "upsert" option for an update or findAndUpdate query.
1775
     *
1776
     * @param bool $bool
1777
     * @return $this
1778
     */
1779 7
    public function upsert($bool = true)
1780
    {
1781 7
        $this->query['upsert'] = (bool) $bool;
1782 7
        return $this;
1783
    }
1784
1785
    /**
1786
     * Specify a JavaScript expression to use for matching documents.
1787
     *
1788
     * @see Expr::where()
1789
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1790
     * @param string|Javascript $javascript
1791
     * @return $this
1792
     */
1793 3
    public function where($javascript)
1794
    {
1795 3
        $this->expr->where($javascript);
1796 3
        return $this;
1797
    }
1798
1799
    /**
1800
     * Get Discriminator Values
1801
     *
1802
     * @param \Traversable $classNames
1803
     * @return array an array of discriminatorValues (mixed type)
1804
     * @throws \InvalidArgumentException If the number of found collections > 1.
1805
     */
1806 2
    private function getDiscriminatorValues($classNames)
1807
    {
1808 2
        $discriminatorValues = [];
1809 2
        $collections = [];
1810 2
        foreach ($classNames as $className) {
1811 2
            $class = $this->dm->getClassMetadata($className);
1812 2
            $discriminatorValues[] = $class->discriminatorValue;
1813 2
            $key = $this->dm->getDocumentDatabase($className)->getDatabaseName() . '.' . $class->getCollection();
1814 2
            $collections[$key] = $key;
1815
        }
1816 2
        if (count($collections) > 1) {
1817 1
            throw new \InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
1818
        }
1819 1
        return $discriminatorValues;
1820
    }
1821
1822
    /**
1823
     * @param string[]|string $documentName an array of document names or just one.
1824
     */
1825 286
    private function setDocumentName($documentName)
1826
    {
1827 286
        if (is_array($documentName)) {
1828 2
            $documentNames = $documentName;
1829 2
            $documentName = $documentNames[0];
1830
1831 2
            $metadata = $this->dm->getClassMetadata($documentName);
1832 2
            $discriminatorField = $metadata->discriminatorField;
1833 2
            $discriminatorValues = $this->getDiscriminatorValues($documentNames);
0 ignored issues
show
Documentation introduced by
$documentNames is of type array, but the function expects a object<Traversable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1834
1835
            // If a defaultDiscriminatorValue is set and it is among the discriminators being queries, add NULL to the list
1836 1
            if ($metadata->defaultDiscriminatorValue && array_search($metadata->defaultDiscriminatorValue, $discriminatorValues) !== false) {
1837 1
                $discriminatorValues[] = null;
1838
            }
1839
1840 1
            $this->field($discriminatorField)->in($discriminatorValues);
1841
        }
1842
1843 285
        if ($documentName === null) {
1844 39
            return;
1845
        }
1846
1847 285
        $this->collection = $this->dm->getDocumentCollection($documentName);
1848 285
        $this->class = $this->dm->getClassMetadata($documentName);
1849
1850
        // Expr also needs to know
1851 285
        $this->expr->setClassMetadata($this->class);
1852 285
    }
1853
}
1854