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 ( d42e2b...ab132c )
by De
03:07
created

Cursor::findOne()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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