diff --git a/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml
index 1423e28130bc0a39250794f2c7ced27873c64230..c2b4021ef63daa757c1c043bd5c77f8b239d3fcc 100644
--- a/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml
+++ b/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml
@@ -39,8 +39,6 @@ source:
 source:
   file: apiargs-MongoDBCollection-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
@@ -72,7 +70,8 @@ source:
   file: apiargs-MongoDBCollection-common-option.yaml
   ref: writeConcern
 post: |
-  This only applies when the :ref:`$out <agg-out>` stage is specified.
+  This only applies when a :ref:`$out <agg-out>` or :ref:`$merge <agg-merge>`
+  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.
diff --git a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml
index 87f03d29d57ccd5c39d341e3bbca949b053cb590..d7529a608e5a2d887178b15cf5feac2b6ad3667a 100644
--- a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml
+++ b/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml
@@ -33,8 +33,6 @@ source:
 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
@@ -48,7 +46,8 @@ source:
   file: apiargs-MongoDBDatabase-common-option.yaml
   ref: writeConcern
 post: |
-  This only applies when the :ref:`$out <agg-out>` stage is specified.
+  This only applies when a :ref:`$out <agg-out>` or :ref:`$merge <agg-merge>`
+  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.
diff --git a/docs/includes/apiargs-aggregate-option.yaml b/docs/includes/apiargs-aggregate-option.yaml
index 8037899ac2c8bf69385bc95b660555b6976d1424..25b121ac36fb4bce1e3239172eb461aafb5bfb4b 100644
--- a/docs/includes/apiargs-aggregate-option.yaml
+++ b/docs/includes/apiargs-aggregate-option.yaml
@@ -24,7 +24,8 @@ source:
   file: apiargs-MongoDBCollection-common-option.yaml
   ref: bypassDocumentValidation
 post: |
-  This only applies when using the :ref:`$out <agg-out>` stage.
+  This only applies when using the :ref:`$out <agg-out>` and
+  :ref:`$out <agg-merge>` stages.
 
   Document validation requires MongoDB 3.2 or later: if you are using an earlier
   version of MongoDB, this option will be ignored.
diff --git a/src/Collection.php b/src/Collection.php
index 0543ba1e66b3ebf776747425081859be758f248d..0152dac1ecd2dd0ef15c4d8e7e1a3187f4666807 100644
--- a/src/Collection.php
+++ b/src/Collection.php
@@ -69,7 +69,7 @@ class Collection
     private static $wireVersionForFindAndModifyWriteConcern = 4;
     private static $wireVersionForReadConcern = 4;
     private static $wireVersionForWritableCommandWriteConcern = 5;
-    private static $wireVersionForReadConcernWithOutStage = 8;
+    private static $wireVersionForReadConcernWithWriteStage = 8;
 
     private $collectionName;
     private $databaseName;
@@ -190,13 +190,13 @@ class Collection
      */
     public function aggregate(array $pipeline, array $options = [])
     {
-        $hasOutStage = \MongoDB\is_last_pipeline_operator_out($pipeline);
+        $hasWriteStage = \MongoDB\is_last_pipeline_operator_write($pipeline);
 
         if ( ! isset($options['readPreference'])) {
             $options['readPreference'] = $this->readPreference;
         }
 
-        if ($hasOutStage) {
+        if ($hasWriteStage) {
             $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY);
         }
 
@@ -210,7 +210,7 @@ class Collection
         if ( ! isset($options['readConcern']) &&
             \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern) &&
             ! \MongoDB\is_in_transaction($options) &&
-            ( ! $hasOutStage || \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcernWithOutStage))
+            ( ! $hasWriteStage || \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage))
         ) {
             $options['readConcern'] = $this->readConcern;
         }
@@ -219,7 +219,7 @@ class Collection
             $options['typeMap'] = $this->typeMap;
         }
 
-        if ($hasOutStage &&
+        if ($hasWriteStage &&
             ! isset($options['writeConcern']) &&
             \MongoDB\server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) &&
             ! \MongoDB\is_in_transaction($options)) {
diff --git a/src/Database.php b/src/Database.php
index ba89c2a030fba9afcd0f7364375e2b03d9244d48..cf95c7af62035106adc1fc90e47b1f032e638b7a 100644
--- a/src/Database.php
+++ b/src/Database.php
@@ -48,7 +48,7 @@ class Database
     ];
     private static $wireVersionForReadConcern = 4;
     private static $wireVersionForWritableCommandWriteConcern = 5;
-    private static $wireVersionForReadConcernWithOutStage = 8;
+    private static $wireVersionForReadConcernWithWriteStage = 8;
 
     private $databaseName;
     private $manager;
@@ -175,13 +175,13 @@ class Database
      */
     public function aggregate(array $pipeline, array $options = [])
     {
-        $hasOutStage = \MongoDB\is_last_pipeline_operator_out($pipeline);
+        $hasWriteStage = \MongoDB\is_last_pipeline_operator_write($pipeline);
 
         if ( ! isset($options['readPreference'])) {
             $options['readPreference'] = $this->readPreference;
         }
 
-        if ($hasOutStage) {
+        if ($hasWriteStage) {
             $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY);
         }
 
@@ -195,7 +195,7 @@ class Database
         if ( ! isset($options['readConcern']) &&
             \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern) &&
             ! \MongoDB\is_in_transaction($options) &&
-            ( ! $hasOutStage || \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcernWithOutStage))
+            ( ! $hasWriteStage || \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage))
         ) {
             $options['readConcern'] = $this->readConcern;
         }
@@ -204,7 +204,7 @@ class Database
             $options['typeMap'] = $this->typeMap;
         }
 
-        if ($hasOutStage &&
+        if ($hasWriteStage &&
             ! isset($options['writeConcern']) &&
             \MongoDB\server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) &&
             ! \MongoDB\is_in_transaction($options)) {
diff --git a/src/Operation/Aggregate.php b/src/Operation/Aggregate.php
index d27cd368b3083dd34200ea15b5fb2586c315cb34..abe9b886512091ec075c82390d967f01a264c07b 100644
--- a/src/Operation/Aggregate.php
+++ b/src/Operation/Aggregate.php
@@ -264,12 +264,12 @@ class Aggregate implements Executable
 
 
         $hasExplain = ! empty($this->options['explain']);
-        $hasOutStage = \MongoDB\is_last_pipeline_operator_out($this->pipeline);
+        $hasWriteStage = \MongoDB\is_last_pipeline_operator_write($this->pipeline);
 
-        $command = $this->createCommand($server, $hasOutStage);
-        $options = $this->createOptions($hasOutStage, $hasExplain);
+        $command = $this->createCommand($server, $hasWriteStage);
+        $options = $this->createOptions($hasWriteStage, $hasExplain);
 
-        $cursor = ($hasOutStage && ! $hasExplain)
+        $cursor = ($hasWriteStage && ! $hasExplain)
             ? $server->executeReadWriteCommand($this->databaseName, $command, $options)
             : $server->executeReadCommand($this->databaseName, $command, $options);
 
@@ -353,11 +353,11 @@ class Aggregate implements Executable
      *
      * @see http://php.net/manual/en/mongodb-driver-server.executereadcommand.php
      * @see http://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php
-     * @param boolean $hasOutStage
+     * @param boolean $hasWriteStage
      * @param boolean $hasExplain
      * @return array
      */
-    private function createOptions($hasOutStage, $hasExplain)
+    private function createOptions($hasWriteStage, $hasExplain)
     {
         $options = [];
 
@@ -365,7 +365,7 @@ class Aggregate implements Executable
             $options['readConcern'] = $this->options['readConcern'];
         }
 
-        if ( ! $hasOutStage && isset($this->options['readPreference'])) {
+        if (!$hasWriteStage && isset($this->options['readPreference'])) {
             $options['readPreference'] = $this->options['readPreference'];
         }
 
@@ -373,7 +373,7 @@ class Aggregate implements Executable
             $options['session'] = $this->options['session'];
         }
 
-        if ($hasOutStage && ! $hasExplain && isset($this->options['writeConcern'])) {
+        if ($hasWriteStage && ! $hasExplain && isset($this->options['writeConcern'])) {
             $options['writeConcern'] = $this->options['writeConcern'];
         }
 
diff --git a/src/functions.php b/src/functions.php
index 71fca5504391c3d5f20db45dd5119c75edc90a87..2ec5790ddec187c879020667cf4d4cc4c585437d 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -127,7 +127,7 @@ function is_in_transaction(array $options)
 }
 
 /**
- * Return whether the aggregation pipeline ends with an $out operator.
+ * Return whether the aggregation pipeline ends with an $out or $merge operator.
  *
  * This is used for determining whether the aggregation pipeline must be
  * executed against a primary server.
@@ -136,7 +136,7 @@ function is_in_transaction(array $options)
  * @param array $pipeline List of pipeline operations
  * @return boolean
  */
-function is_last_pipeline_operator_out(array $pipeline)
+function is_last_pipeline_operator_write(array $pipeline)
 {
     $lastOp = end($pipeline);
 
@@ -146,7 +146,7 @@ function is_last_pipeline_operator_out(array $pipeline)
 
     $lastOp = (array) $lastOp;
 
-    return key($lastOp) === '$out';
+    return in_array(key($lastOp), ['$out', '$merge'], true);
 }
 
 /**
diff --git a/tests/Collection/CrudSpecFunctionalTest.php b/tests/Collection/CrudSpecFunctionalTest.php
index aed43f2c688fe02da7113c1d93ebddabe7845c5a..af741082ab72438809d89e88168664a7931f277f 100644
--- a/tests/Collection/CrudSpecFunctionalTest.php
+++ b/tests/Collection/CrudSpecFunctionalTest.php
@@ -292,7 +292,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
                  * the result here; however, assertEquivalentCollections() will
                  * assert the output collection's contents later.
                  */
-                if ( ! \MongoDB\is_last_pipeline_operator_out($operation['arguments']['pipeline'])) {
+                if ( ! \MongoDB\is_last_pipeline_operator_write($operation['arguments']['pipeline'])) {
                     $this->assertSameDocuments($expectedResult, $actualResult);
                 }
                 break;
diff --git a/tests/SpecTests/Context.php b/tests/SpecTests/Context.php
index 37da8774da1b1ee44e546fb4055456fca8594943..b223ace1bc0f033fee7833d1e807249670ea26a6 100644
--- a/tests/SpecTests/Context.php
+++ b/tests/SpecTests/Context.php
@@ -63,6 +63,15 @@ final class Context
             $o->outcomeCollectionName = $test->outcome->collection->name;
         }
 
+        $o->defaultWriteOptions = [
+            'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
+        ];
+
+        $o->outcomeFindOptions = [
+            'readConcern' => new ReadConcern('local'),
+            'readPreference' => new ReadPreference('primary'),
+        ];
+
         $o->client = new Client(FunctionalTestCase::getUri(), $clientOptions);
 
         return $o;
diff --git a/tests/SpecTests/CrudSpecTest.php b/tests/SpecTests/CrudSpecTest.php
index 277a554180982f5ab3da84c85410dbc9008a0f30..efd3e8802c8921f2b8e0460285d437107c3878a8 100644
--- a/tests/SpecTests/CrudSpecTest.php
+++ b/tests/SpecTests/CrudSpecTest.php
@@ -14,11 +14,6 @@ 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',
         'bulkWrite-arrayFilters: BulkWrite with arrayFilters' => 'Fails due to command assertions',
         'updateWithPipelines: UpdateOne using pipelines' => 'PHPLIB-418',
         'updateWithPipelines: UpdateMany using pipelines' => 'PHPLIB-418',
diff --git a/tests/SpecTests/Operation.php b/tests/SpecTests/Operation.php
index c94c70691ee7dbb56e47ec268d1d9460064a2dfd..d920db0061ca55556e18f2a4a23f5dcc384bb089 100644
--- a/tests/SpecTests/Operation.php
+++ b/tests/SpecTests/Operation.php
@@ -415,7 +415,7 @@ final class Operation
                  * the CRUD specification and is not implemented in the library
                  * since we have no concept of lazy cursors. Rely on examining
                  * the output collection rather than the operation result. */
-                if (\MongoDB\is_last_pipeline_operator_out($this->arguments['pipeline'])) {
+                if (\MongoDB\is_last_pipeline_operator_write($this->arguments['pipeline'])) {
                     return ResultExpectation::ASSERT_NOTHING;
                 }