PHPLIB-402: Add aggregation helper to Database class

parent a9513dce
arg_name: option
name: allowDiskUse
type: boolean
description: |
Enables writing to temporary files. When set to ``true``, aggregation stages
can write data to the ``_tmp`` sub-directory in the ``dbPath`` directory. The
default is ``false``.
interface: phpmethod
operation: ~
optional: true
source:
file: apiargs-aggregate-option.yaml
ref: allowDiskUse
---
arg_name: option
name: batchSize
type: integer
description: |
Specifies the initial batch size for the cursor. A batchSize of ``0`` means an
empty first batch and is useful for quickly returning a cursor or failure
message without doing significant server-side work.
interface: phpmethod
operation: ~
optional: true
source:
file: apiargs-aggregate-option.yaml
ref: batchSize
---
source:
file: apiargs-MongoDBCollection-common-option.yaml
file: apiargs-aggregate-option.yaml
ref: bypassDocumentValidation
post: |
This only applies when using the :ref:`$out <agg-out>` stage.
Document validation requires MongoDB 3.2 or later: if you are using an earlier
version of MongoDB, this option will be ignored.
---
arg_name: option
name: comment
type: string
description: |
Users can specify an arbitrary string to help trace the operation through the
database profiler, currentOp, and logs.
source:
file: apiargs-aggregate-option.yaml
ref: comment
post: |
.. versionadded:: 1.3
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: explain
type: boolean
description: |
Specifies whether or not to return the information on the processing of the
pipeline.
source:
file: apiargs-aggregate-option.yaml
ref: explain
post: |
.. versionadded:: 1.4
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: hint
type: string|array|object
description: |
The index to use. Specify either the index name as a string or the index key
pattern as a document. If specified, then the query system will only consider
plans using the hinted index.
source:
file: apiargs-aggregate-option.yaml
ref: hint
post: |
.. versionadded:: 1.3
interface: phpmethod
operation: ~
optional: true
---
source:
file: apiargs-common-option.yaml
......@@ -96,6 +58,12 @@ type: boolean
description: |
Indicates whether the command will request that the server provide results
using a cursor. The default is ``true``.
.. note::
MongoDB 3.6+ no longer supports returning results without a cursor (excluding
when the explain option is used) and specifying false for this option will
result in a server error.
interface: phpmethod
operation: ~
optional: true
......
source:
file: apiargs-aggregate-option.yaml
ref: allowDiskUse
---
source:
file: apiargs-aggregate-option.yaml
ref: batchSize
---
source:
file: apiargs-aggregate-option.yaml
ref: bypassDocumentValidation
---
source:
file: apiargs-aggregate-option.yaml
ref: comment
---
source:
file: apiargs-aggregate-option.yaml
ref: explain
---
source:
file: apiargs-aggregate-option.yaml
ref: hint
---
source:
file: apiargs-common-option.yaml
ref: maxTimeMS
---
source:
file: apiargs-MongoDBDatabase-common-option.yaml
ref: readConcern
---
source:
file: apiargs-MongoDBDatabase-common-option.yaml
ref: readPreference
post: |
This option will be ignored when using the :ref:`$out <agg-out>` stage.
---
source:
file: apiargs-common-option.yaml
ref: session
---
source:
file: apiargs-MongoDBDatabase-common-option.yaml
ref: typeMap
---
source:
file: apiargs-MongoDBDatabase-common-option.yaml
ref: writeConcern
post: |
This only applies when the :ref:`$out <agg-out>` stage is specified.
This is not supported for server versions prior to 3.4 and will result in an
exception at execution time if used.
...
arg_name: param
name: $pipeline
type: array
description: |
Specifies an :manual:`aggregation pipeline </core/aggregation-pipeline>`
operation.
interface: phpmethod
operation: ~
optional: false
---
source:
file: apiargs-common-param.yaml
ref: $options
...
arg_name: option
name: allowDiskUse
type: boolean
description: |
Enables writing to temporary files. When set to ``true``, aggregation stages
can write data to the ``_tmp`` sub-directory in the ``dbPath`` directory. The
default is ``false``.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: batchSize
type: integer
description: |
Specifies the initial batch size for the cursor. A batchSize of ``0`` means an
empty first batch and is useful for quickly returning a cursor or failure
message without doing significant server-side work.
interface: phpmethod
operation: ~
optional: true
---
source:
file: apiargs-MongoDBCollection-common-option.yaml
ref: bypassDocumentValidation
post: |
This only applies when using the :ref:`$out <agg-out>` stage.
Document validation requires MongoDB 3.2 or later: if you are using an earlier
version of MongoDB, this option will be ignored.
---
arg_name: option
name: comment
type: string
description: |
Users can specify an arbitrary string to help trace the operation through the
database profiler, currentOp, and logs.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: explain
type: boolean
description: |
Specifies whether or not to return the information on the processing of the
pipeline.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: hint
type: string|array|object
description: |
The index to use. Specify either the index name as a string or the index key
pattern as a document. If specified, then the query system will only consider
plans using the hinted index.
interface: phpmethod
operation: ~
optional: true
...
......@@ -45,6 +45,7 @@ Methods
/reference/method/MongoDBDatabase__construct
/reference/method/MongoDBDatabase__get
/reference/method/MongoDBDatabase-aggregate
/reference/method/MongoDBDatabase-command
/reference/method/MongoDBDatabase-createCollection
/reference/method/MongoDBDatabase-drop
......
......@@ -45,7 +45,7 @@ Errors/Exceptions
.. include:: /includes/extracts/error-invalidargumentexception.rst
.. include:: /includes/extracts/error-driver-runtimeexception.rst
.. _php-agg-method-behavior:
.. _php-coll-agg-method-behavior:
Behavior
--------
......@@ -63,6 +63,7 @@ value will be :php:`Traversable <traversable>`.
See Also
--------
- :phpmethod:`MongoDB\\Database::aggregate()`
- :manual:`aggregate </reference/command/aggregate>` command reference in the
MongoDB manual
- :manual:`Aggregation Pipeline </core/aggregation-pipeline>` documentation in
......
==============================
MongoDB\\Database::aggregate()
==============================
.. versionadded:: 1.5
.. default-domain:: mongodb
.. contents:: On this page
:local:
:backlinks: none
:depth: 1
:class: singlecol
Definition
----------
.. phpmethod:: MongoDB\\Database::aggregate()
Runs a specified :manual:`admin/diagnostic pipeline
</reference/operator/aggregation-pipeline/#db-aggregate-stages>` which does
not require an underlying collection. For aggregations on collection data,
see :phpmethod:`MongoDB\\Collection::aggregate()`.
.. code-block:: php
function aggregate(array $pipeline, array $options = []): Traversable
This method has the following parameters:
.. include:: /includes/apiargs/MongoDBDatabase-method-aggregate-param.rst
The ``$options`` parameter supports the following options:
.. include:: /includes/apiargs/MongoDBDatabase-method-aggregate-option.rst
Return Values
-------------
A :php:`MongoDB\\Driver\\Cursor <class.mongodb-driver-cursor>` or
:php:`ArrayIterator <arrayiterator>` object. In both cases, the return value
will be :php:`Traversable <traversable>`.
Errors/Exceptions
-----------------
.. include:: /includes/extracts/error-unexpectedvalueexception.rst
.. include:: /includes/extracts/error-unsupportedexception.rst
.. include:: /includes/extracts/error-invalidargumentexception.rst
.. include:: /includes/extracts/error-driver-runtimeexception.rst
.. _php-db-agg-method-behavior:
.. todo: add examples
See Also
--------
- :phpmethod:`MongoDB\\Collection::aggregate()`
- :manual:`aggregate </reference/command/aggregate>` command reference in the
MongoDB manual
- :manual:`Aggregation Pipeline </core/aggregation-pipeline>` documentation in
the MongoDB Manual
......@@ -401,7 +401,7 @@ The |php-library|\'s :phpmethod:`MongoDB\\Collection::aggregate()` method
returns a :php:`Traversable <traversable>` object, which you can iterate upon to
access the results of the aggregation operation. Refer to the
:phpmethod:`MongoDB\\Collection::aggregate()` method's :ref:`behavior
reference <php-agg-method-behavior>` for more about the method's output.
reference <php-coll-agg-method-behavior>` for more about the method's output.
The following example lists the 5 US states with the most zip codes associated
with them:
......
......@@ -25,9 +25,11 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\GridFS\Bucket;
use MongoDB\Model\CollectionInfoIterator;
use MongoDB\Operation\Aggregate;
use MongoDB\Operation\CreateCollection;
use MongoDB\Operation\DatabaseCommand;
use MongoDB\Operation\DropCollection;
......@@ -35,6 +37,7 @@ use MongoDB\Operation\DropDatabase;
use MongoDB\Operation\ListCollections;
use MongoDB\Operation\ModifyCollection;
use MongoDB\Operation\Watch;
use Traversable;
class Database
{
......@@ -155,6 +158,62 @@ class Database
return $this->databaseName;
}
/**
* Runs an aggregation framework pipeline on the database for pipeline
* stages that do not require an underlying collection, such as $currentOp
* and $listLocalSessions. Requires MongoDB >= 3.6
*
* @see Aggregate::__construct() for supported options
* @param array $pipeline List of pipeline operations
* @param array $options Command options
* @return Traversable
* @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if options are not supported by the selected server
* @throws InvalidArgumentException for parameter/option parsing errors
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/
public function aggregate(array $pipeline, array $options = [])
{
$hasOutStage = \MongoDB\is_last_pipeline_operator_out($pipeline);
if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference;
}
if ($hasOutStage) {
$options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY);
}
$server = $this->manager->selectServer($options['readPreference']);
/* A "majority" read concern is not compatible with the $out stage, so
* avoid providing the Collection's read concern if it would conflict.
*
* A read concern is also not compatible with transactions.
*/
if ( ! isset($options['readConcern']) &&
! ($hasOutStage && $this->readConcern->getLevel() === ReadConcern::MAJORITY) &&
\MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern) &&
! \MongoDB\is_in_transaction($options)) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}
if ($hasOutStage &&
! isset($options['writeConcern']) &&
\MongoDB\server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) &&
! \MongoDB\is_in_transaction($options)) {
$options['writeConcern'] = $this->writeConcern;
}
$operation = new Aggregate($this->databaseName, null, $pipeline, $options);
return $operation->execute($server);
}
/**
* Execute a command on this database.
*
......
......@@ -64,6 +64,16 @@ class CommandExpectations implements CommandSubscriber
return new self($expectedEvents);
}
public static function fromCrud(array $expectedEvents)
{
$o = new self($expectedEvents);
$o->ignoreCommandFailed = true;
$o->ignoreCommandSucceeded = true;
return $o;
}
public static function fromTransactions(array $expectedEvents)
{
$o = new self($expectedEvents);
......
......@@ -53,6 +53,17 @@ final class Context
return $o;
}
public static function fromCrud(stdClass $test, $databaseName, $collectionName)
{
$o = new self($databaseName, $collectionName);
$clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : [];
$o->client = new Client(FunctionalTestCase::getUri(), $clientOptions);
return $o;
}
public static function fromRetryableWrites(stdClass $test, $databaseName, $collectionName)
{
$o = new self($databaseName, $collectionName);
......
<?php
namespace MongoDB\Tests\SpecTests;
use stdClass;
/**
* Crud spec tests.
*
* @see https://github.com/mongodb/specifications/tree/master/source/crud
*/
class CrudSpecTest extends FunctionalTestCase
{
/* These should all pass before the driver can be considered compatible with
* MongoDB 4.2. */
private static $incompleteTests = [
'aggregate-merge: Aggregate with $merge' => 'PHPLIB-438',
'aggregate-merge: Aggregate with $merge and batch size of 0' => 'PHPLIB-438',
'aggregate-merge: Aggregate with $merge and majority readConcern' => 'PHPLIB-438',
'aggregate-merge: Aggregate with $merge and local readConcern' => 'PHPLIB-438',
'aggregate-merge: Aggregate with $merge and available readConcern' => 'PHPLIB-438',
'aggregate-out-readConcern: readConcern majority with out stage' => 'PHPLIB-431',
'aggregate-out-readConcern: readConcern local with out stage' => 'PHPLIB-431',
'aggregate-out-readConcern: readConcern available with out stage' => 'PHPLIB-431',
'aggregate-out-readConcern: readConcern linearizable with out stage' => 'PHPLIB-431',
'aggregate-out-readConcern: invalid readConcern with out stage' => 'PHPLIB-431',
'bulkWrite-arrayFilters: BulkWrite with arrayFilters' => 'Fails due to command assertions',
'updateWithPipelines: UpdateOne using pipelines' => 'PHPLIB-418',
'updateWithPipelines: UpdateMany using pipelines' => 'PHPLIB-418',
'updateWithPipelines: FindOneAndUpdate using pipelines' => 'PHPLIB-418',
];
/**
* Assert that the expected and actual command documents match.
*
* Note: this method may modify the $expected object.
*
* @param stdClass $expected Expected command document
* @param stdClass $actual Actual command document
*/
public static function assertCommandMatches(stdClass $expected, stdClass $actual)
{
static::assertDocumentsMatch($expected, $actual);
}
/**
* Execute an individual test case from the specification.
*
* @dataProvider provideTests
* @param string $name Test name
* @param stdClass $test Individual "tests[]" document
* @param array $runOn Top-level "runOn" array with server requirements
* @param array $data Top-level "data" array to initialize collection
* @param string $databaseName Name of database under test
* @param string $collectionName Name of collection under test
*/
public function testCrud($name, stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null)
{
if (isset(self::$incompleteTests[$name])) {
$this->markTestIncomplete(self::$incompleteTests[$name]);
}
if (isset($runOn)) {
$this->checkServerRequirements($runOn);
}
if (isset($test->skipReason)) {
$this->markTestSkipped($test->skipReason);
}
$databaseName = isset($databaseName) ? $databaseName : $this->getDatabaseName();
$collectionName = isset($collectionName) ? $collectionName : $this->getCollectionName();
$context = Context::fromCrud($test, $databaseName, $collectionName);
$this->setContext($context);
$this->dropTestAndOutcomeCollections();
$this->insertDataFixtures($data);
if (isset($test->failPoint)) {
$this->configureFailPoint($test->failPoint);
}
if (isset($test->expectations)) {
$commandExpectations = CommandExpectations::fromCrud($test->expectations);
$commandExpectations->startMonitoring();
}
foreach ($test->operations as $operation) {
Operation::fromCrud($operation)->assert($this, $context);
}
if (isset($commandExpectations)) {
$commandExpectations->stopMonitoring();
$commandExpectations->assert($this, $context);
}
if (isset($test->outcome->collection->data)) {
$this->assertOutcomeCollectionData($test->outcome->collection->data);
}
}
public function provideTests()
{
$testArgs = [];
foreach (glob(__DIR__ . '/crud/*.json') as $filename) {
$json = $this->decodeJson(file_get_contents($filename));
$group = basename($filename, '.json');
$runOn = isset($json->runOn) ? $json->runOn : null;
$data = isset($json->data) ? $json->data : [];
$databaseName = isset($json->database_name) ? $json->database_name : null;
$collectionName = isset($json->collection_name) ? $json->collection_name : null;
foreach ($json->tests as $test) {
$name = $group . ': ' . $test->description;
$testArgs[$name] = [$name, $test, $runOn, $data, $databaseName, $collectionName];
}
}
return $testArgs;
}
}
......@@ -33,7 +33,6 @@ final class Operation
private $collectionName;
private $collectionOptions = [];
private $databaseName;
private $databaseOptions = [];
private $name;
private $object = self::OBJECT_COLLECTION;
......@@ -101,6 +100,19 @@ final class Operation
return $o;
}
public static function fromCrud(stdClass $operation)
{
$o = new self($operation);
$o->resultExpectation = ResultExpectation::fromCrud($operation, $o->getResultAssertionType());
if (isset($operation->collectionOptions)) {
$o->collectionOptions = (array) $operation->collectionOptions;
}
return $o;
}
public static function fromRetryableWrites(stdClass $operation, stdClass $outcome)
{
$o = new self($operation);
......@@ -122,10 +134,6 @@ final class Operation
$o->collectionOptions = (array) $operation->collectionOptions;
}
if (isset($operation->databaseOptions)) {
$o->databaseOptions = (array) $operation->databaseOptions;
}
return $o;
}
......@@ -184,7 +192,7 @@ final class Operation
return $this->executeForCollection($collection, $context);
case self::OBJECT_DATABASE:
$database = $context->getDatabase($this->databaseOptions);
$database = $context->getDatabase();
return $this->executeForDatabase($database, $context);
case self::OBJECT_SELECT_COLLECTION:
......@@ -192,7 +200,7 @@ final class Operation
return $this->executeForCollection($collection, $context);
case self::OBJECT_SELECT_DATABASE:
$database = $context->selectDatabase($this->databaseName, $this->databaseOptions);
$database = $context->selectDatabase($this->databaseName);
return $this->executeForDatabase($database, $context);
case self::OBJECT_SESSION0:
......@@ -332,6 +340,12 @@ final class Operation
$context->replaceArgumentSessionPlaceholder($args);
switch ($this->name) {
case 'aggregate':
return $database->aggregate(
$args['pipeline'],
array_diff_key($args, ['pipeline' => 1])
);
case 'runCommand':
return $database->command(
$args['command'],
......@@ -453,6 +467,9 @@ final class Operation
private function getResultAssertionTypeForDatabase()
{
switch ($this->name) {
case 'aggregate':
return ResultExpectation::ASSERT_SAME_DOCUMENTS;
case 'runCommand':
return ResultExpectation::ASSERT_MATCHES_DOCUMENT;
......
......@@ -72,6 +72,19 @@ final class ResultExpectation
return $o;
}
public static function fromCrud(stdClass $operation, $defaultAssertionType)
{
if (property_exists($operation, 'result') && !self::isErrorResult($operation->result)) {
$assertionType = $operation->result === null ? self::ASSERT_NULL : $defaultAssertionType;
$expectedValue = $operation->result;
} else {
$assertionType = self::ASSERT_NOTHING;
$expectedValue = null;
}
return new self($assertionType, $expectedValue);
}
public static function fromRetryableWrites(stdClass $outcome, $defaultAssertionType)
{
if (property_exists($outcome, 'result')) {
......
{
"runOn": [
{
"minServerVersion": "4.1.11"
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"collection_name": "test_aggregate_merge",
"tests": [
{
"description": "Aggregate with $merge",
"operations": [
{
"object": "collection",
"name": "aggregate",
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_merge",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
]
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "Aggregate with $merge and batch size of 0",
"operations": [
{
"object": "collection",
"name": "aggregate",
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
],
"batchSize": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_merge",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
],
"cursor": {}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "Aggregate with $merge and majority readConcern",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "majority"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_merge",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
],
"readConcern": {
"level": "majority"
}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "Aggregate with $merge and local readConcern",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "local"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_merge",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
],
"readConcern": {
"level": "local"
}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "Aggregate with $merge and available readConcern",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "available"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_merge",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "other_test_collection"
}
}
],
"readConcern": {
"level": "available"
}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
}
]
}
{
"runOn": [
{
"minServerVersion": "4.1.0",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"collection_name": "test_aggregate_out_readconcern",
"tests": [
{
"description": "readConcern majority with out stage",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "majority"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_out_readconcern",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"readConcern": {
"level": "majority"
}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "readConcern local with out stage",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "local"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_out_readconcern",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"readConcern": {
"level": "local"
}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "readConcern available with out stage",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "available"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_out_readconcern",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"readConcern": {
"level": "available"
}
}
}
}
],
"outcome": {
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "readConcern linearizable with out stage",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "linearizable"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
]
},
"error": true
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_out_readconcern",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"readConcern": {
"level": "linearizable"
}
}
}
}
]
},
{
"description": "invalid readConcern with out stage",
"operations": [
{
"object": "collection",
"name": "aggregate",
"collectionOptions": {
"readConcern": {
"level": "!invalid123"
}
},
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
]
},
"error": true
}
],
"expectations": [
{
"command_started_event": {
"command": {
"aggregate": "test_aggregate_out_readconcern",
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"readConcern": {
"level": "!invalid123"
}
}
}
}
]
}
]
}
{
"runOn": [
{
"minServerVersion": "3.5.6"
}
],
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
}
],
"collection_name": "test",
"database_name": "crud-tests",
"tests": [
{
"description": "BulkWrite with arrayFilters",
"operations": [
{
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "updateOne",
"arguments": {
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
},
"arrayFilters": [
{
"i.b": 3
}
]
}
},
{
"name": "updateMany",
"arguments": {
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
},
"arrayFilters": [
{
"i.b": 1
}
]
}
}
],
"options": {
"ordered": true
}
},
"result": {
"deletedCount": 0,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 3,
"modifiedCount": 3,
"upsertedCount": 0,
"upsertedIds": {}
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test",
"updates": [
{
"q": {},
"u": {
"$set": {
"y.$[i].b": 2
}
},
"arrayFilters": [
{
"i.b": 3
}
]
},
{
"q": {},
"u": {
"$set": {
"y.$[i].b": 2
}
},
"multi": true,
"arrayFilters": [
{
"i.b": 1
}
]
}
],
"ordered": true
},
"command_name": "update",
"database_name": "crud-tests"
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 2
},
{
"b": 2
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 2
}
]
}
]
}
}
}
]
}
{
"runOn": [
{
"minServerVersion": "3.6.0"
}
],
"database_name": "admin",
"tests": [
{
"description": "Aggregate with $listLocalSessions",
"operations": [
{
"name": "aggregate",
"object": "database",
"arguments": {
"pipeline": [
{
"$listLocalSessions": {}
},
{
"$limit": 1
},
{
"$addFields": {
"dummy": "dummy field"
}
},
{
"$project": {
"_id": 0,
"dummy": 1
}
}
]
},
"result": [
{
"dummy": "dummy field"
}
]
}
]
},
{
"description": "Aggregate with $listLocalSessions and allowDiskUse",
"operations": [
{
"name": "aggregate",
"object": "database",
"arguments": {
"pipeline": [
{
"$listLocalSessions": {}
},
{
"$limit": 1
},
{
"$addFields": {
"dummy": "dummy field"
}
},
{
"$project": {
"_id": 0,
"dummy": 1
}
}
],
"allowDiskUse": true
},
"result": [
{
"dummy": "dummy field"
}
]
}
]
}
]
}
{
"data": [
{
"_id": 1,
"x": 1,
"y": 1,
"t": {
"u": {
"v": 1
}
}
},
{
"_id": 2,
"x": 2,
"y": 1
}
],
"minServerVersion": "4.1.11",
"collection_name": "test",
"database_name": "crud-tests",
"tests": [
{
"description": "UpdateOne using pipelines",
"operations": [
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": [
{
"$replaceRoot": {
"newRoot": "$t"
}
},
{
"$addFields": {
"foo": 1
}
}
]
},
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test",
"updates": [
{
"q": {
"_id": 1
},
"u": [
{
"$replaceRoot": {
"newRoot": "$t"
}
},
{
"$addFields": {
"foo": 1
}
}
]
}
]
},
"command_name": "update",
"database_name": "crud-tests"
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"u": {
"v": 1
},
"foo": 1
},
{
"_id": 2,
"x": 2,
"y": 1
}
]
}
}
},
{
"description": "UpdateMany using pipelines",
"operations": [
{
"name": "updateMany",
"arguments": {
"filter": {},
"update": [
{
"$project": {
"x": 1
}
},
{
"$addFields": {
"foo": 1
}
}
]
},
"result": {
"matchedCount": 2,
"modifiedCount": 2,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test",
"updates": [
{
"q": {},
"u": [
{
"$project": {
"x": 1
}
},
{
"$addFields": {
"foo": 1
}
}
],
"multi": true
}
]
},
"command_name": "update",
"database_name": "crud-tests"
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 1,
"foo": 1
},
{
"_id": 2,
"x": 2,
"foo": 1
}
]
}
}
},
{
"description": "FindOneAndUpdate using pipelines",
"operations": [
{
"name": "findOneAndUpdate",
"arguments": {
"filter": {
"_id": 1
},
"update": [
{
"$project": {
"x": 1
}
},
{
"$addFields": {
"foo": 1
}
}
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"findAndModify": "test",
"update": [
{
"$project": {
"x": 1
}
},
{
"$addFields": {
"foo": 1
}
}
]
},
"command_name": "findAndModify",
"database_name": "crud-tests"
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 1,
"foo": 1
},
{
"_id": 2,
"x": 2,
"y": 1
}
]
}
}
}
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment