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.
Completed
Push — master ( ce2758...e13fa2 )
by De
01:33
created

Cursor::one()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 19
nc 4
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 $fields = 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->fields = 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->fields[$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->fields = 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->fields[$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->fields[$field] = array('$slice' => array($skip, $limit));
249
        } else {
250
            $this->fields[$field] = array('$slice' => $limit);
251
        }
252
253
        $this->skipDocumentPool();
254
255
        return $this;
256
    }
257
258
    /**
259
     * Merge expression
260
     * @param \Sokil\Mongo\Expression $expression
261
     * @return \Sokil\Mongo\Cursor
262
     */
263
    public function query(Expression $expression)
264
    {
265
        $this->expression->merge($expression);
266
        return $this;
267
    }
268
269
    /**
270
     * Helper to create new expression
271
     *
272
     * @return \Sokil\Mongo\Expression
273
     */
274
    public function expression()
275
    {
276
        return new $this->options['expressionClass'];
277
    }
278
279
    /**
280
     * Filter by list of \MongoId
281
     *
282
     * @param array $idList list of ids
283
     * @return \Sokil\Mongo\Cursor
284
     */
285
    public function byIdList(array $idList)
286
    {
287
        $this->expression->whereIn('_id', self::mixedToMongoIdList($idList));
288
        return $this;
289
    }
290
291
    /**
292
     * Filter by id
293
     *
294
     * @param string|\MongoId $id id of document
295
     * @return \Sokil\Mongo\Cursor
296
     */
297
    public function byId($id)
298
    {
299
        if ($id instanceof \MongoId) {
300
            $this->expression->where('_id', $id);
301
        } else {
302
            try {
303
                $this->expression->where('_id', new \MongoId($id));
304
            } catch (\MongoException $e) {
305
                $this->expression->where('_id', $id);
306
            }
307
        }
308
309
        return $this;
310
    }
311
312
    /**
313
     * Skip defined number of documents
314
     *
315
     * @param int $skip number of documents to skip
316
     * @return \Sokil\Mongo\Cursor
317
     */
318
    public function skip($skip)
319
    {
320
        $this->skip = (int) $skip;
321
322
        return $this;
323
    }
324
325
    /**
326
     * Limit result set to specified number of elements
327
     *
328
     * @param int $limit number of elements in result set
329
     * @param int|null $offset number of elements to skip
330
     * @return \Sokil\Mongo\Cursor
331
     */
332
    public function limit($limit, $offset = null)
333
    {
334
        $this->limit = (int) $limit;
335
336
        if (null !== $offset) {
337
            $this->skip($offset);
338
        }
339
340
        return $this;
341
    }
342
343
    /**
344
     * Specifies the number of documents to return in each batch of the response from the MongoDB instance.
345
     *
346
     * @param int $size number of documents
347
     * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
348
     * @return \Sokil\Mongo\Cursor
349
     */
350
    public function setBatchSize($size)
351
    {
352
        $this->options['batchSize'] = (int) $size;
353
354
        return $this;
355
    }
356
357
    /**
358
     * Instructs the driver to stop waiting for a response and throw a
359
     * MongoCursorTimeoutException after a set time.
360
     * A timeout can be set at any time and will affect subsequent queries on
361
     * the cursor, including fetching more results from the database.
362
     *
363
     * @param int $ms
364
     * @return Cursor
365
     */
366
    public function setClientTimeout($ms)
367
    {
368
        $this->options['clientTimeout'] = (int) $ms;
369
        return $this;
370
    }
371
372
    /**
373
     * Server-side timeout for a query,
374
     * Specifies a cumulative time limit in milliseconds to be allowed
375
     * by the server for processing operations on the cursor.
376
     *
377
     * @param int $ms
378
     * @return \Sokil\Mongo\Cursor
379
     */
380
    public function setServerTimeout($ms)
381
    {
382
        $this->options['serverTimeout'] = (int) $ms;
383
        return $this;
384
    }
385
386
    /**
387
     * Sort result by specified keys and directions
388
     *
389
     *  An array of fields by which to sort. Each element in the array has as key the field name, and as value either
390
     * 1 for ascending sort, or -1 for descending sort. Each result is first sorted on the first field in the array,
391
     * then (if it exists) on the second field in the array, etc. This means that the order of the fields in the
392
     * fields array is important. See also the examples section.
393
     *
394
     * @param array $sort
395
     * @return Cursor
396
     */
397
    public function sort(array $sort)
398
    {
399
        $this->sort = $sort;
400
        return $this;
401
    }
402
403
    /**
404
     * Count documents in result without applying limit and offset
405
     * @return int count
406
     */
407
    public function count()
408
    {
409
        return (int) $this->collection
410
            ->getMongoCollection()
411
            ->count($this->expression->toArray());
412
    }
413
414
    /**
415
     * Explain expression
416
     *
417
     * @return array
418
     *
419
     * @throws FeatureNotSupportedException
420
     */
421
    public function explain()
422
    {
423
        if (Client::isEmulationMode()) {
424
            throw new FeatureNotSupportedException('Feature not implemented in emulation mode');
425
        }
426
427
        $this->rewind();
428
429
        return $this->cursor->explain();
430
    }
431
432
    /**
433
     * Count documents in result with applying limit and offset
434
     *
435
     * ext-mongo:1.0.7  Added limit and skip as second and third parameters, respectively.
436
     * ext-mongo:1.6.0  The second parameter is now an options array. Passing limit and skip as the second and third
437
     *                  parameters, respectively, is deprecated.
438
     *
439
     * @return int
440
     *
441
     * @throws FeatureNotSupportedException
442
     */
443
    public function limitedCount()
444
    {
445
        if (version_compare(\MongoClient::VERSION, '1.0.7', '<')) {
446
            throw new FeatureNotSupportedException('Limit and skip not supported in ext-mongo versions prior to 1.0.7');
447
        }
448
449
        return (int) $this->collection
450
            ->getMongoCollection()
451
            ->count(
452
                $this->expression->toArray(),
453
                $this->limit,
454
                $this->skip
455
            );
456
    }
457
458
459
    /**
460
     * Gte list of \MongoId of current search query
461
     * @return array
462
     */
463
    public function getIdList()
464
    {
465
        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...
466
    }
467
468
    /**
469
     * Find one document which correspond to expression
470
     *
471
     * @deprecated since v.1.22.2. Use ::one instead
472
     *
473
     * @return Document|array|null
474
     *
475
     * @throws CursorException
476
     */
477
    public function findOne()
478
    {
479
        return $this->one();
480
    }
481
482
    /**
483
     * Find one document which correspond to expression
484
     *
485
     * @return Document|array|null
486
     *
487
     * @throws CursorException
488
     */
489
    public function one()
490
    {
491
        try {
492
            $mongoDocument = $this->collection
493
                ->getMongoCollection()
494
                ->findOne(
495
                    $this->expression->toArray(),
496
                    $this->fields
497
                );
498
        } catch (\Exception $e) {
499
            throw new CursorException(
500
                $e->getMessage(),
501
                $e->getCode(),
502
                $e
503
            );
504
        }
505
506
        if (empty($mongoDocument)) {
507
            return null;
508
        }
509
510
        if (true === $this->isResultAsArray) {
511
            return $mongoDocument;
512
        }
513
514
        return $this->collection->hydrate(
515
            $mongoDocument,
516
            $this->isDocumentPoolUsed()
517
        );
518
    }
519
520
    /**
521
     * Get result of searching
522
     *
523
     * @deprecated since v.1.22.2. Use ::one instead
524
     *
525
     * @return Document[]|array[]
526
     */
527
    public function findAll()
528
    {
529
        return $this->all();
530
    }
531
532
    /**
533
     * Get result of searching
534
     *
535
     * @return array
536
     */
537
    public function all()
538
    {
539
        return iterator_to_array($this);
540
    }
541
542
    /**
543
     * Get random document
544
     *
545
     * @deprecated since v.1.22.2. Use ::one instead
546
     *
547
     * @return Document|null
548
     */
549
    public function findRandom()
550
    {
551
        return $this->random();
552
    }
553
554
    /**
555
     * Get random document
556
     *
557
     * @return Document|null
558
     */
559
    public function random()
560
    {
561
        $count = $this->count();
562
        switch ($count) {
563
            case 0:
564
                return null;
565
            case 1:
566
                return $this->one();
567
            default:
568
                $cursor = $this->skip(mt_rand(0, $count - 1))->limit(1);
569
                $cursor->rewind();
570
                return $cursor->current();
571
        }
572
    }
573
574
    /**
575
     * Get query builder's expression
576
     *
577
     * @return Expression
578
     */
579
    public function getExpression()
580
    {
581
        return $this->expression;
582
    }
583
584
    /**
585
     * Get MongoDB query array
586
     *
587
     * @return array
588
     */
589
    public function getMongoQuery()
590
    {
591
        return $this->expression->toArray();
592
    }
593
    
594
    /**
595
     * Return the values from a single field in the result set of documents
596
     *
597
     * @param string $fieldName
598
     * @return array
599
     */
600
    public function pluck($fieldName)
601
    {
602
        $isEmbeddedDocumentField = false !== strpos($fieldName, '.');
603
604
        $valueList = array();
605
606
        if ($isEmbeddedDocumentField) {
607
            // get result
608
            if ($this->isResultAsArray) {
609
                $cursor = clone $this;
610
                $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...
611
                unset($cursor);
612
            } else {
613
                $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...
614
            }
615
            // get value of field
616
            foreach ($documentObjectList as $key => $documentObject) {
617
                $valueList[$key] = $documentObject->get($fieldName);
618
            }
619
        } else {
620
            // get result
621
            if ($this->isResultAsArray) {
622
                $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...
623
            } else {
624
                $cursor = clone $this;
625
                $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...
626
                unset($cursor);
627
            }
628
            // get values of field
629
            $valueList = array_column($documentArrayList, $fieldName, '_id');
630
        }
631
632
        return $valueList;
633
    }
634
635
    /**
636
     * Get document instance and remove it from collection
637
     *
638
     * @return \Sokil\Mongo\Document
639
     */
640
    public function findAndRemove()
641
    {
642
        $mongoDocument = $this->collection->getMongoCollection()->findAndModify(
643
            $this->expression->toArray(),
644
            null,
645
            $this->fields,
646
            array(
647
                'remove' => true,
648
                'sort' => $this->sort,
649
            )
650
        );
651
652
        if (empty($mongoDocument)) {
653
            return null;
654
        }
655
656
        return $this->collection->hydrate(
657
            $mongoDocument,
658
            $this->isDocumentPoolUsed()
659
        );
660
    }
661
662
    /**
663
     * Find first document and update it
664
     *
665
     * @param Operator $operator operations with document to update
666
     * @param bool $upsert if document not found - create
667
     * @param bool $returnUpdated if true - return updated document
668
     *
669
     * @return null|Document
670
     */
671
    public function findAndUpdate(Operator $operator, $upsert = false, $returnUpdated = true)
672
    {
673
        $mongoDocument = $this->collection
674
            ->getMongoCollection()
675
            ->findAndModify(
676
                $this->expression->toArray(),
677
                $operator ? $operator->toArray() : null,
678
                $this->fields,
679
                array(
680
                    'new' => $returnUpdated,
681
                    'sort' => $this->sort,
682
                    'upsert' => $upsert,
683
                )
684
            );
685
686
        if (empty($mongoDocument)) {
687
            return null;
688
        }
689
690
        return $this->collection->hydrate($mongoDocument, $this->isDocumentPoolUsed());
691
    }
692
693
    /**
694
     * Apply callable to all documents in cursor
695
     *
696
     * @param callable $handler
697
     * @return array
698
     */
699 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...
700
    {
701
        $result = array();
702
703
        foreach ($this as $id => $document) {
704
            $result[$id] = $handler($document);
705
        }
706
707
        return $result;
708
    }
709
710
    /**
711
     * Filter documents in cursor by condition in callable
712
     *
713
     * @param callable $handler
714
     * @return array
715
     */
716 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...
717
    {
718
        $result = array();
719
720
        foreach ($this as $id => $document) {
721
            if (!$handler($document)) {
722
                continue;
723
            }
724
725
            $result[$id] = $document;
726
        }
727
728
        return $result;
729
    }
730
731
    /**
732
     * Get result set of documents.
733
     *
734
     * @return \Sokil\Mongo\ResultSet
735
     */
736
    public function getResultSet()
737
    {
738
        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...
739
    }
740
741
    /**
742
     * Get paginator
743
     *
744
     * @param int $page page number
745
     * @param int $itemsOnPage number of items on page
746
     * @return Paginator
747
     */
748
    public function paginate($page, $itemsOnPage = 30)
749
    {
750
        $paginator = new Paginator($this);
751
        return $paginator
752
            ->setCurrentPage($page)
753
            ->setItemsOnPage($itemsOnPage);
754
    }
755
756
    /**
757
     * Clears the cursor
758
     * Mongo's rewind is reset+next
759
     */
760
    public function reset()
761
    {
762
        if ($this->cursor !== null) {
763
            $this->cursor->reset();
764
        }
765
766
        return $this;
767
    }
768
769
    /**
770
     * Returns the cursor to the beginning of the result set.
771
     * @return void
772
     */
773
    public function rewind()
774
    {
775
        if ($this->cursor !== null) {
776
            $this->cursor->rewind();
777
            return;
778
        }
779
780
        $this->cursor = $this->collection
781
            ->getMongoCollection()
782
            ->find(
783
                $this->expression->toArray(),
784
                $this->fields
785
            );
786
787
        if ($this->skip) {
788
            $this->cursor->skip($this->skip);
789
        }
790
791
        if ($this->limit) {
792
            $this->cursor->limit($this->limit);
793
        }
794
795
        if ($this->options['batchSize']) {
796
            $this->cursor->batchSize($this->options['batchSize']);
797
        }
798
799
        if ($this->options['clientTimeout']) {
800
            $this->cursor->timeout($this->options['clientTimeout']);
801
        }
802
803
        if ($this->options['serverTimeout']) {
804
            $this->cursor->maxTimeMS($this->options['clientTimeout']);
805
        }
806
807
        if (!empty($this->sort)) {
808
            $this->cursor->sort($this->sort);
809
        }
810
811
        if ($this->hint) {
812
            $this->cursor->hint($this->hint);
813
        }
814
815
        // define read preferences
816
        if (!empty($this->readPreference)) {
817
            $this->cursor->setReadPreference(
818
                $this->readPreference['type'],
819
                $this->readPreference['tagsets']
820
            );
821
        }
822
823
        // init cursor state
824
        $this->cursor->rewind();
825
    }
826
827
    /**
828
     * @return bool
829
     */
830
    public function valid()
831
    {
832
        return $this->cursor->valid();
833
    }
834
835
    /**
836
     * @return Document|array|null
837
     */
838
    public function current()
839
    {
840
        $mongoDocument = $this->cursor->current();
841
        if (empty($mongoDocument)) {
842
            return null;
843
        }
844
845
        if ($this->isResultAsArray) {
846
            return $mongoDocument;
847
        }
848
849
        return $this->collection->hydrate(
850
            $mongoDocument,
851
            $this->isDocumentPoolUsed
852
        );
853
    }
854
855
    /**
856
     * @return string
857
     */
858
    public function key()
859
    {
860
        return $this->cursor->key();
861
    }
862
863
    /**
864
     * @return void
865
     */
866
    public function next()
867
    {
868
        $this->cursor->next();
869
    }
870
871
    public function readPrimaryOnly()
872
    {
873
        $this->readPreference = array(
874
            'type'      => \MongoClient::RP_PRIMARY,
875
            'tagsets'   => array(),
876
        );
877
878
        return $this;
879
    }
880
881
    public function readPrimaryPreferred(array $tags = null)
882
    {
883
        $this->readPreference = array(
884
            'type'      => \MongoClient::RP_PRIMARY_PREFERRED,
885
            'tagsets'   => $tags,
886
        );
887
888
        return $this;
889
    }
890
891
    public function readSecondaryOnly(array $tags = null)
892
    {
893
        $this->readPreference = array(
894
            'type'      => \MongoClient::RP_SECONDARY,
895
            'tagsets'   => $tags,
896
        );
897
898
        return $this;
899
    }
900
901
    public function readSecondaryPreferred(array $tags = null)
902
    {
903
        $this->readPreference = array(
904
            'type'      => \MongoClient::RP_SECONDARY_PREFERRED,
905
            'tagsets'   => $tags,
906
        );
907
908
        return $this;
909
    }
910
911
    public function readNearest(array $tags = null)
912
    {
913
        $this->readPreference = array(
914
            'type'      => \MongoClient::RP_NEAREST,
915
            'tagsets'   => $tags,
916
        );
917
918
        return $this;
919
    }
920
921
    /**
922
     * @return array
923
     */
924
    public function getReadPreference()
925
    {
926
        if ($this->cursor) {
927
            return $this->cursor->getReadPreference();
928
        }
929
930
        return $this->readPreference;
931
    }
932
933
    public function isDocumentPoolUsed()
934
    {
935
        return $this->isDocumentPoolUsed;
936
    }
937
938
    public function useDocumentPool()
939
    {
940
        $this->isDocumentPoolUsed = true;
941
        return $this;
942
    }
943
944
    public function skipDocumentPool()
945
    {
946
        $this->isDocumentPoolUsed = false;
947
        return $this;
948
    }
949
950
    /**
951
     * Specify index to use
952
     *
953
     * @link http://docs.mongodb.org/manual/reference/operator/meta/hint/
954
     * @param array|string $specification Specify the index either by the index name or by document
955
     * @return \Sokil\Mongo\Cursor
956
     */
957
    public function hint($specification)
958
    {
959
        $this->hint = $specification;
960
        return $this;
961
    }
962
963
    /**
964
     * Copy selected documents to another collection
965
     *
966
     * @param string $targetCollectionName
967
     * @param string|null $targetDatabaseName Target database name. If not specified - use current
968
     * @param int $batchLimit count of documents to get from old and insert to new collection per time
969
     *
970
     * @return Cursor
971
     *
972
     * @throws WriteException
973
     */
974
    public function copyToCollection(
975
        $targetCollectionName,
976
        $targetDatabaseName = null,
977
        $batchLimit = 100
978
    ) {
979
        // target database
980
        if (empty($targetDatabaseName)) {
981
            $database = $this->collection->getDatabase();
982
        } else {
983
            $database = $this->client->getDatabase($targetDatabaseName);
984
        }
985
986
        // target collection
987
        $targetMongoCollection = $database
988
            ->getCollection($targetCollectionName)
989
            ->getMongoCollection();
990
991
        // cursor
992
        $this->rewind();
993
994
        // copy data
995
        $inProgress = true;
996
        while ($inProgress) {
997
            // get next pack of documents
998
            $documentList = array();
999
            for ($i = 0; $i < $batchLimit; $i++) {
1000
                if (!$this->cursor->valid()) {
1001
                    $inProgress = false;
1002
1003
                    if (!empty($documentList)) {
1004
                        // still need batch insert
1005
                        break;
1006
                    } else {
1007
                        // no documents to insert - just exit
1008
                        break(2);
1009
                    }
1010
                }
1011
1012
                $documentList[] = $this->cursor->current();
1013
                $this->cursor->next();
1014
            }
1015
1016
            // insert
1017
            $result = $targetMongoCollection->batchInsert($documentList);
1018
1019
            // With passed write concern, returns an associative array with the status of the inserts ("ok")
1020
            // and any error that may have occurred ("err").
1021
            // Otherwise, returns TRUE if the batch insert was successfully sent, FALSE otherwise.
1022
            if (is_array($result)) {
1023
                if ($result['ok'] != 1) {
1024
                    throw new WriteException('Batch insert error: ' . $result['err']);
1025
                }
1026
            } elseif (false === $result) {
1027
                throw new WriteException('Batch insert error');
1028
            }
1029
        }
1030
1031
        return $this;
1032
    }
1033
1034
    /**
1035
     * Move selected documents to another collection.
1036
     * Documents will be removed from source collection only after
1037
     * copying them to target collection.
1038
     *
1039
     * @param string $targetCollectionName
1040
     * @param string|null $targetDatabaseName Target database name. If not specified - use current
1041
     * @param int $batchLimit count of documents to get from old and insert to new collection per time
1042
     */
1043
    public function moveToCollection(
1044
        $targetCollectionName,
1045
        $targetDatabaseName = null,
1046
        $batchLimit = 100
1047
    ) {
1048
        // copy to target
1049
        $this->copyToCollection($targetCollectionName, $targetDatabaseName, $batchLimit);
1050
1051
        // remove from source
1052
        $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...
1053
    }
1054
1055
    /**
1056
     * Used to get hash that uniquely identifies current query
1057
     *
1058
     * @return string
1059
     */
1060
    public function getHash()
1061
    {
1062
        $hash = array();
1063
1064
        // expression
1065
        $hash[] = json_encode($this->expression->toArray());
1066
1067
        // sorts
1068 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...
1069
            $sort = $this->sort;
1070
            ksort($sort);
1071
            $hash[] = implode('', array_merge(array_keys($sort), array_values($sort)));
1072
        }
1073
1074
        // fields
1075 View Code Duplication
        if (!empty($this->fields)) {
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...
1076
            $fields = $this->fields;
1077
            ksort($fields);
1078
            $hash[] = implode('', array_merge(array_keys($fields), array_values($fields)));
1079
        }
1080
1081
        // skip and limit
1082
        $hash[] = $this->skip;
1083
        $hash[] = $this->limit;
1084
1085
        // get hash
1086
        return md5(implode(':', $hash));
1087
    }
1088
1089
    /**
1090
     * Get list of MongoId objects from array of strings, MongoId's and Document's
1091
     *
1092
     * @param array $list
1093
     * @return array list of \MongoId
1094
     */
1095
    public static function mixedToMongoIdList(array $list)
1096
    {
1097
        return array_map(function ($element) {
1098
            // MongoId
1099
            if ($element instanceof \MongoId) {
1100
                return $element;
1101
            }
1102
1103
            // \Sokil\Mongo\Document
1104
            if ($element instanceof Document) {
1105
                return $element->getId();
1106
            }
1107
1108
            // array with id key
1109
            if (is_array($element)) {
1110
                if (!isset($element['_id'])) {
1111
                    throw new \InvalidArgumentException('Array must have _id key');
1112
                }
1113
                return $element['_id'];
1114
            }
1115
1116
            // string
1117
            if (is_string($element)) {
1118
                try {
1119
                    return new \MongoId($element);
1120
                } catch (\MongoException $e) {
1121
                    return $element;
1122
                }
1123
            }
1124
1125
            // int
1126
            if (is_int($element)) {
1127
                return $element;
1128
            }
1129
1130
            throw new \InvalidArgumentException(
1131
                'Must be \MongoId, \Sokil\Mongo\Document, array with _id key, string or integer'
1132
            );
1133
        }, array_values($list));
1134
    }
1135
}
1136