Skip to content

Commit 126b906

Browse files
committed
PHPLIB-814: Change stream support for point-in-time pre and post-images
Introduces fullDocumentBeforeChange option. Tests for specifying "whenAvailable" and "required" for both fullDocument and fullDocumentBeforeChange (requires MongoDB 6.0+ with changeStreamPreAndPostImages enabled on the collection). Intentionally omits mention of FULL_DOCUMENT_DEFAULT (related to PHPLIB-808) and FULL_DOCUMENT_BEFORE_CHANGE_OFF constants, since those redundantly specified default behavior. Tests synced with mongodb/specifications#1176
1 parent aefff4c commit 126b906

10 files changed

+1548
-21
lines changed

docs/includes/apiargs-MongoDBClient-method-watch-option.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ source:
1111
file: apiargs-method-watch-option.yaml
1212
ref: fullDocument
1313
---
14+
source:
15+
file: apiargs-method-watch-option.yaml
16+
ref: fullDocumentBeforeChange
17+
---
1418
source:
1519
file: apiargs-method-watch-option.yaml
1620
ref: maxAwaitTimeMS

docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ source:
1111
file: apiargs-method-watch-option.yaml
1212
ref: fullDocument
1313
---
14+
source:
15+
file: apiargs-method-watch-option.yaml
16+
ref: fullDocumentBeforeChange
17+
---
1418
source:
1519
file: apiargs-method-watch-option.yaml
1620
ref: maxAwaitTimeMS

docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ source:
1111
file: apiargs-method-watch-option.yaml
1212
ref: fullDocument
1313
---
14+
source:
15+
file: apiargs-method-watch-option.yaml
16+
ref: fullDocumentBeforeChange
17+
---
1418
source:
1519
file: apiargs-method-watch-option.yaml
1620
ref: maxAwaitTimeMS

docs/includes/apiargs-method-watch-option.yaml

+45-6
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,53 @@ arg_name: option
1313
name: fullDocument
1414
type: string
1515
description: |
16-
Allowed values are 'default' and 'updateLookup'. Defaults to 'default'.
17-
When set to 'updateLookup', the change notification for partial updates will
18-
include both a delta describing the changes to the document, as well as a
19-
copy of the entire document that was changed from some time after the change
20-
occurred. The following values are supported:
16+
Determines how the "fullDocument" response field will be populated for update
17+
operations.
18+
19+
By default, change streams only return the delta of fields (via an
20+
"udateDescription" field) for update operations and "fullDocument" is omitted.
21+
Insert and replace operations always include the "fullDocument" field. Delete
22+
operations omit the field as the document no longer exists.
23+
24+
Specify "updateLookup" to return the current majority-committed version of the
25+
updated document.
26+
27+
MongoDB 6.0+ allows returning the post-image of the modified document if the
28+
collection has ``changeStreamPreAndPostImages`` enabled. Specify
29+
"whenAvailable" to return the post-image if available or a null value if not.
30+
Specify "required" to return the post-image if available or raise an error if
31+
not.
32+
33+
The following values are supported:
2134
22-
- ``MongoDB\Operation\Watch::FULL_DOCUMENT_DEFAULT`` (*default*)
2335
- ``MongoDB\Operation\Watch::FULL_DOCUMENT_UPDATE_LOOKUP``
36+
- ``MongoDB\Operation\Watch::FULL_DOCUMENT_WHEN_AVAILABLE``
37+
- ``MongoDB\Operation\Watch::FULL_DOCUMENT_REQUIRED``
38+
39+
.. note::
40+
41+
This is an option of the ``$changeStream`` pipeline stage.
42+
interface: phpmethod
43+
operation: ~
44+
optional: true
45+
---
46+
arg_name: option
47+
name: fullDocumentBeforeChange
48+
type: string
49+
description: |
50+
Determines how the "fullDocumentBeforeChange" response field will be
51+
populated. By default, the field is omitted.
52+
53+
MongoDB 6.0+ allows returning the pre-image of the modified document if the
54+
collection has ``changeStreamPreAndPostImages`` enabled. Specify
55+
"whenAvailable" to return the pre-image if available or a null value if not.
56+
Specify "required" to return the pre-image if available or raise an error if
57+
not.
58+
59+
The following values are supported:
60+
61+
- ``MongoDB\Operation\Watch::FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE``
62+
- ``MongoDB\Operation\Watch::FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED``
2463
2564
.. note::
2665

src/Operation/Watch.php

+37-9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
5959
{
6060
public const FULL_DOCUMENT_DEFAULT = 'default';
6161
public const FULL_DOCUMENT_UPDATE_LOOKUP = 'updateLookup';
62+
public const FULL_DOCUMENT_WHEN_AVAILABLE = 'whenAvailable';
63+
public const FULL_DOCUMENT_REQUIRED = 'required';
64+
65+
public const FULL_DOCUMENT_BEFORE_CHANGE_OFF = 'off';
66+
public const FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE = 'whenAvailable';
67+
public const FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED = 'required';
6268

6369
/** @var integer */
6470
private static $wireVersionForStartAtOperationTime = 7;
@@ -105,15 +111,33 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
105111
*
106112
* * collation (document): Specifies a collation.
107113
*
108-
* * fullDocument (string): Determines whether the "fullDocument" field
109-
* will be populated for update operations. By default, change streams
110-
* only return the delta of fields during the update operation (via the
111-
* "updateDescription" field). To additionally return the most current
112-
* majority-committed version of the updated document, specify
113-
* "updateLookup" for this option. Defaults to "default".
114+
* * fullDocument (string): Determines how the "fullDocument" response
115+
* field will be populated for update operations.
116+
*
117+
* By default, change streams only return the delta of fields (via an
118+
* "updateDescription" field) for update operations and "fullDocument" is
119+
* omitted. Insert and replace operations always include the
120+
* "fullDocument" field. Delete operations omit the field as the document
121+
* no longer exists.
122+
*
123+
* Specify "updateLookup" to return the current majority-committed
124+
* version of the updated document.
125+
*
126+
* MongoDB 6.0+ allows returning the post-image of the modified document
127+
* if the collection has changeStreamPreAndPostImages enabled. Specify
128+
* "whenAvailable" to return the post-image if available or a null value
129+
* if not. Specify "required" to return the post-image if available or
130+
* raise an error if not.
114131
*
115-
* Insert and replace operations always include the "fullDocument" field
116-
* and delete operations omit the field as the document no longer exists.
132+
* * fullDocumentBeforeChange (string): Determines how the
133+
* "fullDocumentBeforeChange" response field will be populated. By
134+
* default, the field is omitted.
135+
*
136+
* MongoDB 6.0+ allows returning the pre-image of the modified document
137+
* if the collection has changeStreamPreAndPostImages enabled. Specify
138+
* "whenAvailable" to return the pre-image if available or a null value
139+
* if not. Specify "required" to return the pre-image if available or
140+
* raise an error if not.
117141
*
118142
* * maxAwaitTimeMS (integer): The maximum amount of time for the server to
119143
* wait on new documents to satisfy a change stream query.
@@ -181,6 +205,10 @@ public function __construct(Manager $manager, $databaseName, $collectionName, ar
181205
throw InvalidArgumentException::invalidType('"fullDocument" option', $options['fullDocument'], 'string');
182206
}
183207

208+
if (isset($options['fullDocumentBeforeChange']) && ! is_string($options['fullDocumentBeforeChange'])) {
209+
throw InvalidArgumentException::invalidType('"fullDocumentBeforeChange" option', $options['fullDocumentBeforeChange'], 'string');
210+
}
211+
184212
if (! $options['readPreference'] instanceof ReadPreference) {
185213
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
186214
}
@@ -212,7 +240,7 @@ public function __construct(Manager $manager, $databaseName, $collectionName, ar
212240
}
213241

214242
$this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'collation' => 1, 'maxAwaitTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]);
215-
$this->changeStreamOptions = array_intersect_key($options, ['fullDocument' => 1, 'resumeAfter' => 1, 'startAfter' => 1, 'startAtOperationTime' => 1]);
243+
$this->changeStreamOptions = array_intersect_key($options, ['fullDocument' => 1, 'fullDocumentBeforeChange' => 1, 'resumeAfter' => 1, 'startAfter' => 1, 'startAtOperationTime' => 1]);
216244

217245
// Null database name implies a cluster-wide change stream
218246
if ($databaseName === null) {

tests/Operation/WatchTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public function provideInvalidConstructorOptions()
5656
$options[][] = ['fullDocument' => $value];
5757
}
5858

59+
foreach ($this->getInvalidStringValues() as $value) {
60+
$options[][] = ['fullDocumentBeforeChange' => $value];
61+
}
62+
5963
foreach ($this->getInvalidIntegerValues() as $value) {
6064
$options[][] = ['maxAwaitTimeMS' => $value];
6165
}

tests/UnifiedSpecTests/UnifiedSpecTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class UnifiedSpecTest extends FunctionalTestCase
2121
{
2222
/** @var array */
2323
private static $incompleteTests = [
24+
'change-streams/change-streams: to field is set in a rename change event' => 'Not yet implemented (PHPLIB-652, PHPLIB-828)',
25+
'change-streams/change-streams: Test with document comment' => 'Not yet implemented (PHPLIB-749)',
26+
'change-streams/change-streams: Test with string comment' => 'Not yet implemented (PHPLIB-749)',
27+
'change-streams/change-streams: Test that comment is set on getMore' => 'Not yet implemented (PHPLIB-749)',
2428
'command-monitoring/pre-42-server-connection-id: command events do not include server connection id' => 'Not yet implemented (PHPC-1899, PHPLIB-718)',
2529
'command-monitoring/server-connection-id: command events include server connection id' => 'Not yet implemented (PHPC-1899, PHPLIB-718)',
2630
// Many load balancer tests use CMAP events and/or assertNumberConnectionsCheckedOut

tests/UnifiedSpecTests/Util.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ final class Util
7272
Collection::class => [
7373
'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'],
7474
'bulkWrite' => ['requests', 'session', 'ordered', 'bypassDocumentValidation'],
75-
'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS'],
75+
'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'fullDocumentBeforeChange', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS'],
7676
'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'],
7777
'createIndex' => ['keys', 'commitQuorum', 'maxTimeMS', 'name', 'session'],
7878
'dropIndex' => ['name', 'session', 'maxTimeMS'],

0 commit comments

Comments
 (0)