GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Cursor::skipDocumentPool()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
/**
4
 * This file is part of the PHPMongo package.
5
 *
6
 * (c) Dmytro Sokil <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sokil\Mongo;
13
14
use Sokil\Mongo\Exception\CursorException;
15
use Sokil\Mongo\Exception\FeatureNotSupportedException;
16
use Sokil\Mongo\Exception\WriteException;
17
18
/**
19
 * @mixin Expression
20
 */
21
class Cursor implements
22
    \Iterator,
23
    \Countable
24
{
25
    /**
26
     *
27
     * @var Client
28
     */
29
    private $client;
30
31
    /**
32
     *
33
     * @var Collection
34
     */
35
    private $collection;
36
37
    /**
38
     *
39
     * @var array
40
     */
41
    private $projection = array();
42
43
    /**
44
     *
45
     * @var \MongoCursor
46
     */
47
    private $cursor;
48
    /**
49
     *
50
     * @var Expression
51
     */
52
    private $expression;
53
54
    /**
55
     * Offset
56
     * @var int
57
     */
58
    private $skip = 0;
59
60
    /**
61
     * Limit
62
     * @var int
63
     */
64
    private $limit = 0;
65
66
    /**
67
     * Definition of sort
68
     * @var array
69
     */
70
    private $sort = array();
71
72
    /**
73
     * Definition of read preference
74
     * @var array
75
     */
76
    private $readPreference = array();
77
78
    /**
79
     * Return result as array or as Document instance
80
     * @var boolean
81
     */
82
    private $isResultAsArray = false;
83
84
    /**
85
     * Cursor options
86
     * @var array
87
     */
88
    private $options = array(
89
        'expressionClass' => '\Sokil\Mongo\Expression',
90
        /**
91
         * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
92
         * @var int number of documents to return in each batch of the response from the MongoDB instance
93
         */
94
        'batchSize' => null,
95
        // client timeout
96
        'clientTimeout' => null,
97
        // Specifies a cumulative time limit in milliseconds to
98
        // be allowed by the server for processing operations on the cursor.
99
        'serverTimeout' => null,
100
    );
101
102
    /**
103
     * Use document pool to create Document object from array
104
     * @var bool
105
     */
106
    private $isDocumentPoolUsed = true;
107
108
    /**
109
     * Index hinting
110
     * @param \Sokil\Mongo\Collection $collection
111
     * @param array $options
112
     */
113
    private $hint;
114
115
    public function __construct(Collection $collection, array $options = null)
116
    {
117
        $this->collection = $collection;
118
        $this->client = $this->collection->getDatabase()->getClient();
119
120
        if (!empty($options)) {
121
            $this->options = $options + $this->options;
122
        }
123
124
        // expression
125
        $this->expression = $this->expression();
126
    }
127
128
    public function __call($name, $arguments)
129
    {
130
        call_user_func_array(
131
            array($this->expression, $name),
132
            $arguments
133
        );
134
135
        return $this;
136
    }
137
138
    /**
139
     * Get option
140
     *
141
     * @param string|int $name
142
     * @param mixed $default
143
     *
144
     * @return mixed
145
     */
146
    public function getOption($name, $default = null)
147
    {
148
        return isset($this->options[$name]) ? $this->options[$name] : $default;
149
    }
150
151
    /**
152
     * Get result as array
153
     *
154
     * @return Cursor
155
     */
156
    public function asArray()
157
    {
158
        $this->isResultAsArray = true;
159
        return $this;
160
    }
161
162
    /**
163
     * Get result as object
164
     * @return Cursor
165
     */
166
    public function asObject()
167
    {
168
        $this->isResultAsArray = false;
169
        return $this;
170
    }
171
172
    /**
173
     * Check if result returned as array
174
     * @return bool
175
     */
176
    public function isResultAsArray()
177
    {
178
        return $this->isResultAsArray;
179
    }
180
181
    /**
182
     * Return only specified fields
183
     *
184
     * @param array $fields
185
     * @return Cursor
186
     */
187
    public function fields(array $fields)
188
    {
189
        $this->projection = array_fill_keys($fields, 1);
190
        $this->skipDocumentPool();
191
        return $this;
192
    }
193
194
    /**
195
     * Append field to accept list
196
     *
197
     * @param string $field field name
198
     *
199
     * @return Cursor
200
     */
201
    public function field($field)
202
    {
203
        $this->projection[$field] = 1;
204
        $this->skipDocumentPool();
205
        return $this;
206
    }
207
    /**
208
     * Return all fields except specified
209
     *
210
     * @param array $fields
211
     * @return \Sokil\Mongo\Cursor
212
     */
213
    public function skipFields(array $fields)
214
    {
215
        $this->projection = array_fill_keys($fields, 0);
216
        $this->skipDocumentPool();
217
        return $this;
218
    }
219
220
    /**
221
     * Append field to skip list
222
     *
223
     * @param string $field field name
224
     * @return Cursor
225
     */
226
    public function skipField($field)
227
    {
228
        $this->projection[$field] = 0;
229
        $this->skipDocumentPool();
230
        return $this;
231
    }
232
233
    /**
234
     * Paginate list of sub-documents
235
     *
236
     * @param string $field
237
     * @param integer $limit
238
     * @param integer $skip
239
     * @return \Sokil\Mongo\Cursor
240
     * @throws Exception
241
     */
242
    public function slice($field, $limit, $skip = null)
243
    {
244
        $limit  = (int) $limit;
245
        $skip   = (int) $skip;
246
247
        if ($skip) {
248
            $this->projection[$field] = array('$slice' => array($skip, $limit));
249
        } else {
250
            $this->projection[$field] = array('$slice' => $limit);
251
        }
252
253
        $this->skipDocumentPool();
254
255
        return $this;
256
    }
257
258
    /**
259
     * Filter list of sub-documents
260
     *
261
     * @see https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/#project-specific-array-elements-in-the-returned-array
262
     *
263
     * @param string $field
264
     * @param Expression|array|callable $expression
265
     *
266
     * @return Cursor
267
     *
268
     * @throws Exception
269
     */
270
    public function elemMatch($field, $expression)
271
    {
272
        $this->projection[$field] = array(
273
            '$elemMatch' => Expression::convertToArray($expression),
274
        );
275
276
        $this->skipDocumentPool();
277
278
        return $this;
279
    }
280
281
    /**
282
     * Merge expression
283
     * @param \Sokil\Mongo\Expression $expression
284
     * @return \Sokil\Mongo\Cursor
285
     */
286
    public function query(Expression $expression)
287
    {
288
        $this->expression->merge($expression);
289
        return $this;
290
    }
291
292
    /**
293
     * Helper to create new expression
294
     *
295
     * @return \Sokil\Mongo\Expression
296
     */
297
    public function expression()
298
    {
299
        return new $this->options['expressionClass'];
300
    }
301
302
    /**
303
     * Filter by list of \MongoId
304
     *
305
     * @param array $idList list of ids
306
     * @return \Sokil\Mongo\Cursor
307
     */
308
    public function byIdList(array $idList)
309
    {
310
        $this->expression->whereIn('_id', self::mixedToMongoIdList($idList));
311
        return $this;
312
    }
313
314
    /**
315
     * Filter by id
316
     *
317
     * @param string|\MongoId $id id of document
318
     * @return \Sokil\Mongo\Cursor
319
     */
320
    public function byId($id)
321
    {
322
        if ($id instanceof \MongoId) {
323
            $this->expression->where('_id', $id);
324
        } else {
325
            try {
326
                $this->expression->where('_id', new \MongoId($id));
327
            } catch (\MongoException $e) {
328
                $this->expression->where('_id', $id);
329
            }
330
        }
331
332
        return $this;
333
    }
334
335
    /**
336
     * Skip defined number of documents
337
     *
338
     * @param int $skip number of documents to skip
339
     * @return \Sokil\Mongo\Cursor
340
     */
341
    public function skip($skip)
342
    {
343
        $this->skip = (int) $skip;
344
345
        return $this;
346
    }
347
348
    /**
349
     * Limit result set to specified number of elements
350
     *
351
     * @param int $limit number of elements in result set
352
     * @param int|null $offset number of elements to skip
353
     * @return \Sokil\Mongo\Cursor
354
     */
355
    public function limit($limit, $offset = null)
356
    {
357
        $this->limit = (int) $limit;
358
359
        if (null !== $offset) {
360
            $this->skip($offset);
361
        }
362
363
        return $this;
364
    }
365
366
    /**
367
     * Specifies the number of documents to return in each batch of the response from the MongoDB instance.
368
     *
369
     * @param int $size number of documents
370
     * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
371
     * @return \Sokil\Mongo\Cursor
372
     */
373
    public function setBatchSize($size)
374
    {
375
        $this->options['batchSize'] = (int) $size;
376
377
        return $this;
378
    }
379
380
    /**
381
     * Instructs the driver to stop waiting for a response and throw a
382
     * MongoCursorTimeoutException after a set time.
383
     * A timeout can be set at any time and will affect subsequent queries on
384
     * the cursor, including fetching more results from the database.
385
     *
386
     * @param int $ms
387
     * @return Cursor
388
     */
389
    public function setClientTimeout($ms)
390
    {
391
        $this->options['clientTimeout'] = (int) $ms;
392
        return $this;
393
    }
394
395
    /**
396
     * Server-side timeout for a query,
397
     * Specifies a cumulative time limit in milliseconds to be allowed
398
     * by the server for processing operations on the cursor.
399
     *
400
     * @param int $ms
401
     * @return \Sokil\Mongo\Cursor
402
     */
403
    public function setServerTimeout($ms)
404
    {
405
        $this->options['serverTimeout'] = (int) $ms;
406
        return $this;
407
    }
408
409
    /**
410
     * Sort result by specified keys and directions
411
     *
412
     *  An array of fields by which to sort. Each element in the array has as key the field name, and as value either
413
     * 1 for ascending sort, or -1 for descending sort. Each result is first sorted on the first field in the array,
414
     * then (if it exists) on the second field in the array, etc. This means that the order of the fields in the
415
     * fields array is important. See also the examples section.
416
     *
417
     * @param array $sort
418
     * @return Cursor
419
     */
420
    public function sort(array $sort)
421
    {
422
        $this->sort = $sort;
423
        return $this;
424
    }
425
426
    /**
427
     * Count documents in result without applying limit and offset
428
     * @return int count
429
     */
430
    public function count()
431
    {
432
        return (int) $this->collection
433
            ->getMongoCollection()
434
            ->count($this->expression->toArray());
435
    }
436
437
    /**
438
     * Explain expression
439
     *
440
     * @return array
441
     *
442
     * @throws FeatureNotSupportedException
443
     */
444
    public function explain()
445
    {
446
        if (Client::isEmulationMode()) {
447
            throw new FeatureNotSupportedException('Feature not implemented in emulation mode');
448
        }
449
450
        $this->rewind();
451
452
        return $this->cursor->explain();
453
    }
454
455
    /**
456
     * Count documents in result with applying limit and offset
457
     *
458
     * ext-mongo:1.0.7  Added limit and skip as second and third parameters, respectively.
459
     * ext-mongo:1.6.0  The second parameter is now an options array. Passing limit and skip as the second and third
460
     *                  parameters, respectively, is deprecated.
461
     *
462
     * @return int
463
     *
464
     * @throws FeatureNotSupportedException
465
     */
466
    public function limitedCount()
467
    {
468
        if (version_compare(\MongoClient::VERSION, '1.0.7', '<')) {
469
            throw new FeatureNotSupportedException('Limit and skip not supported in ext-mongo versions prior to 1.0.7');
470
        }
471
472
        return (int) $this->collection
473
            ->getMongoCollection()
474
            ->count(
475
                $this->expression->toArray(),
476
                $this->limit,
477
                $this->skip
478
            );
479
    }
480
481
482
    /**
483
     * Gte list of \MongoId of current search query
484
     * @return array
485
     */
486
    public function getIdList()
487
    {
488
        return self::mixedToMongoIdList($this->findAll());
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
489
    }
490
491
    /**
492
     * Find one document which correspond to expression
493
     *
494
     * @deprecated since v.1.22.2. Use ::one instead
495
     *
496
     * @return Document|array|null
497
     *
498
     * @throws CursorException
499
     */
500
    public function findOne()
501
    {
502
        return $this->one();
503
    }
504
505
    /**
506
     * Find one document which correspond to expression
507
     *
508
     * @return Document|array|null
509
     *
510
     * @throws CursorException
511
     */
512
    public function one()
513
    {
514
        try {
515
            $mongoDocument = $this->collection
516
                ->getMongoCollection()
517
                ->findOne(
518
                    $this->expression->toArray(),
519
                    $this->projection
520
                );
521
        } catch (\Exception $e) {
522
            throw new CursorException(
523
                $e->getMessage(),
524
                $e->getCode(),
525
                $e
526
            );
527
        }
528
529
        if (empty($mongoDocument)) {
530
            return null;
531
        }
532
533
        if (true === $this->isResultAsArray) {
534
            return $mongoDocument;
535
        }
536
537
        return $this->collection->hydrate(
538
            $mongoDocument,
539
            $this->isDocumentPoolUsed()
540
        );
541
    }
542
543
    /**
544
     * Get result of searching
545
     *
546
     * @deprecated since v.1.22.2. Use ::one instead
547
     *
548
     * @return Document[]|array[]
549
     */
550
    public function findAll()
551
    {
552
        return $this->all();
553
    }
554
555
    /**
556
     * Get result of searching
557
     *
558
     * @return array
559
     */
560
    public function all()
561
    {
562
        return iterator_to_array($this);
563
    }
564
565
    /**
566
     * Get random document
567
     *
568
     * @deprecated since v.1.22.2. Use ::one instead
569
     *
570
     * @return Document|null
571
     */
572
    public function findRandom()
573
    {
574
        return $this->random();
575
    }
576
577
    /**
578
     * Get random document
579
     *
580
     * @return Document|null
581
     */
582
    public function random()
583
    {
584
        $count = $this->count();
585
        switch ($count) {
586
            case 0:
587
                return null;
588
            case 1:
589
                return $this->one();
590
            default:
591
                $cursor = $this->skip(mt_rand(0, $count - 1))->limit(1);
592
                $cursor->rewind();
593
                return $cursor->current();
594
        }
595
    }
596
597
    /**
598
     * Get query builder's expression
599
     *
600
     * @return Expression
601
     */
602
    public function getExpression()
603
    {
604
        return $this->expression;
605
    }
606
607
    /**
608
     * Get MongoDB query array
609
     *
610
     * @return array
611
     */
612
    public function getMongoQuery()
613
    {
614
        return $this->expression->toArray();
615
    }
616
    
617
    /**
618
     * Return the values from a single field in the result set of documents
619
     *
620
     * @param string $fieldName
621
     * @return array
622
     */
623
    public function pluck($fieldName)
624
    {
625
        $isEmbeddedDocumentField = false !== strpos($fieldName, '.');
626
627
        $valueList = array();
628
629
        if ($isEmbeddedDocumentField) {
630
            // get result
631
            if ($this->isResultAsArray) {
632
                $cursor = clone $this;
633
                $documentObjectList = $cursor->asObject()->findAll();
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
634
                unset($cursor);
635
            } else {
636
                $documentObjectList = $this->findAll();
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
637
            }
638
            // get value of field
639
            foreach ($documentObjectList as $key => $documentObject) {
640
                $valueList[$key] = $documentObject->get($fieldName);
641
            }
642
        } else {
643
            // get result
644
            if ($this->isResultAsArray) {
645
                $documentArrayList = $this->findAll();
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
646
            } else {
647
                $cursor = clone $this;
648
                $documentArrayList = $cursor->asArray()->findAll();
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
649
                unset($cursor);
650
            }
651
            // get values of field
652
            $valueList = array_column($documentArrayList, $fieldName, '_id');
653
        }
654
655
        return $valueList;
656
    }
657
658
    /**
659
     * Get document instance and remove it from collection
660
     *
661
     * @return \Sokil\Mongo\Document
662
     */
663
    public function findAndRemove()
664
    {
665
        $mongoDocument = $this->collection->getMongoCollection()->findAndModify(
666
            $this->expression->toArray(),
667
            null,
668
            $this->projection,
669
            array(
670
                'remove' => true,
671
                'sort' => $this->sort,
672
            )
673
        );
674
675
        if (empty($mongoDocument)) {
676
            return null;
677
        }
678
679
        return $this->collection->hydrate(
680
            $mongoDocument,
681
            $this->isDocumentPoolUsed()
682
        );
683
    }
684
685
    /**
686
     * Find first document and update it
687
     *
688
     * @param Operator $operator operations with document to update
689
     * @param bool $upsert if document not found - create
690
     * @param bool $returnUpdated if true - return updated document
691
     *
692
     * @return null|Document
693
     */
694
    public function findAndUpdate(Operator $operator, $upsert = false, $returnUpdated = true)
695
    {
696
        $mongoDocument = $this->collection
697
            ->getMongoCollection()
698
            ->findAndModify(
699
                $this->expression->toArray(),
700
                $operator ? $operator->toArray() : null,
701
                $this->projection,
702
                array(
703
                    'new' => $returnUpdated,
704
                    'sort' => $this->sort,
705
                    'upsert' => $upsert,
706
                )
707
            );
708
709
        if (empty($mongoDocument)) {
710
            return null;
711
        }
712
713
        return $this->collection->hydrate($mongoDocument, $this->isDocumentPoolUsed());
714
    }
715
716
    /**
717
     * Apply callable to all documents in cursor
718
     *
719
     * @param callable $handler
720
     * @return array
721
     */
722 View Code Duplication
    public function map($handler)
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...
723
    {
724
        $result = array();
725
726
        foreach ($this as $id => $document) {
727
            $result[$id] = $handler($document);
728
        }
729
730
        return $result;
731
    }
732
733
    /**
734
     * Filter documents in cursor by condition in callable
735
     *
736
     * @param callable $handler
737
     * @return array
738
     */
739 View Code Duplication
    public function filter($handler)
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...
740
    {
741
        $result = array();
742
743
        foreach ($this as $id => $document) {
744
            if (!$handler($document)) {
745
                continue;
746
            }
747
748
            $result[$id] = $document;
749
        }
750
751
        return $result;
752
    }
753
754
    /**
755
     * Get result set of documents.
756
     *
757
     * @return \Sokil\Mongo\ResultSet
758
     */
759
    public function getResultSet()
760
    {
761
        return new ResultSet($this->findAll());
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
762
    }
763
764
    /**
765
     * Get paginator
766
     *
767
     * @param int $page page number
768
     * @param int $itemsOnPage number of items on page
769
     * @return Paginator
770
     */
771
    public function paginate($page, $itemsOnPage = 30)
772
    {
773
        $paginator = new Paginator($this);
774
        return $paginator
775
            ->setCurrentPage($page)
776
            ->setItemsOnPage($itemsOnPage);
777
    }
778
779
    /**
780
     * Clears the cursor
781
     * Mongo's rewind is reset+next
782
     */
783
    public function reset()
784
    {
785
        if ($this->cursor !== null) {
786
            $this->cursor->reset();
787
        }
788
789
        return $this;
790
    }
791
792
    /**
793
     * Returns the cursor to the beginning of the result set.
794
     * @return void
795
     */
796
    public function rewind()
797
    {
798
        if ($this->cursor !== null) {
799
            $this->cursor->rewind();
800
            return;
801
        }
802
803
        $this->cursor = $this->collection
804
            ->getMongoCollection()
805
            ->find(
806
                $this->expression->toArray(),
807
                $this->projection
808
            );
809
810
        if ($this->skip) {
811
            $this->cursor->skip($this->skip);
812
        }
813
814
        if ($this->limit) {
815
            $this->cursor->limit($this->limit);
816
        }
817
818
        if ($this->options['batchSize']) {
819
            $this->cursor->batchSize($this->options['batchSize']);
820
        }
821
822
        if ($this->options['clientTimeout']) {
823
            $this->cursor->timeout($this->options['clientTimeout']);
824
        }
825
826
        if ($this->options['serverTimeout']) {
827
            $this->cursor->maxTimeMS($this->options['clientTimeout']);
828
        }
829
830
        if (!empty($this->sort)) {
831
            $this->cursor->sort($this->sort);
832
        }
833
834
        if ($this->hint) {
835
            $this->cursor->hint($this->hint);
836
        }
837
838
        // define read preferences
839
        if (!empty($this->readPreference)) {
840
            $this->cursor->setReadPreference(
841
                $this->readPreference['type'],
842
                $this->readPreference['tagsets']
843
            );
844
        }
845
846
        // init cursor state
847
        $this->cursor->rewind();
848
    }
849
850
    /**
851
     * @return bool
852
     */
853
    public function valid()
854
    {
855
        return $this->cursor->valid();
856
    }
857
858
    /**
859
     * @return Document|array|null
860
     */
861
    public function current()
862
    {
863
        $mongoDocument = $this->cursor->current();
864
        if (empty($mongoDocument)) {
865
            return null;
866
        }
867
868
        if ($this->isResultAsArray) {
869
            return $mongoDocument;
870
        }
871
872
        return $this->collection->hydrate(
873
            $mongoDocument,
874
            $this->isDocumentPoolUsed
875
        );
876
    }
877
878
    /**
879
     * @return string
880
     */
881
    public function key()
882
    {
883
        return $this->cursor->key();
884
    }
885
886
    /**
887
     * @return void
888
     */
889
    public function next()
890
    {
891
        $this->cursor->next();
892
    }
893
894
    public function readPrimaryOnly()
895
    {
896
        $this->readPreference = array(
897
            'type'      => \MongoClient::RP_PRIMARY,
898
            'tagsets'   => array(),
899
        );
900
901
        return $this;
902
    }
903
904
    public function readPrimaryPreferred(array $tags = null)
905
    {
906
        $this->readPreference = array(
907
            'type'      => \MongoClient::RP_PRIMARY_PREFERRED,
908
            'tagsets'   => $tags,
909
        );
910
911
        return $this;
912
    }
913
914
    public function readSecondaryOnly(array $tags = null)
915
    {
916
        $this->readPreference = array(
917
            'type'      => \MongoClient::RP_SECONDARY,
918
            'tagsets'   => $tags,
919
        );
920
921
        return $this;
922
    }
923
924
    public function readSecondaryPreferred(array $tags = null)
925
    {
926
        $this->readPreference = array(
927
            'type'      => \MongoClient::RP_SECONDARY_PREFERRED,
928
            'tagsets'   => $tags,
929
        );
930
931
        return $this;
932
    }
933
934
    public function readNearest(array $tags = null)
935
    {
936
        $this->readPreference = array(
937
            'type'      => \MongoClient::RP_NEAREST,
938
            'tagsets'   => $tags,
939
        );
940
941
        return $this;
942
    }
943
944
    /**
945
     * @return array
946
     */
947
    public function getReadPreference()
948
    {
949
        if ($this->cursor) {
950
            return $this->cursor->getReadPreference();
951
        }
952
953
        return $this->readPreference;
954
    }
955
956
    public function isDocumentPoolUsed()
957
    {
958
        return $this->isDocumentPoolUsed;
959
    }
960
961
    public function useDocumentPool()
962
    {
963
        $this->isDocumentPoolUsed = true;
964
        return $this;
965
    }
966
967
    public function skipDocumentPool()
968
    {
969
        $this->isDocumentPoolUsed = false;
970
971
        return $this;
972
    }
973
974
    /**
975
     * Specify index to use
976
     *
977
     * @link http://docs.mongodb.org/manual/reference/operator/meta/hint/
978
     * @param array|string $specification Specify the index either by the index name or by document
979
     * @return \Sokil\Mongo\Cursor
980
     */
981
    public function hint($specification)
982
    {
983
        $this->hint = $specification;
984
        return $this;
985
    }
986
987
    /**
988
     * Copy selected documents to another collection
989
     *
990
     * @param string $targetCollectionName
991
     * @param string|null $targetDatabaseName Target database name. If not specified - use current
992
     * @param int $batchLimit count of documents to get from old and insert to new collection per time
993
     *
994
     * @return Cursor
995
     *
996
     * @throws WriteException
997
     */
998
    public function copyToCollection(
999
        $targetCollectionName,
1000
        $targetDatabaseName = null,
1001
        $batchLimit = 100
1002
    ) {
1003
        // target database
1004
        if (empty($targetDatabaseName)) {
1005
            $database = $this->collection->getDatabase();
1006
        } else {
1007
            $database = $this->client->getDatabase($targetDatabaseName);
1008
        }
1009
1010
        // target collection
1011
        $targetMongoCollection = $database
1012
            ->getCollection($targetCollectionName)
1013
            ->getMongoCollection();
1014
1015
        // cursor
1016
        $this->rewind();
1017
1018
        // copy data
1019
        $inProgress = true;
1020
        while ($inProgress) {
1021
            // get next pack of documents
1022
            $documentList = array();
1023
            for ($i = 0; $i < $batchLimit; $i++) {
1024
                if (!$this->cursor->valid()) {
1025
                    $inProgress = false;
1026
1027
                    if (!empty($documentList)) {
1028
                        // still need batch insert
1029
                        break;
1030
                    } else {
1031
                        // no documents to insert - just exit
1032
                        break(2);
1033
                    }
1034
                }
1035
1036
                $documentList[] = $this->cursor->current();
1037
                $this->cursor->next();
1038
            }
1039
1040
            // insert
1041
            $result = $targetMongoCollection->batchInsert($documentList);
1042
1043
            // With passed write concern, returns an associative array with the status of the inserts ("ok")
1044
            // and any error that may have occurred ("err").
1045
            // Otherwise, returns TRUE if the batch insert was successfully sent, FALSE otherwise.
1046
            if (is_array($result)) {
1047
                if ($result['ok'] != 1) {
1048
                    throw new WriteException('Batch insert error: ' . $result['err']);
1049
                }
1050
            } elseif (false === $result) {
1051
                throw new WriteException('Batch insert error');
1052
            }
1053
        }
1054
1055
        return $this;
1056
    }
1057
1058
    /**
1059
     * Move selected documents to another collection.
1060
     * Documents will be removed from source collection only after
1061
     * copying them to target collection.
1062
     *
1063
     * @param string $targetCollectionName
1064
     * @param string|null $targetDatabaseName Target database name. If not specified - use current
1065
     * @param int $batchLimit count of documents to get from old and insert to new collection per time
1066
     */
1067
    public function moveToCollection(
1068
        $targetCollectionName,
1069
        $targetDatabaseName = null,
1070
        $batchLimit = 100
1071
    ) {
1072
        // copy to target
1073
        $this->copyToCollection($targetCollectionName, $targetDatabaseName, $batchLimit);
1074
1075
        // remove from source
1076
        $this->collection->batchDelete($this->expression);
0 ignored issues
show
Documentation introduced by
$this->expression is of type object<Sokil\Mongo\Expression>, but the function expects a callable.

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...
1077
    }
1078
1079
    /**
1080
     * Used to get hash that uniquely identifies current query
1081
     *
1082
     * @return string
1083
     */
1084
    public function getHash()
1085
    {
1086
        $hash = array();
1087
1088
        // expression
1089
        $hash[] = json_encode($this->expression->toArray());
1090
1091
        // sorts
1092 View Code Duplication
        if (!empty($this->sort)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1093
            $sort = $this->sort;
1094
            ksort($sort);
1095
            $hash[] = implode('', array_merge(array_keys($sort), array_values($sort)));
1096
        }
1097
1098
        // fields
1099 View Code Duplication
        if (!empty($this->projection)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1100
            $fields = $this->projection;
1101
            ksort($fields);
1102
            $hash[] = implode('', array_merge(array_keys($fields), array_values($fields)));
1103
        }
1104
1105
        // skip and limit
1106
        $hash[] = $this->skip;
1107
        $hash[] = $this->limit;
1108
1109
        // get hash
1110
        return md5(implode(':', $hash));
1111
    }
1112
1113
    /**
1114
     * Get list of MongoId objects from array of strings, MongoId's and Document's
1115
     *
1116
     * @param array $list
1117
     * @return array list of \MongoId
1118
     */
1119
    public static function mixedToMongoIdList(array $list)
1120
    {
1121
        return array_map(function ($element) {
1122
            // MongoId
1123
            if ($element instanceof \MongoId) {
1124
                return $element;
1125
            }
1126
1127
            // \Sokil\Mongo\Document
1128
            if ($element instanceof Document) {
1129
                return $element->getId();
1130
            }
1131
1132
            // array with id key
1133
            if (is_array($element)) {
1134
                if (!isset($element['_id'])) {
1135
                    throw new \InvalidArgumentException('Array must have _id key');
1136
                }
1137
                return $element['_id'];
1138
            }
1139
1140
            // string
1141
            if (is_string($element)) {
1142
                try {
1143
                    return new \MongoId($element);
1144
                } catch (\MongoException $e) {
1145
                    return $element;
1146
                }
1147
            }
1148
1149
            // int
1150
            if (is_int($element)) {
1151
                return $element;
1152
            }
1153
1154
            throw new \InvalidArgumentException(
1155
                'Must be \MongoId, \Sokil\Mongo\Document, array with _id key, string or integer'
1156
            );
1157
        }, array_values($list));
1158
    }
1159
}
1160