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.

Collection::ensureFulltextIndex()   B
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 18
nc 8
nop 4
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 fully qualified class name of collection
34
     */
35
    protected $mongoCollectionClassName = '\MongoCollection';
36
37
    /**
38
     * @var string expression class. This class may be overloaded to define
39
     *  own query methods (whereUserAgeGreatedThan(), etc.)
40
     * @deprecated since 1.13 Use 'expressionClass' declaration in mapping
41
     */
42
    protected $_queryExpressionClass;
43
44
    /**
45
     * @deprecated since 1.13 Use 'documentClass' declaration in mapping
46
     * @var string Default class for document
47
     */
48
    private $documentClass;
49
50
    /**
51
     * List of arrays, where each item array is an index definition.
52
     * Every index definition must contain key 'keys' with list of fields and orders,
53
     * and optional options from @link http://php.net/manual/en/mongocollection.createindex.php:
54
     *
55
     * Example:
56
     * array(
57
     *     array(
58
     *         'keys' => array('field1' => 1, 'field2' => -1),
59
     *         'unique' => true
60
     *     ),
61
     *     ...
62
     * )
63
     * @var array list of indexes
64
     * @deprecated since 1.13 Use 'index' declaration in mapping
65
     */
66
    protected $_index;
67
68
    /**
69
     *
70
     * @var \Sokil\Mongo\Database
71
     */
72
    private $database;
73
74
    /**
75
     *
76
     * @var \MongoCollection
77
     */
78
    private $collection;
79
80
    /**
81
     * @var string
82
     */
83
    private $collectionName;
84
85
    /**
86
     * Implementation of identity map pattern
87
     *
88
     * @var array list of cached documents
89
     */
90
    private $documentPool = array();
91
92
    /**
93
     * @deprecated since 1.13 Use 'versioning' declaration in mapping
94
     * @var bool default value of versioning
95
     */
96
    protected $versioning;
97
98
    /**
99
     * @var \Sokil\Mongo\Collection\Definition collection options
100
     */
101
    private $definition;
102
103
    public function __construct(
104
        Database $database,
105
        $collection,
106
        Definition $definition = null
107
    ) {
108
        // define db
109
        $this->database = $database;
110
111
        // init mongo collection
112
        if ($collection instanceof \MongoCollection) {
113
            $this->collectionName = $collection->getName();
114
            $this->collection = $collection;
115
        } else {
116
            $this->collectionName = $collection;
117
        }
118
119
        // init definition
120
        $this->definition = $definition ? $definition : new Definition();
121
122
        if (!empty($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...
123
            $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...
124
        }
125
126
        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...
127
            $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...
128
        }
129
130
        if (!empty($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...
131
            $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...
132
        }
133
134
        if (!empty($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...
135
            $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...
136
        }
137
    }
138
139
    /**
140
     * Start versioning documents on modify
141
     *
142
     * @deprecated since 1.13 Use 'versioning' declaration in mapping
143
     * @return \Sokil\Mongo\Collection
144
     */
145
    public function enableVersioning()
146
    {
147
        $this->definition->setOption('versioning', true);
148
        return $this;
149
    }
150
151
    /**
152
     * Check if versioning enabled
153
     *
154
     * @deprecated since 1.13 Use 'versioning' declaration in mapping
155
     * @return bool
156
     */
157
    public function isVersioningEnabled()
158
    {
159
        return $this->definition->getOption('versioning');
160
    }
161
162
    /**
163
     * Get option
164
     *
165
     * @param string|int $name
166
     * @return mixed
167
     */
168
    public function getOption($name)
169
    {
170
        return $this->definition->getOption($name);
171
    }
172
173
    public function getOptions()
174
    {
175
        return $this->definition->getOptions();
176
    }
177
178
    public function __get($name)
179
    {
180
        // support of deprecated property, use selg::getMongoCollection instead
181
        if ($name === '_mongoCollection') {
182
            return $this->getMongoCollection();
183
        }
184
185
        return $this->getDocument($name);
186
    }
187
188
    /**
189
     * Get name of collection
190
     *
191
     * @return string name of collection
192
     */
193
    public function getName()
194
    {
195
        return $this->collectionName;
196
    }
197
198
    /**
199
     * Get native collection instance of mongo driver
200
     *
201
     * @return \MongoCollection
202
     */
203
    public function getMongoCollection()
204
    {
205
        if (empty($this->collection)) {
206
            $mongoCollectionClassName = $this->mongoCollectionClassName;
207
            $this->collection = new $mongoCollectionClassName(
208
                $this->database->getMongoDB(),
209
                $this->collectionName
210
            );
211
        }
212
213
        return $this->collection;
214
    }
215
216
    /**
217
     *
218
     * @return \Sokil\Mongo\Database
219
     */
220
    public function getDatabase()
221
    {
222
        return $this->database;
223
    }
224
225
    /**
226
     * Delete collection
227
     *
228
     * @return \Sokil\Mongo\Collection
229
     * @throws \Sokil\Mongo\Exception
230
     */
231
    public function delete()
232
    {
233
        $status = $this->getMongoCollection()->drop();
234
        if ($status['ok'] != 1) {
235
            // check if collection exists
236
            if ('ns not found' !== $status['errmsg']) {
237
                // collection exist
238
                throw new Exception('Error deleting collection ' . $this->getName() . ': ' . $status['errmsg']);
239
            }
240
        }
241
242
        return $this;
243
    }
244
245
    /**
246
     * Override to define class name of document by document data
247
     *
248
     * @param array $documentData
249
     * @return string Document class data
250
     */
251
    public function getDocumentClassName(array $documentData = null)
252
    {
253
        $documentClass = $this->definition->getOption('documentClass');
254
255
        if (is_callable($documentClass)) {
256
            return call_user_func($documentClass, $documentData);
257
        }
258
259
        if (class_exists($documentClass)) {
260
            return $documentClass;
261
        }
262
263
        throw new Exception('Property "documentClass" must be callable or valid name of class');
264
    }
265
266
    /**
267
     * Factory method to get not stored Document instance from array
268
     * @param array $data
269
     * @return Document
270
     */
271
    public function createDocument(array $data = null)
272
    {
273
        $className = $this->getDocumentClassName($data);
274
275
        /* @var $document \Sokil\Mongo\Document */
276
        $document = new $className(
277
            $this,
278
            $data,
279
            array('stored' => false) + $this->definition->getOptions()
280
        );
281
282
        // store document to identity map
283
        if ($this->isDocumentPoolEnabled()) {
284
            $collection = $this;
285
            $document->onAfterInsert(function (\Sokil\Mongo\Event $event) use ($collection) {
286
                $collection->addDocumentToDocumentPool($event->getTarget());
287
            });
288
        }
289
290
        return $document;
291
    }
292
293
    /**
294
     * Factory method to get document object from array of stored document
295
     *
296
     * @param array $data
297
     * @return \Sokil\Mongo\Document
298
     */
299
    public function hydrate($data, $useDocumentPool = true)
300
    {
301
        if (!is_array($data) || !isset($data['_id'])) {
302
            throw new Exception('Document must be stored and has _id key');
303
        }
304
305
        // if document already in pool - return it
306
        if ($useDocumentPool && $this->isDocumentPoolEnabled() && $this->isDocumentInDocumentPool($data['_id'])) {
307
            return $this
308
                ->getDocumentFromDocumentPool($data['_id'])
309
                ->mergeUnmodified($data);
310
        }
311
312
        // init document instance
313
        $className = $this->getDocumentClassName($data);
314
        $document = new $className(
315
            $this,
316
            $data,
317
            array('stored' => true) + $this->definition->getOptions()
318
        );
319
320
        // store document in cache
321
        if ($useDocumentPool && $this->isDocumentPoolEnabled()) {
322
            $this->addDocumentToDocumentPool($document);
323
        }
324
325
        return $document;
326
    }
327
328
    /**
329
     * Total count of documents in collection
330
     *
331
     * @return int
332
     */
333
    public function count()
334
    {
335
        return $this->find()->count();
336
    }
337
338
    /**
339
     * Retrieve a list of distinct values for the given key across a collection.
340
     *
341
     * @param string $selector field selector
342
     * @param array|callable|\Sokil\Mongo\Expression $expression expression to search documents
343
     * @return array distinct values
344
     */
345
    public function getDistinct($selector, $expression = null)
346
    {
347
        if ($expression) {
348
            return $this->getMongoCollection()->distinct(
349
                $selector,
350
                Expression::convertToArray($expression)
351
            );
352
        }
353
354
        return $this->getMongoCollection()->distinct($selector);
355
    }
356
357
    /**
358
     * Create new Expression instance to use in query builder or update operations
359
     *
360
     * @return \Sokil\Mongo\Expression
361
     */
362
    public function expression()
363
    {
364
        $className = $this->definition->getExpressionClass();
365
        return new $className;
366
    }
367
368
    /**
369
     * Create Operator instance to use in update operations
370
     *
371
     * @return \Sokil\Mongo\Operator
372
     */
373
    public function operator()
374
    {
375
        return new Operator();
376
    }
377
378
    /**
379
     * Create document query builder
380
     *
381
     * @param $callable callable|null Function to configure query builder&
382
     * @return Cursor
383
     */
384
    public function find($callable = null)
385
    {
386
        /** @var Cursor $cursor */
387
        $cursor = new Cursor($this, array(
388
            'expressionClass'   => $this->definition->getExpressionClass(),
389
            'batchSize'         => $this->definition->getOption('batchSize'),
390
            'clientTimeout'     => $this->definition->getOption('cursorClientTimeout'),
391
            'serverTimeout'     => $this->definition->getOption('cursorServerTimeout'),
392
        ));
393
394
        if (is_callable($callable)) {
395
            $callable($cursor->getExpression());
396
        }
397
398
        return $cursor;
399
    }
400
401
    /**
402
     * Create document query builder
403
     *
404
     * @param callable|null $callable
405
     *
406
     * @return \Sokil\Mongo\Cursor
407
     */
408
    public function findAsArray($callable = null)
409
    {
410
        return $this
411
            ->find($callable)
412
            ->asArray();
413
    }
414
415
    /**
416
     * Stop storing found documents to pool
417
     *
418
     * @return \Sokil\Mongo\Collection
419
     */
420
    public function disableDocumentPool()
421
    {
422
        $this->definition->setOption('documentPool', false);
423
        return $this;
424
    }
425
426
    /**
427
     * Start storing found documents to pool
428
     *
429
     * @return \Sokil\Mongo\Collection
430
     */
431
    public function enableDocumentPool()
432
    {
433
        $this->definition->setOption('documentPool', true);
434
        return $this;
435
    }
436
437
    /**
438
     * Check if document pool enabled and requested documents store to it
439
     *
440
     * @return bool
441
     */
442
    public function isDocumentPoolEnabled()
443
    {
444
        return $this->definition->getOption('documentPool');
445
    }
446
447
    public function clearDocumentPool()
448
    {
449
        $this->documentPool = array();
450
        return $this;
451
    }
452
453
    /**
454
     * Check if documents are in pool
455
     *
456
     * @return bool
457
     */
458
    public function isDocumentPoolEmpty()
459
    {
460
        return !$this->documentPool;
461
    }
462
463
    /**
464
     * Store document to pool
465
     *
466
     * @param array $document
467
     * @return \Sokil\Mongo\Collection
468
     */
469
    public function addDocumentToDocumentPool(Document $document)
470
    {
471
        $documentId = (string) $document->getId();
472
473
        if (!isset($this->documentPool[$documentId])) {
474
            $this->documentPool[$documentId] = $document;
475
        } else {
476
            // merging because document after
477
            // load and before getting in second place may be changed
478
            // and this changes must be preserved:
479
            //
480
            // 1. Document loads and modifies in current session
481
            // 2. Document loads modified in another session
482
            // 3. Document loads once again in current session. Changes from stage 2 merges as unmodified
483
484
            $this->documentPool[$documentId]->mergeUnmodified($document->toArray());
485
        }
486
487
        return $this;
488
    }
489
490
    /**
491
     * Store documents to identity map
492
     *
493
     * @param array $documents list of Document instances
494
     * @return \Sokil\Mongo\Collection
495
     */
496
    public function addDocumentsToDocumentPool(array $documents)
497
    {
498
        foreach ($documents as $document) {
499
            $this->addDocumentToDocumentPool($document);
500
        }
501
502
        return $this;
503
    }
504
505
    /**
506
     * Remove document instance from identity map
507
     *
508
     * @param \Sokil\Mongo\Document $document
509
     * @return \Sokil\Mongo\Collection
510
     */
511
    public function removeDocumentFromDocumentPool(Document $document)
512
    {
513
        unset($this->documentPool[(string) $document]);
514
        return $this;
515
    }
516
517
    /**
518
     * Get document from identity map by it's id
519
     *
520
     * @param string|int|\MongoId $id
521
     * @return \Sokil\Mongo\Document
522
     */
523
    public function getDocumentFromDocumentPool($id)
524
    {
525
        return $this->documentPool[(string) $id];
526
    }
527
528
    /**
529
     * Get documents from pool if they stored
530
     *
531
     * @param array $ids
532
     */
533
    public function getDocumentsFromDocumentPool(array $ids = null)
534
    {
535
        if (!$ids) {
536
            return $this->documentPool;
537
        }
538
539
        return array_intersect_key(
540
            $this->documentPool,
541
            array_flip(array_map('strval', $ids))
542
        );
543
    }
544
545
    /**
546
     * Get number of documents in document pool
547
     *
548
     * @return int
549
     */
550
    public function documentPoolCount()
551
    {
552
        return count($this->documentPool);
553
    }
554
555
    /**
556
     * Check if document exists in identity map
557
     *
558
     * @param \Sokil\Mongo\Document|\MongoId|int|string $document Document instance or it's id
559
     * @return boolean
560
     */
561
    public function isDocumentInDocumentPool($document)
562
    {
563
        if ($document instanceof Document) {
564
            $document = $document->getId();
565
        }
566
567
        return isset($this->documentPool[(string) $document]);
568
    }
569
570
    /**
571
     * Get document by id
572
     * If callable specified, document always loaded directly omitting document pool.
573
     * Method may return document as array if cursor configured through Cursor::asArray()
574
     *
575
     * @param string|\MongoId $id
576
     * @param callable $callable cursor callable used to configure cursor
577
     * @return \Sokil\Mongo\Document|array|null
578
     */
579
    public function getDocument($id, $callable = null)
580
    {
581
        if (!$this->isDocumentPoolEnabled()) {
582
            return $this->getDocumentDirectly($id, $callable);
583
        }
584
585
        if (!$callable && $this->isDocumentInDocumentPool($id)) {
586
            return $this->getDocumentFromDocumentPool($id);
587
        }
588
589
        $document = $this->getDocumentDirectly($id, $callable);
590
591
        // if callable configure cursor to return document as array,
592
        // than it can't be stored to document pool
593
        if ($document instanceof Document) {
594
            $this->addDocumentToDocumentPool($document);
595
        }
596
597
        return $document;
598
    }
599
600
    /**
601
     * Get Document instance by it's reference
602
     *
603
     * @param array $ref reference to document
604
     * @param bool  $useDocumentPool try to get document from pool or fetch document from database
605
     *
606
     * @return Document|null
607
     */
608 View Code Duplication
    public function getDocumentByReference(array $ref, $useDocumentPool = true)
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...
609
    {
610
        $documentArray = $this->getMongoCollection()->getDBRef($ref);
611
        if (null === $documentArray) {
612
            return null;
613
        }
614
615
        return $this->hydrate($documentArray, $useDocumentPool);
616
    }
617
618
    /**
619
     * Get document by id directly omitting cache
620
     * Method may return document as array if cursor configured through Cursor::asArray()
621
     *
622
     * @param string|\MongoId $id
623
     * @param callable $callable cursor callable used to configure cursor
624
     * @return \Sokil\Mongo\Document|array|null
625
     */
626
    public function getDocumentDirectly($id, $callable = null)
627
    {
628
        $cursor = $this->find();
629
630
        if (is_callable($callable)) {
631
            call_user_func($callable, $cursor);
632
        }
633
634
        return $cursor
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findOne() has been deprecated with message: since v.1.22.2. Use ::one instead

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

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

Loading history...
635
            ->byId($id)
636
            ->skipDocumentPool()
637
            ->findOne();
638
    }
639
640
    /**
641
     * Check if document belongs to collection
642
     *
643
     * @param Document $document
644
     *
645
     * @return bool
646
     */
647
    public function hasDocument(Document $document)
648
    {
649
        $documentCollection = $document->getCollection();
650
        $documentDatabase = $documentCollection->getDatabase();
651
652
        // check connection
653
        if ($documentDatabase->getClient()->getDsn() !== $this->getDatabase()->getClient()->getDsn()) {
654
            return false;
655
        }
656
657
        // check database
658
        if ($documentDatabase->getName() !== $this->getDatabase()->getName()) {
659
            return false;
660
        }
661
662
        // check collection
663
        return $documentCollection->getName() == $this->getName();
664
    }
665
666
    /**
667
     * Get documents by list of id
668
     *
669
     * @param array $idList list of ids
670
     * @param callable $callable cursor callable used to configure cursor
671
     *
672
     * @return Document[]
673
     */
674
    public function getDocuments(array $idList, $callable = null)
675
    {
676
        $idListToFindDirectly = $idList;
677
678
        // try to egt document from pool if enabled
679
        $documentsInDocumentPool = array();
680
        if ($this->isDocumentPoolEnabled() && !$callable) {
681
            $documentsInDocumentPool = $this->getDocumentsFromDocumentPool($idList);
682
            if (count($documentsInDocumentPool) === count($idList)) {
683
                return $documentsInDocumentPool;
684
            }
685
686
            // skip ids already found in pool
687
            $idListToFindDirectly = array_diff_key(
688
                array_map('strval', $idList),
689
                array_keys($documentsInDocumentPool)
690
            );
691
        }
692
693
        // get documents directly
694
        $cursor = $this->find();
695
696
        if (is_callable($callable)) {
697
            call_user_func($callable, $cursor);
698
        }
699
700
        $documentsGettingDirectly = $cursor->byIdList($idListToFindDirectly)->findAll();
0 ignored issues
show
Deprecated Code introduced by
The method Sokil\Mongo\Cursor::findAll() has been deprecated with message: since v.1.22.2. Use ::one instead

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

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

Loading history...
701
        if (empty($documentsGettingDirectly)) {
702
            return $documentsInDocumentPool ? $documentsInDocumentPool : array();
703
        }
704
705
        if ($this->isDocumentPoolEnabled()) {
706
            $this->addDocumentsToDocumentPool($documentsGettingDirectly);
707
        }
708
709
        return $documentsGettingDirectly + $documentsInDocumentPool;
710
    }
711
712
    /**
713
     * Creates batch insert operation handler
714
     * @param int|string $writeConcern Write concern. Default is 1 (Acknowledged)
715
     * @param int $timeout Timeout for write concern. Default is 10000 milliseconds
716
     * @param bool $ordered Determins if MongoDB must apply this batch in order (sequentally,
717
     *   one item at a time) or can rearrange it. Defaults to TRUE
718
     * @return BatchInsert
719
     */
720
    public function createBatchInsert($writeConcern = null, $timeout = null, $ordered = null)
721
    {
722
        return new BatchInsert(
723
            $this,
724
            $writeConcern,
725
            $timeout,
726
            $ordered
727
        );
728
    }
729
730
    /**
731
     * Creates batch update operation handler
732
     * @param int|string $writeConcern Write concern. Default is 1 (Acknowledged)
733
     * @param int $timeout Timeout for write concern. Default is 10000 milliseconds
734
     * @param bool $ordered Determins if MongoDB must apply this batch in order (sequentally,
735
     *   one item at a time) or can rearrange it. Defaults to TRUE
736
     * @return BatchUpdate
737
     */
738
    public function createBatchUpdate($writeConcern = null, $timeout = null, $ordered = null)
739
    {
740
        return new BatchUpdate(
741
            $this,
742
            $writeConcern,
743
            $timeout,
744
            $ordered
745
        );
746
    }
747
748
    /**
749
     * Creates batch delete operation handler
750
     * @param int|string $writeConcern Write concern. Default is 1 (Acknowledged)
751
     * @param int $timeout Timeout for write concern. Default is 10000 milliseconds
752
     * @param bool $ordered Determins if MongoDB must apply this batch in order (sequentally,
753
     *   one item at a time) or can rearrange it. Defaults to TRUE
754
     * @return BatchDelete
755
     */
756
    public function createBatchDelete($writeConcern = null, $timeout = null, $ordered = null)
757
    {
758
        return new BatchDelete(
759
            $this,
760
            $writeConcern,
761
            $timeout,
762
            $ordered
763
        );
764
    }
765
766
    /**
767
     * @deprecated since 1.13. Use Document::delete()
768
     * @param \Sokil\Mongo\Document $document
769
     * @return \Sokil\Mongo\Collection
770
     */
771
    public function deleteDocument(Document $document)
772
    {
773
        $document->delete();
774
        return $this;
775
    }
776
777
    /**
778
     * Delete documents by expression
779
     *
780
     * @param Expression|callable|array $expression
781
     *
782
     * @return Collection
783
     *
784
     * @throws Exception
785
     */
786
    public function batchDelete($expression)
787
    {
788
        // remove
789
        $result = $this->getMongoCollection()->remove(
790
            Expression::convertToArray($expression)
791
        );
792
793
        // check result
794
        if (true !== $result && $result['ok'] != 1) {
795
            throw new Exception('Error removing documents from collection: ' . $result['err']);
796
        }
797
798
        return $this;
799
    }
800
801
    /**
802
     * @deprecated since 1.13. Use Collection::batchDelete();
803
     *
804
     * @param Expression|callable|array $expression
805
     *
806
     * @return Collection
807
     *
808
     * @throws Exception
809
     *
810
     */
811
    public function deleteDocuments($expression = array())
812
    {
813
        return $this->batchDelete($expression);
814
    }
815
816
    /**
817
     * Insert multiple documents defined as arrays
818
     *
819
     * Prior to version 1.5.0 of the driver it was possible to use MongoCollection::batchInsert(),
820
     * however, as of 1.5.0 that method is now discouraged.
821
     *
822
     * You can use Collection::createBatchInsert()
823
     *
824
     * @param array $rows list of documents to insert, defined as arrays
825
     * @return \Sokil\Mongo\Collection
826
     * @throws \Sokil\Mongo\Document\InvalidDocumentException
827
     * @throws \Sokil\Mongo\Exception
828
     */
829
    public function batchInsert($rows, $validate = true)
830
    {
831
        if ($validate) {
832
            $document = $this->createDocument();
833
            foreach ($rows as $row) {
834
                $document->merge($row);
835
836
                if (!$document->isValid()) {
837
                    throw new InvalidDocumentException('Document is invalid on batch insert');
838
                }
839
840
                $document->reset();
841
            }
842
        }
843
844
        $result = $this->getMongoCollection()->batchInsert($rows);
845
846
        // If the w parameter is set to acknowledge the write,
847
        // returns an associative array with the status of the inserts ("ok")
848
        // and any error that may have occurred ("err").
849 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...
850
            if ($result['ok'] != 1) {
851
                throw new Exception('Batch insert error: ' . $result['err']);
852
            }
853
854
            return $this;
855
        }
856
857
        // Otherwise, returns TRUE if the batch insert was successfully sent,
858
        // FALSE otherwise.
859
        if (!$result) {
860
            throw new Exception('Batch insert error');
861
        }
862
863
        return $this;
864
    }
865
866
    /**
867
     * @deprecated since 1.13 Use Collection::batchInsert()
868
     */
869
    public function insertMultiple($rows, $validate = true)
870
    {
871
        return $this->batchInsert($rows, $validate);
872
    }
873
874
    /**
875
     * Direct insert of array to MongoDB without creating document object and validation
876
     *
877
     * @param array $document
878
     * @return \Sokil\Mongo\Collection
879
     * @throws Exception
880
     */
881
    public function insert(array $document)
882
    {
883
        $result = $this->getMongoCollection()->insert($document);
884
885
        // if write concern acknowledged
886 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...
887
            if ($result['ok'] != 1) {
888
                throw new Exception('Insert error: ' . $result['err'] . ': ' . $result['errmsg']);
889
            }
890
891
            return $this;
892
        }
893
894
        // if write concern unacknowledged
895
        if (!$result) {
896
            throw new Exception('Insert error');
897
        }
898
899
        return $this;
900
    }
901
902
    /**
903
     * Update multiple documents
904
     *
905
     * @param \Sokil\Mongo\Expression|array|callable $expression expression to define
906
     *  which documents will change.
907
     * @param \Sokil\Mongo\Operator|array|callable $updateData new data or operators to update
908
     * @param array $options update options, see http://php.net/manual/ru/mongocollection.update.php
909
     * @return \Sokil\Mongo\Collection
910
     * @throws \Sokil\Mongo\Exception
911
     */
912
    public function update($expression, $updateData, array $options = array())
913
    {
914
        // execute update operator
915
        $result = $this->getMongoCollection()->update(
916
            Expression::convertToArray($expression),
917
            Operator::convertToArray($updateData),
918
            $options
919
        );
920
921
        // if write concern acknowledged
922 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...
923
            if ($result['ok'] != 1) {
924
                throw new Exception(sprintf('Update error: %s: %s', $result['err'], $result['errmsg']));
925
            }
926
            return $this;
927
        }
928
929
        // if write concern unacknowledged
930
        if (!$result) {
931
            throw new Exception('Update error');
932
        }
933
934
        return $this;
935
    }
936
937
    /**
938
     * Update multiple documents
939
     *
940
     * @param \Sokil\Mongo\Expression|array|callable $expression expression to define
941
     *  which documents will change.
942
     * @param \Sokil\Mongo\Operator|array|callable $updateData new data or operators
943
     *  to update
944
     * @return \Sokil\Mongo\Collection
945
     * @throws \Sokil\Mongo\Exception
946
     */
947
    public function batchUpdate($expression, $updateData)
948
    {
949
        return $this->update($expression, $updateData, array(
950
            'multiple'  => true,
951
        ));
952
    }
953
954
    /**
955
     * @deprecated since 1.13 Use Collection::batchUpdate()
956
     */
957
    public function updateMultiple($expression, $updateData)
958
    {
959
        return $this->batchUpdate($expression, $updateData);
960
    }
961
962
    /**
963
     * Update all documents
964
     *
965
     * @deprecated since 1.13. Use Collection::batchUpdate([])
966
     * @param \Sokil\Mongo\Operator|array|callable $updateData new data or operators
967
     * @return \Sokil\Mongo\Collection
968
     * @throws \Sokil\Mongo\Exception
969
     */
970
    public function updateAll($updateData)
971
    {
972
        return $this->update(array(), $updateData, array(
973
            'multiple'  => true,
974
        ));
975
    }
976
977
    /**
978
     * Start aggregation
979
     *
980
     * @link http://docs.mongodb.org/manual/reference/operator/aggregation/
981
     * @return \Sokil\Mongo\Pipeline
982
     */
983
    public function createAggregator()
984
    {
985
        return new Pipeline($this);
986
    }
987
988
    /**
989
     * Aggregate using pipeline
990
     * @link http://docs.mongodb.org/manual/reference/operator/aggregation/
991
     *
992
     * @param callable|array|Pipeline $pipeline list of pipeline stages
993
     * @param array aggregate options
994
     * @param bool $asCursor return result as cursor
995
     *
996
     * @throws \Sokil\Mongo\Exception
997
     * @return array result of aggregation
998
     */
999
    public function aggregate(
1000
        $pipeline,
1001
        array $options = array(),
1002
        $asCursor = false
1003
    ) {
1004
        // configure through callable
1005
        if (is_callable($pipeline)) {
1006
            $pipelineConfiguratorCallable = $pipeline;
1007
            $pipeline = $this->createAggregator();
1008
            call_user_func($pipelineConfiguratorCallable, $pipeline);
1009
        }
1010
1011
        // get aggregation array
1012
        if ($pipeline instanceof Pipeline) {
1013
            if (!empty($options)) {
1014
                $options = array_merge($pipeline->getOptions(), $options);
1015
            } else {
1016
                $options = $pipeline->getOptions();
1017
            }
1018
            $pipeline = $pipeline->toArray();
1019
        } elseif (!is_array($pipeline)) {
1020
            throw new Exception('Wrong pipeline specified');
1021
        }
1022
1023
        // Check options for supporting by database
1024
        if (!empty($options)) {
1025
            $this->validateAggregationOptions($options);
1026
        }
1027
1028
        // return result as cursor
1029
        if ($asCursor) {
1030
            if (version_compare(\MongoClient::VERSION, '1.5.0', '>=')) {
1031
                return $this->getMongoCollection()->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...
1032
            } else {
1033
                throw new FeatureNotSupportedException('Aggregate cursor supported from driver version 1.5');
1034
            }
1035
        }
1036
1037
        // prepare command
1038
        $command = array(
1039
            'aggregate' => $this->getName(),
1040
            'pipeline'  => $pipeline,
1041
        );
1042
1043
        // add options
1044
        if (!empty($options)) {
1045
            $command += $options;
1046
        }
1047
1048
        // aggregate
1049
        $status = $this->database->executeCommand($command);
1050
        if ($status['ok'] != 1) {
1051
            throw new Exception('Aggregate error: ' . $status['errmsg']);
1052
        }
1053
1054
        // explain response
1055
        if (!empty($command['explain'])) {
1056
            return $status['stages'];
1057
        }
1058
1059
        // result response
1060
        return $status['result'];
1061
    }
1062
1063
    /**
1064
     * Check if aggragator options supported by database
1065
     *
1066
     * @param array $options
1067
     * @throws FeatureNotSupportedException
1068
     */
1069
    private function validateAggregationOptions(array $options)
1070
    {
1071
        // get db version
1072
        $client = $this->getDatabase()->getClient();
1073
        $dbVersion = $client->getDbVersion();
1074
1075
        // check options for db < 2.6
1076
        if (version_compare($dbVersion, '2.6.0', '<')) {
1077
            if (!empty($options['explain'])) {
1078
                throw new FeatureNotSupportedException(
1079
                    'Explain of aggregation implemented only from 2.6.0'
1080
                );
1081
            }
1082
1083
            if (!empty($options['allowDiskUse'])) {
1084
                throw new FeatureNotSupportedException(
1085
                    'Option allowDiskUse of aggregation implemented only from 2.6.0'
1086
                );
1087
            }
1088
1089
            if (!empty($options['cursor'])) {
1090
                throw new FeatureNotSupportedException(
1091
                    'Option cursor of aggregation implemented only from 2.6.0'
1092
                );
1093
            }
1094
        }
1095
1096
        // check options for db < 3.2
1097
        if (version_compare($dbVersion, '3.2.0', '<')) {
1098
            if (!empty($options['bypassDocumentValidation'])) {
1099
                throw new FeatureNotSupportedException(
1100
                    'Option bypassDocumentValidation of aggregation implemented only from 3.2.0'
1101
                );
1102
            }
1103
1104
            if (!empty($options['readConcern'])) {
1105
                throw new FeatureNotSupportedException(
1106
                    'Option readConcern of aggregation implemented only from 3.2.0'
1107
                );
1108
            }
1109
        }
1110
    }
1111
1112
    /**
1113
     * Explain aggregation
1114
     *
1115
     * @deprecated use pipeline option 'explain' in Collection::aggregate() or method Pipeline::explain()
1116
     * @param array|Pipeline $pipeline
1117
     * @return array result
1118
     * @throws Exception
1119
     */
1120
    public function explainAggregate($pipeline)
1121
    {
1122
        if (version_compare($this->getDatabase()->getClient()->getDbVersion(), '2.6.0', '<')) {
1123
            throw new Exception('Explain of aggregation implemented only from 2.6.0');
1124
        }
1125
1126
        if ($pipeline instanceof Pipeline) {
1127
            $pipeline = $pipeline->toArray();
1128
        } elseif (!is_array($pipeline)) {
1129
            throw new Exception('Wrong pipeline specified');
1130
        }
1131
1132
        // aggregate
1133
        return $this->database->executeCommand(array(
1134
            'aggregate' => $this->getName(),
1135
            'pipeline'  => $pipeline,
1136
            'explain'   => true
1137
        ));
1138
    }
1139
1140
    /**
1141
     * Validates a collection. The method scans a collection’s data structures
1142
     * for correctness and returns a single document that describes the
1143
     * relationship between the logical collection and the physical
1144
     * representation of the data.
1145
     *
1146
     * @link http://docs.mongodb.org/manual/reference/method/db.collection.validate/
1147
     * @param bool $full Specify true to enable a full validation and to return
1148
     *      full statistics. MongoDB disables full validation by default because it
1149
     *      is a potentially resource-intensive operation.
1150
     * @return array
1151
     * @throws Exception
1152
     */
1153
    public function validate($full = false)
1154
    {
1155
        $response = $this->getMongoCollection()->validate($full);
1156
        if (empty($response) || $response['ok'] != 1) {
1157
            throw new Exception($response['errmsg']);
1158
        }
1159
1160
        return $response;
1161
    }
1162
1163
    /**
1164
     * Create index
1165
     *
1166
     * @deprecated since 1.19 Use self::createIndex()
1167
     * @param array $key
1168
     * @param array $options see @link http://php.net/manual/en/mongocollection.ensureindex.php
1169
     * @return \Sokil\Mongo\Collection
1170
     */
1171
    public function ensureIndex(array $key, array $options = array())
1172
    {
1173
        return $this->createIndex($key, $options);
1174
    }
1175
1176
    /**
1177
     * Create index
1178
     *
1179
     * @param array $key
1180
     * @param array $options see @link http://php.net/manual/en/mongocollection.ensureindex.php
1181
     * @return \Sokil\Mongo\Collection
1182
     */
1183
    public function createIndex(array $key, array $options = array())
1184
    {
1185
        $this->getMongoCollection()->createIndex($key, $options);
1186
        return $this;
1187
    }
1188
    
1189
    /**
1190
     * Delete index
1191
     *
1192
     * @param array $key
1193
     * @return \Sokil\Mongo\Collection
1194
     */
1195
    public function deleteIndex(array $key)
1196
    {
1197
        $this->getMongoCollection()->deleteIndex($key);
1198
        return $this;
1199
    }
1200
1201
    /**
1202
     * Create unique index
1203
     *
1204
     * @param array $key
1205
     * @param boolean $dropDups
1206
     * @return \Sokil\Mongo\Collection
1207
     */
1208
    public function ensureUniqueIndex(array $key, $dropDups = false)
1209
    {
1210
        $this->getMongoCollection()->createIndex($key, array(
1211
            'unique'    => true,
1212
            'dropDups'  => (bool) $dropDups,
1213
        ));
1214
1215
        return $this;
1216
    }
1217
1218
    /**
1219
     * Create sparse index.
1220
     *
1221
     * Sparse indexes only contain entries for documents that have the indexed
1222
     * field, even if the index field contains a null value. The index skips
1223
     * over any document that is missing the indexed field.
1224
     *
1225
     * @link http://docs.mongodb.org/manual/core/index-sparse/
1226
     *
1227
     * @param string|array $key An array specifying the index's fields as its
1228
     *  keys. For each field, the value is either the index direction or index
1229
     *  type. If specifying direction, specify 1 for ascending or -1
1230
     *  for descending.
1231
     *
1232
     * @return Collection
1233
     */
1234
    public function ensureSparseIndex(array $key)
1235
    {
1236
        $this->getMongoCollection()->createIndex(
1237
            $key,
1238
            array(
1239
                'sparse'    => true,
1240
            )
1241
        );
1242
1243
        return $this;
1244
    }
1245
1246
    /**
1247
     * Create TTL index
1248
     *
1249
     * @link http://docs.mongodb.org/manual/tutorial/expire-data/
1250
     *
1251
     * If seconds not specified then document expired at specified time, as described at
1252
     * @link http://docs.mongodb.org/manual/tutorial/expire-data/#expire-documents-at-a-certain-clock-time
1253
     *
1254
     * @param string|array $key key must be date to use TTL
1255
     * @param int $seconds
1256
     * @return \Sokil\Mongo\Collection
1257
     */
1258
    public function ensureTTLIndex(array $key, $seconds = 0)
1259
    {
1260
        $this->getMongoCollection()->createIndex($key, array(
1261
            'expireAfterSeconds' => $seconds,
1262
        ));
1263
1264
        return $this;
1265
    }
1266
1267
    /**
1268
     * Create geo index 2dsphere
1269
     *
1270
     * @link http://docs.mongodb.org/manual/tutorial/build-a-2dsphere-index/
1271
     *
1272
     * @param string $field
1273
     * @return \Sokil\Mongo\Collection
1274
     */
1275 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...
1276
    {
1277
        if (is_array($field)) {
1278
            $keys = array_fill_keys($field, '2dsphere');
1279
        } else {
1280
            $keys = array(
1281
                $field => '2dsphere',
1282
            );
1283
        }
1284
1285
        $this->getMongoCollection()->createIndex($keys);
1286
1287
        return $this;
1288
    }
1289
1290
    /**
1291
     * Create geo index 2dsphere
1292
     *
1293
     * @link http://docs.mongodb.org/manual/tutorial/build-a-2d-index/
1294
     *
1295
     * @param string $field
1296
     * @return \Sokil\Mongo\Collection
1297
     */
1298 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...
1299
    {
1300
        if (is_array($field)) {
1301
            $keys = array_fill_keys($field, '2d');
1302
        } else {
1303
            $keys = array(
1304
                $field => '2d',
1305
            );
1306
        }
1307
1308
        $this->getMongoCollection()->createIndex($keys);
1309
1310
        return $this;
1311
    }
1312
1313
    /**
1314
     * Create fulltext index
1315
     *
1316
     * @link https://docs.mongodb.org/manual/core/index-text/
1317
     * @link https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/
1318
     *
1319
     * If a collection contains documents or embedded documents that are in different languages,
1320
     * include a field named language in the documents or embedded documents and specify as its value the language
1321
     * for that document or embedded document.
1322
     *
1323
     * The specified language in the document overrides the default language for the text index.
1324
     * The specified language in an embedded document override the language specified in an enclosing document or
1325
     * the default language for the index.
1326
     *
1327
     * @param   array|string    $field              definition of fields where full text index ensured. May be
1328
     *                                              string to ensure index on one field, array of fields  to
1329
     *                                              create full text index on few fields, and * widdcard '$**' to
1330
     *                                              create index on all fields of collection. Default value is '$**'
1331
     *
1332
     * @param   array           $weights            For a text index, the weight of an indexed field denotes the
1333
     *                                              significance of the field relative to the other indexed fields
1334
     *                                              in terms of the text search score.
1335
     *
1336
     * @param   string          $defaultLanguage    Default language associated with the indexed data determines
1337
     *                                              the rules to parse word roots (i.e. stemming) and ignore stop
1338
     *                                              words. The default language for the indexed data is english.
1339
     *
1340
     * @param   string          $languageOverride   To use a field with a name other than language, include the
1341
     *                                              language_override option when creating the index.
1342
     *
1343
     * @return Collection
1344
     */
1345
    public function ensureFulltextIndex(
1346
        $field = '$**',
1347
        array $weights = null,
1348
        $defaultLanguage = Language::ENGLISH,
1349
        $languageOverride = null
1350
    ) {
1351
        // keys
1352
        if (is_array($field)) {
1353
            $keys = array_fill_keys($field, 'text');
1354
        } else {
1355
            $keys = array(
1356
                $field => 'text',
1357
            );
1358
        }
1359
1360
        // options
1361
        $options = array(
1362
            'default_language' => $defaultLanguage,
1363
        );
1364
1365
        if (!empty($weights)) {
1366
            $options['weights'] = $weights;
1367
        }
1368
1369
        if (!empty($languageOverride)) {
1370
            $options['language_override'] = $languageOverride;
1371
        }
1372
1373
        // create index
1374
        $this->getMongoCollection()->createIndex($keys, $options);
1375
1376
        return $this;
1377
    }
1378
1379
1380
1381
    /**
1382
     * Create indexes based on self::$_index metadata
1383
     *
1384
     * @return \Sokil\Mongo\Collection
1385
     * @throws \Exception
1386
     */
1387
    public function initIndexes()
1388
    {
1389
        // read index definition from collection options
1390
        // if not specified - use defined in property
1391
        $indexDefinition = $this->definition->getOption('index');
1392
1393
        // ensure indexes
1394
        foreach ($indexDefinition as $options) {
1395
            if (empty($options['keys'])) {
1396
                throw new Exception('Keys not specified');
1397
            }
1398
1399
            $keys = $options['keys'];
1400
            unset($options['keys']);
1401
1402
            if (is_string($keys)) {
1403
                $keys = array($keys => 1);
1404
            }
1405
1406
            $this->getMongoCollection()->createIndex($keys, $options);
1407
        }
1408
1409
        return $this;
1410
    }
1411
1412
    /**
1413
     * Get index info
1414
     * @return array
1415
     */
1416
    public function getIndexes()
1417
    {
1418
        return $this->getMongoCollection()->getIndexInfo();
1419
    }
1420
1421
    public function readPrimaryOnly()
1422
    {
1423
        $this->getMongoCollection()->setReadPreference(\MongoClient::RP_PRIMARY);
1424
        return $this;
1425
    }
1426
1427
    public function readPrimaryPreferred(array $tags = null)
1428
    {
1429
        $this->getMongoCollection()->setReadPreference(\MongoClient::RP_PRIMARY_PREFERRED, $tags);
1430
        return $this;
1431
    }
1432
1433
    public function readSecondaryOnly(array $tags = null)
1434
    {
1435
        $this->getMongoCollection()->setReadPreference(\MongoClient::RP_SECONDARY, $tags);
1436
        return $this;
1437
    }
1438
1439
    public function readSecondaryPreferred(array $tags = null)
1440
    {
1441
        $this->getMongoCollection()->setReadPreference(\MongoClient::RP_SECONDARY_PREFERRED, $tags);
1442
        return $this;
1443
    }
1444
1445
    public function readNearest(array $tags = null)
1446
    {
1447
        $this->getMongoCollection()->setReadPreference(\MongoClient::RP_NEAREST, $tags);
1448
        return $this;
1449
    }
1450
1451
    public function getReadPreference()
1452
    {
1453
        return $this->getMongoCollection()->getReadPreference();
1454
    }
1455
1456
    /**
1457
     * Define write concern for all requests to current collection
1458
     *
1459
     * @param string|integer $w write concern
1460
     * @param int $timeout timeout in milliseconds
1461
     * @throws \Sokil\Mongo\Exception
1462
     * @return \Sokil\Mongo\Collection
1463
     */
1464
    public function setWriteConcern($w, $timeout = 10000)
1465
    {
1466
        if (!$this->getMongoCollection()->setWriteConcern($w, (int) $timeout)) {
1467
            throw new Exception('Error setting write concern');
1468
        }
1469
1470
        return $this;
1471
    }
1472
1473
    /**
1474
     * Define unacknowledged write concern for all requests to current collection
1475
     *
1476
     * @param int $timeout timeout in milliseconds
1477
     * @throws \Sokil\Mongo\Exception
1478
     * @return \Sokil\Mongo\Collection
1479
     */
1480
    public function setUnacknowledgedWriteConcern($timeout = 10000)
1481
    {
1482
        $this->setWriteConcern(0, (int) $timeout);
1483
        return $this;
1484
    }
1485
1486
    /**
1487
     * Define majority write concern for all requests to current collection
1488
     *
1489
     * @param int $timeout timeout in milliseconds
1490
     * @throws \Sokil\Mongo\Exception
1491
     * @return \Sokil\Mongo\Collection
1492
     */
1493
    public function setMajorityWriteConcern($timeout = 10000)
1494
    {
1495
        $this->setWriteConcern('majority', (int) $timeout);
1496
        return $this;
1497
    }
1498
1499
    /**
1500
     * Get currently active write concern on all requests to collection
1501
     *
1502
     * @return int|string write concern
1503
     */
1504
    public function getWriteConcern()
1505
    {
1506
        return $this->getMongoCollection()->getWriteConcern();
1507
    }
1508
1509
    /**
1510
     * Get collection stat
1511
     *
1512
     * @return array collection stat
1513
     */
1514
    public function stats()
1515
    {
1516
        return $this->getDatabase()->executeCommand(array(
1517
            'collstats' => $this->getName(),
1518
        ));
1519
    }
1520
}
1521