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
Branch master (8481f6)
by De
04:12 queued 02:05
created

Cursor   D

Complexity

Total Complexity 111

Size/Duplication

Total Lines 1025
Duplicated Lines 5.37 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 111
lcom 1
cbo 11
dl 55
loc 1025
rs 4
c 0
b 0
f 0

58 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 2
A __call() 0 5 1
A getOption() 0 4 2
A asArray() 0 5 1
A asObject() 0 5 1
A isResultAsArray() 0 4 1
A fields() 0 8 1
A skipFields() 0 8 1
A field() 0 8 1
A skipField() 0 8 1
A slice() 0 16 2
A query() 0 5 1
A expression() 0 4 1
A byIdList() 0 5 1
A byId() 0 14 3
A skip() 0 6 1
A limit() 0 10 2
A setBatchSize() 0 6 1
A setClientTimeout() 0 6 1
A setServerTimeout() 0 6 1
A sort() 0 5 1
D getCursor() 0 60 11
A count() 0 6 1
A explain() 0 8 2
A limitedCount() 0 6 1
A getIdList() 0 4 1
B findOne() 0 30 4
A findAll() 0 4 1
A findRandom() 0 17 3
A getExpression() 0 4 1
A getMongoQuery() 0 4 1
A pluck() 7 19 4
A pluckDotNotated() 7 17 3
A findAndRemove() 0 21 2
A findAndUpdate() 0 21 3
A map() 10 10 2
A filter() 14 14 3
A getResultSet() 0 4 1
A paginate() 0 9 1
A current() 0 16 3
A key() 0 4 1
A next() 0 5 1
A rewind() 0 5 1
A valid() 0 4 1
A readPrimaryOnly() 0 9 1
A readPrimaryPreferred() 0 9 1
A readSecondaryOnly() 0 9 1
A readSecondaryPreferred() 0 9 1
A readNearest() 0 9 1
A getReadPreference() 0 8 2
A isDocumentPoolUsed() 0 4 1
A useDocumentPool() 0 5 1
A skipDocumentPool() 0 5 1
A hint() 0 5 1
B copyToCollection() 7 56 9
A moveToCollection() 0 8 1
B getHash() 10 28 3
C mixedToMongoIdList() 0 39 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Cursor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Cursor, and based on these observations, apply Extract Interface, too.

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
17
class Cursor implements \Iterator, \Countable
18
{
19
    /**
20
     *
21
     * @var \Sokil\Mongo\Client
22
     */
23
    private $client;
24
25
    /**
26
     *
27
     * @var \Sokil\Mongo\Collection
28
     */
29
    private $collection;
30
31
    /**
32
     *
33
     * @var array
34
     */
35
    private $fields = array();
36
37
    /**
38
     *
39
     * @var \MongoCursor
40
     */
41
    private $cursor;
42
    /**
43
     *
44
     * @var \Sokil\Mongo\Expression
45
     */
46
    private $expression;
47
48
    /**
49
     * Offset
50
     * @var int
51
     */
52
    private $skip = 0;
53
54
    /**
55
     * Limit
56
     * @var int
57
     */
58
    private $limit = 0;
59
60
    /**
61
     * Definition of sort
62
     * @var array
63
     */
64
    private $sort = array();
65
66
    /**
67
     * Definition of read preference
68
     * @var array
69
     */
70
    private $readPreference = array();
71
72
    /**
73
     * Return result as array or as Document instance
74
     * @var boolean 
75
     */
76
    private $isResultAsArray = false;
77
78
    /**
79
     * Cursor options
80
     * @var array
81
     */
82
    private $options = array(
83
        'expressionClass' => '\Sokil\Mongo\Expression',
84
        /**
85
         * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
86
         * @var int number of documents to return in each batch of the response from the MongoDB instance
87
         */
88
        'batchSize' => null,
89
        // client timeout
90
        'clientTimeout' => null,
91
        // Specifies a cumulative time limit in milliseconds to be allowed by the server for processing operations on the cursor.
92
        'serverTimeout' => null,
93
    );
94
95
    /**
96
     * Use document pool to create Document object from array
97
     * @var bool
98
     */
99
    private $isDocumentPoolUsed = true;
100
101
    /**
102
     * Index hinting
103
     * @param \Sokil\Mongo\Collection $collection
104
     * @param array $options
105
     */
106
    private $hint;
107
108
    public function __construct(Collection $collection, array $options = null)
109
    {
110
        $this->collection = $collection;
111
112
        $this->client = $this->collection->getDatabase()->getClient();
113
114
        if ($options) {
115
            $this->options = $options + $this->options;
116
        }
117
118
        // expression
119
        $this->expression = $this->expression();
120
    }
121
122
    public function __call($name, $arguments)
123
    {
124
        call_user_func_array(array($this->expression, $name), $arguments);
125
        return $this;
126
    }
127
128
    /**
129
     * Get option
130
     *
131
     * @param string|int $name
132
     * @return mixed
133
     */
134
    public function getOption($name, $default = null)
135
    {
136
        return isset($this->options[$name]) ? $this->options[$name] : $default;
137
    }
138
139
    /**
140
     * Get result as array
141
     * @return $this
142
     */
143
    public function asArray()
144
    {
145
        $this->isResultAsArray = true;
146
        return $this;
147
    }
148
149
    /**
150
     * Get result as object
151
     * @return $this
152
     */
153
    public function asObject()
154
    {
155
        $this->isResultAsArray = false;
156
        return $this;
157
    }
158
159
    /**
160
     * Check if result returned as array
161
     * @return bool
162
     */
163
    public function isResultAsArray()
164
    {
165
        return $this->isResultAsArray;
166
    }
167
168
    /**
169
     * Return only specified fields
170
     *
171
     * @param array $fields
172
     * @return \Sokil\Mongo\Cursor
173
     */
174
    public function fields(array $fields)
175
    {
176
        $this->fields = array_fill_keys($fields, 1);
177
178
        $this->skipDocumentPool();
179
180
        return $this;
181
    }
182
183
    /**
184
     * Return all fields except specified
185
     *
186
     * @param array $fields
187
     * @return \Sokil\Mongo\Cursor
188
     */
189
    public function skipFields(array $fields)
190
    {
191
        $this->fields = array_fill_keys($fields, 0);
192
193
        $this->skipDocumentPool();
194
195
        return $this;
196
    }
197
198
    /**
199
     * Append field to accept list
200
     *
201
     * @param string $field field name
202
     * @return \Sokil\Mongo\Cursor
203
     */
204
    public function field($field)
205
    {
206
        $this->fields[$field] = 1;
207
208
        $this->skipDocumentPool();
209
210
        return $this;
211
    }
212
213
    /**
214
     * Append field to skip list
215
     *
216
     * @param string $field field name
217
     * @return Cursor
218
     */
219
    public function skipField($field)
220
    {
221
        $this->fields[$field] = 0;
222
223
        $this->skipDocumentPool();
224
225
        return $this;
226
    }
227
228
    /**
229
     * Paginate list of sub-documents
230
     *
231
     * @param string $field
232
     * @param integer $limit
233
     * @param integer $skip
234
     * @return \Sokil\Mongo\Cursor
235
     * @throws Exception
236
     */
237
    public function slice($field, $limit, $skip = null)
238
    {
239
        $limit  = (int) $limit;
240
        $skip   = (int) $skip;
241
242
        if($skip) {
243
            $this->fields[$field] = array('$slice' => array($skip, $limit));
244
        }
245
        else {
246
            $this->fields[$field] = array('$slice' => $limit);
247
        }
248
249
        $this->skipDocumentPool();
250
251
        return $this;
252
    }
253
254
    /**
255
     * Merge expression
256
     * @param \Sokil\Mongo\Expression $expression
257
     * @return \Sokil\Mongo\Cursor
258
     */
259
    public function query(Expression $expression)
260
    {
261
        $this->expression->merge($expression);
262
        return $this;
263
    }
264
265
    /**
266
     * Helper to create new expression
267
     *
268
     * @return \Sokil\Mongo\Expression
269
     */
270
    public function expression()
271
    {
272
        return new $this->options['expressionClass'];
273
    }
274
275
    /**
276
     * Filter by list of \MongoId
277
     *
278
     * @param array $idList list of ids
279
     * @return \Sokil\Mongo\Cursor
280
     */
281
    public function byIdList(array $idList)
282
    {
283
        $this->expression->whereIn('_id', self::mixedToMongoIdList($idList));
284
        return $this;
285
    }
286
287
    /**
288
     * Filter by id
289
     *
290
     * @param string|\MongoId $id id of document
291
     * @return \Sokil\Mongo\Cursor
292
     */
293
    public function byId($id)
294
    {
295
        if($id instanceof \MongoId) {
296
            $this->expression->where('_id', $id);
297
        } else {
298
            try {
299
                $this->expression->where('_id', new \MongoId($id));
300
            } catch (\MongoException $e) {
301
                $this->expression->where('_id', $id);
302
            }
303
        }
304
305
        return $this;
306
    }
307
308
    /**
309
     * Skip defined number of documents
310
     *
311
     * @param int $skip number of documents to skip
312
     * @return \Sokil\Mongo\Cursor
313
     */
314
    public function skip($skip)
315
    {
316
        $this->skip = (int) $skip;
317
318
        return $this;
319
    }
320
321
    /**
322
     * Limit result set to specified number of elements
323
     *
324
     * @param int $limit number of elements in result set
325
     * @param int|null $offset number of elements to skip
326
     * @return \Sokil\Mongo\Cursor
327
     */
328
    public function limit($limit, $offset = null)
329
    {
330
        $this->limit = (int) $limit;
331
332
        if(null !== $offset) {
333
            $this->skip($offset);
334
        }
335
336
        return $this;
337
    }
338
339
    /**
340
     * Specifies the number of documents to return in each batch of the response from the MongoDB instance.
341
     *
342
     * @param int $size number of documents
343
     * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
344
     * @return \Sokil\Mongo\Cursor
345
     */
346
    public function setBatchSize($size)
347
    {
348
        $this->options['batchSize'] = (int) $size;
349
350
        return $this;
351
    }
352
353
    /**
354
     * Instructs the driver to stop waiting for a response and throw a
355
     * MongoCursorTimeoutException after a set time,
356
     * A timeout can be set at any time and will affect subsequent queries on
357
     * the cursor, including fetching more results from the database.
358
     * @param type $ms
359
     * @return \Sokil\Mongo\Cursor
360
     */
361
    public function setClientTimeout($ms)
362
    {
363
        $this->options['clientTimeout'] = (int) $ms;
364
365
        return $this;
366
    }
367
368
    /**
369
     * Server-side timeout for a query,
370
     * Specifies a cumulative time limit in milliseconds to be allowed
371
     * by the server for processing operations on the cursor.
372
     * @param type $ms
373
     * @return \Sokil\Mongo\Cursor
374
     */
375
    public function setServerTimeout($ms)
376
    {
377
        $this->options['serverTimeout'] = (int) $ms;
378
379
        return $this;
380
    }
381
382
    /**
383
     * Sort result by specified keys and directions
384
     *
385
     *  An array of fields by which to sort. Each element in the array has as key the field name, and as value either
386
     * 1 for ascending sort, or -1 for descending sort. Each result is first sorted on the first field in the array,
387
     * then (if it exists) on the second field in the array, etc. This means that the order of the fields in the
388
     * fields array is important. See also the examples section.
389
     *
390
     * @param array $sort
391
     * @return \Sokil\Mongo\Cursor
392
     */
393
    public function sort(array $sort)
394
    {
395
        $this->sort = $sort;
396
        return $this;
397
    }
398
399
    /**
400
     *
401
     * @return \MongoCursor
402
     */
403
    private function getCursor()
404
    {
405
        if($this->cursor) {
406
            return $this->cursor;
407
        }
408
409
        $this->cursor = $this->collection
410
            ->getMongoCollection()
411
            ->find($this->expression->toArray(), $this->fields);
412
413
        if($this->skip) {
414
            $this->cursor->skip($this->skip);
415
        }
416
417
        if($this->limit) {
418
            $this->cursor->limit($this->limit);
419
        }
420
421
        if($this->options['batchSize']) {
422
            $this->cursor->batchSize($this->options['batchSize']);
423
        }
424
425
        if($this->options['clientTimeout']) {
426
            $this->cursor->timeout($this->options['clientTimeout']);
427
        }
428
429
        if($this->options['serverTimeout']) {
430
            $this->cursor->maxTimeMS($this->options['clientTimeout']);
431
        }
432
433
        if (!empty($this->sort)) {
434
            $this->cursor->sort($this->sort);
435
        }
436
437
        if($this->hint) {
438
            $this->cursor->hint($this->hint);
439
        }
440
441
        // log request
442
        if($this->client->hasLogger()) {
443
            $this->client->getLogger()->debug(get_called_class() . ': ' . json_encode(array(
444
                'collection' => $this->collection->getName(),
445
                'query' => $this->expression->toArray(),
446
                'project' => $this->fields,
447
                'sort' => $this->sort,
448
            )));
449
        }
450
451
        $this->cursor->rewind();
452
453
        // define read preferences
454
        if (!empty($this->readPreference)) {
455
            $this->cursor->setReadPreference(
456
                $this->readPreference['type'],
457
                $this->readPreference['tagsets']
458
            );
459
        }
460
461
        return $this->cursor;
462
    }
463
464
    /**
465
     * Count documents in result without applying limit and offset
466
     * @return int count
467
     */
468
    public function count()
469
    {
470
        return (int) $this->collection
471
            ->getMongoCollection()
472
            ->count($this->expression->toArray());
473
    }
474
475
    public function explain()
476
    {
477
        if (Client::isEmulationMode()) {
478
            throw new FeatureNotSupportedException('Feature not implemented in emulation mode');
479
        }
480
481
        return $this->getCursor()->explain();
482
    }
483
484
    /**
485
     * Count documents in result with applying limit and offset
486
     * @return int count
487
     */
488
    public function limitedCount()
489
    {
490
        return (int) $this->collection
491
            ->getMongoCollection()
492
            ->count($this->expression->toArray(), $this->limit, $this->skip);
493
    }
494
495
496
    /**
497
     * Gte list of \MongoId of current search query
498
     * @return array
499
     */
500
    public function getIdList()
501
    {
502
        return self::mixedToMongoIdList($this->findAll());
503
    }
504
505
    /**
506
     * Find one document which correspond to expression
507
     * 
508
     * @return \Sokil\Mongo\Document|array|null
509
     */
510
    public function findOne()
511
    {
512
        try {
513
            $mongoDocument = $this->collection
514
                ->getMongoCollection()
515
                ->findOne(
516
                    $this->expression->toArray(),
517
                    $this->fields
518
                );
519
        } catch (\Exception $e) {
520
            throw new CursorException(
521
                $e->getMessage(),
522
                $e->getCode(),
523
                $e
524
            );
525
        }
526
527
        if (empty($mongoDocument)) {
528
            return null;
529
        }
530
531
        if (true === $this->isResultAsArray) {
532
            return $mongoDocument;
533
        }
534
535
        return $this->collection->hydrate(
536
            $mongoDocument,
537
            $this->isDocumentPoolUsed()
538
        );
539
    }
540
541
    /**
542
     *
543
     * @return array result of searching
544
     */
545
    public function findAll()
546
    {
547
        return iterator_to_array($this);
548
    }
549
550
    /**
551
     * Get random document
552
     * @return
553
     */
554
    public function findRandom()
555
    {
556
        $count = $this->count();
557
558
        if(!$count) {
559
            return null;
560
        }
561
562
        if(1 === $count) {
563
            return $this->findOne();
564
        }
565
566
        return $this
567
            ->skip(mt_rand(0, $count - 1))
568
            ->limit(1)
569
            ->current();
570
    }
571
572
    /**
573
     * Get query builder's expression
574
     *
575
     * @return Expression
576
     */
577
    public function getExpression()
578
    {
579
        return $this->expression;
580
    }
581
582
    /**
583
     * Get MongoDB query array
584
     * 
585
     * @return array
586
     */
587
    public function getMongoQuery()
588
    {
589
        return $this->expression->toArray();
590
    }
591
    
592
    /**
593
     * Return the values from a single field in the result set of documents
594
     *
595
     * @param string $fieldName
596
     * @return array
597
     */
598
    public function pluck($fieldName)
599
    {
600
        // if field with embedded document or native php function not exists
601
        if (false !== strpos($fieldName, '.') || !function_exists('array_column')) {
602
            return $this->pluckDotNotated($fieldName);
603
        }
604
605
        // use native php function if field without embedded document
606 View Code Duplication
        if ($this->isResultAsArray) {
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...
607
            $result = $this->findAll();
608
        } else {
609
            $cursor = clone $this;
610
            $result = $cursor->asArray()->findAll();
611
            unset($cursor);
612
        }
613
614
        return array_column($result, $fieldName, '_id');
615
616
    }
617
618
    /**
619
     * Pluck by dot-notated field name
620
     * 
621
     * @param string $fieldName field name
622
     * @return array
623
     */
624
    private function pluckDotNotated($fieldName)
625
    {
626 View Code Duplication
        if ($this->isResultAsArray) {
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...
627
            $cursor = clone $this;
628
            $result = $cursor->asObject()->findAll();
629
            unset($cursor);
630
        } else {
631
            $result = $this->findAll();
632
        }
633
634
        $list = array();
635
        foreach($result as $key => $document) {
636
            $list[$key] = $document->get($fieldName);
637
        }
638
639
        return $list;
640
    }
641
642
    /**
643
     * Get document instance and remove it from collection
644
     *
645
     * @return \Sokil\Mongo\Document
646
     */
647
    public function findAndRemove()
648
    {
649
        $mongoDocument = $this->collection->getMongoCollection()->findAndModify(
650
            $this->expression->toArray(),
651
            null,
652
            $this->fields,
653
            array(
654
                'remove' => true,
655
                'sort' => $this->sort,
656
            )
657
        );
658
659
        if (empty($mongoDocument)) {
660
            return null;
661
        }
662
663
        return $this->collection->hydrate(
664
            $mongoDocument,
665
            $this->isDocumentPoolUsed()
666
        );
667
    }
668
669
    /**
670
     * Find first document and update it
671
     *
672
     * @param Operator $operator operations with document to update
673
     * @param bool $upsert if document not found - create
674
     * @param bool $returnUpdated if true - return updated document
675
     *
676
     * @return null|Document
677
     */
678
    public function findAndUpdate(Operator $operator, $upsert = false, $returnUpdated = true)
679
    {
680
        $mongoDocument = $this->collection
681
            ->getMongoCollection()
682
            ->findAndModify(
683
                $this->expression->toArray(),
684
                $operator ? $operator->toArray() : null,
685
                $this->fields,
686
                array(
687
                    'new' => $returnUpdated,
688
                    'sort' => $this->sort,
689
                    'upsert' => $upsert,
690
                )
691
            );
692
693
        if (empty($mongoDocument)) {
694
            return null;
695
        }
696
697
        return $this->collection->hydrate($mongoDocument, $this->isDocumentPoolUsed());
698
    }
699
700 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...
701
    {
702
        $result = array();
703
704
        foreach($this as $id => $document) {
705
            $result[$id] = $handler($document);
706
        }
707
708
        return $result;
709
    }
710
711 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...
712
    {
713
        $result = array();
714
715
        foreach($this as $id => $document) {
716
            if(!$handler($document)) {
717
                continue;
718
            }
719
720
            $result[$id] = $document;
721
        }
722
723
        return $result;
724
    }
725
726
    /**
727
     * Get result set of documents.
728
     * 
729
     * @return \Sokil\Mongo\ResultSet
730
     */
731
    public function getResultSet()
732
    {
733
        return new ResultSet($this->findAll());
734
    }
735
736
    /**
737
     * Get paginator
738
     *
739
     * @param int $page page number
740
     * @param int $itemsOnPage number of items on page
741
     * @return \Sokil\Mongo\Paginator
742
     */
743
    public function paginate($page, $itemsOnPage = 30)
744
    {
745
        $paginator = new Paginator($this);
746
747
        return $paginator
748
            ->setCurrentPage($page)
749
            ->setItemsOnPage($itemsOnPage);
750
751
    }
752
753
    public function current()
754
    {
755
        $mongoDocument = $this->getCursor()->current();
756
        if (empty($mongoDocument)) {
757
            return null;
758
        }
759
760
        if ($this->isResultAsArray) {
761
            return $mongoDocument;
762
        }
763
764
        return $this->collection->hydrate(
765
            $mongoDocument,
766
            $this->isDocumentPoolUsed()
767
        );
768
    }
769
770
    public function key()
771
    {
772
        return $this->getCursor()->key();
773
    }
774
775
    public function next()
776
    {
777
        $this->getCursor()->next();
778
        return $this;
779
    }
780
781
    public function rewind()
782
    {
783
        $this->getCursor()->rewind();
784
        return $this;
785
    }
786
787
    public function valid()
788
    {
789
        return $this->getCursor()->valid();
790
    }
791
792
    public function readPrimaryOnly()
793
    {
794
        $this->readPreference = array(
795
            'type'      => \MongoClient::RP_PRIMARY,
796
            'tagsets'   => array(),
797
        );
798
799
        return $this;
800
    }
801
802
    public function readPrimaryPreferred(array $tags = null)
803
    {
804
        $this->readPreference = array(
805
            'type'      => \MongoClient::RP_PRIMARY_PREFERRED,
806
            'tagsets'   => $tags,
807
        );
808
809
        return $this;
810
    }
811
812
    public function readSecondaryOnly(array $tags = null)
813
    {
814
        $this->readPreference = array(
815
            'type'      => \MongoClient::RP_SECONDARY,
816
            'tagsets'   => $tags,
817
        );
818
819
        return $this;
820
    }
821
822
    public function readSecondaryPreferred(array $tags = null)
823
    {
824
        $this->readPreference = array(
825
            'type'      => \MongoClient::RP_SECONDARY_PREFERRED,
826
            'tagsets'   => $tags,
827
        );
828
829
        return $this;
830
    }
831
832
    public function readNearest(array $tags = null)
833
    {
834
        $this->readPreference = array(
835
            'type'      => \MongoClient::RP_NEAREST,
836
            'tagsets'   => $tags,
837
        );
838
839
        return $this;
840
    }
841
842
    /**
843
     * @return array
844
     */
845
    public function getReadPreference()
846
    {
847
        if($this->cursor) {
848
            return $this->cursor->getReadPreference();
849
        }
850
851
        return $this->readPreference;
852
    }
853
854
    public function isDocumentPoolUsed()
855
    {
856
        return $this->isDocumentPoolUsed;
857
    }
858
859
    public function useDocumentPool()
860
    {
861
        $this->isDocumentPoolUsed = true;
862
        return $this;
863
    }
864
865
    public function skipDocumentPool()
866
    {
867
        $this->isDocumentPoolUsed = false;
868
        return $this;
869
    }
870
871
    /**
872
     * Specify index to use
873
     *
874
     * @link http://docs.mongodb.org/manual/reference/operator/meta/hint/
875
     * @param array|string $specification Specify the index either by the index name or by document
876
     * @return \Sokil\Mongo\Cursor
877
     */
878
    public function hint($specification)
879
    {
880
        $this->hint = $specification;
881
        return $this;
882
    }
883
884
    /**
885
     * Copy selected documents to another collection
886
     *
887
     * @param type $targetCollectionName
888
     * @param type $targetDatabaseName Target database name. If not specified - use current
889
     */
890
    public function copyToCollection($targetCollectionName, $targetDatabaseName = null)
891
    {
892
        // target database
893
        if(!$targetDatabaseName) {
894
            $database = $this->collection->getDatabase();
895
        } else {
896
            $database = $this->client->getDatabase($targetDatabaseName);
897
        }
898
899
        // target collection
900
        $targetMongoCollection = $database
901
            ->getCollection($targetCollectionName)
902
            ->getMongoCollection();
903
904
        // cursor
905
        $cursor = $this->getCursor();
906
907
        $batchLimit = 100;
908
        $inProgress = true;
909
910
        // copy data
911
        while($inProgress) {
912
            // get next pack of documents
913
            $documentList = array();
914
            for($i = 0; $i < $batchLimit; $i++) {
915
                if(!$cursor->valid()) {
916
                    $inProgress = false;
917
918
                    if($documentList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documentList of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
919
                        // still need batch insert
920
                        break;
921
                    } else {
922
                        // no documents to insert - just exit
923
                        break(2);
924
                    }
925
                }
926
927
                $documentList[] = $cursor->current();
928
                $cursor->next();
929
            }
930
931
            // insert
932
            $result = $targetMongoCollection->batchInsert($documentList);
933
934
            // check result
935 View Code Duplication
            if(is_array($result)) {
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...
936
                if($result['ok'] != 1) {
937
                    throw new Exception('Batch insert error: ' . $result['err']);
938
                }
939
            } elseif(!$result) {
940
                throw new Exception('Batch insert error');
941
            }
942
        }
943
944
        return $this;
945
    }
946
947
    /**
948
     * Move selected documents to another collection.
949
     * Dociuments will be removed from source collection only after
950
     * copying them to target collection.
951
     *
952
     * @param type $targetCollectionName
953
     * @param type $targetDatabaseName Target database name. If not specified - use current
954
     */
955
    public function moveToCollection($targetCollectionName, $targetDatabaseName = null)
956
    {
957
        // copy to target
958
        $this->copyToCollection($targetCollectionName, $targetDatabaseName);
959
960
        // remove from source
961
        $this->collection->deleteDocuments($this->expression);
0 ignored issues
show
Documentation introduced by
$this->expression is of type object<Sokil\Mongo\Expression>, but the function expects a array.

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...
Deprecated Code introduced by
The method Sokil\Mongo\Collection::deleteDocuments() has been deprecated with message: since 1.13. Use Collection::batchDelete();

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...
962
    }
963
964
    /**
965
     * Used to get hash that uniquely identifies current query
966
     */
967
    public function getHash()
968
    {
969
        $hash = array();
970
971
        // expression
972
        $hash[] = json_encode($this->expression->toArray());
973
974
        // sorts
975 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...
976
            $sort = $this->sort;
977
            ksort($sort);
978
            $hash[] = implode('', array_merge(array_keys($sort), array_values($sort)));
979
        }
980
981
        // fields
982 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...
983
            $fields = $this->fields;
984
            ksort($fields);
985
            $hash[] = implode('', array_merge(array_keys($fields), array_values($fields)));
986
        }
987
988
        // skip and limit
989
        $hash[] = $this->skip;
990
        $hash[] = $this->limit;
991
992
        // get hash
993
        return md5(implode(':', $hash));
994
    }
995
996
    /**
997
     * Get list of MongoId objects from array of strings, MongoId's and Document's
998
     *
999
     * @param array $list
1000
     * @return array list of \MongoId
1001
     */
1002
    public static function mixedToMongoIdList(array $list)
1003
    {
1004
        return array_map(function($element) {
1005
            // MongoId
1006
            if($element instanceof \MongoId) {
1007
                return $element;
1008
            }
1009
1010
            // \Sokil\Mongo\Document
1011
            if($element instanceof Document) {
1012
                return $element->getId();
1013
            }
1014
1015
            // array with id key
1016
            if(is_array($element)) {
1017
                if(!isset($element['_id'])) {
1018
                    throw new \InvalidArgumentException('Array must have _id key');
1019
                }
1020
                return $element['_id'];
1021
            }
1022
1023
            // string
1024
            if(is_string($element)) {
1025
                try {
1026
                    return new \MongoId($element);
1027
                } catch (\MongoException $e) {
1028
                    return $element;
1029
                }
1030
            }
1031
1032
            // int
1033
            if(is_int($element)) {
1034
                return $element;
1035
            }
1036
1037
            throw new \InvalidArgumentException('Must be \MongoId, \Sokil\Mongo\Document, array with _id key, string or integer');
1038
1039
        }, array_values($list));
1040
    }
1041
}
1042