├── .doctrine-project.json ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG-1.0.md ├── CHANGELOG-1.1.md ├── CHANGELOG-1.2.md ├── CHANGELOG-1.3.md ├── CHANGELOG-1.4.md ├── CHANGELOG-1.5.md ├── CHANGELOG-1.6.md ├── LICENSE ├── README.markdown ├── UPGRADE-1.1.md ├── UPGRADE-1.2.md ├── UPGRADE-1.3.md ├── UPGRADE-1.4.md ├── UPGRADE-1.5.md ├── UPGRADE-1.6.md ├── composer.json ├── docs └── en │ └── index.rst ├── lib └── Doctrine │ └── MongoDB │ ├── Aggregation │ ├── Builder.php │ ├── Expr.php │ ├── Stage.php │ └── Stage │ │ ├── AbstractBucket.php │ │ ├── AddFields.php │ │ ├── Bucket.php │ │ ├── Bucket │ │ ├── AbstractOutput.php │ │ ├── BucketAutoOutput.php │ │ └── BucketOutput.php │ │ ├── BucketAuto.php │ │ ├── CollStats.php │ │ ├── Count.php │ │ ├── Facet.php │ │ ├── GeoNear.php │ │ ├── GraphLookup.php │ │ ├── GraphLookup │ │ └── Match.php │ │ ├── Group.php │ │ ├── IndexStats.php │ │ ├── Limit.php │ │ ├── Lookup.php │ │ ├── Match.php │ │ ├── Operator.php │ │ ├── Out.php │ │ ├── Project.php │ │ ├── Redact.php │ │ ├── ReplaceRoot.php │ │ ├── Sample.php │ │ ├── Skip.php │ │ ├── Sort.php │ │ ├── SortByCount.php │ │ └── Unwind.php │ ├── ArrayIterator.php │ ├── Collection.php │ ├── CommandCursor.php │ ├── Configuration.php │ ├── Connection.php │ ├── Cursor.php │ ├── CursorInterface.php │ ├── Database.php │ ├── EagerCursor.php │ ├── Event │ ├── AggregateEventArgs.php │ ├── CreateCollectionEventArgs.php │ ├── DistinctEventArgs.php │ ├── EventArgs.php │ ├── FindEventArgs.php │ ├── GroupEventArgs.php │ ├── MapReduceEventArgs.php │ ├── MutableEventArgs.php │ ├── NearEventArgs.php │ └── UpdateEventArgs.php │ ├── Events.php │ ├── Exception │ └── ResultException.php │ ├── GridFS.php │ ├── GridFSFile.php │ ├── Iterator.php │ ├── IteratorAggregate.php │ ├── Loggable.php │ ├── LoggableCollection.php │ ├── LoggableCursor.php │ ├── LoggableDatabase.php │ ├── LoggableGridFS.php │ ├── Query │ ├── Builder.php │ ├── Expr.php │ └── Query.php │ ├── Traits │ └── LoggableCollectionTrait.php │ └── Util │ └── ReadPreference.php ├── phpunit.xml.dist └── tests └── Doctrine └── MongoDB └── Tests ├── Aggregation ├── AggregationOperatorsProviderTrait.php ├── AggregationTestCase.php ├── BuilderTest.php ├── ExprTest.php ├── FunctionalTest.php └── Stage │ ├── AddFieldsTest.php │ ├── BucketAutoTest.php │ ├── BucketTest.php │ ├── CollStatsTest.php │ ├── CountTest.php │ ├── FacetTest.php │ ├── GeoNearTest.php │ ├── GraphLookupTest.php │ ├── GroupStub.php │ ├── GroupTest.php │ ├── IndexStatsTest.php │ ├── LimitTest.php │ ├── LookupTest.php │ ├── MatchStub.php │ ├── MatchTest.php │ ├── OperatorStub.php │ ├── OperatorTest.php │ ├── OutTest.php │ ├── ProjectTest.php │ ├── RedactTest.php │ ├── ReplaceRootTest.php │ ├── SampleTest.php │ ├── SkipTest.php │ ├── SortByCountTest.php │ ├── SortTest.php │ └── UnwindTest.php ├── ArrayIteratorTest.php ├── CollectionEventsChangingContextTest.php ├── CollectionEventsTest.php ├── CollectionTest.php ├── CommandCursorFunctionalTest.php ├── CommandCursorTest.php ├── ConnectionFunctionalTest.php ├── ConnectionTest.php ├── CursorFunctionalTest.php ├── CursorTest.php ├── DatabaseEventsTest.php ├── DatabaseTest.php ├── DatabaseTestCase.php ├── EagerCursorTest.php ├── Event ├── AggregateEventArgsTest.php ├── CreateCollectionEventArgsTest.php ├── DistinctEventArgsTest.php ├── EventArgsTest.php ├── FindEventArgsTest.php ├── GroupEventArgsTest.php ├── MapReduceEventArgsTest.php ├── MutableEventArgsTest.php ├── NearEventArgsTest.php └── UpdateEventArgsTest.php ├── EventTest.php ├── FunctionalTest.php ├── GridFSFileTest.php ├── GridFSTest.php ├── LoggableCollectionTest.php ├── LoggableCursorTest.php ├── LoggableDatabaseTest.php ├── LoggableGridFSTest.php ├── Query ├── BuilderStub.php ├── BuilderTest.php ├── ExprTest.php ├── FunctionalTest.php └── QueryTest.php ├── RetryTest.php ├── TestCase.php ├── Util └── ReadPreferenceTest.php └── file.txt /.doctrine-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "active": true, 3 | "name": "MongoDB Abstraction Layer", 4 | "shortName": "MongoDB", 5 | "slug": "mongodb", 6 | "docsSlug": "doctrine-mongodb", 7 | "versions": [ 8 | { 9 | "name": "master", 10 | "branchName": "master", 11 | "slug": "latest" 12 | }, 13 | { 14 | "name": "1.6", 15 | "branchName": "1.6.x", 16 | "slug": "1.6", 17 | "current": true, 18 | "maintained": false 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: phpdoctrine 2 | tidelift: packagist/doctrine%2Fmongodb 3 | custom: https://www.doctrine-project.org/sponsorship.html 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | phpunit.xml 2 | composer.lock 3 | vendor/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: php 4 | 5 | services: 6 | - mongodb 7 | 8 | env: 9 | global: 10 | - DRIVER_VERSION="stable" 11 | - ADAPTER_VERSION="^1.0.0" 12 | 13 | matrix: 14 | fast_finish: true 15 | include: 16 | - php: 5.6 17 | env: DRIVER_VERSION="1.6.7" COMPOSER_FLAGS="--prefer-lowest" 18 | addons: 19 | apt: 20 | sources: 21 | - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.0 multiverse" 22 | key_url: "https://www.mongodb.org/static/pgp/server-3.0.asc" 23 | - "mongodb-upstart" 24 | packages: ['mongodb-org-server'] 25 | - php: 5.6 26 | addons: 27 | apt: 28 | sources: 29 | - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" 30 | key_url: "https://www.mongodb.org/static/pgp/server-3.4.asc" 31 | - "mongodb-upstart" 32 | packages: ['mongodb-org-server'] 33 | - php: 7.0 34 | addons: 35 | apt: 36 | sources: 37 | - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" 38 | key_url: "https://www.mongodb.org/static/pgp/server-3.4.asc" 39 | - "mongodb-upstart" 40 | packages: ['mongodb-org-server'] 41 | - php: 7.1 42 | addons: 43 | apt: 44 | sources: 45 | - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" 46 | key_url: "https://www.mongodb.org/static/pgp/server-3.4.asc" 47 | - "mongodb-upstart" 48 | packages: ['mongodb-org-server'] 49 | - php: 7.2 50 | addons: 51 | apt: 52 | sources: 53 | - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" 54 | key_url: "https://www.mongodb.org/static/pgp/server-3.4.asc" 55 | - "mongodb-upstart" 56 | packages: ['mongodb-org-server'] 57 | - php: 7.3 58 | addons: 59 | apt: 60 | sources: 61 | - sourceline: "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" 62 | key_url: "https://www.mongodb.org/static/pgp/server-3.4.asc" 63 | - "mongodb-upstart" 64 | packages: ['mongodb-org-server'] 65 | 66 | before_script: 67 | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "5." ]]; then yes '' | pecl -q install -f mongo-${DRIVER_VERSION}; fi 68 | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then pecl install -f mongodb-${DRIVER_VERSION}; fi 69 | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then composer config "platform.ext-mongo" "1.6.16" && composer require "alcaeus/mongo-php-adapter=${ADAPTER_VERSION}"; fi 70 | - composer update ${COMPOSER_FLAGS} 71 | 72 | script: 73 | - ./vendor/bin/phpunit 74 | -------------------------------------------------------------------------------- /CHANGELOG-1.0.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.0.x 2 | =================== 3 | 4 | This changelog references the relevant changes (bug and security fixes) done 5 | in 1.0.x patch versions. 6 | 7 | To get the diff for a specific change, go to 8 | https://github.com/doctrine/mongodb/commit/XXX where XXX is the commit hash. 9 | To get the diff between two versions, go to 10 | https://github.com/doctrine/mongodb/compare/XXX...YYY where XXX and YYY are 11 | the older and newer versions, respectively. 12 | 13 | To generate a changelog summary since the last version, run 14 | `git log --no-merges --oneline XXX...1.0.x` 15 | 16 | 1.0.11 (2015-02-24) 17 | ------------------- 18 | 19 | * e18ed83 Start tracking composer.lock in git 20 | * 996a4d3 Allow ~2.1 for doctrine/common 21 | 22 | 1.0.10 (2015-01-30) 23 | ------------------- 24 | 25 | * 7cc3ad0 Fix Connection::isConnected() for driver versions 1.5.0+ 26 | * 2a68c2a Allow 1.6.x driver versions in composer.json 27 | * 3d0c361 Do not allow PHP 5.6 test failures 28 | * 95140f4 Removed php.ini modify because mongo extension is already installed in travis box 29 | * e7277dc Removed 1.5.1 version from travis build matrix 30 | * 1a20082 Show mongo extension info in travis log 31 | * 2a51491 Added last stable mongo extension version to travis build matrix 32 | 33 | 1.0.9 (2014-04-29) 34 | ------------------ 35 | 36 | * 74cc098 Respect $options when $server is null in Connection constructor 37 | * f8a382d Add driver 1.4.5 and 1.5.1 to test matrix 38 | * 251ae2b Add PHP 5.6 and no longer allow failures for 5.5 builds 39 | * 30aa713 Collection::ensureIndex() should convert write options (fixes #168) 40 | * f781f2b Update issues/releases links in README 41 | 42 | 1.0.8 (2014-03-28) 43 | ------------------ 44 | 45 | * 8d697ad Fix Query construction in EagerCursor preparation test 46 | * ac1ca70 Use current() in EagerCursor::getSingleResult() 47 | 48 | 1.0.7 (2014-03-28) 49 | ------------------ 50 | 51 | * 91429c0 Revert "Allow string or array Cursor::hint() argument" 52 | * 3bddf57 Fix getMockCursor doc blocks in EagerCursor and Query tests 53 | 54 | 1.0.6 (2014-03-27) 55 | ------------------ 56 | 57 | * acf548c Allow string or array Cursor::hint() argument 58 | * 5b85faf EagerCursor::getSingleResult() should return null for no results 59 | * 204b326 EagerCursor::getSingleResult() should always return first element 60 | * 0253ded Split EagerCursorTest into unit and functional tests 61 | * 4f06d4c Fixed pre and post CreateCollection dispatching 62 | 63 | 1.0.5 (2014-01-09) 64 | ------------------ 65 | 66 | * 4a8822b: Cursor::getSingleResult() should not use keys in toArray() 67 | 68 | 1.0.4 (2013-11-26) 69 | ------------------ 70 | 71 | * fabcf49: Allow 1.5.x driver versions in composer.json 72 | * c7b6ef9: Convert "safe" write option to "w" for drivers 1.3.0+ 73 | * 89c7f44: Check for null $mongo property in Connected::getStatus() 74 | * 9b71337: Support MongoClient in Connection::isConnected() 75 | * 009ea85: Remove executable bit from class files 76 | * f70e2a7: Remove code duplication in LoggableCursor and make docs consistent 77 | * d68f7bd: Make Cursor return values consistent with MongoCursor 78 | * 8bf686d: Clean up Cursor and EagerCursor docs and tests 79 | 80 | 1.0.3 (2013-05-23) 81 | ------------------ 82 | 83 | * 531dc00: Allow 1.4.x driver versions in composer.json 84 | * bcdf464: Test driver 1.3.7 and PHP 5.5 in Travis 85 | * cbd7ad9: Force driver install for Travis, and test against 1.3.4 86 | 87 | 1.0.2 (2013-03-04) 88 | ------------------ 89 | 90 | * ec10f4d: Test MongoCursor read preferences via slaveOkay() 91 | * 1592926: Add missing use statement in Cursor class 92 | * 1432c87: Test latest Mongo driver in Travis CI builds 93 | * 24a1e89: Make ReadPreferenceTest skip message consistent 94 | * 51805f8: Skip test if mongo pecl extension is < 1.3.0 95 | 96 | 1.0.1 (2013-01-10) 97 | ------------------ 98 | 99 | * adf94c7: Only convert tag sets if necessary for setReadPreference() 100 | * a0534a0: Only convert numeric read preference types (driver <=1.3.2) 101 | * b317c8e: Fix bad reference to exception class in ReadPreference 102 | -------------------------------------------------------------------------------- /CHANGELOG-1.2.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.2.x 2 | =================== 3 | 4 | This changelog references the relevant changes (bug and security fixes) done 5 | in 1.2.x patch versions. 6 | 7 | 1.2.2 (2016-03-19) 8 | ------------------ 9 | 10 | * [#250](https://github.com/doctrine/mongodb/pull/250): Fix wrong syntax for dateToString operator 11 | 12 | 1.2.1 (2015-11-24) 13 | ------------------ 14 | 15 | * [#229](https://github.com/doctrine/mongodb/pull/229): Fix EagerCursor::skip() method calling limit method of base cursor instead of skip method 16 | * [#231](https://github.com/doctrine/mongodb/pull/231): Remove count method declaration in CursorInterface to fix fatal error for PHP 5.3 17 | * [#237](https://github.com/doctrine/mongodb/pull/237): Fix bug where timeout is set to 1 ms 18 | 19 | 1.2.0 (2015-08-18) 20 | ------------------ 21 | 22 | * [#171](https://github.com/doctrine/mongodb/pull/171): Implement `$minDistance` query operator and geoNear option 23 | * Adds `minDistance()` method to query builder 24 | * [#183](https://github.com/doctrine/mongodb/pull/183): Rewrite `Collection::update()` "multi" option to "multiple" 25 | * [#184](https://github.com/doctrine/mongodb/pull/184): Query builder support for $text operator in MongoDB 2.6 26 | * Adds `language()`, `text()`, `selectMeta()`, and `sortMeta()` methods to query builder 27 | * [#186](https://github.com/doctrine/mongodb/pull/186): Fixed `Connection::convertWriteTimeout()` docs 28 | * [#192](https://github.com/doctrine/mongodb/pull/192): Support `$meta` expressions in `Cursor::sort()` 29 | * This is typically used for sorting by text search scores 30 | * [#197](https://github.com/doctrine/mongodb/pull/197): Support aggregation command cursors and client options 31 | * Introduces a CommandCursor class, which decorates a MongoCommandCursor 32 | * Command cursors require driver 1.5+ 33 | * Socket timeouts for command cursors require driver 1.6+ 34 | * [#201](https://github.com/doctrine/mongodb/pull/201): Fix issue when an operator follows `equals()` 35 | * [#203](https://github.com/doctrine/mongodb/pull/203): Ensure Query projection option is renamed for findAndModify 36 | * [#205](https://github.com/doctrine/mongodb/pull/205): Don't pass empty arrays to array_combine() 37 | * [#209](https://github.com/doctrine/mongodb/pull/209): Add a common cursor interface 38 | * The Cursor and EagerCursor classes implement this interface 39 | * [#212](https://github.com/doctrine/mongodb/pull/212): Query builder support for new update operators in MongoDB 2.6 40 | * Adds `bitAnd()`, `bitOr()`, `bitXor()`, `currentDate()`, `max()`, `min()`, and `mul()` methods to query builder 41 | * [#213](https://github.com/doctrine/mongodb/pull/213): Add aggregation builder 42 | * The builder may be created with `Collection::createAggregationBuilder()` 43 | * Top-level builder methods correspond to pipeline operators 44 | * [#214](https://github.com/doctrine/mongodb/pull/214): Removes parameter type hint from `Builder::geoWithin()` 45 | * Now accepts an array or `GeoJson\Geometry\Geometry` instance 46 | * [#215](https://github.com/doctrine/mongodb/pull/215): Add test environment preferring lowest package dependencies 47 | * [#222](https://github.com/doctrine/mongodb/pull/222): Add support for `MongoCollection::parallelCollectionScan()` 48 | * [#223](https://github.com/doctrine/mongodb/pull/223): Support `$useKeys` option when EagerCursor converts to an array 49 | * [#224](https://github.com/doctrine/mongodb/pull/224): Support client and socket timeout options in `Collection::count()` 50 | * `Collection::count()` now supports an options array as its second argument (`$limit` integer is still supported) 51 | * Socket timeout (i.e. `socketTimeoutMS`) may be specified alongside command options 52 | * [#225](https://github.com/doctrine/mongodb/pull/225): Include all cursor methods in cursor interface 53 | * Expands the common CursorInterface to support all methods in Cursor and EagerCursor 54 | * Allows ODM to be more agnostic when decorating a Cursor or EagerCursor 55 | * [#226](https://github.com/doctrine/mongodb/pull/226): Implement getter for cursor `useIdentifierKeys` option 56 | -------------------------------------------------------------------------------- /CHANGELOG-1.3.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.3.x 2 | =================== 3 | 4 | This changelog references the relevant changes (bug and security fixes) done 5 | in 1.3.x patch versions. 6 | 7 | 1.3.0 (2016-03-19) 8 | ------------------ 9 | 10 | * [#227](https://github.com/doctrine/mongodb/pull/227): Specify time limit operation on a mongodb cursor 11 | * Adds `maxTimeMS()` method to query builder and cursor 12 | * [#233](https://github.com/doctrine/mongodb/pull/233): Allow Event Listeners the ability to modify context information in the event 13 | * Allows changes to the options for the following events: preAggregate, preBatchInsert, preDistinct, preFind, preFindAndRemove, preFindAndUpdate, preFindOne, preGetDBRef, preGroup, preInsert, preMapReduce, preNear, preRemove, postRemove, preSave, preUpdate, postUpdate 14 | * [#234](https://github.com/doctrine/mongodb/pull/234): Add support for `$comment` operator 15 | * Adds `comment()` method to query builder 16 | * [#235](https://github.com/doctrine/mongodb/pull/235): Add support for `$setOnInsert` operator 17 | * Adds `setOnInsert()` method to query builder 18 | * [#238](https://github.com/doctrine/mongodb/pull/238): Bump PHP and mongo version requirements 19 | * [#240](https://github.com/doctrine/mongodb/pull/240): Add new MongoDB 3.2 features to aggregation builder 20 | * Adds `sample()`, `indexStats()` and `lookup()` methods to aggregation builder 21 | * Adds `avg()`, `max()`, `min()`, `stdDevPop()`, `stdDevSamp()`, `sum()` methods to project stage 22 | * Adds `minDistance()` method to geoNear stage 23 | * Adds `includeArrayIndex()` and `preserveNullAndEmptyArrays()` methods to unwind stage 24 | * [#241](https://github.com/doctrine/mongodb/pull/241): Add query operators introduced with MongoDB 3.2 25 | * Adds `bitsAllClear()`, `bitsAllSet()`, `bitsAnyClear()`, `bitsAnySet()`, `caseSensitive()`, `diacriticSensitive()` methods to query builder 26 | * [#251](https://github.com/doctrine/mongodb/pull/251): Corrected fluent interface docblocks 27 | * [#255](https://github.com/doctrine/mongodb/pull/255): Add expr method to aggregation expression object 28 | * Adds `expr()` method to the aggregation Expr class 29 | * [#256](https://github.com/doctrine/mongodb/pull/256): Allow using operators in group stages 30 | * Adds all methods from the `Operator` class to the group stage 31 | -------------------------------------------------------------------------------- /CHANGELOG-1.4.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.4.x 2 | =================== 3 | 4 | This changelog references the relevant changes (bug and security fixes) done 5 | in 1.4.x patch versions. 6 | 7 | 1.4.0 (2016-11-22) 8 | ------------------ 9 | 10 | * [#244](https://github.com/doctrine/mongodb/pull/244): Use short array syntax 11 | * [#263](https://github.com/doctrine/mongodb/pull/263): Fix wrong parameter in count method 12 | * [#264](https://github.com/doctrine/mongodb/pull/264): Drop support for PHP 5.5 13 | * [#273](https://github.com/doctrine/mongodb/pull/273): Remove unused use statements 14 | * [#274](https://github.com/doctrine/mongodb/pull/274): Loggable GridFS collection 15 | * [#275](https://github.com/doctrine/mongodb/pull/275): Deprecate update() and multiple() methods in query builder 16 | * [#246](https://github.com/doctrine/mongodb/pull/246): Open up aggregation builder API for use in ODM 17 | * [#278](https://github.com/doctrine/mongodb/pull/278): Run travis tests against MongoDB 3.2 and 2.6 18 | * [#276](https://github.com/doctrine/mongodb/pull/276): Support passing driver options to connection class 19 | * [#277](https://github.com/doctrine/mongodb/pull/277): Pass query options when running count command 20 | * [#280](https://github.com/doctrine/mongodb/pull/280): Use argument unpacking insteda of call_user_func 21 | * [#279](https://github.com/doctrine/mongodb/pull/279): Allow passing multiple arguments to add* methods 22 | -------------------------------------------------------------------------------- /CHANGELOG-1.5.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.5.x 2 | =================== 3 | 4 | This changelog references the relevant changes (bug and security fixes) done 5 | in 1.5.x patch versions. 6 | 7 | 1.5.0 (2017-06-30) 8 | ------------------ 9 | 10 | * [#286](https://github.com/doctrine/mongodb/pull/286): Fixed `$sample` stage 11 | * [#288](https://github.com/doctrine/mongodb/pull/288): Add `$addFields` operator to AggregationBuilder 12 | * [#291](https://github.com/doctrine/mongodb/pull/291): Allow installing PHPUnit 6 on PHP 7 13 | * [#292](https://github.com/doctrine/mongodb/pull/292): Add support for aggregation pipeline stages added in MongoDB 3.4 14 | * [#293](https://github.com/doctrine/mongodb/pull/293): Add aggregation operators added in MongoDB 3.4 15 | * [#294](https://github.com/doctrine/mongodb/pull/294): Fix coding style 16 | -------------------------------------------------------------------------------- /CHANGELOG-1.6.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.6.x 2 | =================== 3 | 4 | This changelog references the relevant changes (bug and security fixes) done 5 | in 1.6.x patch versions. 6 | 7 | 1.6.3 (2018-07-20) 8 | ------------------ 9 | 10 | * [#326](https://github.com/doctrine/mongodb/pull/326): Fix wrong element deletion in popFirst and popLast 11 | 12 | 1.6.2 (2018-04-30) 13 | ------------------ 14 | 15 | * [#324](https://github.com/doctrine/mongodb/pull/324): Use primary read preference when reading newly peristed GridFS files 16 | 17 | 1.6.1 (2017-10-09) 18 | ------------------ 19 | 20 | * [#306](https://github.com/doctrine/mongodb/pull/306): Fix wrong stage name for `$bucketAuto` pipeline stage 21 | * [#307](https://github.com/doctrine/mongodb/pull/307): Convert empty match stages to object 22 | 23 | 1.6.0 (2017-08-09) 24 | ------------------ 25 | 26 | * [#298](https://github.com/doctrine/mongodb/pull/298): Fix BC break when converting expression objects 27 | * [#301](https://github.com/doctrine/mongodb/pull/301): Test against PHP 7.2 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2018 Doctrine Project 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Doctrine MongoDB 2 | 3 | This library is no longer maintained. Please use the [Official MongoDB Driver for PHP](https://docs.mongodb.com/drivers/php/) instead. 4 | -------------------------------------------------------------------------------- /UPGRADE-1.2.md: -------------------------------------------------------------------------------- 1 | UPGRADE from 1.1 to 1.2 2 | ======================= 3 | 4 | Pull requests completed for the 1.2.0 release: 5 | 6 | * [#171](https://github.com/doctrine/mongodb/pull/171): Implement `$minDistance` query operator and geoNear option 7 | * Adds `minDistance()` method to query builder 8 | * [#183](https://github.com/doctrine/mongodb/pull/183): Rewrite `Collection::update()` "multi" option to "multiple" 9 | * [#184](https://github.com/doctrine/mongodb/pull/184): Query builder support for $text operator in MongoDB 2.6 10 | * Adds `language()`, `text()`, `selectMeta()`, and `sortMeta()` methods to query builder 11 | * [#186](https://github.com/doctrine/mongodb/pull/186): Fixed `Connection::convertWriteTimeout()` docs 12 | * [#192](https://github.com/doctrine/mongodb/pull/192): Support `$meta` expressions in `Cursor::sort()` 13 | * This is typically used for sorting by text search scores 14 | * [#197](https://github.com/doctrine/mongodb/pull/197): Support aggregation command cursors and client options 15 | * Introduces a CommandCursor class, which decorates a MongoCommandCursor 16 | * Command cursors require driver 1.5+ 17 | * Socket timeouts for command cursors require driver 1.6+ 18 | * [#201](https://github.com/doctrine/mongodb/pull/201): Fix issue when an operator follows `equals()` 19 | * [#203](https://github.com/doctrine/mongodb/pull/203): Ensure Query projection option is renamed for findAndModify 20 | * [#205](https://github.com/doctrine/mongodb/pull/205): Don't pass empty arrays to array_combine() 21 | * [#209](https://github.com/doctrine/mongodb/pull/209): Add a common cursor interface 22 | * The Cursor and EagerCursor classes implement this interface 23 | * [#212](https://github.com/doctrine/mongodb/pull/212): Query builder support for new update operators in MongoDB 2.6 24 | * Adds `bitAnd()`, `bitOr()`, `bitXor()`, `currentDate()`, `max()`, `min()`, and `mul()` methods to query builder 25 | * [#213](https://github.com/doctrine/mongodb/pull/213): Add aggregation builder 26 | * The builder may be created with `Collection::createAggregationBuilder()` 27 | * Top-level builder methods correspond to pipeline operators 28 | * [#214](https://github.com/doctrine/mongodb/pull/214): Removes parameter type hint from `Builder::geoWithin()` 29 | * Now accepts an array or `GeoJson\Geometry\Geometry` instance 30 | * [#215](https://github.com/doctrine/mongodb/pull/215): Add test environment preferring lowest package dependencies 31 | * [#222](https://github.com/doctrine/mongodb/pull/222): Add support for `MongoCollection::parallelCollectionScan()` 32 | * [#223](https://github.com/doctrine/mongodb/pull/223): Support `$useKeys` option when EagerCursor converts to an array 33 | * [#224](https://github.com/doctrine/mongodb/pull/224): Support client and socket timeout options in `Collection::count()` 34 | * `Collection::count()` now supports an options array as its second argument (`$limit` integer is still supported) 35 | * Socket timeout (i.e. `socketTimeoutMS`) may be specified alongside command options 36 | * [#225](https://github.com/doctrine/mongodb/pull/225): Include all cursor methods in cursor interface 37 | * Expands the common CursorInterface to support all methods in Cursor and EagerCursor 38 | * Allows ODM to be more agnostic when decorating a Cursor or EagerCursor 39 | * [#226](https://github.com/doctrine/mongodb/pull/226): Implement getter for cursor `useIdentifierKeys` option 40 | -------------------------------------------------------------------------------- /UPGRADE-1.3.md: -------------------------------------------------------------------------------- 1 | UPGRADE from 1.2 to 1.3 2 | ======================= 3 | 4 | Pull requests completed for the 1.3.0 release: 5 | 6 | * [#227](https://github.com/doctrine/mongodb/pull/227): Specify time limit operation on a mongodb cursor 7 | * Adds `maxTimeMS()` method to query builder and cursor 8 | * [#233](https://github.com/doctrine/mongodb/pull/233): Allow Event Listeners the ability to modify context information in the event 9 | * Allows changes to the options for the following events: preAggregate, preBatchInsert, preDistinct, preFind, preFindAndRemove, preFindAndUpdate, preFindOne, preGetDBRef, preGroup, preInsert, preMapReduce, preNear, preRemove, postRemove, preSave, preUpdate, postUpdate 10 | * [#234](https://github.com/doctrine/mongodb/pull/234): Add support for `$comment` operator 11 | * Adds `comment()` method to query builder 12 | * [#235](https://github.com/doctrine/mongodb/pull/235): Add support for `$setOnInsert` operator 13 | * Adds `setOnInsert()` method to query builder 14 | * [#238](https://github.com/doctrine/mongodb/pull/238): Bump PHP and mongo version requirements 15 | * [#240](https://github.com/doctrine/mongodb/pull/240): Add new MongoDB 3.2 features to aggregation builder 16 | * Adds `sample()`, `indexStats()` and `lookup()` methods to aggregation builder 17 | * Adds `avg()`, `max()`, `min()`, `stdDevPop()`, `stdDevSamp()`, `sum()` methods to project stage 18 | * Adds `minDistance()` method to geoNear stage 19 | * Adds `includeArrayIndex()` and `preserveNullAndEmptyArrays()` methods to unwind stage 20 | * [#241](https://github.com/doctrine/mongodb/pull/241): Add query operators introduced with MongoDB 3.2 21 | * Adds `bitsAllClear()`, `bitsAllSet()`, `bitsAnyClear()`, `bitsAnySet()`, `caseSensitive()`, `diacriticSensitive()` methods to query builder 22 | * [#251](https://github.com/doctrine/mongodb/pull/251): Corrected fluent interface docblocks 23 | * [#255](https://github.com/doctrine/mongodb/pull/255): Add expr method to aggregation expression object 24 | * Adds `expr()` method to the aggregation Expr class 25 | * [#256](https://github.com/doctrine/mongodb/pull/256): Allow using operators in group stages 26 | * Adds all methods from the `Operator` class to the group stage 27 | -------------------------------------------------------------------------------- /UPGRADE-1.4.md: -------------------------------------------------------------------------------- 1 | Upgrade from 1.3 to 1.4 2 | ======================= 3 | 4 | PHP version support 5 | ------------------- 6 | 7 | * Support for PHP 5.5 has been dropped as it has reached its end of life. 8 | * PHP 7 and PHP 7.1 are supported using [mongo-php-adapter](https://github.com/alcaeus/mongo-php-adapter). 9 | 10 | Query builder 11 | ------------- 12 | 13 | * The `update()` and `multiple()` methods have been deprecated. Use `updateOne` 14 | or `updateMany` instead. 15 | * The `addAnd()`, `addNor()` and `addOr()` methods now accept multiple parameters. 16 | Before: 17 | ```php 18 | $builder 19 | ->addAnd($someExpression) 20 | ->addAnd($otherExpression); 21 | ``` 22 | 23 | After: 24 | ```php 25 | $builder->addAnd($someExpression, $otherExpression); 26 | ``` 27 | 28 | Aggregation builder 29 | ------------------- 30 | 31 | * The `addAnd()` and `addOr()` methods now accept multiple parameters. 32 | 33 | Connection 34 | ---------- 35 | 36 | * Passing driver options to the connection class is now supported. You can pass 37 | a stream context as shown in the PHP documentation: 38 | ```php 39 | $context = stream_context_create([ 40 | 'mongodb' => [ 41 | 'log_cmd_insert' => function () { 42 | // Logic goes here... 43 | } 44 | ], 45 | 'ssl' => [ 46 | 'allow_self_signed' => false, 47 | ] 48 | ]); 49 | $connection = new \Doctrine\MongoDB\Connection(null, [], null, null, ['context' => $context]); 50 | -------------------------------------------------------------------------------- /UPGRADE-1.5.md: -------------------------------------------------------------------------------- 1 | Upgrade from 1.4 to 1.5 2 | ======================= 3 | 4 | Aggregation builder 5 | ------------------- 6 | 7 | * Add support for aggregation pipeline stages added in MongoDB 3.4: 8 | * `$addFields` 9 | * `$bucket` 10 | * `$bucketAuto` 11 | * `$collStats` 12 | * `$count` 13 | * `$facet` 14 | * `$graphLookup` 15 | * `$replaceRoot` 16 | * `$sortByCount` 17 | * Add support for aggregation expression operators added in MongoDB 3.4: 18 | * `$in` 19 | * `$indexOfArray` 20 | * `$range` 21 | * `$reverseArray` 22 | * `$reduce` 23 | * `$zip` 24 | * `$indexOfBytes` 25 | * `$indexOfCP` 26 | * `$split` 27 | * `$strLenBytes` 28 | * `$strLenCP` 29 | * `$substrBytes` 30 | * `$substrCP` 31 | * `$switch` 32 | * `$isoDayOfWeek` 33 | * `$isoWeek` 34 | * `$isoWeekYear` 35 | * `$type` 36 | * The `$project` stage now supports field exclusion via the new `excludeFields` 37 | method. 38 | 39 | Deprecations 40 | ------------ 41 | * The `excludeIdField` method in the `$project` aggregation pipeline stage has 42 | been deprecated in favor of the new `excludeFields` method. 43 | * The protected methods relating to the `$switch` aggregation expression 44 | operator in `Doctrine\MongoDB\Aggregation\Expr`are deprecated. They will be 45 | renamed as follows when the aggregation builder is moved to Doctrine MongoDB 46 | ODM: 47 | * `caseInternal` => `case` 48 | * `defaultInternal` => `default` 49 | * `switchInternal` => `switch` 50 | * `thenInternal` => `then` 51 | * The magic `__call` method in `Doctrine\MongoDB\Aggregation\Expr` will also be 52 | removed. 53 | -------------------------------------------------------------------------------- /UPGRADE-1.6.md: -------------------------------------------------------------------------------- 1 | Upgrade from 1.5 to 1.6 2 | ======================= 3 | 4 | Aggregation builder 5 | ------------------- 6 | 7 | * Fixes a BC break when converting expression objects in the 8 | `Doctrine\MongoDB\Aggregation\Expr` class. 9 | * Adds extension points to customize expression conversion in the following stages: 10 | * `$bucket` 11 | * `$bucketAuto` 12 | * `$graphLookup` 13 | * `$replaceRoot` 14 | 15 | Version support 16 | --------------- 17 | * Support for MongoDB server versions below 3.0 has been dropped. These are no 18 | longer supported by MongoDB. We recommend you upgrade to a recent version 19 | * The 1.5 version of the legacy driver is no longer supported. This library now 20 | requires at least version 1.6.7 of the legacy driver. 21 | * PHP 7.2 is also supported using [mongo-php-adapter](https://github.com/alcaeus/mongo-php-adapter) 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doctrine/mongodb", 3 | "type": "library", 4 | "description": "PHP Doctrine MongoDB project is a library that provides a wrapper around the native PHP Mongo PECL extension to provide additional functionality.", 5 | "keywords": [ 6 | "database", 7 | "persistence", 8 | "php", 9 | "mongodb", 10 | "abstraction" 11 | ], 12 | "homepage": "https://www.doctrine-project.org/projects/mongodb.html", 13 | "license": "MIT", 14 | "authors": [ 15 | { "name": "Bulat Shakirzyanov", "email": "mallluhuct@gmail.com" }, 16 | { "name": "Kris Wallsmith", "email": "kris.wallsmith@gmail.com" }, 17 | { "name": "Jonathan H. Wage", "email": "jonwage@gmail.com" }, 18 | { "name": "Jeremy Mikola", "email": "jmikola@gmail.com" }, 19 | { "name": "Maciej Malarz", "email": "malarzm@gmail.com" }, 20 | { "name": "Andreas Braun", "email": "alcaeus@alcaeus.org" } 21 | ], 22 | "require": { 23 | "php": "^5.6 || ^7.0", 24 | "ext-mongo": "^1.6.7", 25 | "doctrine/common": "^2.2" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^5.7 || ^6.0", 29 | "jmikola/geojson": "^1.0" 30 | }, 31 | "suggest": { 32 | "jmikola/geojson": "Support GeoJSON geometry objects in 2dsphere queries", 33 | "alcaeus/mongo-php-adapter": "Allows usage of PHP 7" 34 | }, 35 | "autoload": { 36 | "psr-0": { "Doctrine\\MongoDB": "lib/" } 37 | }, 38 | "autoload-dev": { 39 | "psr-0": { "Doctrine\\MongoDB\\Tests": "tests/" } 40 | }, 41 | "extra": { 42 | "branch-alias": { 43 | "dev-master": "1.6.x-dev" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/en/index.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | The Doctrine MongoDB project is an abstraction layer on top of the legacy PHP driver that the Doctrine MongoDB ODM project is built on top of. 5 | 6 | .. caution:: 7 | 8 | This project has been deprecated and the MongoDB ODM project will soon no longer depend on it. The project is also in bug-fixes-only mode. 9 | 10 | Connecting 11 | ---------- 12 | 13 | Creating new connections is easy using the ``Doctrine\MongoDB\Connection`` class: 14 | 15 | .. code-block:: php 16 | use Doctrine\MongoDB\Connection; 17 | 18 | $connection = new Connection('mongodb://localhost'); 19 | 20 | Databases 21 | --------- 22 | 23 | With the connection you can start selecting databases using the ``selectDatabase`` method: 24 | 25 | .. code-block:: php 26 | $database = $connection->selectDatabase('my_project_database'); 27 | 28 | Collections 29 | ----------- 30 | 31 | Now you are ready to select a collection and insert some data using the ``insert`` method: 32 | 33 | .. code-block:: php 34 | $users = $database->selectCollection('users'); 35 | 36 | $user = [ 37 | 'username' => 'jwage', 38 | ]; 39 | 40 | $users->insert($user); 41 | 42 | Reading 43 | ------- 44 | 45 | Reading data is easy using the ``find`` and ``findOne`` methods: 46 | 47 | .. code-block:: php 48 | $user = $users->findOne(['username' => 'jwage']); 49 | 50 | Updating 51 | -------- 52 | 53 | Updating a record is simple using the ``update`` method: 54 | 55 | .. code-block:: php 56 | $users->update(['username' => 'jwage'], ['$set' => ['isActive' => true]]); 57 | 58 | Deleting 59 | ------- 60 | 61 | Delete data from the collection using the ``remove`` method: 62 | 63 | .. code-block:: php 64 | $collection->remove(['username' => 'jwage']); 65 | 66 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/AbstractBucket.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 1.5 14 | */ 15 | abstract class AbstractBucket extends Stage 16 | { 17 | /** 18 | * @var Bucket\BucketOutput|null 19 | */ 20 | protected $output; 21 | 22 | /** 23 | * @var Expr 24 | */ 25 | protected $groupBy; 26 | 27 | /** 28 | * An expression to group documents by. To specify a field path, prefix the 29 | * field name with a dollar sign $ and enclose it in quotes. 30 | * 31 | * @param array|Expr $expression 32 | * @return $this 33 | */ 34 | public function groupBy($expression) 35 | { 36 | $this->groupBy = $expression; 37 | 38 | return $this; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function getExpression() 45 | { 46 | $stage = [ 47 | $this->getStageName() => [ 48 | 'groupBy' => $this->convertExpression($this->groupBy), 49 | ] + $this->getExtraPipelineFields(), 50 | ]; 51 | 52 | if ($this->output !== null) { 53 | $stage[$this->getStageName()]['output'] = $this->output->getExpression(); 54 | } 55 | 56 | return $stage; 57 | } 58 | 59 | /** 60 | * Converts an expression object into an array, recursing into nested items 61 | * 62 | * This method is meant to be overwritten by extending classes to apply 63 | * custom conversions (e.g. field name translation in MongoDB ODM) to the 64 | * expression object. 65 | * 66 | * @param mixed|self $expression 67 | * @return string|array 68 | */ 69 | protected function convertExpression($expression) 70 | { 71 | return Expr::convertExpression($expression); 72 | } 73 | 74 | /** 75 | * @return array 76 | */ 77 | abstract protected function getExtraPipelineFields(); 78 | 79 | /** 80 | * Returns the stage name with the dollar prefix 81 | * 82 | * @return string 83 | */ 84 | abstract protected function getStageName(); 85 | } 86 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/AddFields.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class AddFields extends Operator 11 | { 12 | /** 13 | * {@inheritdoc} 14 | */ 15 | public function getExpression() 16 | { 17 | return [ 18 | '$addFields' => $this->expr->getExpression() 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Bucket.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class Bucket extends AbstractBucket 15 | { 16 | /** 17 | * @var array 18 | */ 19 | private $boundaries; 20 | 21 | /** 22 | * @var mixed 23 | */ 24 | private $default; 25 | 26 | /** 27 | * An array of values based on the groupBy expression that specify the 28 | * boundaries for each bucket. 29 | * 30 | * Each adjacent pair of values acts as the inclusive lower boundary and the 31 | * exclusive upper boundary for the bucket. You must specify at least two 32 | * boundaries. The specified values must be in ascending order and all of 33 | * the same type. The exception is if the values are of mixed numeric types. 34 | * 35 | * @param array ...$boundaries 36 | * 37 | * @return $this 38 | */ 39 | public function boundaries(...$boundaries) 40 | { 41 | $this->boundaries = $boundaries; 42 | return $this; 43 | } 44 | 45 | /** 46 | * A literal that specifies the _id of an additional bucket that contains 47 | * all documents whose groupBy expression result does not fall into a bucket 48 | * specified by boundaries. 49 | * 50 | * @param mixed $default 51 | * 52 | * @return $this 53 | */ 54 | public function defaultBucket($default) 55 | { 56 | $this->default = $default; 57 | return $this; 58 | } 59 | 60 | /** 61 | * A document that specifies the fields to include in the output documents 62 | * in addition to the _id field. To specify the field to include, you must 63 | * use accumulator expressions. 64 | * 65 | * @return Bucket\BucketOutput 66 | */ 67 | public function output() 68 | { 69 | if (! $this->output) { 70 | $this->output = new Stage\Bucket\BucketOutput($this->builder, $this); 71 | } 72 | 73 | return $this->output; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | protected function getExtraPipelineFields() 80 | { 81 | $fields = ['boundaries' => $this->boundaries]; 82 | if ($this->default !== null) { 83 | $fields['default'] = $this->default; 84 | } 85 | 86 | return $fields; 87 | } 88 | 89 | protected function getStageName() 90 | { 91 | return '$bucket'; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Bucket/BucketAutoOutput.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class BucketAutoOutput extends AbstractOutput 15 | { 16 | /** 17 | * @param Builder $builder 18 | * @param Stage\BucketAuto $bucket 19 | */ 20 | public function __construct(Builder $builder, Stage\BucketAuto $bucket) 21 | { 22 | parent::__construct($builder, $bucket); 23 | } 24 | 25 | /** 26 | * An expression to group documents by. To specify a field path, prefix the 27 | * field name with a dollar sign $ and enclose it in quotes. 28 | * 29 | * @return Stage\BucketAuto 30 | */ 31 | public function groupBy($expression) 32 | { 33 | return $this->bucket->groupBy($expression); 34 | } 35 | 36 | /** 37 | * A positive 32-bit integer that specifies the number of buckets into which input documents are grouped. 38 | * 39 | * @param int $buckets 40 | * 41 | * @return Stage\BucketAuto 42 | */ 43 | public function buckets($buckets) 44 | { 45 | return $this->bucket->buckets($buckets); 46 | } 47 | 48 | /** 49 | * A string that specifies the preferred number series to use to ensure that 50 | * the calculated boundary edges end on preferred round numbers or their 51 | * powers of 10. 52 | * 53 | * @param string $granularity 54 | * 55 | * @return Stage\BucketAuto 56 | */ 57 | public function granularity($granularity) 58 | { 59 | return $this->bucket->granularity($granularity); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Bucket/BucketOutput.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 1.5 14 | */ 15 | class BucketOutput extends AbstractOutput 16 | { 17 | /** 18 | * @param Builder $builder 19 | * @param Stage\Bucket $bucket 20 | */ 21 | public function __construct(Builder $builder, Stage\Bucket $bucket) 22 | { 23 | parent::__construct($builder, $bucket); 24 | } 25 | 26 | /** 27 | * An expression to group documents by. To specify a field path, prefix the 28 | * field name with a dollar sign $ and enclose it in quotes. 29 | * 30 | * @param mixed|Expr $expression 31 | * @return Stage\Bucket 32 | */ 33 | public function groupBy($expression) 34 | { 35 | return $this->bucket->groupBy($expression); 36 | } 37 | 38 | /** 39 | * An array of values based on the groupBy expression that specify the 40 | * boundaries for each bucket. 41 | * 42 | * Each adjacent pair of values acts as the inclusive lower boundary and the 43 | * exclusive upper boundary for the bucket. You must specify at least two 44 | * boundaries. The specified values must be in ascending order and all of 45 | * the same type. The exception is if the values are of mixed numeric types. 46 | * 47 | * @param array ...$boundaries 48 | * 49 | * @return Stage\Bucket 50 | */ 51 | public function boundaries(...$boundaries) 52 | { 53 | return $this->bucket->boundaries(...$boundaries); 54 | } 55 | 56 | /** 57 | * A literal that specifies the _id of an additional bucket that contains 58 | * all documents whose groupBy expression result does not fall into a bucket 59 | * specified by boundaries. 60 | * 61 | * @param mixed $default 62 | * 63 | * @return Stage\Bucket 64 | */ 65 | public function defaultBucket($default) 66 | { 67 | return $this->bucket->defaultBucket($default); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/BucketAuto.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class BucketAuto extends AbstractBucket 15 | { 16 | /** 17 | * @var int 18 | */ 19 | private $buckets; 20 | 21 | /** 22 | * @var string 23 | */ 24 | private $granularity; 25 | 26 | /** 27 | * A positive 32-bit integer that specifies the number of buckets into which 28 | * input documents are grouped. 29 | * 30 | * @param int $buckets 31 | * 32 | * @return $this 33 | */ 34 | public function buckets($buckets) 35 | { 36 | $this->buckets = $buckets; 37 | return $this; 38 | } 39 | 40 | /** 41 | * A string that specifies the preferred number series to use to ensure that 42 | * the calculated boundary edges end on preferred round numbers or their 43 | * powers of 10. 44 | * 45 | * @param string $granularity 46 | * 47 | * @return $this 48 | */ 49 | public function granularity($granularity) 50 | { 51 | $this->granularity = $granularity; 52 | return $this; 53 | } 54 | 55 | /** 56 | * A document that specifies the fields to include in the output documents 57 | * in addition to the _id field. To specify the field to include, you must 58 | * use accumulator expressions. 59 | * 60 | * @return Bucket\BucketAutoOutput 61 | */ 62 | public function output() 63 | { 64 | if (! $this->output) { 65 | $this->output = new Stage\Bucket\BucketAutoOutput($this->builder, $this); 66 | } 67 | 68 | return $this->output; 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | */ 74 | protected function getExtraPipelineFields() 75 | { 76 | $fields = ['buckets' => $this->buckets]; 77 | if ($this->granularity !== null) { 78 | $fields['granularity'] = $this->granularity; 79 | } 80 | 81 | return $fields; 82 | } 83 | 84 | protected function getStageName() 85 | { 86 | return '$bucketAuto'; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/CollStats.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class CollStats extends Stage 15 | { 16 | const LATENCY_STATS_NONE = 0; 17 | const LATENCY_STATS_SIMPLE = 1; 18 | const LATENCY_STATS_HISTOGRAMS = 2; 19 | 20 | /** 21 | * @var int 22 | */ 23 | private $latencyStats = self::LATENCY_STATS_NONE; 24 | 25 | /** 26 | * @var bool 27 | */ 28 | private $storageStats = false; 29 | 30 | /** 31 | * @param Builder $builder 32 | */ 33 | public function __construct(Builder $builder) 34 | { 35 | parent::__construct($builder); 36 | } 37 | 38 | /** 39 | * Adds latency statistics to the return document. 40 | * 41 | * @param bool $histograms Adds latency histogram information to latencyStats if true. 42 | * 43 | * @return $this 44 | */ 45 | public function showLatencyStats($histograms = false) 46 | { 47 | $this->latencyStats = $histograms ? self::LATENCY_STATS_HISTOGRAMS : self::LATENCY_STATS_SIMPLE; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Adds storage statistics to the return document. 54 | * 55 | * @return $this 56 | */ 57 | public function showStorageStats() 58 | { 59 | $this->storageStats = true; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function getExpression() 68 | { 69 | $collStats = []; 70 | if ($this->latencyStats !== self::LATENCY_STATS_NONE) { 71 | $collStats['latencyStats'] = ['histograms' => $this->latencyStats === self::LATENCY_STATS_HISTOGRAMS]; 72 | } 73 | 74 | if ($this->storageStats) { 75 | $collStats['storageStats'] = []; 76 | } 77 | 78 | return ['$collStats' => $collStats]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Count.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class Count extends Stage 15 | { 16 | /** 17 | * @var string 18 | */ 19 | private $fieldName; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param string $fieldName 24 | */ 25 | public function __construct(Builder $builder, $fieldName) 26 | { 27 | parent::__construct($builder); 28 | 29 | $this->fieldName = $fieldName; 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function getExpression() 36 | { 37 | return [ 38 | '$count' => $this->fieldName 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Facet.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class Facet extends Stage 15 | { 16 | /** 17 | * @var Builder[] 18 | */ 19 | private $pipelines = []; 20 | 21 | /** 22 | * @var string 23 | */ 24 | private $field; 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function getExpression() 30 | { 31 | return [ 32 | '$facet' => array_map(function (Builder $builder) { return $builder->getPipeline(); }, $this->pipelines), 33 | ]; 34 | } 35 | 36 | /** 37 | * Set the current field for building the pipeline stage. 38 | * 39 | * @param string $field 40 | * 41 | * @return $this 42 | */ 43 | public function field($field) 44 | { 45 | $this->field = $field; 46 | return $this; 47 | } 48 | 49 | /** 50 | * Use the given pipeline for the current field. 51 | * 52 | * @param Builder|Stage $builder 53 | * @return $this 54 | */ 55 | public function pipeline($builder) 56 | { 57 | if (! $this->field) { 58 | throw new \LogicException(__METHOD__ . ' requires you set a current field using field().'); 59 | } 60 | 61 | if ($builder instanceof Stage) { 62 | $builder = $builder->builder; 63 | } 64 | 65 | if (! $builder instanceof Builder) { 66 | throw new \InvalidArgumentException(__METHOD__ . ' expects either an aggregation builder or an aggregation stage.'); 67 | } 68 | 69 | $this->pipelines[$this->field] = $builder; 70 | return $this; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/GraphLookup/Match.php: -------------------------------------------------------------------------------- 1 | graphLookup = $graphLookup; 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function getExpression() 32 | { 33 | return $this->query->getQuery() ?: (object) []; 34 | } 35 | 36 | /** 37 | * Target collection for the $graphLookup operation to search, recursively 38 | * matching the connectFromField to the connectToField. 39 | * 40 | * The from collection cannot be sharded and must be in the same database as 41 | * any other collections used in the operation. 42 | * 43 | * @param string $from 44 | * 45 | * @return GraphLookup 46 | */ 47 | public function from($from) 48 | { 49 | return $this->graphLookup->from($from); 50 | } 51 | 52 | /** 53 | * Expression that specifies the value of the connectFromField with which to 54 | * start the recursive search. 55 | * 56 | * Optionally, startWith may be array of values, each of which is 57 | * individually followed through the traversal process. 58 | * 59 | * @param string|array|Expr $expression 60 | * 61 | * @return GraphLookup 62 | */ 63 | public function startWith($expression) 64 | { 65 | return $this->graphLookup->startWith($expression); 66 | } 67 | 68 | /** 69 | * Field name whose value $graphLookup uses to recursively match against the 70 | * connectToField of other documents in the collection. 71 | * 72 | * Optionally, connectFromField may be an array of field names, each of 73 | * which is individually followed through the traversal process. 74 | * 75 | * @param string $connectFromField 76 | * 77 | * @return GraphLookup 78 | */ 79 | public function connectFromField($connectFromField) 80 | { 81 | return $this->graphLookup->connectFromField($connectFromField); 82 | } 83 | 84 | /** 85 | * Field name in other documents against which to match the value of the 86 | * field specified by the connectFromField parameter. 87 | * 88 | * @param string $connectToField 89 | * 90 | * @return GraphLookup 91 | */ 92 | public function connectToField($connectToField) 93 | { 94 | return $this->graphLookup->connectToField($connectToField); 95 | } 96 | 97 | /** 98 | * Name of the array field added to each output document. 99 | * 100 | * Contains the documents traversed in the $graphLookup stage to reach the 101 | * document. 102 | * 103 | * @param string $alias 104 | * 105 | * @return GraphLookup 106 | */ 107 | public function alias($alias) 108 | { 109 | return $this->graphLookup->alias($alias); 110 | } 111 | 112 | /** 113 | * Non-negative integral number specifying the maximum recursion depth. 114 | * 115 | * @param int $maxDepth 116 | * 117 | * @return GraphLookup 118 | */ 119 | public function maxDepth($maxDepth) 120 | { 121 | return $this->graphLookup->maxDepth($maxDepth); 122 | } 123 | 124 | /** 125 | * Name of the field to add to each traversed document in the search path. 126 | * 127 | * The value of this field is the recursion depth for the document, 128 | * represented as a NumberLong. Recursion depth value starts at zero, so the 129 | * first lookup corresponds to zero depth. 130 | * 131 | * @param string $depthField 132 | * 133 | * @return GraphLookup 134 | */ 135 | public function depthField($depthField) 136 | { 137 | return $this->graphLookup->depthField($depthField); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/IndexStats.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 1.3 12 | */ 13 | class IndexStats extends Stage 14 | { 15 | /** 16 | * {@inheritdoc} 17 | */ 18 | public function getExpression() 19 | { 20 | return [ 21 | '$indexStats' => new \stdClass() 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Limit.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.2 13 | */ 14 | class Limit extends Stage 15 | { 16 | /** 17 | * @var integer 18 | */ 19 | private $limit; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param integer $limit 24 | */ 25 | public function __construct(Builder $builder, $limit) 26 | { 27 | parent::__construct($builder); 28 | 29 | $this->limit = (integer) $limit; 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function getExpression() 36 | { 37 | return [ 38 | '$limit' => $this->limit 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Lookup.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.3 13 | */ 14 | class Lookup extends Stage 15 | { 16 | /** 17 | * @var string 18 | */ 19 | private $from; 20 | 21 | /** 22 | * @var string 23 | */ 24 | private $localField; 25 | 26 | /** 27 | * @var string 28 | */ 29 | private $foreignField; 30 | 31 | /** 32 | * @var string 33 | */ 34 | private $as; 35 | 36 | /** 37 | * Lookup constructor. 38 | * 39 | * @param Builder $builder 40 | * @param string $from Specifies the collection in the same database to 41 | * perform the join with. 42 | */ 43 | public function __construct(Builder $builder, $from) 44 | { 45 | parent::__construct($builder); 46 | 47 | $this->from($from); 48 | } 49 | 50 | /** 51 | * Specifies the collection in the same database to perform the join with. 52 | * 53 | * The from collection cannot be sharded. 54 | * 55 | * @param string $from 56 | * 57 | * @return $this 58 | */ 59 | public function from($from) 60 | { 61 | $this->from = $from; 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * Specifies the field from the documents input to the $lookup stage. 68 | * 69 | * $lookup performs an equality match on the localField to the foreignField 70 | * from the documents of the from collection. If an input document does not 71 | * contain the localField, the $lookup treats the field as having a value of 72 | * null for matching purposes. 73 | * 74 | * @param string $localField 75 | * 76 | * @return $this 77 | */ 78 | public function localField($localField) 79 | { 80 | $this->localField = $localField; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * Specifies the field from the documents in the from collection. 87 | * 88 | * $lookup performs an equality match on the foreignField to the localField 89 | * from the input documents. If a document in the from collection does not 90 | * contain the foreignField, the $lookup treats the value as null for 91 | * matching purposes. 92 | * 93 | * @param string $foreignField 94 | * 95 | * @return $this 96 | */ 97 | public function foreignField($foreignField) 98 | { 99 | $this->foreignField = $foreignField; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * Specifies the name of the new array field to add to the input documents. 106 | * 107 | * The new array field contains the matching documents from the from 108 | * collection. If the specified name already exists in the input document, 109 | * the existing field is overwritten. 110 | * 111 | * @param string $alias 112 | * 113 | * @return $this 114 | */ 115 | public function alias($alias) 116 | { 117 | $this->as = $alias; 118 | 119 | return $this; 120 | } 121 | 122 | /** 123 | * {@inheritdoc} 124 | */ 125 | public function getExpression() 126 | { 127 | return [ 128 | '$lookup' => [ 129 | 'from' => $this->from, 130 | 'localField' => $this->localField, 131 | 'foreignField' => $this->foreignField, 132 | 'as' => $this->as, 133 | ] 134 | ]; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Out.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.2 13 | */ 14 | class Out extends Stage 15 | { 16 | /** 17 | * @var string 18 | */ 19 | private $collection; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param string $collection 24 | */ 25 | public function __construct(Builder $builder, $collection) 26 | { 27 | parent::__construct($builder); 28 | 29 | $this->out($collection); 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function getExpression() 36 | { 37 | return [ 38 | '$out' => $this->collection 39 | ]; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function out($collection) 46 | { 47 | $this->collection = (string) $collection; 48 | 49 | return $this; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Redact.php: -------------------------------------------------------------------------------- 1 | 9 | * @since 1.2 10 | */ 11 | class Redact extends Operator 12 | { 13 | /** 14 | * {@inheritdoc} 15 | */ 16 | public function getExpression() 17 | { 18 | return [ 19 | '$redact' => $this->expr->getExpression() 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/ReplaceRoot.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class ReplaceRoot extends Operator 15 | { 16 | /** 17 | * @var string|null 18 | */ 19 | private $expression; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param string|null $expression Optional. A replacement expression that 24 | * resolves to a document. 25 | */ 26 | public function __construct(Builder $builder, $expression = null) 27 | { 28 | parent::__construct($builder); 29 | 30 | $this->expression = $expression; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function getExpression() 37 | { 38 | return [ 39 | '$replaceRoot' => $this->expression !== null ? $this->convertExpression($this->expression) : $this->expr->getExpression() 40 | ]; 41 | } 42 | 43 | /** 44 | * Converts an expression object into an array, recursing into nested items 45 | * 46 | * This method is meant to be overwritten by extending classes to apply 47 | * custom conversions (e.g. field name translation in MongoDB ODM) to the 48 | * expression object. 49 | * 50 | * @param mixed|self $expression 51 | * @return string|array 52 | */ 53 | protected function convertExpression($expression) 54 | { 55 | return Expr::convertExpression($expression); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Sample.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.3 13 | */ 14 | class Sample extends Stage 15 | { 16 | /** 17 | * @var integer 18 | */ 19 | private $size; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param integer $size 24 | */ 25 | public function __construct(Builder $builder, $size) 26 | { 27 | parent::__construct($builder); 28 | 29 | $this->size = (integer) $size; 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function getExpression() 36 | { 37 | return [ 38 | '$sample' => ['size' => $this->size] 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Skip.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.2 13 | */ 14 | class Skip extends Stage 15 | { 16 | /** 17 | * @var integer 18 | */ 19 | private $skip; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param integer $skip 24 | */ 25 | public function __construct(Builder $builder, $skip) 26 | { 27 | parent::__construct($builder); 28 | 29 | $this->skip = (integer) $skip; 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function getExpression() 36 | { 37 | return [ 38 | '$skip' => $this->skip 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Sort.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.2 13 | */ 14 | class Sort extends Stage 15 | { 16 | /** 17 | * @var array 18 | */ 19 | private $sort = []; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param array|string $fieldName Field name or array of field/order pairs 24 | * @param int|string $order Field order (if one field is specified) 25 | */ 26 | public function __construct(Builder $builder, $fieldName, $order = null) 27 | { 28 | parent::__construct($builder); 29 | 30 | $allowedMetaSort = ['textScore']; 31 | 32 | $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order]; 33 | 34 | foreach ($fields as $fieldName => $order) { 35 | if (is_string($order)) { 36 | if (in_array($order, $allowedMetaSort)) { 37 | $order = ['$meta' => $order]; 38 | } else { 39 | $order = strtolower($order) === 'asc' ? 1 : -1; 40 | } 41 | } 42 | 43 | $this->sort[$fieldName] = $order; 44 | } 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function getExpression() 51 | { 52 | return [ 53 | '$sort' => $this->sort 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/SortByCount.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.5 13 | */ 14 | class SortByCount extends Stage 15 | { 16 | /** 17 | * @var string 18 | */ 19 | private $fieldName; 20 | 21 | /** 22 | * @param Builder $builder 23 | * @param string $fieldName Expression to group by. To specify a field path, 24 | * prefix the field name with a dollar sign $ and enclose it in quotes. 25 | * The expression can not evaluate to an object. 26 | */ 27 | public function __construct(Builder $builder, $fieldName) 28 | { 29 | parent::__construct($builder); 30 | 31 | $this->fieldName = (string) $fieldName; 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function getExpression() 38 | { 39 | return [ 40 | '$sortByCount' => $this->fieldName 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Aggregation/Stage/Unwind.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.2 13 | */ 14 | class Unwind extends Stage 15 | { 16 | /** 17 | * @var string 18 | */ 19 | private $fieldName; 20 | 21 | /** 22 | * @var string 23 | */ 24 | private $includeArrayIndex; 25 | 26 | /** 27 | * @var bool 28 | */ 29 | private $preserveNullAndEmptyArrays = false; 30 | 31 | /** 32 | * @param Builder $builder 33 | * @param string $fieldName 34 | */ 35 | public function __construct(Builder $builder, $fieldName) 36 | { 37 | parent::__construct($builder); 38 | 39 | $this->fieldName = (string) $fieldName; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function getExpression() 46 | { 47 | // Fallback behavior for MongoDB < 3.2 48 | if ($this->includeArrayIndex === null && ! $this->preserveNullAndEmptyArrays) { 49 | return [ 50 | '$unwind' => $this->fieldName 51 | ]; 52 | } 53 | 54 | $unwind = ['path' => $this->fieldName]; 55 | 56 | foreach (['includeArrayIndex', 'preserveNullAndEmptyArrays'] as $option) { 57 | if ( ! $this->$option) { 58 | continue; 59 | } 60 | 61 | $unwind[$option] = $this->$option; 62 | } 63 | 64 | return [ 65 | '$unwind' => $unwind 66 | ]; 67 | } 68 | 69 | /** 70 | * The name of a new field to hold the array index of the element. The name 71 | * cannot start with a dollar sign $. 72 | * 73 | * @param string $includeArrayIndex 74 | * @return $this 75 | * 76 | * @since 1.3 77 | */ 78 | public function includeArrayIndex($includeArrayIndex) 79 | { 80 | $this->includeArrayIndex = $includeArrayIndex; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * If true, if the path is null, missing, or an empty array, $unwind outputs 87 | * the document. 88 | * 89 | * @param bool $preserveNullAndEmptyArrays 90 | * @return $this 91 | * 92 | * @since 1.3 93 | */ 94 | public function preserveNullAndEmptyArrays($preserveNullAndEmptyArrays = true) 95 | { 96 | $this->preserveNullAndEmptyArrays = $preserveNullAndEmptyArrays; 97 | 98 | return $this; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Configuration.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Configuration 12 | { 13 | /** 14 | * Array of attributes for this configuration instance. 15 | * 16 | * @var array 17 | */ 18 | protected $attributes = [ 19 | 'mongoCmd' => '$', 20 | 'retryConnect' => 0, 21 | 'retryQuery' => 0, 22 | ]; 23 | 24 | /** 25 | * Gets the logger callable. 26 | * 27 | * @return callable 28 | */ 29 | public function getLoggerCallable() 30 | { 31 | return isset($this->attributes['loggerCallable']) ? $this->attributes['loggerCallable'] : null; 32 | } 33 | 34 | /** 35 | * Set the logger callable. 36 | * 37 | * @param callable $loggerCallable 38 | */ 39 | public function setLoggerCallable($loggerCallable) 40 | { 41 | $this->attributes['loggerCallable'] = $loggerCallable; 42 | } 43 | 44 | /** 45 | * Get the MongoDB command prefix. 46 | * 47 | * @deprecated 1.1 No longer supported; will be removed for 1.2 48 | * @return string 49 | */ 50 | public function getMongoCmd() 51 | { 52 | trigger_error('MongoDB command prefix option is no longer used', E_USER_DEPRECATED); 53 | return $this->attributes['mongoCmd']; 54 | } 55 | 56 | /** 57 | * Set the MongoDB command prefix. 58 | * 59 | * @deprecated 1.1 No longer supported; will be removed for 1.2 60 | * @param string $cmd 61 | */ 62 | public function setMongoCmd($cmd) 63 | { 64 | trigger_error('MongoDB command prefix option is no longer used', E_USER_DEPRECATED); 65 | $this->attributes['mongoCmd'] = $cmd; 66 | } 67 | 68 | /** 69 | * Get the number of times to retry connection attempts after an exception. 70 | * 71 | * @return integer 72 | */ 73 | public function getRetryConnect() 74 | { 75 | return $this->attributes['retryConnect']; 76 | } 77 | 78 | /** 79 | * Set the number of times to retry connection attempts after an exception. 80 | * 81 | * @param boolean|integer $retryConnect 82 | */ 83 | public function setRetryConnect($retryConnect) 84 | { 85 | $this->attributes['retryConnect'] = (integer) $retryConnect; 86 | } 87 | 88 | /** 89 | * Get the number of times to retry queries after an exception. 90 | * 91 | * @return integer 92 | */ 93 | public function getRetryQuery() 94 | { 95 | return $this->attributes['retryQuery']; 96 | } 97 | 98 | /** 99 | * Set the number of times to retry queries after an exception. 100 | * 101 | * @param boolean|integer $retryQuery 102 | */ 103 | public function setRetryQuery($retryQuery) 104 | { 105 | $this->attributes['retryQuery'] = (integer) $retryQuery; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/AggregateEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for the aggregate command. 26 | * 27 | * @since 1.1 28 | * @author Jeremy Mikola 29 | */ 30 | class AggregateEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $pipeline; 34 | private $options; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @param object $invoker 40 | * @param array $pipeline 41 | */ 42 | public function __construct($invoker, array $pipeline, array $options = []) 43 | { 44 | $this->invoker = $invoker; 45 | $this->pipeline = $pipeline; 46 | $this->options = $options; 47 | } 48 | 49 | /** 50 | * @return object 51 | */ 52 | public function getInvoker() 53 | { 54 | return $this->invoker; 55 | } 56 | 57 | /** 58 | * @return array 59 | */ 60 | public function getPipeline() 61 | { 62 | return $this->pipeline; 63 | } 64 | 65 | /** 66 | * @since 1.2 67 | * @return array 68 | */ 69 | public function getOptions() 70 | { 71 | return $this->options; 72 | } 73 | 74 | /** 75 | * @param array $pipeline 76 | * @since 1.3 77 | */ 78 | public function setPipeline(array $pipeline) 79 | { 80 | $this->pipeline = $pipeline; 81 | } 82 | 83 | /** 84 | * @param array $options 85 | * @since 1.3 86 | */ 87 | public function setOptions(array $options) 88 | { 89 | $this->options = $options; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/CreateCollectionEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for creating a collection. 26 | * 27 | * @since 1.0 28 | * @author Jonathan H. Wage 29 | */ 30 | class CreateCollectionEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $name; 34 | private $options; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @todo Remove support for separate capped, size and max parameters in 2.0 40 | * @param object $invoker 41 | * @param string $name 42 | * @param boolean|array $cappedOrOptions 43 | * @param integer $size 44 | * @param integer $max 45 | */ 46 | public function __construct($invoker, $name, $cappedOrOptions, $size = 0, $max = 0) 47 | { 48 | $this->invoker = $invoker; 49 | $this->name = $name; 50 | 51 | $options = is_array($cappedOrOptions) 52 | ? $cappedOrOptions 53 | : ['capped' => $cappedOrOptions, 'size' => $size, 'max' => $max]; 54 | 55 | $this->options = $options; 56 | } 57 | 58 | /** 59 | * @return object 60 | */ 61 | public function getInvoker() 62 | { 63 | return $this->invoker; 64 | } 65 | 66 | /** 67 | * @return string 68 | */ 69 | public function getName() 70 | { 71 | return $this->name; 72 | } 73 | 74 | /** 75 | * @return array|bool 76 | */ 77 | public function getOptions() 78 | { 79 | return $this->options; 80 | } 81 | 82 | /** 83 | * @deprecated 1.1 Replaced by options; will be removed for 2.0 84 | * @return mixed 85 | */ 86 | public function getCapped() 87 | { 88 | return $this->options['capped']; 89 | } 90 | 91 | /** 92 | * @deprecated 1.1 Replaced by options; will be removed for 2.0 93 | * @return mixed 94 | */ 95 | public function getSize() 96 | { 97 | return $this->options['size']; 98 | } 99 | 100 | /** 101 | * @deprecated 1.1 Replaced by options; will be removed for 2.0 102 | * @return mixed 103 | */ 104 | public function getMax() 105 | { 106 | return $this->options['max']; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/DistinctEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for the distinct command. 26 | * 27 | * @since 1.0 28 | * @author Jonathan H. Wage 29 | */ 30 | class DistinctEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $field; 34 | private $query; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @param object $invoker 40 | * @param string $field 41 | * @param array $query 42 | */ 43 | public function __construct($invoker, $field, array $query) 44 | { 45 | $this->invoker = $invoker; 46 | $this->field = (string) $field; 47 | $this->query = $query; 48 | } 49 | 50 | /** 51 | * @return object 52 | */ 53 | public function getInvoker() 54 | { 55 | return $this->invoker; 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | public function getField() 62 | { 63 | return $this->field; 64 | } 65 | 66 | /** 67 | * @return array 68 | */ 69 | public function getQuery() 70 | { 71 | return $this->query; 72 | } 73 | 74 | /** 75 | * @param $query 76 | * @since 1.3 77 | */ 78 | public function setQuery($query) 79 | { 80 | $this->query = $query; 81 | } 82 | 83 | /** 84 | * @param string $field 85 | * @since 1.3 86 | */ 87 | public function setField($field) 88 | { 89 | $this->field = (string) $field; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/EventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for generic queries. 26 | * 27 | * @since 1.0 28 | * @author Jonathan H. Wage 29 | */ 30 | class EventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $data; 34 | private $options; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @param object $invoker 40 | * @param mixed $data 41 | * @param array $options 42 | */ 43 | public function __construct($invoker, $data = null, array $options = []) 44 | { 45 | $this->invoker = $invoker; 46 | $this->data = $data; 47 | $this->options = $options; 48 | } 49 | 50 | /** 51 | * @return object 52 | */ 53 | public function getInvoker() 54 | { 55 | return $this->invoker; 56 | } 57 | 58 | /** 59 | * @return mixed|null 60 | */ 61 | public function getData() 62 | { 63 | return $this->data; 64 | } 65 | 66 | /** 67 | * @return array 68 | */ 69 | public function getOptions() 70 | { 71 | return $this->options; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/FindEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for find queries. 26 | * 27 | * @since 1.1 28 | * @author Jeremy Mikola 29 | */ 30 | class FindEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $query; 34 | private $fields; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @param object $invoker 40 | * @param array $query 41 | * @param array $fields 42 | */ 43 | public function __construct($invoker, array $query, array $fields) 44 | { 45 | $this->invoker = $invoker; 46 | $this->query = $query; 47 | $this->fields = $fields; 48 | } 49 | 50 | /** 51 | * @return object 52 | */ 53 | public function getInvoker() 54 | { 55 | return $this->invoker; 56 | } 57 | 58 | /** 59 | * @return array 60 | */ 61 | public function getQuery() 62 | { 63 | return $this->query; 64 | } 65 | 66 | /** 67 | * @return array 68 | */ 69 | public function getFields() 70 | { 71 | return $this->fields; 72 | } 73 | 74 | /** 75 | * @param array $query 76 | * @since 1.3 77 | */ 78 | public function setQuery(array $query) 79 | { 80 | $this->query = $query; 81 | } 82 | 83 | /** 84 | * @param array $fields 85 | * @since 1.3 86 | */ 87 | public function setFields(array $fields) 88 | { 89 | $this->fields = $fields; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/GroupEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for the group command. 26 | * 27 | * @since 1.0 28 | * @author Jonathan H. Wage 29 | */ 30 | class GroupEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $keys; 34 | private $initial; 35 | private $reduce; 36 | private $options; 37 | 38 | /** 39 | * Constructor. 40 | * 41 | * @param object $invoker 42 | * @param array|string|\MongoCode $keys 43 | * @param array $initial 44 | * @param string|\MongoCode $reduce 45 | * @param array $options 46 | */ 47 | public function __construct($invoker, $keys, array $initial, $reduce, array $options = []) 48 | { 49 | $this->invoker = $invoker; 50 | $this->keys = $keys; 51 | $this->initial = $initial; 52 | $this->reduce = $reduce; 53 | $this->options = $options; 54 | } 55 | 56 | /** 57 | * @return object 58 | */ 59 | public function getInvoker() 60 | { 61 | return $this->invoker; 62 | } 63 | 64 | /** 65 | * @return array|\MongoCode|string 66 | */ 67 | public function getKeys() 68 | { 69 | return $this->keys; 70 | } 71 | 72 | /** 73 | * @return array 74 | */ 75 | public function getInitial() 76 | { 77 | return $this->initial; 78 | } 79 | 80 | /** 81 | * @return \MongoCode|string 82 | */ 83 | public function getReduce() 84 | { 85 | return $this->reduce; 86 | } 87 | 88 | /** 89 | * @return array 90 | */ 91 | public function getOptions() 92 | { 93 | return $this->options; 94 | } 95 | 96 | /** 97 | * @param $keys 98 | * @since 1.3 99 | */ 100 | public function setKeys($keys) 101 | { 102 | $this->keys = $keys; 103 | } 104 | 105 | /** 106 | * @param array $initial 107 | * @since 1.3 108 | */ 109 | public function setInitial(array $initial) 110 | { 111 | $this->initial = $initial; 112 | } 113 | 114 | /** 115 | * @param $reduce 116 | * @since 1.3 117 | */ 118 | public function setReduce($reduce) 119 | { 120 | $this->reduce = $reduce; 121 | } 122 | 123 | /** 124 | * @param array $options 125 | * @since 1.3 126 | */ 127 | public function setOptions(array $options) 128 | { 129 | $this->options = $options; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/MapReduceEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for the mapReduce command. 26 | * 27 | * @since 1.0 28 | * @author Jonathan H. Wage 29 | */ 30 | class MapReduceEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $map; 34 | private $reduce; 35 | private $query; 36 | private $out; 37 | private $options; 38 | 39 | /** 40 | * Constructor. 41 | * 42 | * @param object $invoker 43 | * @param string|\MongoCode $map 44 | * @param string|\MongoCode $reduce 45 | * @param array $out 46 | * @param array $query 47 | * @param array $options 48 | */ 49 | public function __construct($invoker, $map, $reduce, array $out, array $query, array $options = []) 50 | { 51 | $this->invoker = $invoker; 52 | $this->map = $map; 53 | $this->reduce = $reduce; 54 | $this->out = $out; 55 | $this->query = $query; 56 | $this->options = $options; 57 | } 58 | 59 | /** 60 | * @return object 61 | */ 62 | public function getInvoker() 63 | { 64 | return $this->invoker; 65 | } 66 | 67 | /** 68 | * @return \MongoCode|string 69 | */ 70 | public function getMap() 71 | { 72 | return $this->map; 73 | } 74 | 75 | /** 76 | * @return \MongoCode|string 77 | */ 78 | public function getReduce() 79 | { 80 | return $this->reduce; 81 | } 82 | 83 | /** 84 | * @return array 85 | */ 86 | public function getOut() 87 | { 88 | return $this->out; 89 | } 90 | 91 | /** 92 | * @return array 93 | */ 94 | public function getQuery() 95 | { 96 | return $this->query; 97 | } 98 | 99 | /** 100 | * @return array 101 | */ 102 | public function getOptions() 103 | { 104 | return $this->options; 105 | } 106 | 107 | /** 108 | * @param array $query 109 | * @since 1.3 110 | */ 111 | public function setQuery(array $query) 112 | { 113 | $this->query = $query; 114 | } 115 | 116 | /** 117 | * @param $map 118 | * @since 1.3 119 | */ 120 | public function setMap($map) 121 | { 122 | $this->map = $map; 123 | } 124 | 125 | /** 126 | * @param $reduce 127 | * @since 1.3 128 | */ 129 | public function setReduce($reduce) 130 | { 131 | $this->reduce = $reduce; 132 | } 133 | 134 | /** 135 | * @param array $out 136 | * @since 1.3 137 | */ 138 | public function setOut(array $out) 139 | { 140 | $this->out = $out; 141 | } 142 | 143 | /** 144 | * @param array $options 145 | * @since 1.3 146 | */ 147 | public function setOptions(array $options) 148 | { 149 | $this->options = $options; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/MutableEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | /** 23 | * Mutable event args for query and command results. 24 | * 25 | * @since 1.1 26 | * @author Jeremy Mikola 27 | */ 28 | class MutableEventArgs extends EventArgs 29 | { 30 | private $changedData; 31 | private $changedOptions; 32 | private $isDataChanged = false; 33 | private $isOptionsChanged = false; 34 | 35 | /** 36 | * @return mixed|null 37 | */ 38 | public function getData() 39 | { 40 | return $this->isDataChanged ? $this->changedData : parent::getData(); 41 | } 42 | 43 | /** 44 | * @param mixed $data 45 | */ 46 | public function setData($data) 47 | { 48 | $this->isDataChanged = parent::getData() !== $data; 49 | $this->changedData = $this->isDataChanged ? $data : null; 50 | } 51 | 52 | /** 53 | * @return bool 54 | */ 55 | public function isDataChanged() 56 | { 57 | return $this->isDataChanged; 58 | } 59 | 60 | /** 61 | * @since 1.3 62 | * @return array 63 | */ 64 | public function getOptions() 65 | { 66 | return $this->isOptionsChanged ? $this->changedOptions : parent::getOptions(); 67 | } 68 | 69 | /** 70 | * @since 1.3 71 | * @param mixed $options 72 | */ 73 | public function setOptions(array $options) 74 | { 75 | $this->isOptionsChanged = parent::getOptions() !== $options; 76 | $this->changedOptions = $this->isOptionsChanged ? $options : null; 77 | } 78 | 79 | /** 80 | * @since 1.3 81 | * @return bool 82 | */ 83 | public function isOptionsChanged() 84 | { 85 | return $this->isOptionsChanged; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/NearEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | use GeoJson\Geometry\Point; 24 | 25 | /** 26 | * Event args for the geoNear command. 27 | * 28 | * @since 1.0 29 | * @author Jonathan H. Wage 30 | */ 31 | class NearEventArgs extends BaseEventArgs 32 | { 33 | private $invoker; 34 | private $query; 35 | private $near; 36 | private $options; 37 | 38 | /** 39 | * Constructor. 40 | * 41 | * @param object $invoker 42 | * @param array $query 43 | * @param array|Point $near 44 | * @param array $options 45 | */ 46 | public function __construct($invoker, array $query, $near, array $options = []) 47 | { 48 | $this->invoker = $invoker; 49 | $this->near = $near; 50 | $this->query = $query; 51 | $this->options = $options; 52 | } 53 | 54 | /** 55 | * @return object 56 | */ 57 | public function getInvoker() 58 | { 59 | return $this->invoker; 60 | } 61 | 62 | /** 63 | * @return array 64 | */ 65 | public function getQuery() 66 | { 67 | return $this->query; 68 | } 69 | 70 | /** 71 | * @return array|Point 72 | */ 73 | public function getNear() 74 | { 75 | return $this->near; 76 | } 77 | 78 | /** 79 | * @return array 80 | */ 81 | public function getOptions() 82 | { 83 | return $this->options; 84 | } 85 | 86 | /** 87 | * @param array $query 88 | * @since 1.3 89 | */ 90 | public function setQuery(array $query) 91 | { 92 | $this->query = $query; 93 | } 94 | 95 | /** 96 | * @param $near 97 | * @since 1.3 98 | */ 99 | public function setNear($near) 100 | { 101 | $this->near = $near; 102 | } 103 | 104 | /** 105 | * @param array $options 106 | * @since 1.3 107 | */ 108 | public function setOptions(array $options) 109 | { 110 | $this->options = $options; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Event/UpdateEventArgs.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Event; 21 | 22 | use Doctrine\Common\EventArgs as BaseEventArgs; 23 | 24 | /** 25 | * Event args for update queries. 26 | * 27 | * @since 1.0 28 | * @author Jonathan H. Wage 29 | */ 30 | class UpdateEventArgs extends BaseEventArgs 31 | { 32 | private $invoker; 33 | private $query; 34 | private $newObj; 35 | private $options; 36 | 37 | /** 38 | * Constructor. 39 | * 40 | * @param object $invoker 41 | * @param array $query 42 | * @param array $newObj 43 | * @param array $options 44 | */ 45 | public function __construct($invoker, array $query, array $newObj, array $options = []) 46 | { 47 | $this->invoker = $invoker; 48 | $this->query = $query; 49 | $this->newObj = $newObj; 50 | $this->options = $options; 51 | } 52 | 53 | /** 54 | * @return object 55 | */ 56 | public function getInvoker() 57 | { 58 | return $this->invoker; 59 | } 60 | 61 | /** 62 | * @return array 63 | */ 64 | public function getQuery() 65 | { 66 | return $this->query; 67 | } 68 | 69 | /** 70 | * @return array 71 | */ 72 | public function getNewObj() 73 | { 74 | return $this->newObj; 75 | } 76 | 77 | /** 78 | * @return array 79 | */ 80 | public function getOptions() 81 | { 82 | return $this->options; 83 | } 84 | 85 | /** 86 | * @param array $query 87 | * @since 1.3 88 | */ 89 | public function setQuery(array $query) 90 | { 91 | $this->query = $query; 92 | } 93 | 94 | /** 95 | * @param array $newObj 96 | * @since 1.3 97 | */ 98 | public function setNewObj(array $newObj) 99 | { 100 | $this->newObj = $newObj; 101 | } 102 | 103 | /** 104 | * @param array $options 105 | * @since 1.3 106 | */ 107 | public function setOptions(array $options) 108 | { 109 | $this->options = $options; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Events.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | final class Events 14 | { 15 | private function __construct() {} 16 | 17 | const preAggregate = 'collectionPreAggregate'; 18 | const postAggregate = 'collectionPostAggregate'; 19 | 20 | const preBatchInsert = 'collectionPreBatchInsert'; 21 | const postBatchInsert = 'collectionPostBatchInsert'; 22 | 23 | const preCreateCollection = 'preCreateCollection'; 24 | const postCreateCollection = 'postCreateCollection'; 25 | 26 | const preConnect = 'preConnect'; 27 | const postConnect = 'postConnect'; 28 | 29 | const preDistinct = 'collectionPreDistinct'; 30 | const postDistinct = 'collectionPostDistinct'; 31 | 32 | const preDropCollection = 'preDropCollection'; 33 | const postDropCollection = 'postDropCollection'; 34 | 35 | const preDropDatabase = 'preDropDatabase'; 36 | const postDropDatabase = 'postDropDatabase'; 37 | 38 | const preFind = 'collectionPreFind'; 39 | const postFind = 'collectionPostFind'; 40 | 41 | const preFindAndRemove = 'collectionPreFindAndRemove'; 42 | const postFindAndRemove = 'collectionPostFindAndRemove'; 43 | 44 | const preFindAndUpdate = 'collectionPreFindAndUpdate'; 45 | const postFindAndUpdate = 'collectionPostFindAndUpdate'; 46 | 47 | const preFindOne = 'collectionPreFindOne'; 48 | const postFindOne = 'collectionPostFindOne'; 49 | 50 | const preGetDBRef = 'collectionPreGetDBRef'; 51 | const postGetDBRef = 'collectionPostGetDBRef'; 52 | 53 | const preGetGridFS = 'preGetGridFS'; 54 | const postGetGridFS = 'postGetGridFS'; 55 | 56 | const preGroup = 'collectionPreGroup'; 57 | const postGroup = 'collectionPostGroup'; 58 | 59 | const preInsert = 'collectionPreInsert'; 60 | const postInsert = 'collectionPostInsert'; 61 | 62 | const preMapReduce = 'preMapReduce'; 63 | const postMapReduce = 'postMapReduce'; 64 | 65 | const preNear = 'collectionPreNear'; 66 | const postNear = 'collectionPostNear'; 67 | 68 | const preRemove = 'collectionPreRemove'; 69 | const postRemove = 'collectionPostRemove'; 70 | 71 | const preSave = 'collectionPreSave'; 72 | const postSave = 'collectionPostSave'; 73 | 74 | const preSelectCollection = 'preSelectCollection'; 75 | const postSelectCollection = 'postSelectCollection'; 76 | 77 | const preSelectDatabase = 'preSelectDatabase'; 78 | const postSelectDatabase = 'postSelectDatabase'; 79 | 80 | const preUpdate = 'collectionPreUpdate'; 81 | const postUpdate = 'collectionPostUpdate'; 82 | } 83 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Exception/ResultException.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\MongoDB\Exception; 21 | 22 | use RuntimeException; 23 | 24 | /** 25 | * ResultException is thrown when a database command fails. 26 | * 27 | * This is similar to the driver's MongoResultException class, which cannot be 28 | * be used due to inaccessibility of its result document property. 29 | * 30 | * @see http://php.net/manual/en/class.mongoresultexception.php 31 | */ 32 | class ResultException extends RuntimeException 33 | { 34 | /** 35 | * The command result document. 36 | * 37 | * @var array 38 | */ 39 | private $document; 40 | 41 | /** 42 | * Constructor. 43 | * 44 | * @param array $document Command result document 45 | */ 46 | public function __construct(array $document) 47 | { 48 | $message = isset($document['errmsg']) ? $document['errmsg'] : 'Unknown error executing command'; 49 | $code = isset($document['code']) ? $document['code'] : 0; 50 | 51 | parent::__construct($message, $code); 52 | } 53 | 54 | /** 55 | * Get the command result document. 56 | * 57 | * @return array 58 | */ 59 | public function getDocument() 60 | { 61 | return $this->document; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Iterator.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | interface Iterator extends \Iterator, \Countable 12 | { 13 | /** 14 | * Return the first element or null if no elements exist. 15 | * 16 | * @return array|object|null 17 | */ 18 | function getSingleResult(); 19 | 20 | /** 21 | * Return all elements as an array. 22 | * 23 | * @return array 24 | */ 25 | function toArray(); 26 | } 27 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/IteratorAggregate.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Bulat Shakirzyanov 11 | */ 12 | interface IteratorAggregate extends \IteratorAggregate, \Countable 13 | { 14 | /** 15 | * Return the first element or null if no elements exist. 16 | * 17 | * @return array|object|null 18 | */ 19 | function getSingleResult(); 20 | 21 | /** 22 | * Return all elements as an array. 23 | * 24 | * @return array 25 | */ 26 | function toArray(); 27 | } 28 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Loggable.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | interface Loggable 12 | { 13 | /** 14 | * Log something using the configured logger callable. 15 | * 16 | * @param array $log 17 | */ 18 | public function log(array $log); 19 | } 20 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/LoggableCollection.php: -------------------------------------------------------------------------------- 1 | 13 | * @author Bulat Shakirzyanov 14 | */ 15 | class LoggableCollection extends Collection implements Loggable 16 | { 17 | use LoggableCollectionTrait; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param Database $database Database to which this collection belongs 23 | * @param \MongoCollection $mongoCollection MongoCollection instance being wrapped 24 | * @param EventManager $evm EventManager instance 25 | * @param integer $numRetries Number of times to retry queries 26 | * @param callable $loggerCallable The logger callable 27 | */ 28 | public function __construct(Database $database, \MongoCollection $mongoCollection, EventManager $evm, $numRetries, $loggerCallable) 29 | { 30 | if ( ! is_callable($loggerCallable)) { 31 | throw new \InvalidArgumentException('$loggerCallable must be a valid callback'); 32 | } 33 | $this->loggerCallable = $loggerCallable; 34 | parent::__construct($database, $mongoCollection, $evm, $numRetries); 35 | } 36 | 37 | /** 38 | * Wraps a MongoCursor instance with a LoggableCursor. 39 | * 40 | * @see Collection::wrapCursor() 41 | * @param \MongoCursor $cursor 42 | * @param array $query 43 | * @param array $fields 44 | * @return LoggableCursor 45 | */ 46 | protected function wrapCursor(\MongoCursor $cursor, $query, $fields) 47 | { 48 | return new LoggableCursor($this, $cursor, $query, $fields, $this->numRetries, $this->loggerCallable); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/LoggableCursor.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class LoggableCursor extends Cursor implements Loggable 12 | { 13 | /** 14 | * The logger callable. 15 | * 16 | * @var callable 17 | */ 18 | protected $loggerCallable; 19 | 20 | /** 21 | * Constructor. 22 | * 23 | * @param Collection $collection Collection used to create this Cursor 24 | * @param \MongoCursor $mongoCursor MongoCursor being wrapped 25 | * @param array $query Query criteria 26 | * @param array $fields Selected fields (projection) 27 | * @param integer $numRetries Number of times to retry queries 28 | * @param callable $loggerCallable Logger callable 29 | */ 30 | public function __construct(Collection $collection, \MongoCursor $mongoCursor, array $query, array $fields, $numRetries, $loggerCallable) 31 | { 32 | if ( ! is_callable($loggerCallable)) { 33 | throw new \InvalidArgumentException('$loggerCallable must be a valid callback'); 34 | } 35 | $this->loggerCallable = $loggerCallable; 36 | parent::__construct($collection, $mongoCursor, $query, $fields, $numRetries); 37 | } 38 | 39 | /** 40 | * Log something using the configured logger callable. 41 | * 42 | * @see Loggable::log() 43 | * @param array $data 44 | */ 45 | public function log(array $data) 46 | { 47 | $data['query'] = $this->query; 48 | $data['fields'] = $this->fields; 49 | call_user_func($this->loggerCallable, $data); 50 | } 51 | 52 | /** 53 | * Get the logger callable. 54 | * 55 | * @return callable 56 | */ 57 | public function getLoggerCallable() 58 | { 59 | return $this->loggerCallable; 60 | } 61 | 62 | /** 63 | * @see Cursor::hint() 64 | */ 65 | public function hint($keyPattern) 66 | { 67 | $this->log([ 68 | 'hint' => true, 69 | 'keyPattern' => $keyPattern, 70 | ]); 71 | 72 | return parent::hint($keyPattern); 73 | } 74 | 75 | /** 76 | * @see Cursor::limit() 77 | */ 78 | public function limit($num) 79 | { 80 | $this->log([ 81 | 'limit' => true, 82 | 'limitNum' => $num, 83 | ]); 84 | 85 | return parent::limit($num); 86 | } 87 | 88 | /** 89 | * @see Cursor::maxTimeMS() 90 | */ 91 | public function maxTimeMS($ms) 92 | { 93 | $this->log([ 94 | 'maxTimeMS' => true, 95 | 'maxTimeMSNum' => $ms, 96 | ]); 97 | 98 | return parent::maxTimeMS($ms); 99 | } 100 | 101 | /** 102 | * @see Cursor::skip() 103 | */ 104 | public function skip($num) 105 | { 106 | $this->log([ 107 | 'skip' => true, 108 | 'skipNum' => $num, 109 | ]); 110 | 111 | return parent::skip($num); 112 | } 113 | 114 | /** 115 | * @see Cursor::snapshot() 116 | */ 117 | public function snapshot() 118 | { 119 | $this->log([ 120 | 'snapshot' => true, 121 | ]); 122 | 123 | return parent::snapshot(); 124 | } 125 | 126 | /** 127 | * @see Cursor::sort() 128 | */ 129 | public function sort($fields) 130 | { 131 | $this->log([ 132 | 'sort' => true, 133 | 'sortFields' => $fields, 134 | ]); 135 | 136 | return parent::sort($fields); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/LoggableGridFS.php: -------------------------------------------------------------------------------- 1 | loggerCallable = $loggerCallable; 28 | } 29 | 30 | /* 31 | * @see GridFS::storeFile() 32 | */ 33 | public function storeFile($file, array &$document, array $options = []) 34 | { 35 | $this->log([ 36 | 'storeFile' => true, 37 | 'count' => count($document), 38 | 'options' => $options, 39 | ]); 40 | 41 | return parent::storeFile($file, $document, $options); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Doctrine/MongoDB/Util/ReadPreference.php: -------------------------------------------------------------------------------- 1 | 14 | * @deprecated 1.3 No longer required; will be removed for 2.0 15 | */ 16 | final class ReadPreference 17 | { 18 | /** 19 | * Read preference types. 20 | * 21 | * The indexes correspond to the numeric values for each read preference 22 | * returned by getReadPreference() methods, while the values correspond to 23 | * the string constants expected by setReadPreference() methods. 24 | */ 25 | private static $types = [ 26 | \MongoClient::RP_PRIMARY, 27 | \MongoClient::RP_PRIMARY_PREFERRED, 28 | \MongoClient::RP_SECONDARY, 29 | \MongoClient::RP_SECONDARY_PREFERRED, 30 | \MongoClient::RP_NEAREST, 31 | ]; 32 | 33 | /** 34 | * Private constructor (prevents instantiation) 35 | */ 36 | private function __construct() {} 37 | 38 | /** 39 | * Converts a numeric type returned by getReadPreference() methods to the 40 | * constant accepted by setReadPreference() methods. 41 | * 42 | * @param integer $type 43 | * @return string 44 | */ 45 | public static function convertNumericType($type) 46 | { 47 | if (! isset(self::$types[$type])) { 48 | throw new \InvalidArgumentException('Unknown numeric read preference type: ' . $type); 49 | } 50 | 51 | return self::$types[$type]; 52 | } 53 | 54 | /** 55 | * Converts return values from getReadPreference() methods to the format 56 | * accepted by setReadPreference() methods. 57 | * 58 | * This is necessary for MongoClient, MongoDB, and MongoCollection classes 59 | * in driver versions between 1.3.0 and 1.3.3. 60 | * 61 | * @since 1.1 62 | * @param array $readPref 63 | * @return array 64 | */ 65 | public static function convertReadPreference(array $readPref) 66 | { 67 | if (is_numeric($readPref['type'])) { 68 | $readPref['type'] = self::convertNumericType($readPref['type']); 69 | } 70 | 71 | if (isset($readPref['type_string'])) { 72 | unset($readPref['type_string']); 73 | } 74 | 75 | if ( ! empty($readPref['tagsets'])) { 76 | $readPref['tagsets'] = self::convertTagSets($readPref['tagsets']); 77 | } 78 | 79 | return $readPref; 80 | } 81 | 82 | /** 83 | * Converts tag sets returned by getReadPreference() methods to the format 84 | * accepted by setReadPreference() methods. 85 | * 86 | * Example input: 87 | * 88 | * [['dc:east', 'use:reporting'], ['dc:west'], []] 89 | * 90 | * Example output: 91 | * 92 | * [['dc' => 'east', 'use' => 'reporting'], ['dc' => 'west'], []] 93 | * 94 | * @param array $tagSets 95 | * @return array 96 | */ 97 | public static function convertTagSets(array $tagSets) 98 | { 99 | return array_map(function(array $tagSet) { 100 | /* If the tag set does not contain a zeroth element, or that element 101 | * does not contain a colon character, we can assume this tag set is 102 | * already in the format expected by setReadPreference(). 103 | */ 104 | if (! isset($tagSet[0]) || false === strpos($tagSet[0], ':')) { 105 | return $tagSet; 106 | } 107 | 108 | $result = []; 109 | 110 | foreach ($tagSet as $tagAndValue) { 111 | list($tag, $value) = explode(':', $tagAndValue, 2); 112 | $result[$tag] = $value; 113 | } 114 | 115 | return $result; 116 | }, $tagSets); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | ./tests/Doctrine/ 15 | 16 | 17 | 18 | 19 | 20 | ./lib/Doctrine/MongoDB 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/AggregationTestCase.php: -------------------------------------------------------------------------------- 1 | getMockCollection()); 21 | } 22 | 23 | /** 24 | * @return \PHPUnit_Framework_MockObject_MockObject|\Doctrine\MongoDB\Collection 25 | */ 26 | protected function getMockCollection() 27 | { 28 | return $this->getMockBuilder('Doctrine\MongoDB\Collection') 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | } 32 | 33 | /** 34 | * @return \PHPUnit_Framework_MockObject_MockObject|\Doctrine\MongoDB\Aggregation\\Expr 35 | */ 36 | protected function getMockAggregationExpr() 37 | { 38 | return $this->getMockBuilder('Doctrine\MongoDB\Aggregation\Expr') 39 | ->disableOriginalConstructor() 40 | ->getMock(); 41 | } 42 | 43 | /** 44 | * @return \PHPUnit_Framework_MockObject_MockObject|\Doctrine\MongoDB\Query\\Expr 45 | */ 46 | protected function getMockQueryExpr() 47 | { 48 | return $this->getMockBuilder('Doctrine\MongoDB\Query\Expr') 49 | ->disableOriginalConstructor() 50 | ->getMock(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/FunctionalTest.php: -------------------------------------------------------------------------------- 1 | createAggregationBuilder() 12 | ->match() 13 | ->execute(); 14 | 15 | $this->assertCount(0, $result); 16 | } 17 | 18 | public function testGeoNearWithEmptyQueryDoesNotCauseError() 19 | { 20 | if (version_compare($this->getServerVersion(), '3.4.0', '>=')) { 21 | $this->conn->selectDatabase('admin')->command(['setFeatureCompatibilityVersion' => '3.4']); 22 | } 23 | 24 | $this->getCollection()->ensureIndex(['location' => '2dsphere']); 25 | $result = $this->createAggregationBuilder() 26 | ->geoNear(0, 0) 27 | ->distanceField('distance') 28 | ->spherical() 29 | ->execute(); 30 | 31 | $this->assertCount(0, $result); 32 | } 33 | 34 | public function testGraphLookupWithoutMatch() 35 | { 36 | if (version_compare($this->getServerVersion(), '3.4.0', '<')) { 37 | $this->markTestSkipped('$graphLookup is only available on server version 3.4 and later.'); 38 | } 39 | 40 | $result = $this->createAggregationBuilder() 41 | ->graphLookup('employees') 42 | ->startWith('$reportsTo') 43 | ->connectFromField('reportsTo') 44 | ->connectToField('name') 45 | ->alias('reportingHierarchy') 46 | ->execute(); 47 | 48 | $this->assertCount(0, $result); 49 | } 50 | 51 | /** 52 | * @return \Doctrine\MongoDB\Aggregation\Builder 53 | */ 54 | protected function createAggregationBuilder() 55 | { 56 | return $this->getCollection()->createAggregationBuilder(); 57 | } 58 | 59 | /** 60 | * @return \Doctrine\MongoDB\Collection 61 | */ 62 | protected function getCollection() 63 | { 64 | $db = $this->conn->selectDatabase(self::$dbName); 65 | return $db->selectCollection('test'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/AddFieldsTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | $addFieldsStage 17 | ->field('product') 18 | ->multiply('$field', 5); 19 | 20 | $this->assertSame(['$addFields' => ['product' => ['$multiply' => ['$field', 5]]]], $addFieldsStage->getExpression()); 21 | } 22 | 23 | public function testProjectFromBuilder() 24 | { 25 | $builder = $this->getTestAggregationBuilder(); 26 | $builder 27 | ->addFields() 28 | ->field('product') 29 | ->multiply('$field', 5); 30 | 31 | $this->assertSame([['$addFields' => ['product' => ['$multiply' => ['$field', 5]]]]], $builder->getPipeline()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/BucketAutoTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | $bucketStage 17 | ->groupBy('$someField') 18 | ->buckets(3) 19 | ->granularity('R10') 20 | ->output() 21 | ->field('averageValue') 22 | ->avg('$value'); 23 | 24 | $this->assertSame(['$bucketAuto' => [ 25 | 'groupBy' => '$someField', 26 | 'buckets' => 3, 27 | 'granularity' => 'R10', 28 | 'output' => ['averageValue' => ['$avg' => '$value']] 29 | ]], $bucketStage->getExpression()); 30 | } 31 | 32 | public function testBucketAutoFromBuilder() 33 | { 34 | $builder = $this->getTestAggregationBuilder(); 35 | $builder->bucketAuto() 36 | ->groupBy('$someField') 37 | ->buckets(3) 38 | ->granularity('R10') 39 | ->output() 40 | ->field('averageValue') 41 | ->avg('$value'); 42 | 43 | $this->assertSame([['$bucketAuto' => [ 44 | 'groupBy' => '$someField', 45 | 'buckets' => 3, 46 | 'granularity' => 'R10', 47 | 'output' => ['averageValue' => ['$avg' => '$value']] 48 | ]]], $builder->getPipeline()); 49 | } 50 | 51 | public function testBucketAutoSkipsUndefinedProperties() 52 | { 53 | $bucketStage = new BucketAuto($this->getTestAggregationBuilder()); 54 | $bucketStage 55 | ->groupBy('$someField') 56 | ->buckets(3); 57 | 58 | $this->assertSame(['$bucketAuto' => [ 59 | 'groupBy' => '$someField', 60 | 'buckets' => 3, 61 | ]], $bucketStage->getExpression()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/BucketTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | $bucketStage 17 | ->groupBy('$someField') 18 | ->boundaries(1, 2, 3) 19 | ->defaultBucket(0) 20 | ->output() 21 | ->field('averageValue') 22 | ->avg('$value'); 23 | 24 | $this->assertSame(['$bucket' => [ 25 | 'groupBy' => '$someField', 26 | 'boundaries' => [1, 2, 3], 27 | 'default' => 0, 28 | 'output' => ['averageValue' => ['$avg' => '$value']] 29 | ]], $bucketStage->getExpression()); 30 | } 31 | 32 | public function testBucketFromBuilder() 33 | { 34 | $builder = $this->getTestAggregationBuilder(); 35 | $builder->bucket() 36 | ->groupBy('$someField') 37 | ->boundaries(1, 2, 3) 38 | ->defaultBucket(0) 39 | ->output() 40 | ->field('averageValue') 41 | ->avg('$value'); 42 | 43 | $this->assertSame([['$bucket' => [ 44 | 'groupBy' => '$someField', 45 | 'boundaries' => [1, 2, 3], 46 | 'default' => 0, 47 | 'output' => ['averageValue' => ['$avg' => '$value']] 48 | ]]], $builder->getPipeline()); 49 | } 50 | 51 | public function testBucketSkipsUndefinedProperties() 52 | { 53 | $bucketStage = new Bucket($this->getTestAggregationBuilder()); 54 | $bucketStage 55 | ->groupBy('$someField') 56 | ->boundaries(1, 2, 3); 57 | 58 | $this->assertSame(['$bucket' => [ 59 | 'groupBy' => '$someField', 60 | 'boundaries' => [1, 2, 3], 61 | ]], $bucketStage->getExpression()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/CollStatsTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | 17 | $this->assertSame(['$collStats' => []], $collStatsStage->getExpression()); 18 | } 19 | 20 | public function testCollStatsStageWithLatencyStats() 21 | { 22 | $collStatsStage = new CollStats($this->getTestAggregationBuilder()); 23 | $collStatsStage->showLatencyStats(); 24 | 25 | $this->assertSame(['$collStats' => ['latencyStats' => ['histograms' => false]]], $collStatsStage->getExpression()); 26 | } 27 | 28 | public function testCollStatsStageWithLatencyStatsHistograms() 29 | { 30 | $collStatsStage = new CollStats($this->getTestAggregationBuilder()); 31 | $collStatsStage->showLatencyStats(true); 32 | 33 | $this->assertSame(['$collStats' => ['latencyStats' => ['histograms' => true]]], $collStatsStage->getExpression()); 34 | } 35 | 36 | public function testCollStatsStageWithStorageStats() 37 | { 38 | $collStatsStage = new CollStats($this->getTestAggregationBuilder()); 39 | $collStatsStage->showStorageStats(); 40 | 41 | $this->assertSame(['$collStats' => ['storageStats' => []]], $collStatsStage->getExpression()); 42 | } 43 | 44 | public function testCollStatsFromBuilder() 45 | { 46 | $builder = $this->getTestAggregationBuilder(); 47 | $builder->collStats() 48 | ->showLatencyStats(true) 49 | ->showStorageStats(); 50 | 51 | $this->assertSame([[ 52 | '$collStats' => [ 53 | 'latencyStats' => ['histograms' => true], 54 | 'storageStats' => [] 55 | ] 56 | ]], $builder->getPipeline()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/CountTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 'document_count'); 16 | 17 | $this->assertSame(['$count' => 'document_count'], $countStage->getExpression()); 18 | } 19 | 20 | public function testCountFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->count('document_count'); 24 | 25 | $this->assertSame([['$count' => 'document_count']], $builder->getPipeline()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/FacetTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(); 16 | $nestedBuilder->sortByCount('$tags'); 17 | 18 | $facetStage = new Facet($this->getTestAggregationBuilder()); 19 | $facetStage 20 | ->field('someField') 21 | ->pipeline($nestedBuilder) 22 | ->field('otherField') 23 | ->pipeline($this->getTestAggregationBuilder()->sortByCount('$comments')); 24 | 25 | $this->assertSame([ 26 | '$facet' => [ 27 | 'someField' => [['$sortByCount' => '$tags']], 28 | 'otherField' => [['$sortByCount' => '$comments']], 29 | ] 30 | ], $facetStage->getExpression()); 31 | } 32 | 33 | public function testFacetFromBuilder() 34 | { 35 | $nestedBuilder = $this->getTestAggregationBuilder(); 36 | $nestedBuilder->sortByCount('$tags'); 37 | 38 | $builder = $this->getTestAggregationBuilder(); 39 | $builder->facet() 40 | ->field('someField') 41 | ->pipeline($nestedBuilder) 42 | ->field('otherField') 43 | ->pipeline($this->getTestAggregationBuilder()->sortByCount('$comments')); 44 | 45 | $this->assertSame([[ 46 | '$facet' => [ 47 | 'someField' => [['$sortByCount' => '$tags']], 48 | 'otherField' => [['$sortByCount' => '$comments']], 49 | ] 50 | ]], $builder->getPipeline()); 51 | } 52 | 53 | public function testFacetThrowsExceptionWithoutFieldName() 54 | { 55 | $facetStage = new Facet($this->getTestAggregationBuilder()); 56 | 57 | $this->expectException(\LogicException::class); 58 | $this->expectExceptionMessage('requires you set a current field using field().'); 59 | $facetStage->pipeline($this->getTestAggregationBuilder()); 60 | } 61 | 62 | public function testFacetThrowsExceptionOnInvalidPipeline() 63 | { 64 | $facetStage = new Facet($this->getTestAggregationBuilder()); 65 | 66 | $this->expectException(\InvalidArgumentException::class); 67 | $this->expectExceptionMessage('expects either an aggregation builder or an aggregation stage.'); 68 | $facetStage 69 | ->field('someField') 70 | ->pipeline(new \stdClass()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/GeoNearTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 0, 0); 16 | $geoNearStage 17 | ->distanceField('distance') 18 | ->field('someField') 19 | ->equals('someValue'); 20 | 21 | $stage = ['near' => [0, 0], 'spherical' => false, 'distanceField' => 'distance', 'query' => ['someField' => 'someValue']]; 22 | $this->assertSame(['$geoNear' => $stage], $geoNearStage->getExpression()); 23 | } 24 | 25 | public function testGeoNearFromBuilder() 26 | { 27 | $builder = $this->getTestAggregationBuilder(); 28 | $builder 29 | ->geoNear(0, 0) 30 | ->distanceField('distance') 31 | ->field('someField') 32 | ->equals('someValue'); 33 | 34 | $stage = ['near' => [0, 0], 'spherical' => false, 'distanceField' => 'distance', 'query' => ['someField' => 'someValue']]; 35 | $this->assertSame([['$geoNear' => $stage]], $builder->getPipeline()); 36 | } 37 | 38 | /** 39 | * @dataProvider provideOptionalSettings 40 | */ 41 | public function testOptionalSettings($field, $value) 42 | { 43 | $geoNearStage = new GeoNear($this->getTestAggregationBuilder(), 0, 0); 44 | 45 | $pipeline = $geoNearStage->getExpression(); 46 | $this->assertArrayNotHasKey($field, $pipeline['$geoNear']); 47 | 48 | $geoNearStage->$field($value); 49 | $pipeline = $geoNearStage->getExpression(); 50 | 51 | $this->assertSame($value, $pipeline['$geoNear'][$field]); 52 | } 53 | 54 | public static function provideOptionalSettings() 55 | { 56 | return [ 57 | 'distanceMultiplier' => ['distanceMultiplier', 15.0], 58 | 'includeLocs' => ['includeLocs', 'dist.location'], 59 | 'maxDistance' => ['maxDistance', 15.0], 60 | 'minDistance' => ['minDistance', 15.0], 61 | 'num' => ['num', 15], 62 | 'uniqueDocs' => ['uniqueDocs', true], 63 | ]; 64 | } 65 | 66 | public function testLimitDoesNotCreateExtraStage() 67 | { 68 | $builder = $this->getTestAggregationBuilder(); 69 | $builder 70 | ->geoNear(0, 0) 71 | ->limit(1); 72 | 73 | $stage = ['near' => [0, 0], 'spherical' => false, 'distanceField' => null, 'query' => [], 'num' => 1]; 74 | $this->assertSame([['$geoNear' => $stage]], $builder->getPipeline()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/GraphLookupTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 'employees'); 16 | $graphLookupStage 17 | ->startWith('$reportsTo') 18 | ->connectFromField('reportsTo') 19 | ->connectToField('name') 20 | ->alias('reportingHierarchy'); 21 | 22 | $this->assertEquals( 23 | ['$graphLookup' => [ 24 | 'from' => 'employees', 25 | 'startWith' => '$reportsTo', 26 | 'connectFromField' => 'reportsTo', 27 | 'connectToField' => 'name', 28 | 'as' => 'reportingHierarchy', 29 | 'restrictSearchWithMatch' => (object) [], 30 | ]], 31 | $graphLookupStage->getExpression() 32 | ); 33 | } 34 | 35 | public function testGraphLookupFromBuilder() 36 | { 37 | $builder = $this->getTestAggregationBuilder(); 38 | $builder->graphLookup('employees') 39 | ->startWith('$reportsTo') 40 | ->connectFromField('reportsTo') 41 | ->connectToField('name') 42 | ->alias('reportingHierarchy'); 43 | 44 | $this->assertEquals( 45 | [['$graphLookup' => [ 46 | 'from' => 'employees', 47 | 'startWith' => '$reportsTo', 48 | 'connectFromField' => 'reportsTo', 49 | 'connectToField' => 'name', 50 | 'as' => 'reportingHierarchy', 51 | 'restrictSearchWithMatch' => (object) [], 52 | ]]], 53 | $builder->getPipeline() 54 | ); 55 | } 56 | 57 | public function testGraphLookupWithMatch() 58 | { 59 | $builder = $this->getTestAggregationBuilder(); 60 | $builder->graphLookup('employees') 61 | ->startWith('$reportsTo') 62 | ->restrictSearchWithMatch() 63 | ->field('hobbies') 64 | ->equals('golf') 65 | ->connectFromField('reportsTo') 66 | ->connectToField('name') 67 | ->alias('reportingHierarchy') 68 | ->maxDepth(1) 69 | ->depthField('depth'); 70 | 71 | $this->assertSame( 72 | [['$graphLookup' => [ 73 | 'from' => 'employees', 74 | 'startWith' => '$reportsTo', 75 | 'connectFromField' => 'reportsTo', 76 | 'connectToField' => 'name', 77 | 'as' => 'reportingHierarchy', 78 | 'restrictSearchWithMatch' => ['hobbies' => 'golf'], 79 | 'maxDepth' => 1, 80 | 'depthField' => 'depth', 81 | ]]], 82 | $builder->getPipeline() 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/GroupStub.php: -------------------------------------------------------------------------------- 1 | expr = $query; 13 | } 14 | 15 | public function getExpression() 16 | { 17 | return $this->expr->getExpression(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/GroupTest.php: -------------------------------------------------------------------------------- 1 | getMockAggregationExpr(); 20 | $expr 21 | ->expects($this->once()) 22 | ->method($method) 23 | ->with(...$args); 24 | 25 | $stage = new GroupStub($this->getTestAggregationBuilder()); 26 | $stage->setQuery($expr); 27 | 28 | $this->assertSame($stage, $stage->$method(...$args)); 29 | } 30 | 31 | public function provideProxiedExprMethods() 32 | { 33 | $expression = new Expr(); 34 | $expression 35 | ->field('dayOfMonth') 36 | ->dayOfMonth('$dateField') 37 | ->field('dayOfWeek') 38 | ->dayOfWeek('$dateField'); 39 | 40 | return [ 41 | 'addToSet()' => ['addToSet', ['$field']], 42 | 'avg()' => ['avg', ['$field']], 43 | 'expression()' => ['expression', [$expression]], 44 | 'first()' => ['first', ['$field']], 45 | 'last()' => ['last', ['$field']], 46 | 'max()' => ['max', ['$field']], 47 | 'min()' => ['min', ['$field']], 48 | 'push()' => ['push', ['$field']], 49 | 'stdDevPop()' => ['stdDevPop', ['$field']], 50 | 'stdDevSamp()' => ['stdDevSamp', ['$field']], 51 | 'sum()' => ['sum', ['$field']], 52 | ]; 53 | } 54 | 55 | public function testGroupStage() 56 | { 57 | $groupStage = new Group($this->getTestAggregationBuilder()); 58 | $groupStage 59 | ->field('_id') 60 | ->expression('$field') 61 | ->field('count') 62 | ->sum(1); 63 | 64 | $this->assertSame(['$group' => ['_id' => '$field', 'count' => ['$sum' => 1]]], $groupStage->getExpression()); 65 | } 66 | 67 | public function testGroupFromBuilder() 68 | { 69 | $builder = $this->getTestAggregationBuilder(); 70 | $builder 71 | ->group() 72 | ->field('_id') 73 | ->expression('$field') 74 | ->field('count') 75 | ->sum(1); 76 | 77 | $this->assertSame([['$group' => ['_id' => '$field', 'count' => ['$sum' => 1]]]], $builder->getPipeline()); 78 | } 79 | 80 | public function testGroupWithOperatorInId() 81 | { 82 | $groupStage = new Group($this->getTestAggregationBuilder()); 83 | $groupStage 84 | ->field('_id') 85 | ->year('$dateField') 86 | ->field('count') 87 | ->sum(1); 88 | 89 | $this->assertSame(['$group' => ['_id' => ['$year' => '$dateField'], 'count' => ['$sum' => 1]]], $groupStage->getExpression()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/IndexStatsTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | 17 | $this->assertEquals(['$indexStats' => new \stdClass()], $indexStatsStage->getExpression()); 18 | } 19 | 20 | public function testIndexStatsFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->indexStats(); 24 | 25 | $this->assertEquals([['$indexStats' => new \stdClass()]], $builder->getPipeline()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/LimitTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 10); 16 | 17 | $this->assertSame(['$limit' => 10], $limitStage->getExpression()); 18 | } 19 | 20 | public function testLimitFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->limit(10); 24 | 25 | $this->assertSame([['$limit' => 10]], $builder->getPipeline()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/LookupTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 'collection'); 16 | $lookupStage 17 | ->localField('local.field') 18 | ->foreignField('foreign.field') 19 | ->alias('lookedUp'); 20 | 21 | $this->assertSame( 22 | ['$lookup' => ['from' => 'collection', 'localField' => 'local.field', 'foreignField' => 'foreign.field', 'as' => 'lookedUp']], 23 | $lookupStage->getExpression() 24 | ); 25 | } 26 | 27 | public function testLookupFromBuilder() 28 | { 29 | $builder = $this->getTestAggregationBuilder(); 30 | $builder->lookup('collection') 31 | ->localField('local.field') 32 | ->foreignField('foreign.field') 33 | ->alias('lookedUp'); 34 | 35 | $this->assertSame([['$lookup' => ['from' => 'collection', 'localField' => 'local.field', 'foreignField' => 'foreign.field', 'as' => 'lookedUp']]], $builder->getPipeline()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/MatchStub.php: -------------------------------------------------------------------------------- 1 | query = $query; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/MatchTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | $matchStage 17 | ->field('someField') 18 | ->equals('someValue'); 19 | 20 | $this->assertSame(['$match' => ['someField' => 'someValue']], $matchStage->getExpression()); 21 | } 22 | 23 | public function testMatchFromBuilder() 24 | { 25 | $builder = $this->getTestAggregationBuilder(); 26 | $builder 27 | ->match() 28 | ->field('someField') 29 | ->equals('someValue'); 30 | 31 | $this->assertSame([['$match' => ['someField' => 'someValue']]], $builder->getPipeline()); 32 | } 33 | 34 | /** 35 | * @dataProvider provideProxiedExprMethods 36 | */ 37 | public function testProxiedExprMethods($method, array $args = []) 38 | { 39 | $expr = $this->getMockQueryExpr(); 40 | $expr 41 | ->expects($this->once()) 42 | ->method($method) 43 | ->with(...$args); 44 | 45 | $stage = $this->getStubStage(); 46 | $stage->setQuery($expr); 47 | 48 | $this->assertSame($stage, $stage->$method(...$args)); 49 | } 50 | 51 | public function provideProxiedExprMethods() 52 | { 53 | return [ 54 | 'field()' => ['field', ['fieldName']], 55 | 'equals()' => ['equals', ['value']], 56 | 'in()' => ['in', [['value1', 'value2']]], 57 | 'notIn()' => ['notIn', [['value1', 'value2']]], 58 | 'notEqual()' => ['notEqual', ['value']], 59 | 'gt()' => ['gt', [1]], 60 | 'gte()' => ['gte', [1]], 61 | 'lt()' => ['gt', [1]], 62 | 'lte()' => ['gte', [1]], 63 | 'range()' => ['range', [0, 1]], 64 | 'size()' => ['size', [1]], 65 | 'exists()' => ['exists', [true]], 66 | 'type()' => ['type', [7]], 67 | 'all()' => ['all', [['value1', 'value2']]], 68 | 'maxDistance' => ['maxDistance', [5]], 69 | 'minDistance' => ['minDistance', [5]], 70 | 'mod()' => ['mod', [2, 0]], 71 | 'geoIntersects()' => ['geoIntersects', [$this->getMockGeometry()]], 72 | 'geoWithin()' => ['geoWithin', [$this->getMockGeometry()]], 73 | 'geoWithinBox()' => ['geoWithinBox', [1, 2, 3, 4]], 74 | 'geoWithinCenter()' => ['geoWithinCenter', [1, 2, 3]], 75 | 'geoWithinCenterSphere()' => ['geoWithinCenterSphere', [1, 2, 3]], 76 | 'geoWithinPolygon()' => ['geoWithinPolygon', [[0, 0], [1, 1], [1, 0]]], 77 | 'addAnd() array' => ['addAnd', [[]]], 78 | 'addAnd() Expr' => ['addAnd', [$this->getMockQueryExpr()]], 79 | 'addOr() array' => ['addOr', [[]]], 80 | 'addOr() Expr' => ['addOr', [$this->getMockQueryExpr()]], 81 | 'addNor() array' => ['addNor', [[]]], 82 | 'addNor() Expr' => ['addNor', [$this->getMockQueryExpr()]], 83 | 'not()' => ['not', [$this->getMockQueryExpr()]], 84 | 'language()' => ['language', ['en']], 85 | 'text()' => ['text', ['foo']], 86 | ]; 87 | } 88 | 89 | private function getStubStage() 90 | { 91 | return new MatchStub($this->getTestAggregationBuilder()); 92 | } 93 | 94 | private function getMockGeometry() 95 | { 96 | return $this->getMockBuilder('GeoJson\Geometry\Geometry') 97 | ->disableOriginalConstructor() 98 | ->getMock(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/OperatorStub.php: -------------------------------------------------------------------------------- 1 | expr = $query; 13 | } 14 | 15 | public function getExpression() 16 | { 17 | return $this->expr->getExpression(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/OperatorTest.php: -------------------------------------------------------------------------------- 1 | getStubStage(); 20 | 21 | $this->assertSame($stage, $stage->$operator(...$args)); 22 | $this->assertSame($expected, $stage->getExpression()); 23 | } 24 | 25 | public function testExpression() 26 | { 27 | $stage = $this->getStubStage(); 28 | 29 | $nestedExpr = new Expr(); 30 | $nestedExpr 31 | ->field('dayOfMonth') 32 | ->dayOfMonth('$dateField') 33 | ->field('dayOfWeek') 34 | ->dayOfWeek('$dateField'); 35 | 36 | $this->assertSame($stage, $stage->field('nested')->expression($nestedExpr)); 37 | $this->assertSame( 38 | [ 39 | 'nested' => [ 40 | 'dayOfMonth' => ['$dayOfMonth' => '$dateField'], 41 | 'dayOfWeek' => ['$dayOfWeek' => '$dateField'] 42 | ] 43 | ], 44 | $stage->getExpression() 45 | ); 46 | } 47 | 48 | public function testSwitch() 49 | { 50 | $stage = $this->getStubStage(); 51 | 52 | $stage->switch() 53 | ->case((new Expr())->eq('$numElements', 0)) 54 | ->then('Zero elements given') 55 | ->case((new Expr())->eq('$numElements', 1)) 56 | ->then('One element given') 57 | ->default((new Expr())->concat('$numElements', ' elements given')); 58 | 59 | $this->assertSame( 60 | [ 61 | '$switch' => [ 62 | 'branches' => [ 63 | ['case' => ['$eq' => ['$numElements', 0]], 'then' => 'Zero elements given'], 64 | ['case' => ['$eq' => ['$numElements', 1]], 'then' => 'One element given'], 65 | ], 66 | 'default' => ['$concat' => ['$numElements', ' elements given']], 67 | ] 68 | ], 69 | $stage->getExpression() 70 | ); 71 | } 72 | 73 | public function testCallingCaseWithoutSwitchThrowsException() 74 | { 75 | $stage = $this->getStubStage(); 76 | 77 | $this->expectException(\BadMethodCallException::class); 78 | $this->expectExceptionMessage('Doctrine\MongoDB\Aggregation\Expr::case requires a valid switch statement (call switch() first).'); 79 | 80 | $stage->case('$field'); 81 | } 82 | 83 | public function testCallingThenWithoutCaseThrowsException() 84 | { 85 | $stage = $this->getStubStage(); 86 | 87 | $this->expectException(\BadMethodCallException::class); 88 | $this->expectExceptionMessage('Doctrine\MongoDB\Aggregation\Expr::then requires a valid case statement (call case() first).'); 89 | 90 | $stage->then('$field'); 91 | } 92 | 93 | public function testCallingThenWithoutCaseAfterSuccessfulCaseThrowsException() 94 | { 95 | $stage = $this->getStubStage(); 96 | 97 | $stage->switch() 98 | ->case('$field') 99 | ->then('$field'); 100 | 101 | $this->expectException(\BadMethodCallException::class); 102 | $this->expectExceptionMessage('Doctrine\MongoDB\Aggregation\Expr::then requires a valid case statement (call case() first).'); 103 | 104 | $stage->then('$field'); 105 | } 106 | 107 | public function testCallingDefaultWithoutSwitchThrowsException() 108 | { 109 | $stage = $this->getStubStage(); 110 | 111 | $this->expectException(\BadMethodCallException::class); 112 | $this->expectExceptionMessage('Doctrine\MongoDB\Aggregation\Expr::default requires a valid switch statement (call switch() first).'); 113 | 114 | $stage->default('$field'); 115 | } 116 | 117 | /** 118 | * @return OperatorStub 119 | */ 120 | public function getStubStage() 121 | { 122 | return new OperatorStub($this->getTestAggregationBuilder()); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/OutTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 'someCollection'); 16 | 17 | $this->assertSame(['$out' => 'someCollection'], $outStage->getExpression()); 18 | } 19 | 20 | public function testOutFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->out('someCollection'); 24 | 25 | $this->assertSame([['$out' => 'someCollection']], $builder->getPipeline()); 26 | } 27 | 28 | public function testSubsequentOutStagesAreOverwritten() 29 | { 30 | $builder = $this->getTestAggregationBuilder(); 31 | $builder 32 | ->out('someCollection') 33 | ->out('otherCollection'); 34 | 35 | $this->assertSame([['$out' => 'otherCollection']], $builder->getPipeline()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/ProjectTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 17 | $projectStage 18 | ->excludeFields(['_id']) 19 | ->includeFields(['$field', '$otherField']) 20 | ->field('product') 21 | ->multiply('$field', 5); 22 | 23 | $this->assertSame(['$project' => ['_id' => false, '$field' => true, '$otherField' => true, 'product' => ['$multiply' => ['$field', 5]]]], $projectStage->getExpression()); 24 | } 25 | 26 | public function testProjectFromBuilder() 27 | { 28 | $builder = $this->getTestAggregationBuilder(); 29 | $builder 30 | ->project() 31 | ->excludeFields(['_id']) 32 | ->includeFields(['$field', '$otherField']) 33 | ->field('product') 34 | ->multiply('$field', 5); 35 | 36 | $this->assertSame([['$project' => ['_id' => false, '$field' => true, '$otherField' => true, 'product' => ['$multiply' => ['$field', 5]]]]], $builder->getPipeline()); 37 | } 38 | 39 | /** 40 | * @dataProvider provideAccumulators 41 | */ 42 | public function testAccumulatorsWithMultipleArguments($operator) 43 | { 44 | $projectStage = new Project($this->getTestAggregationBuilder()); 45 | $projectStage 46 | ->field('something') 47 | ->$operator('$expression1', '$expression2'); 48 | 49 | $this->assertSame(['$project' => ['something' => ['$' . $operator => ['$expression1', '$expression2']]]], $projectStage->getExpression()); 50 | } 51 | 52 | public function provideAccumulators() 53 | { 54 | $operators = ['avg', 'max', 'min', 'stdDevPop', 'stdDevSamp', 'sum']; 55 | 56 | return array_combine($operators, array_map(function ($operator) { return [$operator]; }, $operators)); 57 | } 58 | 59 | /** 60 | * @dataProvider provideProxiedExprMethods 61 | */ 62 | public function testProxiedExprMethods($method, array $args = []) 63 | { 64 | $expr = $this->getMockAggregationExpr(); 65 | $expr 66 | ->expects($this->once()) 67 | ->method($method) 68 | ->with(...$args); 69 | 70 | $stage = new GroupStub($this->getTestAggregationBuilder()); 71 | $stage->setQuery($expr); 72 | 73 | $this->assertSame($stage, $stage->$method(...$args)); 74 | } 75 | 76 | public static function provideProxiedExprMethods() 77 | { 78 | $expression = new Expr(); 79 | $expression 80 | ->field('dayOfMonth') 81 | ->dayOfMonth('$dateField') 82 | ->field('dayOfWeek') 83 | ->dayOfWeek('$dateField'); 84 | 85 | return [ 86 | 'avg()' => ['avg', ['$field']], 87 | 'max()' => ['max', ['$field']], 88 | 'min()' => ['min', ['$field']], 89 | 'stdDevPop()' => ['stdDevPop', ['$field']], 90 | 'stdDevSamp()' => ['stdDevSamp', ['$field']], 91 | 'sum()' => ['sum', ['$field']], 92 | ]; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/RedactTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(); 16 | 17 | $redactStage = new Redact($builder, 'someCollection'); 18 | $redactStage 19 | ->cond( 20 | $builder->expr()->lte('$accessLevel', 3), 21 | '$$KEEP', 22 | '$$REDACT' 23 | ); 24 | 25 | $this->assertSame(['$redact' => ['$cond' => ['if' => ['$lte' => ['$accessLevel', 3]], 'then' => '$$KEEP', 'else' => '$$REDACT']]], $redactStage->getExpression()); 26 | } 27 | 28 | public function testRedactFromBuilder() 29 | { 30 | $builder = $this->getTestAggregationBuilder(); 31 | $builder 32 | ->redact() 33 | ->cond( 34 | $builder->expr()->lte('$accessLevel', 3), 35 | '$$KEEP', 36 | '$$REDACT' 37 | ); 38 | 39 | $this->assertSame([['$redact' => ['$cond' => ['if' => ['$lte' => ['$accessLevel', 3]], 'then' => '$$KEEP', 'else' => '$$REDACT']]]], $builder->getPipeline()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/ReplaceRootTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder()); 16 | $replaceRootStage 17 | ->field('product') 18 | ->multiply('$field', 5); 19 | 20 | $this->assertSame(['$replaceRoot' => ['product' => ['$multiply' => ['$field', 5]]]], $replaceRootStage->getExpression()); 21 | } 22 | 23 | public function testReplaceRootFromBuilder() 24 | { 25 | $builder = $this->getTestAggregationBuilder(); 26 | $builder 27 | ->replaceRoot() 28 | ->field('product') 29 | ->multiply('$field', 5); 30 | 31 | $this->assertSame([['$replaceRoot' => ['product' => ['$multiply' => ['$field', 5]]]]], $builder->getPipeline()); 32 | } 33 | 34 | public function testReplaceWithEmbeddedDocument() 35 | { 36 | $builder = $this->getTestAggregationBuilder(); 37 | $builder->replaceRoot('$some.embedded.document'); 38 | 39 | $this->assertSame([['$replaceRoot' => '$some.embedded.document']], $builder->getPipeline()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/SampleTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 10); 16 | 17 | $this->assertSame(['$sample' => ['size' => 10]], $sampleStage->getExpression()); 18 | } 19 | 20 | public function testSampleFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->sample(10); 24 | 25 | $this->assertSame([['$sample' => ['size' => 10]]], $builder->getPipeline()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/SkipTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 10); 16 | 17 | $this->assertSame(['$skip' => 10], $skipStage->getExpression()); 18 | } 19 | 20 | public function testSkipFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->skip(10); 24 | 25 | $this->assertSame([['$skip' => 10]], $builder->getPipeline()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/SortByCountTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), '$expression'); 16 | 17 | $this->assertSame(['$sortByCount' => '$expression'], $sortByCountStage->getExpression()); 18 | } 19 | 20 | public function testSortByCountFromBuilder() 21 | { 22 | $builder = $this->getTestAggregationBuilder(); 23 | $builder->sortByCount('$fieldName'); 24 | 25 | $this->assertSame([['$sortByCount' => '$fieldName']], $builder->getPipeline()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/SortTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), $field, $order); 19 | 20 | $this->assertSame(['$sort' => $expectedSort], $sortStage->getExpression()); 21 | } 22 | 23 | /** 24 | * @dataProvider provideSortOptions 25 | */ 26 | public function testSortFromBuilder($expectedSort, $field, $order = null) 27 | { 28 | $builder = $this->getTestAggregationBuilder(); 29 | $builder->sort($field, $order); 30 | 31 | $this->assertSame([['$sort' => $expectedSort]], $builder->getPipeline()); 32 | } 33 | 34 | public static function provideSortOptions() 35 | { 36 | return [ 37 | 'singleFieldSeparated' => [ 38 | ['field' => -1], 39 | 'field', 40 | 'desc' 41 | ], 42 | 'singleFieldCombined' => [ 43 | ['field' => -1], 44 | ['field' => 'desc'] 45 | ], 46 | 'multipleFields' => [ 47 | ['field' => -1, 'otherField' => 1], 48 | ['field' => 'desc', 'otherField' => 'asc'] 49 | ], 50 | 'sortMeta' => [ 51 | ['field' => ['$meta' => 'textScore'], 'invalidField' => -1], 52 | ['field' => 'textScore', 'invalidField' => 'nonExistingMetaField'] 53 | ] 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Aggregation/Stage/UnwindTest.php: -------------------------------------------------------------------------------- 1 | getTestAggregationBuilder(), 'fieldName'); 16 | 17 | $this->assertSame(['$unwind' => 'fieldName'], $unwindStage->getExpression()); 18 | } 19 | 20 | public function testUnwindStageWithNewFields() 21 | { 22 | $unwindStage = new Unwind($this->getTestAggregationBuilder(), 'fieldName'); 23 | $unwindStage 24 | ->preserveNullAndEmptyArrays() 25 | ->includeArrayIndex('index'); 26 | 27 | $this->assertSame(['$unwind' => ['path' => 'fieldName', 'includeArrayIndex' => 'index', 'preserveNullAndEmptyArrays' => true]], $unwindStage->getExpression()); 28 | } 29 | 30 | public function testUnwindFromBuilder() 31 | { 32 | $builder = $this->getTestAggregationBuilder(); 33 | $builder->unwind('fieldName'); 34 | 35 | $this->assertSame([['$unwind' => 'fieldName']], $builder->getPipeline()); 36 | } 37 | 38 | public function testSubsequentUnwindStagesArePreserved() 39 | { 40 | $builder = $this->getTestAggregationBuilder(); 41 | $builder 42 | ->unwind('fieldName') 43 | ->unwind('otherField'); 44 | 45 | $this->assertSame([['$unwind' => 'fieldName'], ['$unwind' => 'otherField']], $builder->getPipeline()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/ArrayIteratorTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('ArrayAccess', $arrayIterator); 14 | $this->assertFalse($arrayIterator->offsetExists(0)); 15 | $this->assertNull($arrayIterator['undefinedOffset']); 16 | 17 | $arrayIterator[0] = null; 18 | 19 | $this->assertFalse($arrayIterator->offsetExists(0)); 20 | $this->assertArrayNotHasKey(0, $arrayIterator); 21 | $this->assertNull($arrayIterator[0]); 22 | 23 | $arrayIterator[] = true; 24 | 25 | $this->assertTrue($arrayIterator->offsetExists(1)); 26 | $this->assertArrayHasKey(1, $arrayIterator); 27 | $this->assertTrue($arrayIterator[1]); 28 | 29 | unset($arrayIterator[0]); 30 | 31 | $this->assertFalse($arrayIterator->offsetExists(0)); 32 | $this->assertEmpty($arrayIterator[0]); 33 | } 34 | 35 | public function testCount() 36 | { 37 | $this->assertInstanceOf('Countable', new ArrayIterator()); 38 | 39 | $this->assertCount(0, new ArrayIterator()); 40 | $this->assertCount(1, new ArrayIterator([1])); 41 | $this->assertCount(2, new ArrayIterator([1, 2])); 42 | } 43 | 44 | public function testGetSingleResult() 45 | { 46 | $arrayIterator = new ArrayIterator([1, 2, 3]); 47 | 48 | $this->assertSame(1, $arrayIterator->getSingleResult()); 49 | 50 | $arrayIterator->next(); 51 | $arrayIterator->next(); 52 | $arrayIterator->next(); 53 | 54 | $this->assertSame(1, $arrayIterator->getSingleResult()); 55 | } 56 | 57 | public function testGetSingleResultWithEmptyArray() 58 | { 59 | $arrayIterator = new ArrayIterator(); 60 | 61 | $this->assertNull($arrayIterator->getSingleResult()); 62 | } 63 | 64 | public function testIteration() 65 | { 66 | $arrayIterator = new ArrayIterator([1, 2, 3]); 67 | 68 | $this->assertInstanceOf('Iterator', $arrayIterator); 69 | 70 | $this->assertSame(0, $arrayIterator->key()); 71 | $this->assertSame(1, $arrayIterator->current()); 72 | $this->assertTrue($arrayIterator->valid()); 73 | 74 | $arrayIterator->next(); 75 | 76 | $this->assertSame(1, $arrayIterator->key()); 77 | $this->assertSame(2, $arrayIterator->current()); 78 | $this->assertTrue($arrayIterator->valid()); 79 | 80 | $arrayIterator->next(); 81 | 82 | $this->assertSame(2, $arrayIterator->key()); 83 | $this->assertSame(3, $arrayIterator->current()); 84 | $this->assertTrue($arrayIterator->valid()); 85 | 86 | $arrayIterator->next(); 87 | 88 | $this->assertNull($arrayIterator->key()); 89 | $this->assertFalse($arrayIterator->current()); 90 | $this->assertFalse($arrayIterator->valid()); 91 | 92 | $arrayIterator->rewind(); 93 | 94 | $this->assertSame(0, $arrayIterator->key()); 95 | $this->assertSame(1, $arrayIterator->current()); 96 | $this->assertTrue($arrayIterator->valid()); 97 | } 98 | 99 | public function testToArray() 100 | { 101 | $arrayIterator = new ArrayIterator([1, 2, 3]); 102 | 103 | $this->assertSame([1, 2, 3], $arrayIterator->toArray()); 104 | } 105 | 106 | public function testFirstAndLast() 107 | { 108 | $arrayIterator = new ArrayIterator([1, 2, 3]); 109 | 110 | $this->assertSame(1, $arrayIterator->first()); 111 | $this->assertSame(3, $arrayIterator->last()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/CommandCursorFunctionalTest.php: -------------------------------------------------------------------------------- 1 | getServerVersion(), '2.6.0', '<')) { 15 | $this->markTestSkipped('This test is not applicable to server versions < 2.6.0'); 16 | } 17 | 18 | $this->docs = [ 19 | ['_id' => 1], 20 | ['_id' => 2], 21 | ['_id' => 3], 22 | ]; 23 | 24 | $this->collection = $this->conn->selectCollection(self::$dbName, 'CommandCursorFunctionalTest'); 25 | $this->collection->drop(); 26 | 27 | foreach ($this->docs as $doc) { 28 | $this->collection->insert($doc); 29 | } 30 | } 31 | 32 | public function testCount() 33 | { 34 | $commandCursor = $this->collection->aggregate( 35 | [['$sort' => ['_id' => 1]]], 36 | ['cursor' => true] 37 | ); 38 | 39 | $this->assertCount(3, $commandCursor); 40 | } 41 | 42 | public function testGetSingleResult() 43 | { 44 | $commandCursor = $this->collection->aggregate( 45 | [['$sort' => ['_id' => 1]]], 46 | ['cursor' => true] 47 | ); 48 | 49 | $this->assertEquals($this->docs[0], $commandCursor->getSingleResult()); 50 | } 51 | 52 | public function testGetSingleResultRewindsBeforeReturningFirstResult() 53 | { 54 | $commandCursor = $this->collection->aggregate( 55 | [['$sort' => ['_id' => 1]]], 56 | ['cursor' => true] 57 | ); 58 | 59 | $commandCursor->rewind(); 60 | $commandCursor->next(); 61 | $this->assertTrue($commandCursor->valid()); 62 | $this->assertEquals($this->docs[1], $commandCursor->current()); 63 | $this->assertEquals($this->docs[0], $commandCursor->getSingleResult()); 64 | } 65 | 66 | /** 67 | * @covers Doctrine\MongoDB\Cursor::getSingleResult 68 | */ 69 | public function testGetSingleResultReturnsNullForEmptyResultSet() 70 | { 71 | $commandCursor = $this->collection->aggregate( 72 | [['$match' => ['_id' => 0]]], 73 | ['cursor' => true] 74 | ); 75 | 76 | $this->assertNull($commandCursor->getSingleResult()); 77 | } 78 | 79 | public function testToArray() 80 | { 81 | $commandCursor = $this->collection->aggregate( 82 | [['$sort' => ['_id' => 1]]], 83 | ['cursor' => true] 84 | ); 85 | 86 | $this->assertEquals($this->docs, $commandCursor->toArray()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/CommandCursorTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('This test is not applicable to drivers without MongoCommandCursor'); 13 | } 14 | } 15 | 16 | public function testBatchSize() 17 | { 18 | $mongoCommandCursor = $this->getMockMongoCommandCursor(); 19 | 20 | $mongoCommandCursor->expects($this->once()) 21 | ->method('batchSize') 22 | ->with(10); 23 | 24 | $commandCursor = new CommandCursor($mongoCommandCursor); 25 | $this->assertSame($commandCursor, $commandCursor->batchSize(10)); 26 | } 27 | 28 | public function testDead() 29 | { 30 | $mongoCommandCursor = $this->getMockMongoCommandCursor(); 31 | 32 | $mongoCommandCursor->expects($this->once()) 33 | ->method('dead') 34 | ->will($this->returnValue(true)); 35 | 36 | $commandCursor = new CommandCursor($mongoCommandCursor); 37 | $this->assertTrue($commandCursor->dead()); 38 | } 39 | 40 | public function testGetMongoCommandCursor() 41 | { 42 | $mongoCommandCursor = $this->getMockMongoCommandCursor(); 43 | $commandCursor = new CommandCursor($mongoCommandCursor); 44 | $this->assertSame($mongoCommandCursor, $commandCursor->getMongoCommandCursor()); 45 | } 46 | 47 | public function testInfo() 48 | { 49 | $mongoCommandCursor = $this->getMockMongoCommandCursor(); 50 | 51 | $mongoCommandCursor->expects($this->once()) 52 | ->method('info') 53 | ->will($this->returnValue(['info'])); 54 | 55 | $commandCursor = new CommandCursor($mongoCommandCursor); 56 | $this->assertEquals(['info'], $commandCursor->info()); 57 | } 58 | 59 | public function testTimeout() 60 | { 61 | if ( ! method_exists('MongoCommandCursor', 'timeout')) { 62 | $this->markTestSkipped('This test is not applicable to drivers without MongoCommandCursor::timeout()'); 63 | } 64 | 65 | $mongoCommandCursor = $this->getMockMongoCommandCursor(); 66 | 67 | $mongoCommandCursor->expects($this->once()) 68 | ->method('timeout') 69 | ->with(1000); 70 | 71 | $commandCursor = new CommandCursor($mongoCommandCursor); 72 | $this->assertSame($commandCursor, $commandCursor->timeout(1000)); 73 | } 74 | 75 | /** 76 | * @expectedException \BadMethodCallException 77 | */ 78 | public function testTimeoutShouldThrowExceptionForOldDrivers() 79 | { 80 | if (method_exists('MongoCommandCursor', 'timeout')) { 81 | $this->markTestSkipped('This test is not applicable to drivers with MongoCommandCursor::timeout()'); 82 | } 83 | 84 | $commandCursor = new CommandCursor($this->getMockMongoCommandCursor()); 85 | $commandCursor->timeout(1000); 86 | } 87 | 88 | private function getMockMongoCommandCursor() 89 | { 90 | return $this->getMockBuilder('MongoCommandCursor') 91 | ->disableOriginalConstructor() 92 | ->getMock(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/ConnectionFunctionalTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('Test will not work with polyfills for ext-mongo'); 13 | } 14 | 15 | $this->assertFalse($this->conn->isConnected()); 16 | $this->conn->connect(); 17 | $this->assertTrue($this->conn->isConnected()); 18 | $this->conn->close(); 19 | $this->assertFalse($this->conn->isConnected()); 20 | } 21 | 22 | public function testDriverOptions() 23 | { 24 | if (! extension_loaded('mongo')) { 25 | $this->markTestSkipped('Test will not work with polyfills for ext-mongo'); 26 | } 27 | 28 | $callCount = 0; 29 | $streamContext = stream_context_create([ 30 | 'mongodb' => [ 31 | 'log_cmd_delete' => function () use (&$callCount) { 32 | $callCount++; 33 | }, 34 | ], 35 | ]); 36 | 37 | $connection = new Connection(null, [], null, null, ['context' => $streamContext]); 38 | 39 | $connection->selectCollection('test', 'collection')->remove([]); 40 | 41 | $this->assertSame(1, $callCount); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/CursorFunctionalTest.php: -------------------------------------------------------------------------------- 1 | conn->selectDatabase(self::$dbName); 10 | $coll = $db->selectCollection('users'); 11 | 12 | $doc1 = ['test' => 'test']; 13 | $coll->insert($doc1); 14 | 15 | $doc2 = ['test' => 'test']; 16 | $coll->insert($doc2); 17 | 18 | $cursor = $coll->find(['test' => 'test']); 19 | $cursor->limit(1); 20 | 21 | $this->assertEquals(1, $cursor->count(true)); 22 | $this->assertEquals(2, $cursor->count()); 23 | 24 | $cursor->recreate(); 25 | $this->assertEquals(1, $cursor->count(true)); 26 | $this->assertEquals(2, $cursor->count()); 27 | } 28 | 29 | public function testGetSingleResult() 30 | { 31 | $db = $this->conn->selectDatabase(self::$dbName); 32 | $coll = $db->selectCollection('users'); 33 | 34 | $doc1 = ['test' => 'test', 'doc' => 1]; 35 | $coll->insert($doc1); 36 | 37 | $doc2 = ['test' => 'test', 'doc' => 2]; 38 | $coll->insert($doc2); 39 | 40 | $cursor = $coll->find(['test' => 'test']); 41 | $doc = $cursor->getSingleResult(); 42 | $this->assertEquals(['_id' => $doc1['_id'], 'test' => 'test', 'doc' => 1], $doc); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/DatabaseTestCase.php: -------------------------------------------------------------------------------- 1 | setLoggerCallable(function($msg) {}); 21 | $this->conn = new Connection(null, [], $config); 22 | } 23 | 24 | public function tearDown() 25 | { 26 | $collections = $this->conn->selectDatabase(self::$dbName)->listCollections(); 27 | foreach ($collections as $collection) { 28 | $collection->drop(); 29 | } 30 | 31 | $this->conn->close(); 32 | unset($this->conn); 33 | } 34 | 35 | protected function getServerVersion() 36 | { 37 | $result = $this->conn->selectDatabase(self::$dbName)->command(['buildInfo' => 1]); 38 | 39 | return $result['version']; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/AggregateEventArgsTest.php: -------------------------------------------------------------------------------- 1 | ['_id' => 1]]]; 14 | 15 | $aggregateEventArgs = new AggregateEventArgs($invoker, $pipeline); 16 | 17 | $this->assertSame($invoker, $aggregateEventArgs->getInvoker()); 18 | $this->assertSame($pipeline, $aggregateEventArgs->getPipeline()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/CreateCollectionEventArgsTest.php: -------------------------------------------------------------------------------- 1 | true, 16 | 'size' => 10485760, 17 | 'autoIndexId' => false, 18 | ]; 19 | 20 | $createCollectionEventArgs = new CreateCollectionEventArgs($invoker, $name, $options); 21 | 22 | $this->assertSame($invoker, $createCollectionEventArgs->getInvoker()); 23 | $this->assertSame($name, $createCollectionEventArgs->getName()); 24 | $this->assertSame($options, $createCollectionEventArgs->getOptions()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/DistinctEventArgsTest.php: -------------------------------------------------------------------------------- 1 | 1]; 15 | 16 | $distinctEventArgs = new DistinctEventArgs($invoker, $field, $query); 17 | 18 | $this->assertSame($invoker, $distinctEventArgs->getInvoker()); 19 | $this->assertSame($field, $distinctEventArgs->getField()); 20 | $this->assertSame($query, $distinctEventArgs->getQuery()); 21 | 22 | $field2 = 'y'; 23 | $query2 = ['y' => 2]; 24 | 25 | $distinctEventArgs->setQuery($query2); 26 | $distinctEventArgs->setField($field2); 27 | 28 | $this->assertSame($field2, $distinctEventArgs->getField()); 29 | $this->assertSame($query2, $distinctEventArgs->getQuery()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/EventArgsTest.php: -------------------------------------------------------------------------------- 1 | 1]; 14 | $options = ['w' => 1]; 15 | 16 | $eventArgs = new EventArgs($invoker, $data, $options); 17 | 18 | $this->assertSame($invoker, $eventArgs->getInvoker()); 19 | $this->assertSame($data, $eventArgs->getData()); 20 | $this->assertSame($options, $eventArgs->getOptions()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/FindEventArgsTest.php: -------------------------------------------------------------------------------- 1 | 1]; 14 | $fields = ['_id' => 0]; 15 | 16 | $findEventArgs = new FindEventArgs($invoker, $query, $fields); 17 | 18 | $this->assertSame($invoker, $findEventArgs->getInvoker()); 19 | $this->assertSame($query, $findEventArgs->getQuery()); 20 | $this->assertSame($fields, $findEventArgs->getFields()); 21 | 22 | $query2 = ['x' => 2]; 23 | $fields2 = ['_id' => 1]; 24 | 25 | $findEventArgs->setQuery($query2); 26 | $findEventArgs->setFields($fields2); 27 | 28 | $this->assertSame($query2, $findEventArgs->getQuery()); 29 | $this->assertSame($fields2, $findEventArgs->getFields()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/GroupEventArgsTest.php: -------------------------------------------------------------------------------- 1 | 0]; 15 | $reduce = new \MongoCode(''); 16 | $options = ['finalize' => new \MongoCode('')]; 17 | 18 | $groupEventArgs = new GroupEventArgs($invoker, $keys, $initial, $reduce, $options); 19 | 20 | $this->assertSame($invoker, $groupEventArgs->getInvoker()); 21 | $this->assertSame($keys, $groupEventArgs->getKeys()); 22 | $this->assertSame($initial, $groupEventArgs->getInitial()); 23 | $this->assertSame($reduce, $groupEventArgs->getReduce()); 24 | $this->assertSame($options, $groupEventArgs->getOptions()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/MapReduceEventArgsTest.php: -------------------------------------------------------------------------------- 1 | true]; 16 | $query = ['x' => 1]; 17 | $options = ['finalize' => new \MongoCode('')]; 18 | 19 | $mapReduceEventArgs = new MapReduceEventArgs($invoker, $map, $reduce, $out, $query, $options); 20 | 21 | $this->assertSame($invoker, $mapReduceEventArgs->getInvoker()); 22 | $this->assertSame($map, $mapReduceEventArgs->getMap()); 23 | $this->assertSame($reduce, $mapReduceEventArgs->getReduce()); 24 | $this->assertSame($out, $mapReduceEventArgs->getOut()); 25 | $this->assertSame($query, $mapReduceEventArgs->getQuery()); 26 | $this->assertSame($options, $mapReduceEventArgs->getOptions()); 27 | 28 | $map2 = new \MongoCode('a'); 29 | $reduce2 = new \MongoCode('b'); 30 | $out2 = ['inline' => false]; 31 | $query2 = ['x' => 2]; 32 | $options2 = ['finalize' => new \MongoCode('c')]; 33 | 34 | $mapReduceEventArgs->setMap($map2); 35 | $mapReduceEventArgs->setReduce($reduce2); 36 | $mapReduceEventArgs->setOut($out2); 37 | $mapReduceEventArgs->setQuery($query2); 38 | $mapReduceEventArgs->setOptions($options2); 39 | 40 | $this->assertSame($map2, $mapReduceEventArgs->getMap()); 41 | $this->assertSame($reduce2, $mapReduceEventArgs->getReduce()); 42 | $this->assertSame($out2, $mapReduceEventArgs->getOut()); 43 | $this->assertSame($query2, $mapReduceEventArgs->getQuery()); 44 | $this->assertSame($options2, $mapReduceEventArgs->getOptions()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/MutableEventArgsTest.php: -------------------------------------------------------------------------------- 1 | 1]; 14 | $options = ['w' => 1]; 15 | 16 | $mutableEventArgs = new MutableEventArgs($invoker, $data, $options); 17 | 18 | $this->assertSame($invoker, $mutableEventArgs->getInvoker()); 19 | $this->assertSame($data, $mutableEventArgs->getData()); 20 | $this->assertSame($options, $mutableEventArgs->getOptions()); 21 | $this->assertFalse($mutableEventArgs->isDataChanged()); 22 | $this->assertFalse($mutableEventArgs->isOptionsChanged()); 23 | } 24 | 25 | /** 26 | * @dataProvider provideChangedData 27 | */ 28 | public function testIsDataChanged($oldData, $newData) 29 | { 30 | $invoker = new \stdClass(); 31 | 32 | $mutableEventArgs = new MutableEventArgs($invoker, $oldData); 33 | 34 | $this->assertFalse($mutableEventArgs->isDataChanged()); 35 | $this->assertSame($oldData, $mutableEventArgs->getData()); 36 | 37 | $mutableEventArgs->setData($oldData); 38 | 39 | $this->assertSame($oldData, $mutableEventArgs->getData()); 40 | $this->assertFalse($mutableEventArgs->isDataChanged()); 41 | 42 | $mutableEventArgs->setData($newData); 43 | 44 | $this->assertSame($newData, $mutableEventArgs->getData()); 45 | $this->assertTrue($mutableEventArgs->isDataChanged()); 46 | 47 | $mutableEventArgs->setData($newData); 48 | 49 | $this->assertSame($newData, $mutableEventArgs->getData()); 50 | $this->assertTrue($mutableEventArgs->isDataChanged()); 51 | 52 | $mutableEventArgs->setData($oldData); 53 | 54 | $this->assertSame($oldData, $mutableEventArgs->getData()); 55 | $this->assertFalse($mutableEventArgs->isDataChanged()); 56 | } 57 | 58 | public function provideChangedData() 59 | { 60 | return [ 61 | [new \stdClass(), new \stdClass()], 62 | [['ok' => 1], ['ok' => 0]], 63 | ['foo', 'bar'], 64 | [1, 1.0], 65 | ]; 66 | } 67 | 68 | /** 69 | * @dataProvider provideChangedOptions 70 | */ 71 | public function testIsOptionsChanged($oldOptions, $newOptions) 72 | { 73 | $invoker = new \stdClass(); 74 | 75 | $mutableEventArgs = new MutableEventArgs($invoker, [], $oldOptions); 76 | 77 | $this->assertFalse($mutableEventArgs->isOptionsChanged()); 78 | $this->assertSame($oldOptions, $mutableEventArgs->getOptions()); 79 | 80 | $mutableEventArgs->setOptions($oldOptions); 81 | 82 | $this->assertSame($oldOptions, $mutableEventArgs->getOptions()); 83 | $this->assertFalse($mutableEventArgs->isOptionsChanged()); 84 | 85 | $mutableEventArgs->setOptions($newOptions); 86 | 87 | $this->assertSame($newOptions, $mutableEventArgs->getOptions()); 88 | $this->assertTrue($mutableEventArgs->isOptionsChanged()); 89 | 90 | $mutableEventArgs->setOptions($newOptions); 91 | 92 | $this->assertSame($newOptions, $mutableEventArgs->getOptions()); 93 | $this->assertTrue($mutableEventArgs->isOptionsChanged()); 94 | 95 | $mutableEventArgs->setOptions($oldOptions); 96 | 97 | $this->assertSame($oldOptions, $mutableEventArgs->getOptions()); 98 | $this->assertFalse($mutableEventArgs->isOptionsChanged()); 99 | } 100 | 101 | public function provideChangedOptions() 102 | { 103 | return [ 104 | [['foo' => 'bar'], ['foo' => 'baz']], 105 | [[], ['foo' => 'bar']], 106 | [['foo' => 'bar'], []], 107 | ]; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/NearEventArgsTest.php: -------------------------------------------------------------------------------- 1 | 1]; 14 | $near = [10, 20]; 15 | $options = ['limit' => 5]; 16 | 17 | $nearEventArgs = new NearEventArgs($invoker, $query, $near, $options); 18 | 19 | $this->assertSame($invoker, $nearEventArgs->getInvoker()); 20 | $this->assertSame($query, $nearEventArgs->getQuery()); 21 | $this->assertSame($near, $nearEventArgs->getNear()); 22 | $this->assertSame($options, $nearEventArgs->getOptions()); 23 | 24 | $query2 = ['x' => 2]; 25 | $near2 = [20, 30]; 26 | $options2 = ['limit' => 6]; 27 | 28 | $nearEventArgs->setQuery($query2); 29 | $nearEventArgs->setNear($near2); 30 | $nearEventArgs->setOptions($options2); 31 | 32 | $this->assertSame($query2, $nearEventArgs->getQuery()); 33 | $this->assertSame($near2, $nearEventArgs->getNear()); 34 | $this->assertSame($options2, $nearEventArgs->getOptions()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Event/UpdateEventArgsTest.php: -------------------------------------------------------------------------------- 1 | 1]; 14 | $newObj = ['$set' => ['x' => 2]]; 15 | $options = ['upsert' => true]; 16 | 17 | $updateEventArgs = new UpdateEventArgs($invoker, $query, $newObj, $options); 18 | 19 | $this->assertSame($invoker, $updateEventArgs->getInvoker()); 20 | $this->assertSame($query, $updateEventArgs->getQuery()); 21 | $this->assertSame($newObj, $updateEventArgs->getNewObj()); 22 | $this->assertSame($options, $updateEventArgs->getOptions()); 23 | 24 | // Ensure the setters work. 25 | $query2 = ['x' => 2]; 26 | $newObj2 = ['$set' => ['x' => 2]]; 27 | $options2 = ['upsert' => false]; 28 | 29 | $updateEventArgs->setQuery($query2); 30 | $updateEventArgs->setNewObj($newObj2); 31 | $updateEventArgs->setOptions($options2); 32 | 33 | $this->assertSame($query2, $updateEventArgs->getQuery()); 34 | $this->assertSame($newObj2, $updateEventArgs->getNewObj()); 35 | $this->assertSame($options2, $updateEventArgs->getOptions()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/EventTest.php: -------------------------------------------------------------------------------- 1 | createMock(ListenerStub::class); 13 | $listener 14 | ->expects($this->once()) 15 | ->method('preConnect'); 16 | $manager = new EventManager(); 17 | 18 | $manager->addEventListener(\Doctrine\MongoDB\Events::preConnect, $listener); 19 | 20 | $connection = new Connection(null, [], null, $manager); 21 | $connection->initialize(); 22 | } 23 | } 24 | 25 | class ListenerStub { 26 | function preConnect() {} 27 | } 28 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/FunctionalTest.php: -------------------------------------------------------------------------------- 1 | conn->selectDatabase(self::$dbName); 12 | $coll = $db->createCollection('test'); 13 | $criteria = []; 14 | $newObj = ['$set' => ['test' => 'test']]; 15 | $coll->upsert($criteria, $newObj); 16 | 17 | $check = $coll->findOne(); 18 | 19 | $this->assertNotNull($coll->findOne()); 20 | 21 | $coll->upsert(['_id' => $check['_id']], ['$set' => ['boo' => 'test']]); 22 | $this->assertEquals(1, $coll->find()->count()); 23 | $check = $coll->findOne(); 24 | $this->assertArrayHasKey('boo', $check); 25 | } 26 | 27 | public function testMapReduce() 28 | { 29 | $data = [ 30 | [ 31 | 'username' => 'jones', 32 | 'likes' => 20, 33 | 'text' => 'Hello world!' 34 | ], 35 | [ 36 | 'username' => 'bob', 37 | 'likes' => 100, 38 | 'text' => 'Hello world!' 39 | ], 40 | [ 41 | 'username' => 'bob', 42 | 'likes' => 100, 43 | 'text' => 'Hello world!' 44 | ], 45 | ]; 46 | 47 | $db = $this->conn->selectDatabase(self::$dbName); 48 | $coll = $db->createCollection('test'); 49 | $coll->batchInsert($data); 50 | 51 | $map = 'function() { 52 | emit(this.username, { count: 1, likes: this.likes }); 53 | }'; 54 | 55 | $reduce = 'function(key, values) { 56 | var result = {count: 0, likes: 0}; 57 | 58 | values.forEach(function(value) { 59 | result.count += value.count; 60 | result.likes += value.likes; 61 | }); 62 | 63 | return result; 64 | }'; 65 | 66 | $finalize = 'function (key, value) { value.test = "test"; return value; }'; 67 | 68 | $db = $this->conn->selectDatabase(self::$dbName); 69 | $coll = $db->selectCollection('test'); 70 | $qb = $coll->createQueryBuilder() 71 | ->map($map)->reduce($reduce)->finalize($finalize); 72 | $query = $qb->getQuery(); 73 | $results = $query->execute(); 74 | $this->assertEquals(2, $results->count()); 75 | $result = $results->getSingleResult(); 76 | $this->assertEquals(['count' => 2.0, 'likes' => 200.0, 'test' => 'test'], $result['value']); 77 | } 78 | 79 | public function testIsFieldIndexed() 80 | { 81 | $db = $this->conn->selectDatabase(self::$dbName); 82 | 83 | $coll = $db->selectCollection('users'); 84 | $this->assertFalse($coll->isFieldIndexed('test')); 85 | $coll->ensureIndex(['test' => 1]); 86 | $this->assertTrue($coll->isFieldIndexed('test')); 87 | } 88 | 89 | public function testFunctional() 90 | { 91 | $db = $this->conn->selectDatabase(self::$dbName); 92 | 93 | $coll = $db->selectCollection('users'); 94 | 95 | $document = ['test' => 'jwage']; 96 | $coll->insert($document); 97 | 98 | $coll->update(['_id' => $document['_id']], ['$set' => ['test' => 'jon']]); 99 | 100 | $cursor = $coll->find(); 101 | $this->assertInstanceOf('Doctrine\MongoDB\Cursor', $cursor); 102 | } 103 | 104 | public function testFunctionalGridFS() 105 | { 106 | $db = $this->conn->selectDatabase(self::$dbName); 107 | $files = $db->getGridFS('files'); 108 | $file = [ 109 | 'title' => 'test file', 110 | 'testing' => 'ok', 111 | 'file' => new GridFSFile(__DIR__.'/FunctionalTest.php') 112 | ]; 113 | $files->insert($file); 114 | 115 | $this->assertArrayHasKey('_id', $file); 116 | 117 | $path = __DIR__.'/TestCase.php'; 118 | $files->update(['_id' => $file['_id']], ['$set' => ['title' => 'test', 'file' => new GridFSFile($path)]]); 119 | 120 | $file = $files->find()->getSingleResult(); 121 | $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $file['file']); 122 | $this->assertEquals(file_get_contents($path), $file['file']->getBytes()); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/LoggableCollectionTest.php: -------------------------------------------------------------------------------- 1 | getTestLoggableCollection($loggerCallable); 21 | $collection->log(['test' => 'test']); 22 | 23 | $this->assertEquals(['collection' => self::collectionName, 'db' => self::databaseName, 'test' => 'test'], $called); 24 | } 25 | 26 | private function getTestLoggableCollection($loggerCallable) 27 | { 28 | $database = $this->getMockBuilder('Doctrine\MongoDB\Database') 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | 32 | $database->expects($this->any()) 33 | ->method('getName') 34 | ->will($this->returnValue(self::databaseName)); 35 | 36 | $mongoCollection = $this->getMockBuilder('MongoCollection') 37 | ->disableOriginalConstructor() 38 | ->getMock(); 39 | 40 | $mongoCollection->expects($this->any()) 41 | ->method('getName') 42 | ->will($this->returnValue(self::collectionName)); 43 | 44 | $eventManager = $this->getMockBuilder('Doctrine\Common\EventManager') 45 | ->disableOriginalConstructor() 46 | ->getMock(); 47 | 48 | return new LoggableCollection($database, $mongoCollection, $eventManager, 0, $loggerCallable); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/LoggableCursorTest.php: -------------------------------------------------------------------------------- 1 | setLoggerCallable(function(array $message) use ($log) { 19 | if (isset($message['find'])) { 20 | return; 21 | } 22 | 23 | $this->assertArraySubset($log, $message); 24 | }); 25 | $conn = new Connection(null, [], $config); 26 | 27 | /** @var LoggableCursor $cursor */ 28 | $cursor = $conn->selectCollection('foo', 'bar')->find(); 29 | $this->assertInstanceOf(LoggableCursor::class, $cursor); 30 | 31 | $cursor->$method($argument); 32 | } 33 | 34 | public function provideLoggedMethods() 35 | { 36 | return [ 37 | ['sort', ['sort' => true, 'sortFields' => ['foo' => true]], ['foo' => true]], 38 | ['skip', ['skip' => true, 'skipNum' => 5], 5], 39 | ['limit', ['limit' => true, 'limitNum' => 5], 5], 40 | ['hint', ['hint' => true, 'keyPattern' => 'indexName'], 'indexName'], 41 | ['snapshot', ['snapshot' => true]], 42 | ['maxTimeMS', ['maxTimeMS' => true, 'maxTimeMSNum' => 10], 10] 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/LoggableDatabaseTest.php: -------------------------------------------------------------------------------- 1 | getTestLoggableDatabase($loggerCallable); 20 | $db->log(['test' => 'test']); 21 | 22 | $this->assertEquals(['db' => self::databaseName, 'test' => 'test'], $called); 23 | } 24 | 25 | private function getTestLoggableDatabase($loggerCallable) 26 | { 27 | $connection = $this->getMockBuilder('Doctrine\MongoDB\Connection') 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | 31 | $mongoDB = $this->getMockBuilder('MongoDB') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | 35 | $mongoDB->expects($this->any()) 36 | ->method('__toString') 37 | ->will($this->returnValue(self::databaseName)); 38 | 39 | $eventManager = $this->getMockBuilder('Doctrine\Common\EventManager') 40 | ->disableOriginalConstructor() 41 | ->getMock(); 42 | 43 | return new LoggableDatabase($connection, $mongoDB, $eventManager, 0, $loggerCallable); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/LoggableGridFSTest.php: -------------------------------------------------------------------------------- 1 | getLoggableGridFS($this->getLoggerCallable($logResult)); 21 | $loggableGridFS->log(['test' => 'test']); 22 | 23 | $expected = [ 24 | 'collection' => self::COLLECTION_NAME, 25 | 'db' => self::DATABASE_NAME, 26 | 'test' => 'test' 27 | ]; 28 | 29 | $this->assertEquals($expected, $logResult); 30 | } 31 | 32 | public function testStoreFileLog() 33 | { 34 | $logResult = false; 35 | 36 | $loggableGridFS = $this->getLoggableGridFS($this->getLoggerCallable($logResult)); 37 | 38 | $mongoGridFSFile = $this->getMockBuilder(MongoGridFSFile::class) 39 | ->disableOriginalConstructor() 40 | ->getMock(); 41 | 42 | $this->mongoCollection->expects($this->any()) 43 | ->method('get') 44 | ->will($this->returnValue($mongoGridFSFile)); 45 | 46 | $document = $document = ['foo' => 'bar']; 47 | $loggableGridFS->storeFile(__FILE__, $document, array('foo' => 'bar')); 48 | 49 | $expectedLog = [ 50 | 'storeFile' => true, 51 | 'options' => array('foo' => 'bar'), 52 | 'db' => self::DATABASE_NAME, 53 | 'count' => 1, 54 | 'collection' => self::COLLECTION_NAME, 55 | ]; 56 | 57 | $this->assertEquals($expectedLog, $logResult); 58 | } 59 | 60 | private function getLoggerCallable(&$called) 61 | { 62 | return function($msg) use (&$called) { 63 | $called = $msg; 64 | }; 65 | } 66 | 67 | private function getLoggableGridFS($loggerCallable) 68 | { 69 | $database = $this->getMockBuilder(Database::class) 70 | ->disableOriginalConstructor() 71 | ->getMock(); 72 | 73 | $database->expects($this->any()) 74 | ->method('getName') 75 | ->will($this->returnValue(self::DATABASE_NAME)); 76 | 77 | $this->mongoCollection = $this->getMockBuilder(MongoGridFS::class) 78 | ->disableOriginalConstructor() 79 | ->getMock(); 80 | 81 | $this->mongoCollection->expects($this->any()) 82 | ->method('getName') 83 | ->will($this->returnValue(self::COLLECTION_NAME)); 84 | 85 | $eventManager = $this->getMockBuilder(EventManager::class) 86 | ->disableOriginalConstructor() 87 | ->getMock(); 88 | 89 | return new LoggableGridFS($database, $this->mongoCollection, $eventManager, 0, $loggerCallable); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/Query/BuilderStub.php: -------------------------------------------------------------------------------- 1 | expr = $expr; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 0, 22 | 'type_string' => \MongoClient::RP_PRIMARY, 23 | 'tagsets' => [['dc:east']], 24 | ]; 25 | 26 | $expected = [ 27 | 'type' => \MongoClient::RP_PRIMARY, 28 | 'tagsets' => [['dc' => 'east']], 29 | ]; 30 | 31 | $this->assertEquals($expected, ReadPreference::convertReadPreference($readPref)); 32 | } 33 | 34 | /** 35 | * @dataProvider provideTagSets 36 | */ 37 | public function testConvertTagSets($tagSet, $expected) 38 | { 39 | $this->assertEquals($expected, ReadPreference::convertTagSets($tagSet)); 40 | } 41 | 42 | public function provideTagSets() 43 | { 44 | return [ 45 | [ 46 | [ 47 | ['dc:east', 'use:reporting'], 48 | ['dc:west'], 49 | [], 50 | ], 51 | [ 52 | ['dc' => 'east', 'use' => 'reporting'], 53 | ['dc' => 'west'], 54 | [], 55 | ], 56 | ], 57 | [ 58 | [[]], 59 | [[]], 60 | ], 61 | /* This tag set is impractical, since an empty set matches anything, 62 | * but we want to test that elements beyond the first are converted. 63 | */ 64 | [ 65 | [ 66 | [], 67 | ['dc:west'], 68 | ['dc:east', 'use:reporting'], 69 | ], 70 | [ 71 | [], 72 | ['dc' => 'west'], 73 | ['dc' => 'east', 'use' => 'reporting'], 74 | ], 75 | ], 76 | ]; 77 | } 78 | 79 | /** 80 | * @dataProvider provideTagSetsAcceptedBySetReadPreference 81 | */ 82 | public function testConvertTagSetsShouldNotAlterTagSetsAcceptedBySetReadPreference($tagSet) 83 | { 84 | $this->assertEquals($tagSet, ReadPreference::convertTagSets($tagSet)); 85 | } 86 | 87 | public function provideTagSetsAcceptedBySetReadPreference() 88 | { 89 | return [ 90 | [ 91 | [ 92 | ['dc' => 'east', 'use' => 'reporting'], 93 | ['dc' => 'west'], 94 | [], 95 | ], 96 | ], 97 | /* These numeric tag names are likely impractical, but they should 98 | * be accepted by setReadPreference() and thus not modified. 99 | */ 100 | [ 101 | [ 102 | ['0' => 'zero', '1' => 'one'], 103 | [], 104 | ], 105 | ], 106 | ]; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tests/Doctrine/MongoDB/Tests/file.txt: -------------------------------------------------------------------------------- 1 | These are the bytes... --------------------------------------------------------------------------------