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 ( a9ac0a...15bc5c )
by De
04:59
created

Collection::deleteIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
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\Document\InvalidDocumentException;
15
use Sokil\Mongo\Exception\FeatureNotSupportedException;
16
use Sokil\Mongo\Collection\Definition;
17
use Sokil\Mongo\Enum\Language;
18
19
/**
20
 * Instance of this class is a representation of mongo collection.
21
 * It aggregates \MongoCollection instance.
22
 *
23
 * @link https://github.com/sokil/php-mongo#selecting-database-and-collection Selecting collection
24
 * @link https://github.com/sokil/php-mongo#querying-documents Querying documents
25
 * @link https://github.com/sokil/php-mongo#update-few-documents Update few documents
26
 * @link https://github.com/sokil/php-mongo#deleting-collections-and-documents Deleting collection
27
 *
28
 * @author Dmytro Sokil <[email protected]>
29
 */
30
class Collection implements \Countable
31
{
32
    /**
33
     * @var string expression class. This class may be overloaded to define
34
     *  own query methods (whereUserAgeGreatedThan(), etc.)
35
     * @deprecated since 1.13 Use 'expressionClass' declaration in mapping
36
     */
37
    protected $_queryExpressionClass;
38
39
    /**
40
     * @deprecated since 1.13 Use 'documentClass' declaration in mapping
41
     * @var string Default class for document
42
     */
43
    private $documentClass;
44
45
    /**
46
     * List of arrays, where each item array is an index definition.
47
     * Every index definition must contain key 'keys' with list of fields and orders,
48
     * and optional options from @link http://php.net/manual/en/mongocollection.createindex.php:
49
     *
50
     * Example:
51
     * array(
52
     *     array(
53
     *         'keys' => array('field1' => 1, 'field2' => -1),
54
     *         'unique' => true
55
     *     ),
56
     *     ...
57
     * )
58
     * @var array list of indexes
59
     * @deprecated since 1.13 Use 'index' declaration in mapping
60
     */
61
    protected $_index;
62
63
    /**
64
     *
65
     * @var \Sokil\Mongo\Database
66
     */
67
    protected $_database;
68
69
    /**
70
     *
71
     * @var \MongoCollection
72
     */
73
    protected $_mongoCollection;
74
75
    /**
76
     * Implementation of identity map pattern
77
     *
78
     * @var array list of cached documents
79
     */
80
    private $documentPool = array();
81
82
    /**
83
     *
84
     * @var bool cache or not documents
85
     */
86
    private $isDocumentPoolEnabled = true;
87
88
    /**
89
     * @deprecated since 1.13 Use 'versioning' declaration in mapping
90
     * @var bool default value of versioning
91
     */
92
    protected $versioning;
93
94
    /**
95
     * @var \Sokil\Mongo\Collection\Definition collection options
96
     */
97
    private $definition;
98
99
    public function __construct(Database $database, $collection, Definition $definition = null)
100
    {
101
        // define db
102
        $this->_database = $database;
103
104
        $this->initCollection($collection);
105
106
        // init definition
107
        $this->definition = $definition ? $definition : new Definition();
108
109
        if($this->documentClass) {
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$documentClass has been deprecated with message: since 1.13 Use 'documentClass' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
110
            $this->definition->setOption('documentClass', $this->documentClass);
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$documentClass has been deprecated with message: since 1.13 Use 'documentClass' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
111
        }
112
        if($this->versioning !== null) {
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$versioning has been deprecated with message: since 1.13 Use 'versioning' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
113
            $this->definition->setOption('versioning', $this->versioning);
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$versioning has been deprecated with message: since 1.13 Use 'versioning' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
114
        }
115
        if($this->_index) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_index 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...
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$_index has been deprecated with message: since 1.13 Use 'index' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
116
            $this->definition->setOption('index', $this->_index);
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$_index has been deprecated with message: since 1.13 Use 'index' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
117
        }
118
        if($this->_queryExpressionClass) {
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$_queryExpressionClass has been deprecated with message: since 1.13 Use 'expressionClass' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
119
            $this->definition->setOption('expressionClass', $this->_queryExpressionClass);
0 ignored issues
show
Deprecated Code introduced by
The property Sokil\Mongo\Collection::$_queryExpressionClass has been deprecated with message: since 1.13 Use 'expressionClass' declaration in mapping

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
120
        }
121
    }
122
123
    protected function initCollection($collection)
124
    {
125
        // init mongo collection
126
        if($collection instanceof \MongoCollection) {
127
            $this->_mongoCollection = $collection;
128
        } else {
129
            $this->_mongoCollection = $this->_database->getMongoDB()->selectCollection($collection);
130
        }
131
    }
132
133
    /**
134
     * Start versioning documents on modify
135
     *
136
     * @deprecated since 1.13 Use 'versioning' declaration in mapping
137
     * @return \Sokil\Mongo\Collection
138
     */
139
    public function enableVersioning()
140
    {
141
        $this->definition->setOption('versioning', true);
142
        return $this;
143
    }
144
145
    /**
146
     * Check if versioning enabled
147
     *
148
     * @deprecated since 1.13 Use 'versioning' declaration in mapping
149
     * @return bool
150
     */
151
    public function isVersioningEnabled()
152
    {
153
        return $this->definition->getOption('versioning');
154
    }
155
156
    /**
157
     * Get option
158
     *
159
     * @param string|int $name
160
     * @return mixed
161
     */
162
    public function getOption($name)
163
    {
164
        return $this->definition->getOption($name);
165
    }
166
167
    public function getOptions()
168
    {
169
        return $this->definition->getOptions();
170
    }
171
172
    public function __get($name)
173
    {
174
        return $this->getDocument($name);
175
    }
176
177
    /**
178
     * Get name of collection
179
     * 
180
     * @return string name of collection
181
     */
182
    public function getName()
183
    {
184
        return $this->_mongoCollection->getName();
185
    }
186
187
    /**
188
     * Get native collection instance of mongo driver
189
     * 
190
     * @return \MongoCollection
191
     */
192
    public function getMongoCollection()
193
    {
194
        return $this->_mongoCollection;
195
    }
196
197
    /**
198
     *
199
     * @return \Sokil\Mongo\Database
200
     */
201
    public function getDatabase()
202
    {
203
        return $this->_database;
204
    }
205
206
    /**
207
     * Delete collection
208
     * 
209
     * @return \Sokil\Mongo\Collection
210
     * @throws \Sokil\Mongo\Exception
211
     */
212
    public function delete() {
213
        $status = $this->_mongoCollection->drop();
214
        if($status['ok'] != 1) {
215
            // check if collection exists
216
            if('ns not found' !== $status['errmsg']) {
217
                // collection exist
218
                throw new Exception('Error deleting collection ' . $this->getName() . ': ' . $status['errmsg']);
219
            }
220
        }
221
222
        return $this;
223
    }
224
225
    /**
226
     * Override to define class name of document by document data
227
     *
228
     * @param array $documentData
229
     * @return string Document class data
230
     */
231
    public function getDocumentClassName(array $documentData = null)
232
    {
233
        $documentClass = $this->definition->getOption('documentClass');
234
235
        if(is_callable($documentClass)) {
236
            return call_user_func($documentClass, $documentData);
237
        }
238
239
        if(class_exists($documentClass)) {
240
            return $documentClass;
241
        }
242
243
        throw new Exception('Property "documentClass" must be callable or valid name of class');
244
    }
245
246
    /**
247
     * Factory method to get not stored Document instance from array
248
     * @param array $data
249
     * @return \Sokil\Mongo\Document
250
     */
251
    public function createDocument(array $data = null)
252
    {
253
        $className = $this->getDocumentClassName($data);
254
255
        /* @var $document \Sokil\Mongo\Document */
256
        $document = new $className(
257
            $this,
258
            $data,
259
            array('stored' => false) + $this->definition->getOptions()
260
        );
261
262
        // store document to identity map
263
        if($this->isDocumentPoolEnabled()) {
264
            $collection = $this;
265
            $document->onAfterInsert(function(\Sokil\Mongo\Event $event) use($collection) {
0 ignored issues
show
Bug introduced by
The call to onAfterInsert() misses a required argument $priority.

This check looks for function calls that miss required arguments.

Loading history...
266
                $collection->addDocumentToDocumentPool($event->getTarget());
267
            });
268
        }
269
270
        return $document;
271
    }
272
273
    /**
274
     * Factory method to get document object from array of stored document
275
     *
276
     * @param array $data
277
     * @return \Sokil\Mongo\Document
278
     */
279
    public function hydrate($data, $useDocumentPool = true)
280
    {
281
        if(!is_array($data) || !isset($data['_id'])) {
282
            throw new Exception('Document must be stored and has _id key');
283
        }
284
285
        // if document already in pool - return it
286
        if($useDocumentPool && $this->isDocumentPoolEnabled() && $this->isDocumentInDocumentPool($data['_id'])) {
287
            return $this
288
                ->getDocumentFromDocumentPool($data['_id'])
289
                ->mergeUnmodified($data);
290
        }
291
292
        // init document instance
293
        $className = $this->getDocumentClassName($data);
294
        $document = new $className(
295
            $this,
296
            $data,
297
            array('stored' => true) + $this->definition->getOptions()
298
        );
299
300
        // store document in cache
301
        if($useDocumentPool && $this->isDocumentPoolEnabled()) {
302
            $this->addDocumentToDocumentPool($document);
303
        }
304
305
        return $document;
306
    }
307
308
    /**
309
     * Total count of documents in collection
310
     * 
311
     * @return int 
312
     */
313
    public function count()
314
    {
315
        return $this->find()->count();
316
    }
317
318
    /**
319
     * Retrieve a list of distinct values for the given key across a collection.
320
     *
321
     * @param string $selector field selector
322
     * @param array|callable|\Sokil\Mongo\Expression $expression expression to search documents
323
     * @return array distinct values
324
     */
325
    public function getDistinct($selector, $expression = null)
326
    {
327
        if($expression) {
328
            return $this->_mongoCollection->distinct(
329
                $selector,
330
                Expression::convertToArray($expression)
331
            );
332
        }
333
334
        return $this->_mongoCollection->distinct($selector);
335
    }
336
337
    /**
338
     * Create new Expression instance to use in query builder or update operations
339
     * 
340
     * @return \Sokil\Mongo\Expression
341
     */
342
    public function expression()
343
    {
344
        $className = $this->definition->getExpressionClass();
345
        return new $className;
346
    }
347
348
    /**
349
     * Create Operator instance to use in update operations
350
     * 
351
     * @return \Sokil\Mongo\Operator
352
     */
353
    public function operator()
354
    {
355
        return new Operator();
356
    }
357
358
    /**
359
     * Create document query builder
360
     *
361
     * @param $callable callable|null Function to configure query builder&
362
     * @return \Sokil\Mongo\Cursor|\Sokil\Mongo\Expression
363
     */
364
    public function find($callable = null)
365
    {
366
        /** @var \Sokil\Mongo\Cursor $cursor */
367
        $cursor = new Cursor($this, array(
368
            'expressionClass'   => $this->definition->getExpressionClass(),
369
            'batchSize'         => $this->definition->getOption('batchSize'),
370
            'clientTimeout'     => $this->definition->getOption('cursorClientTimeout'),
371
            'serverTimeout'     => $this->definition->getOption('cursorServerTimeout'),
372
        ));
373
374
        if(is_callable($callable)) {
375
            $callable($cursor->getExpression());
376
        }
377
378
        return $cursor;
379
    }
380
381
    /**
382
     * Create document query builder
383
     *
384
     * @return \Sokil\Mongo\Cursor
385
     */
386
    public function findAsArray($callable = null)
387
    {
388
        return $this
389
            ->find($callable)
390
            ->asArray();
391
    }
392
393
    /**
394
     * Stop storing found documents to pool
395
     *
396
     * @return \Sokil\Mongo\Collection
397
     */
398
    public function disableDocumentPool()
399
    {
400
        $this->isDocumentPoolEnabled = false;
401
        return $this;
402
    }
403
404
    /**
405
     * Start storing found documents to pool
406
     *
407
     * @return \Sokil\Mongo\Collection
408
     */
409
    public function enableDocumentPool()
410
    {
411
        $this->isDocumentPoolEnabled = true;
412
        return $this;
413
    }
414
415
    /**
416
     * Check if document pool enabled and requested documents store to it
417
     *
418
     * @return bool
419
     */
420
    public function isDocumentPoolEnabled()
421
    {
422
        return $this->isDocumentPoolEnabled;
423
    }
424
425
    public function clearDocumentPool()
426
    {
427
        $this->documentPool = array();
428
        return $this;
429
    }
430
431
    /**
432
     * Check if documents are in pool
433
     *
434
     * @return bool
435
     */
436
    public function isDocumentPoolEmpty()
437
    {
438
        return !$this->documentPool;
439
    }
440
441
    /**
442
     * Store document to pool
443
     *
444
     * @param array $document
445
     * @return \Sokil\Mongo\Collection
446
     */
447
    public function addDocumentToDocumentPool(Document $document)
448
    {
449
        $documentId = (string) $document->getId();
450
451
        if(!isset($this->documentPool[$documentId])) {
452
            $this->documentPool[$documentId] = $document;
453
        } else {
454
            // merging because document after
455
            // load and before getting in second place may be changed
456
            // and this changes must be preserved:
457
            //
458
            // 1. Document loads and modifies in current session
459
            // 2. Document loads modified in another session
460
            // 3. Document loads once again in current session. Changes from stage 2 merges as unmodified
461
462
            $this->documentPool[$documentId]->mergeUnmodified($document->toArray());
463
        }
464
465
        return $this;
466
    }
467
468
    /**
469
     * Store documents to identity map
470
     *
471
     * @param array $documents list of Document instances
472
     * @return \Sokil\Mongo\Collection
473
     */
474
    public function addDocumentsToDocumentPool(array $documents)
475
    {
476
        foreach($documents as $document) {
477
            $this->addDocumentToDocumentPool($document);
478
        }
479
480
        return $this;
481
    }
482
483
    /**
484
     * Remove document instance from identity map
485
     *
486
     * @param \Sokil\Mongo\Document $document
487
     * @return \Sokil\Mongo\Collection
488
     */
489
    public function removeDocumentFromDocumentPool(Document $document)
490
    {
491
        unset($this->documentPool[(string) $document]);
492
        return $this;
493
    }
494
495
    /**
496
     * Get document from identity map by it's id
497
     *
498
     * @param string|int|\MongoId $id
499
     * @return \Sokil\Mongo\Document
500
     */
501
    public function getDocumentFromDocumentPool($id)
502
    {
503
        return $this->documentPool[(string) $id];
504
    }
505
506
    /**
507
     * Get documents from pool if they stored
508
     *
509
     * @param array $ids
510
     */
511
    public function getDocumentsFromDocumentPool(array $ids = null)
512
    {
513
        if(!$ids) {
514
            return $this->documentPool;
515
        }
516
517
        return array_intersect_key(
518
            $this->documentPool,
519
            array_flip(array_map('strval', $ids))
520
        );
521
    }
522
523
    /**
524
     * Get number of documents in document pool
525
     *
526
     * @return int
527
     */
528
    public function documentPoolCount()
529
    {
530
        return count($this->documentPool);
531
    }
532
533
    /**
534
     * Check if document exists in identity map
535
     *
536
     * @param \Sokil\Mongo\Document|\MongoId|int|string $document Document instance or it's id
537
     * @return boolean
538
     */
539
    public function isDocumentInDocumentPool($document)
540
    {
541
        if($document instanceof Document) {
542
            $document = $document->getId();
543
        }
544
545
        return isset($this->documentPool[(string) $document]);
546
    }
547
548
    /**
549
     * Get document by id
550
     * If callable specified, document always loaded directly omitting document pool.
551
     * Method may return document as array if cursor configured through Cursor::asArray()
552
     *
553
     * @param string|\MongoId $id
554
     * @param callable $callable cursor callable used to configure cursor
555
     * @return \Sokil\Mongo\Document|array|null
556
     */
557
    public function getDocument($id, $callable = null)
558
    {
559
        if(!$this->isDocumentPoolEnabled) {
560
            return $this->getDocumentDirectly($id, $callable);
561
        }
562
563
        if(!$callable && $this->isDocumentInDocumentPool($id)) {
564
            return $this->getDocumentFromDocumentPool($id);
565
        }
566
567
        $document = $this->getDocumentDirectly($id, $callable);
568
569
        // if callable configure cursor to return document as array,
570
        // than it can't be stored to document pool
571
        if($document instanceof Document) {
572
            $this->addDocumentToDocumentPool($document);
573
        }
574
575
        return $document;
576
    }
577
578
579
    /**
580
     * Get document by id directly omitting cache
581
     * Method may return document as array if cursor configured through Cursor::asArray()
582
     * 
583
     * @param string|\MongoId $id
584
     * @param callable $callable cursor callable used to configure cursor
585
     * @return \Sokil\Mongo\Document|array|null
586
     */
587
    public function getDocumentDirectly($id, $callable = null)
588
    {
589
        $cursor = $this->find();
590
591
        if(is_callable($callable)) {
592
            call_user_func($callable, $cursor);
593
        }
594
595
        return $cursor
596
            ->byId($id)
597
            ->skipDocumentPool()
598
            ->findOne();
599
    }
600
601
    /**
602
     * Check if document belongs to collection
603
     *
604
     * @param \Sokil\Mongo\Document $document
605
     * @return type
606
     */
607
    public function hasDocument(Document $document)
608
    {
609
        // check connection
610
        if($document->getCollection()->getDatabase()->getClient()->getDsn() !== $this->getDatabase()->getClient()->getDsn()) {
611
            return false;
612
        }
613
614
        // check database
615
        if ($document->getCollection()->getDatabase()->getName() !== $this->getDatabase()->getName()) {
616
            return false;
617
        }
618
619
        // check collection
620
        return $document->getCollection()->getName() == $this->getName();
621
    }
622
623
    /**
624
     * Get documents by list of id
625
     *
626
     * @param array $idList list of ids
627
     * @param callable $callable cursor callable used to configure cursor
628
     * @return array|null
629
     */
630
    public function getDocuments(array $idList, $callable = null)
631
    {
632
        $idListToFindDirectly = $idList;
633
634
        // try to egt document from pool if enabled
635
        $documentsInDocumentPool = array();
636
        if ($this->isDocumentPoolEnabled && !$callable) {
637
            $documentsInDocumentPool = $this->getDocumentsFromDocumentPool($idList);
638
            if (count($documentsInDocumentPool) === count($idList)) {
639
                return $documentsInDocumentPool;
640
            }
641
642
            // skip ids already found in pool
643
            $idListToFindDirectly = array_diff_key(
644
                array_map('strval', $idList),
645
                array_keys($documentsInDocumentPool)
646
            );
647
        }
648
649
        // get documents directly
650
        $cursor = $this->find();
651
652
        if (is_callable($callable)) {
653
            call_user_func($callable, $cursor);
654
        }
655
656
        $documentsGettingDirectly = $cursor->byIdList($idListToFindDirectly)->findAll();
657
        if (!$documentsGettingDirectly) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documentsGettingDirectly 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...
658
            return $documentsInDocumentPool ? $documentsInDocumentPool : array();
659
        }
660
661
        if ($this->isDocumentPoolEnabled) {
662
            $this->addDocumentsToDocumentPool($documentsGettingDirectly);
663
        }
664
665
        return $documentsGettingDirectly + $documentsInDocumentPool;
666
    }
667
668
    /**
669
     * Creates batch insert operation handler
670
     * @param int|string $writeConcern Write concern. Default is 1 (Acknowledged)
671
     * @param int $timeout Timeout for write concern. Default is 10000 milliseconds
672
     * @param bool $ordered Determins if MongoDB must apply this batch in order (sequentally,
673
     *   one item at a time) or can rearrange it. Defaults to TRUE
674
     * @return BatchInsert
675
     */
676
    public function createBatchInsert($writeConcern = null, $timeout = null, $ordered = null)
677
    {
678
        return new BatchInsert(
679
            $this,
680
            $writeConcern,
681
            $timeout,
682
            $ordered
683
        );
684
    }
685
686
    /**
687
     * Creates batch update operation handler
688
     * @param int|string $writeConcern Write concern. Default is 1 (Acknowledged)
689
     * @param int $timeout Timeout for write concern. Default is 10000 milliseconds
690
     * @param bool $ordered Determins if MongoDB must apply this batch in order (sequentally,
691
     *   one item at a time) or can rearrange it. Defaults to TRUE
692
     * @return BatchUpdate
693
     */
694
    public function createBatchUpdate($writeConcern = null, $timeout = null, $ordered = null)
695
    {
696
        return new BatchUpdate(
697
            $this,
698
            $writeConcern,
699
            $timeout,
700
            $ordered
701
        );
702
    }
703
704
    /**
705
     * Creates batch delete operation handler
706
     * @param int|string $writeConcern Write concern. Default is 1 (Acknowledged)
707
     * @param int $timeout Timeout for write concern. Default is 10000 milliseconds
708
     * @param bool $ordered Determins if MongoDB must apply this batch in order (sequentally,
709
     *   one item at a time) or can rearrange it. Defaults to TRUE
710
     * @return BatchDelete
711
     */
712
    public function createBatchDelete($writeConcern = null, $timeout = null, $ordered = null)
713
    {
714
        return new BatchDelete(
715
            $this,
716
            $writeConcern,
717
            $timeout,
718
            $ordered
719
        );
720
    }
721
722
    /**
723
     * @deprecated since 1.13. Use Document::delete()
724
     * @param \Sokil\Mongo\Document $document
725
     * @return \Sokil\Mongo\Collection
726
     */
727
    public function deleteDocument(Document $document)
728
    {
729
        $document->delete();
730
        return $this;
731
    }
732
733
    /**
734
     * Delete documents by expression
735
     * 
736
     * @param callable|array|\Sokil\Mongo\Expression $expression
737
     * @return \Sokil\Mongo\Collection
738
     * @throws Exception
739
     */
740
    public function batchDelete($expression = array())
741
    {
742
        // remove
743
        $result = $this->_mongoCollection->remove(
744
            Expression::convertToArray($expression)
745
        );
746
747
        // check result
748
        if(true !== $result && $result['ok'] != 1) {
749
            throw new Exception('Error removing documents from collection: ' . $result['err']);
750
        }
751
752
        return $this;
753
    }
754
755
    /**
756
     * @deprecated since 1.13. Use Collection::batchDelete();
757
     */
758
    public function deleteDocuments($expression = array())
759
    {
760
        return $this->batchDelete($expression);
761
    }
762
763
    /**
764
     * Insert multiple documents defined as arrays
765
     *
766
     * Prior to version 1.5.0 of the driver it was possible to use MongoCollection::batchInsert(),
767
     * however, as of 1.5.0 that method is now discouraged.
768
     *
769
     * You can use Collection::createBatchInsert()
770
     *
771
     * @param array $rows list of documents to insert, defined as arrays
772
     * @return \Sokil\Mongo\Collection
773
     * @throws \Sokil\Mongo\Document\InvalidDocumentException
774
     * @throws \Sokil\Mongo\Exception
775
     */
776
    public function batchInsert($rows, $validate = true)
777
    {
778
        if($validate) {
779
            $document = $this->createDocument();
780
            foreach($rows as $row) {
781
                $document->merge($row);
782
783
                if(!$document->isValid()) {
784
                    throw new InvalidDocumentException('Document is invalid on batch insert');
785
                }
786
787
                $document->reset();
788
            }
789
        }
790
791
        $result = $this->_mongoCollection->batchInsert($rows);
792
793
        // If the w parameter is set to acknowledge the write,
794
        // returns an associative array with the status of the inserts ("ok")
795
        // and any error that may have occurred ("err").
796 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...
797
            if($result['ok'] != 1) {
798
                throw new Exception('Batch insert error: ' . $result['err']);
799
            }
800
801
            return $this;
802
        }
803
804
        // Otherwise, returns TRUE if the batch insert was successfully sent,
805
        // FALSE otherwise.
806
        if(!$result) {
807
            throw new Exception('Batch insert error');
808
        }
809
810
        return $this;
811
    }
812
813
    /**
814
     * @deprecated since 1.13 Use Collection::batchInsert()
815
     */
816
    public function insertMultiple($rows, $validate = true)
817
    {
818
        return $this->batchInsert($rows, $validate);
819
    }
820
821
    /**
822
     * Direct insert of array to MongoDB without creating document object and validation
823
     *
824
     * @param array $document
825
     * @return \Sokil\Mongo\Collection
826
     * @throws Exception
827
     */
828
    public function insert(array $document)
829
    {
830
        $result = $this->_mongoCollection->insert($document);
831
832
        // if write concern acknowledged
833 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...
834
            if($result['ok'] != 1) {
835
                throw new Exception('Insert error: ' . $result['err'] . ': ' . $result['errmsg']);
836
            }
837
838
            return $this;
839
        }
840
841
        // if write concern unacknowledged
842
        if(!$result) {
843
            throw new Exception('Insert error');
844
        }
845
846
        return $this;
847
    }
848
849
    /**
850
     * Update multiple documents
851
     *
852
     * @param \Sokil\Mongo\Expression|array|callable $expression expression to define
853
     *  which documents will change.
854
     * @param \Sokil\Mongo\Operator|array|callable $updateData new data or operators to update
855
     * @param array $options update options, see http://php.net/manual/ru/mongocollection.update.php
856
     * @return \Sokil\Mongo\Collection
857
     * @throws \Sokil\Mongo\Exception
858
     */
859
    public function update($expression, $updateData, array $options = array())
860
    {
861
        // execute update operator
862
        $result = $this->_mongoCollection->update(
863
            Expression::convertToArray($expression),
864
            Operator::convertToArray($updateData),
865
            $options
866
        );
867
868
        // if write concern acknowledged
869 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...
870
            if($result['ok'] != 1) {
871
                throw new Exception(sprintf('Update error: %s: %s', $result['err'], $result['errmsg']));
872
            }
873
            return $this;
874
        }
875
876
        // if write concern unacknowledged
877
        if(!$result) {
878
            throw new Exception('Update error');
879
        }
880
881
        return $this;
882
    }
883
884
    /**
885
     * Update multiple documents
886
     *
887
     * @param \Sokil\Mongo\Expression|array|callable $expression expression to define
888
     *  which documents will change.
889
     * @param \Sokil\Mongo\Operator|array|callable $updateData new data or operators
890
     *  to update
891
     * @return \Sokil\Mongo\Collection
892
     * @throws \Sokil\Mongo\Exception
893
     */
894
    public function batchUpdate($expression, $updateData)
895
    {
896
        return $this->update($expression, $updateData, array(
897
            'multiple'  => true,
898
        ));
899
    }
900
901
    /**
902
     * @deprecated since 1.13 Use Collection::batchUpdate()
903
     */
904
    public function updateMultiple($expression, $updateData)
905
    {
906
        return $this->batchUpdate($expression, $updateData);
907
    }
908
909
    /**
910
     * Update all documents
911
     *
912
     * @deprecated since 1.13. Use Collection::batchUpdate([])
913
     * @param \Sokil\Mongo\Operator|array|callable $updateData new data or operators
914
     * @return \Sokil\Mongo\Collection
915
     * @throws \Sokil\Mongo\Exception
916
     */
917
    public function updateAll($updateData)
918
    {
919
        return $this->update(array(), $updateData, array(
920
            'multiple'  => true,
921
        ));
922
    }
923
924
    /**
925
     * Start aggregation
926
     *
927
     * @link http://docs.mongodb.org/manual/reference/operator/aggregation/
928
     * @return \Sokil\Mongo\Pipeline
929
     */
930
    public function createAggregator()
931
    {
932
        return new Pipeline($this);
933
    }
934
935
    /**
936
     * Aggregate using pipeline
937
     * @link http://docs.mongodb.org/manual/reference/operator/aggregation/
938
     *
939
     * @param callable|array|Pipeline $pipeline list of pipeline stages
940
     * @param array aggregate options
941
     * @param bool $asCursor return result as cursor
942
     *
943
     * @throws \Sokil\Mongo\Exception
944
     * @return array result of aggregation
945
     */
946
    public function aggregate(
947
        $pipeline,
948
        array $options = array(),
949
        $asCursor = false
950
    ) {
951
        // configure through callable
952
        if (is_callable($pipeline)) {
953
            $pipelineConfiguratorCallable = $pipeline;
954
            $pipeline = $this->createAggregator();
955
            call_user_func($pipelineConfiguratorCallable, $pipeline);
956
        }
957
958
        // get aggregation array
959
        if ($pipeline instanceof Pipeline) {
960
            if ($options && is_array($options)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options 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...
961
                $options = array_merge($pipeline->getOptions(), $options);
962
            } else {
963
                $options = $pipeline->getOptions();
964
            }
965
            $pipeline = $pipeline->toArray();
966
        } else if (!is_array($pipeline)) {
967
            throw new Exception('Wrong pipeline specified');
968
        }
969
970
        // log
971
        $client = $this->_database->getClient();
972
        if ($client->isDebugEnabled()) {
973
            // record pipeline
974
            if ($client->hasLogger()) {
975
                $client->getLogger()->debug(
976
                    get_called_class() . ':<br><b>Pipeline</b>:<br>' .
977
                    json_encode($pipeline)
978
                );
979
            }
980
981
            // Check options only in debug mode. In production common exception will raised
982
            if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options 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...
983
                // get db version
984
                $dbVersion = $client->getDbVersion();
985
986
                // check options for db < 2.6
987
                if (version_compare($dbVersion, '2.6.0', '<')) {
988
                    if (!empty($options['explain'])) {
989
                        throw new FeatureNotSupportedException('Explain of aggregation implemented only from 2.6.0');
990
                    }
991
992
                    if (!empty($options['allowDiskUse'])) {
993
                        throw new FeatureNotSupportedException('Option allowDiskUse of aggregation implemented only from 2.6.0');
994
                    }
995
996
                    if (!empty($options['cursor'])) {
997
                        throw new FeatureNotSupportedException('Option cursor of aggregation implemented only from 2.6.0');
998
                    }
999
                }
1000
1001
                // check options for db < 3.2
1002
                if (version_compare($dbVersion, '3.2.0', '<')) {
1003
                    if (!empty($options['bypassDocumentValidation'])) {
1004
                        throw new FeatureNotSupportedException('Option bypassDocumentValidation of aggregation implemented only from 3.2.0');
1005
                    }
1006
1007
                    if (!empty($options['readConcern'])) {
1008
                        throw new FeatureNotSupportedException('Option readConcern of aggregation implemented only from 3.2.0');
1009
                    }
1010
                }
1011
            }
1012
        }
1013
1014
        // return result as cursor
1015
        if ($asCursor) {
1016
            if (version_compare(\MongoClient::VERSION, '1.5.0', '<')) {
1017
                throw new FeatureNotSupportedException('Aggregate cursor supported from driver version 1.5');
1018
            }
1019
            $cursor = $this->_mongoCollection->aggregateCursor($pipeline, $options);
0 ignored issues
show
Bug introduced by
The method aggregateCursor() does not exist on MongoCollection. Did you maybe mean aggregate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1020
            return $cursor;
1021
        }
1022
1023
        // prepare command
1024
        $command = array(
1025
            'aggregate' => $this->getName(),
1026
            'pipeline'  => $pipeline,
1027
        );
1028
1029
        // add options
1030
        if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options 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...
1031
            $command += $options;
1032
        }
1033
1034
        // aggregate
1035
        $status = $this->_database->executeCommand($command);
1036
1037
        if($status['ok'] != 1) {
1038
            throw new Exception('Aggregate error: ' . $status['errmsg']);
1039
        }
1040
1041
        // explain response
1042
        if (!empty($command['explain'])) {
1043
            return $status['stages'];
1044
        }
1045
1046
        // result response
1047
        return $status['result'];
1048
    }
1049
1050
    /**
1051
     * Explain aggregation
1052
     *
1053
     * @deprecated use pipeline option 'explain' in Collection::aggregate() or method Pipeline::explain()
1054
     * @param array|Pipeline $pipeline
1055
     * @return array result
1056
     * @throws Exception
1057
     */
1058
    public function explainAggregate($pipeline)
1059
    {
1060
        if (version_compare($this->getDatabase()->getClient()->getDbVersion(), '2.6.0', '<')) {
1061
            throw new Exception('Explain of aggregation implemented only from 2.6.0');
1062
        }
1063
1064
        if ($pipeline instanceof Pipeline) {
1065
            $pipeline = $pipeline->toArray();
1066
        } else if (!is_array($pipeline)) {
1067
            throw new Exception('Wrong pipeline specified');
1068
        }
1069
1070
        // aggregate
1071
        return $this->_database->executeCommand(array(
1072
            'aggregate' => $this->getName(),
1073
            'pipeline'  => $pipeline,
1074
            'explain'   => true
1075
        ));
1076
    }
1077
1078
    /**
1079
     * Validates a collection. The method scans a collection’s data structures
1080
     * for correctness and returns a single document that describes the
1081
     * relationship between the logical collection and the physical
1082
     * representation of the data.
1083
     *
1084
     * @link http://docs.mongodb.org/manual/reference/method/db.collection.validate/
1085
     * @param bool $full Specify true to enable a full validation and to return
1086
     *      full statistics. MongoDB disables full validation by default because it
1087
     *      is a potentially resource-intensive operation.
1088
     * @return array
1089
     * @throws Exception
1090
     */
1091
    public function validate($full = false)
1092
    {
1093
        $response = $this->_mongoCollection->validate($full);
1094
        if(!$response || $response['ok'] != 1) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $response 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...
1095
            throw new Exception($response['errmsg']);
1096
        }
1097
1098
        return $response;
1099
    }
1100
1101
    /**
1102
     * Create index
1103
     *
1104
     * @param array $key
1105
     * @param array $options see @link http://php.net/manual/en/mongocollection.ensureindex.php
1106
     * @return \Sokil\Mongo\Collection
1107
     */
1108
    public function ensureIndex(array $key, array $options = array())
1109
    {
1110
        $this->_mongoCollection->ensureIndex($key, $options);
1111
        return $this;
1112
    }
1113
    
1114
    /**
1115
     * Delete index
1116
     *
1117
     * @param array $key
1118
     * @return \Sokil\Mongo\Collection
1119
     */
1120
    public function deleteIndex(array $key)
1121
    {
1122
        $this->_mongoCollection->deleteIndex($key, $options);
0 ignored issues
show
Bug introduced by
The variable $options does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1123
        return $this;
1124
    }
1125
1126
    /**
1127
     * Create unique index
1128
     *
1129
     * @param array $key
1130
     * @param boolean $dropDups
1131
     * @return \Sokil\Mongo\Collection
1132
     */
1133
    public function ensureUniqueIndex(array $key, $dropDups = false)
1134
    {
1135
        $this->_mongoCollection->ensureIndex($key, array(
1136
            'unique'    => true,
1137
            'dropDups'  => (bool) $dropDups,
1138
        ));
1139
1140
        return $this;
1141
    }
1142
1143
    /**
1144
     * Create sparse index.
1145
     *
1146
     * Sparse indexes only contain entries for documents that have the indexed
1147
     * field, even if the index field contains a null value. The index skips
1148
     * over any document that is missing the indexed field.
1149
     *
1150
     * @link http://docs.mongodb.org/manual/core/index-sparse/
1151
     *
1152
     * @param string|array $key An array specifying the index's fields as its
1153
     *  keys. For each field, the value is either the index direction or index
1154
     *  type. If specifying direction, specify 1 for ascending or -1
1155
     *  for descending.
1156
     *
1157
     * @return \Sokil\Mongo\Collection
1158
     */
1159
    public function ensureSparseIndex(array $key)
1160
    {
1161
        $this->_mongoCollection->ensureIndex($key, array(
1162
            'sparse'    => true,
1163
        ));
1164
1165
        return $this;
1166
    }
1167
1168
    /**
1169
     * Create TTL index
1170
     *
1171
     * @link http://docs.mongodb.org/manual/tutorial/expire-data/
1172
     *
1173
     * If seconds not specified then document expired at specified time, as
1174
     * described at @link http://docs.mongodb.org/manual/tutorial/expire-data/#expire-documents-at-a-certain-clock-time
1175
     *
1176
     * @param string|array $key key must be date to use TTL
1177
     * @param int $seconds
1178
     * @return \Sokil\Mongo\Collection
1179
     */
1180
    public function ensureTTLIndex(array $key, $seconds = 0)
1181
    {
1182
        $this->_mongoCollection->ensureIndex($key, array(
1183
            'expireAfterSeconds' => $seconds,
1184
        ));
1185
1186
        return $this;
1187
    }
1188
1189
    /**
1190
     * Create geo index 2dsphere
1191
     *
1192
     * @link http://docs.mongodb.org/manual/tutorial/build-a-2dsphere-index/
1193
     *
1194
     * @param string $field
1195
     * @return \Sokil\Mongo\Collection
1196
     */
1197 View Code Duplication
    public function ensure2dSphereIndex($field)
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...
1198
    {
1199
        if (is_array($field)) {
1200
            $keys = array_fill_keys($field, '2dsphere');
1201
        } else {
1202
            $keys = array(
1203
                $field => '2dsphere',
1204
            );
1205
        }
1206
1207
        $this->_mongoCollection->ensureIndex($keys);
1208
1209
        return $this;
1210
    }
1211
1212
    /**
1213
     * Create geo index 2dsphere
1214
     *
1215
     * @link http://docs.mongodb.org/manual/tutorial/build-a-2d-index/
1216
     *
1217
     * @param string $field
1218
     * @return \Sokil\Mongo\Collection
1219
     */
1220 View Code Duplication
    public function ensure2dIndex($field)
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...
1221
    {
1222
        if (is_array($field)) {
1223
            $keys = array_fill_keys($field, '2d');
1224
        } else {
1225
            $keys = array(
1226
                $field => '2d',
1227
            );
1228
        }
1229
1230
        $this->_mongoCollection->ensureIndex($keys);
1231
1232
        return $this;
1233
    }
1234
1235
    /**
1236
     * Create fulltext index
1237
     *
1238
     * @link https://docs.mongodb.org/manual/core/index-text/
1239
     * @link https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/
1240
     *
1241
     * If a collection contains documents or embedded documents that are in different languages,
1242
     * include a field named language in the documents or embedded documents and specify as its value the language
1243
     * for that document or embedded document.
1244
     *
1245
     * The specified language in the document overrides the default language for the text index.
1246
     * The specified language in an embedded document override the language specified in an enclosing document or
1247
     * the default language for the index.
1248
     *
1249
     * @param array|string $field definition of fields where full text index ensured.
1250
     *  May be string to ensure index on one field, array of fields to create full text index on few fields, and
1251
     *   widdcard '$**'  to create index on all fields of collection. Default value is '$**'
1252
     * @param $weights For a text index, the weight of an indexed field denotes the significance of the field
1253
     *   relative to the other indexed fields in terms of the text search score.
1254
     * @param $defaultLanguage The default language associated with the indexed data determines the rules to parse
1255
     *  word roots (i.e. stemming) and ignore stop words. The default language for the indexed data is english.
1256
     * @param $languageOverride To use a field with a name other than language, include the
1257
     *   language_override option when creating the index.
1258
     *
1259
     * @return Collection
1260
     */
1261
    public function ensureFulltextIndex(
1262
        $field = '$**',
1263
        array $weights = null,
1264
        $defaultLanguage = Language::ENGLISH,
1265
        $languageOverride = null
1266
    ) {
1267
        // keys
1268
        if (is_array($field)) {
1269
            $keys = array_fill_keys($field, 'text');
1270
        } else {
1271
            $keys = array(
1272
                $field => 'text',
1273
            );
1274
        }
1275
1276
        // options
1277
        $options = array(
1278
            'default_language' => $defaultLanguage,
1279
        );
1280
1281
        if (is_array($weights) && $weights) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $weights 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...
1282
            $options['weights'] = $weights;
1283
        }
1284
1285
        if ($languageOverride) {
1286
            $options['language_override'] = $languageOverride;
1287
        }
1288
1289
        // create index
1290
        $this->_mongoCollection->ensureIndex($keys, $options);
1291
1292
        return $this;
1293
    }
1294
1295
1296
1297
    /**
1298
     * Create indexes based on self::$_index metadata
1299
     *
1300
     * @return \Sokil\Mongo\Collection
1301
     * @throws \Exception
1302
     */
1303
    public function initIndexes()
1304
    {
1305
        // read index definition from collection options
1306
        // if not specified - use defined in property
1307
        $indexDefinition = $this->definition->getOption('index');
1308
1309
        // ensure indexes
1310
        foreach($indexDefinition as $options) {
1311
1312
            if(empty($options['keys'])) {
1313
                throw new Exception('Keys not specified');
1314
            }
1315
1316
            $keys = $options['keys'];
1317
            unset($options['keys']);
1318
1319
            $this->_mongoCollection->ensureIndex($keys, $options);
1320
        }
1321
1322
        return $this;
1323
    }
1324
1325
    /**
1326
     * Get index info
1327
     * @return array
1328
     */
1329
    public function getIndexes()
1330
    {
1331
        return $this->_mongoCollection->getIndexInfo();
1332
    }
1333
1334
    public function readPrimaryOnly()
1335
    {
1336
        $this->_mongoCollection->setReadPreference(\MongoClient::RP_PRIMARY);
1337
        return $this;
1338
    }
1339
1340
    public function readPrimaryPreferred(array $tags = null)
1341
    {
1342
        $this->_mongoCollection->setReadPreference(\MongoClient::RP_PRIMARY_PREFERRED, $tags);
1343
        return $this;
1344
    }
1345
1346
    public function readSecondaryOnly(array $tags = null)
1347
    {
1348
        $this->_mongoCollection->setReadPreference(\MongoClient::RP_SECONDARY, $tags);
1349
        return $this;
1350
    }
1351
1352
    public function readSecondaryPreferred(array $tags = null)
1353
    {
1354
        $this->_mongoCollection->setReadPreference(\MongoClient::RP_SECONDARY_PREFERRED, $tags);
1355
        return $this;
1356
    }
1357
1358
    public function readNearest(array $tags = null)
1359
    {
1360
        $this->_mongoCollection->setReadPreference(\MongoClient::RP_NEAREST, $tags);
1361
        return $this;
1362
    }
1363
1364
    public function getReadPreference()
1365
    {
1366
        return $this->_mongoCollection->getReadPreference();
1367
    }
1368
1369
    /**
1370
     * Define write concern for all requests to current collection
1371
     *
1372
     * @param string|integer $w write concern
1373
     * @param int $timeout timeout in milliseconds
1374
     * @throws \Sokil\Mongo\Exception
1375
     * @return \Sokil\Mongo\Collection
1376
     */
1377
    public function setWriteConcern($w, $timeout = 10000)
1378
    {
1379
        if(!$this->_mongoCollection->setWriteConcern($w, (int) $timeout)) {
1380
            throw new Exception('Error setting write concern');
1381
        }
1382
1383
        return $this;
1384
    }
1385
1386
    /**
1387
     * Define unacknowledged write concern for all requests to current collection
1388
     *
1389
     * @param int $timeout timeout in milliseconds
1390
     * @throws \Sokil\Mongo\Exception
1391
     * @return \Sokil\Mongo\Collection
1392
     */
1393
    public function setUnacknowledgedWriteConcern($timeout = 10000)
1394
    {
1395
        $this->setWriteConcern(0, (int) $timeout);
1396
        return $this;
1397
    }
1398
1399
    /**
1400
     * Define majority write concern for all requests to current collection
1401
     *
1402
     * @param int $timeout timeout in milliseconds
1403
     * @throws \Sokil\Mongo\Exception
1404
     * @return \Sokil\Mongo\Collection
1405
     */
1406
    public function setMajorityWriteConcern($timeout = 10000)
1407
    {
1408
        $this->setWriteConcern('majority', (int) $timeout);
1409
        return $this;
1410
    }
1411
1412
    /**
1413
     * Get currently active write concern on all requests to collection
1414
     *
1415
     * @return int|string write concern
1416
     */
1417
    public function getWriteConcern()
1418
    {
1419
        return $this->_mongoCollection->getWriteConcern();
1420
    }
1421
1422
    /**
1423
     * Get collection stat
1424
     *
1425
     * @return array collection stat
1426
     */
1427
    public function stats()
1428
    {
1429
        return $this->getDatabase()->executeCommand(array(
1430
            'collstats' => $this->getName(),
1431
        ));
1432
    }
1433
}
1434