From 195044adf98646776495ee97cacbd5041559a948 Mon Sep 17 00:00:00 2001 From: norareidy Date: Thu, 9 Jan 2025 11:16:01 -0500 Subject: [PATCH 1/3] DOCSP-45200: Change streams --- source/includes/read/change-streams.rb | 48 +++++ source/read.txt | 1 + source/read/change-streams.txt | 249 +++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 source/includes/read/change-streams.rb create mode 100644 source/read/change-streams.txt diff --git a/source/includes/read/change-streams.rb b/source/includes/read/change-streams.rb new file mode 100644 index 00000000..aabf8bb4 --- /dev/null +++ b/source/includes/read/change-streams.rb @@ -0,0 +1,48 @@ +require 'bundler/inline' + +gemfile do + source 'https://rubygems.org' + gem 'mongo' +end + +uri = '' + +Mongo::Client.new(uri) do |client| + # Accesses the database and collection + # start-db-coll + database = client.use('sample_restaurants') + collection = database[:restaurants] + # end-db-coll + + # Monitors and prints changes to the "restaurants" collection + # start-open-change-stream + stream = collection.watch + enum = stream.to_enum + while (doc = enum.next) + puts doc + break if doc['operationType'] == 'invalidate' + end + # end-open-change-stream + + # Passes a pipeline argument to watch to monitor only update operations + # start-change-stream-pipeline + pipeline = [{ '$match' => { 'operationType' => 'update' } }] + stream = collection.watch(pipeline) + enum = stream.to_enum + while (doc = enum.next) + puts doc + break if doc['operationType'] == 'invalidate' + end + # end-change-stream-pipeline + + # Passes an options argument to watch to include the post-image of updated documents + # start-change-stream-post-image + options = { full_document: 'updateLookup' } + stream = collection.watch([], options) + enum = stream.to_enum + while (doc = enum.next) + puts doc + break if doc['operationType'] == 'invalidate' + end + # end-change-stream-post-image +end diff --git a/source/read.txt b/source/read.txt index e248219f..1d0717ec 100644 --- a/source/read.txt +++ b/source/read.txt @@ -28,3 +28,4 @@ Read Data from MongoDB Specify Fields to Return Distinct Field Values Count Documents + Monitor Changes diff --git a/source/read/change-streams.txt b/source/read/change-streams.txt new file mode 100644 index 00000000..4dea8385 --- /dev/null +++ b/source/read/change-streams.txt @@ -0,0 +1,249 @@ +.. _ruby-change-streams: + +==================== +Monitor Data Changes +==================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: watch, code example + +Overview +-------- + +In this guide, you can learn how to use a **change stream** to monitor real-time +changes to your data. A change stream is a {+mdb-server+} feature that +allows your application to subscribe to data changes on a collection, database, +or deployment. + +When using the {+driver-short+}, you can call the ``watch`` method to return a +``ChangeStream`` object. Then, you can iterate through the change stream +to monitor data changes, such as updates, insertions, and deletions. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``restaurants`` collection in the ``sample_restaurants`` +database from the :atlas:`Atlas sample datasets `. To access this collection +from your {+language+} application, create a ``Mongo::Client`` object that connects to +an Atlas cluster and assign the following values to your ``database`` and ``collection`` +variables: + +.. literalinclude:: /includes/read/change-streams.rb + :language: ruby + :dedent: + :start-after: start-db-coll + :end-before: end-db-coll + +To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see the +:atlas:`Get Started with Atlas ` guide. + +Open a Change Stream +-------------------- + +To open a change stream, call the ``watch`` method. The object on which you +call the ``watch`` method determines the scope of events that the change +stream monitors. You can call the ``watch`` method on the following objects + +- ``Mongo::Client``: Monitor all changes in the MongoDB deployment +- ``Mongo::Database``: Monitor changes in all collections in the database +- ``Mongo::Collection``: Monitor changes in the collection + +The following example opens a change stream on the ``restaurants`` collection +and outputs changes as they occur: + +.. literalinclude:: /includes/read/change-streams.rb + :start-after: start-open-change-stream + :end-before: end-open-change-stream + :language: ruby + :dedent: + +To begin watching for changes, run the preceding code. Then, in a separate +shell, modify the ``restaurants`` collection. The following example updates +a document that has a ``name`` field value of ``'Blarney Castle'``: + +.. _ruby-change-stream-update: + +.. literalinclude:: /includes/read/change-streams.rb + :start-after: start-update-for-change-stream + :end-before: end-update-for-change-stream + :language: ruby + :dedent: + +When you update the collection, the change stream application prints the change +as it occurs. The printed change event resembles the following output: + +.. code-block:: none + :copyable: false + + {"_id"=>{"_data"=>"..."}, "operationType"=>"update", "clusterTime"=>#<...>, + "ns"=>{"db"=>"sample_restaurants", "coll"=>"restaurants"}, "documentKey"=> + {"_id"=>BSON::ObjectId('...')}, "updateDescription"=>{"updatedFields"=> + {"cuisine"=>"Irish"}, "removedFields"=>[], "truncatedArrays"=>[]}} + +Modify the Change Stream Output +------------------------------- + +To modify the change stream output, you can pass pipeline stages in an array as a +parameter to the ``watch`` method. You can include the following stages in the +array: + +- ``$addFields`` or ``$set``: Adds new fields to documents +- ``$match``: Filters the documents +- ``$project``: Projects a subset of the document fields +- ``$replaceWith`` or ``$replaceRoot``: Replaces the input document with the + specified document +- ``$redact``: Restricts the contents of the documents +- ``$unset``: Removes fields from documents + +The following example passes a pipeline that includes the ``$match`` stage to the +``watch`` method. This instructs the ``watch`` method to output events only +when update operations occur: + +.. literalinclude:: /includes/read/change-streams.rb + :start-after: start-change-stream-pipeline + :end-before: end-change-stream-pipeline + :language: ruby + :dedent: + +Modify watch() Behavior +----------------------- + +To modify the behavior of the ``watch()`` method, you can pass an options hash +as a parameter to ``watch``. The following table describes some of the options that +you can set: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Option + - Description + + * - ``full_document`` + - | Specifies whether to show the full document after the change, rather + than showing only the changes made to the document. To learn more about + this option, see the :ref:`ruby-change-stream-pre-post-image` section of this + guide. + + * - ``full_document_before_change`` + - | Specifies whether to show the full document as it was before the change, rather + than showing only the changes made to the document. To learn more about + this option, see :ref:`ruby-change-stream-pre-post-image`. + + * - ``resume_after`` + - | Specifies the logical starting point for the change stream. + | This option is mutually exclusive with ``start_at_operation_time``. + + * - ``start_at_operation_time`` + - | Instructs the change stream to only provide changes that occurred at or after + the specified timestamp. + | This option is mutually exclusive with ``resume_after``. + + * - ``collation`` + - | Sets the collation to use for the change stream cursor. + +For a full list of ``watch`` options, see `watch <{+api-root+}/Mongo/Collection.html#watch-instance_method>`__ +in the API documentation. + +.. _ruby-change-stream-pre-post-image: + +Include Pre-Images and Post-Images +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. important:: + + You can enable pre-images and post-images on collections only if your + deployment uses MongoDB v6.0 or later. + +By default, when you perform an operation on a collection, the corresponding +change event includes only the modified fields and their values before and +after the operation. + +You can instruct the ``watch`` method to return the document's **pre-image**, the +full version of the document *before* changes, in addition to the modified fields. To +include the pre-image in the change stream event, pass an options hash to ``watch`` +that sets the ``full_document_before_change`` option. You can set this option to the +following string values: + +- ``'whenAvailable'``: The change event includes a pre-image + of the modified document for change events. If the pre-image is not available, this + change event field has a ``nil`` value. +- ``'required'``: The change event includes a pre-image + of the modified document for change events. If the pre-image is not available, the + server raises an error. +- ``'off'``: (Default) The change event does not include a pre-image of the modified document. + +You can also instruct the ``watch`` method to return the document's **post-image**, +the full version of the document *after* changes, in addition to the modified fields. +To include the post-image in the change stream event, pass an options hash to ``watch`` +that sets the ``full_document`` option. You can set this option to the following string +values: + +- ``'updateLookup'``: The change event includes a + copy of the entire changed document from some time after the change. +- ``'whenAvailable'``: The change event includes a post-image of the modified + document for change events. If the post-image is not available, this change + event field has a ``nil`` value. +- ``'required'``: The change event includes a post-image of the modified document + for change events. If the post-image is not available, the server raises an error. +- ``'default'``: (Default) The change event does not include a post-image of the modified document. + +The following example calls the ``watch`` method on a collection and includes the post-image +of updated documents by setting the ``full_document`` option: + +.. literalinclude:: /includes/read/change-streams.rb + :start-after: start-change-stream-post-image + :end-before: end-change-stream-post-image + :language: ruby + :dedent: + +With the change stream application running in a separate shell, updating a +document in the ``restaurants`` collection by using the :ref:`preceding update +example ` prints a change event resembling the following +output: + +.. code-block:: none + :copyable: false + :emphasize-lines: 3-6 + + {"_id"=>{"_data"=>"..."}, "operationType"=>"update", "clusterTime"=> + #<...1>, "wallTime"=>..., "fullDocument"=>{"_id"=>BSON::ObjectId('...'), + "address"=>{"building"=>"202-24", "coord"=>[-73.9250442, 40.5595462], + "street"=>"Rockaway Point Boulevard", "zipcode"=>"11697"}, + "borough"=>"Queens", "cuisine"=>"Irish", "grades"=>[...], + "name"=>"Blarney Castle", "restaurant_id"=>"40366356"}, "ns"=> + {"db"=>"sample_restaurants", "coll"=>"restaurants"}, "documentKey"=> + {"_id"=>BSON::ObjectId('...')}, "updateDescription"=>{"updatedFields"=> + {"cuisine"=>"Irish"}, "removedFields"=>[], "truncatedArrays"=>[]}} + +.. tip:: + + To learn more about pre-images and post-images, see + :manual:`Change Streams with Document Pre- and Post-Images ` + in the {+mdb-server+} manual. + +Additional Information +---------------------- + +To learn more about change streams, see :manual:`Change Streams +` in the {+mdb-server+} manual. + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about any of the methods or types discussed in this +guide, see the following API documentation: + +- `Mongo::Client::watch <{+api-root+}/Mongo/Client.html#watch-instance_method>`__ +- `Mongo::Database::watch <{+api-root+}/Mongo/Database.html#watch-instance_method>`__ +- `Mongo::Collection::watch <{+api-root+}/Mongo/Collection.html#watch-instance_method>`__ \ No newline at end of file From 1b24ae76e7ea9caaf5e9dec5eb7c8eba86f3d6d4 Mon Sep 17 00:00:00 2001 From: norareidy Date: Thu, 9 Jan 2025 11:31:27 -0500 Subject: [PATCH 2/3] edits --- source/includes/read/change-streams.rb | 12 ++++++++++-- source/read/change-streams.txt | 26 +++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/source/includes/read/change-streams.rb b/source/includes/read/change-streams.rb index aabf8bb4..ae05b06d 100644 --- a/source/includes/read/change-streams.rb +++ b/source/includes/read/change-streams.rb @@ -23,7 +23,15 @@ break if doc['operationType'] == 'invalidate' end # end-open-change-stream - + + # Updates a document that has a 'name' value of 'Blarney Castle' + # start-update-for-change-stream + collection.update_one( + { 'name' => 'Blarney Castle' }, + { '$set' => { 'cuisine' => 'Irish' } } + ) + # end-update-for-change-stream + # Passes a pipeline argument to watch to monitor only update operations # start-change-stream-pipeline pipeline = [{ '$match' => { 'operationType' => 'update' } }] @@ -34,7 +42,7 @@ break if doc['operationType'] == 'invalidate' end # end-change-stream-pipeline - + # Passes an options argument to watch to include the post-image of updated documents # start-change-stream-post-image options = { full_document: 'updateLookup' } diff --git a/source/read/change-streams.txt b/source/read/change-streams.txt index 4dea8385..c14ff5aa 100644 --- a/source/read/change-streams.txt +++ b/source/read/change-streams.txt @@ -26,8 +26,9 @@ allows your application to subscribe to data changes on a collection, database, or deployment. When using the {+driver-short+}, you can call the ``watch`` method to return a -``ChangeStream`` object. Then, you can iterate through the change stream -to monitor data changes, such as updates, insertions, and deletions. +``Mongo::Collection::View::ChangeStream`` object. Then, you can convert the change +stream to an ``Enumerator`` object and iterate through its content to monitor data +changes, such as updates, insertions, and deletions. Sample Data ~~~~~~~~~~~ @@ -54,12 +55,14 @@ To open a change stream, call the ``watch`` method. The object on which you call the ``watch`` method determines the scope of events that the change stream monitors. You can call the ``watch`` method on the following objects -- ``Mongo::Client``: Monitor all changes in the MongoDB deployment -- ``Mongo::Database``: Monitor changes in all collections in the database -- ``Mongo::Collection``: Monitor changes in the collection +- ``Mongo::Client``: Monitors changes to all collections across all databases + in a deployment, excluding :manual:`system collections ` + or collections in the ``admin``, ``local``, and ``config`` databases +- ``Mongo::Database``: Monitors changes to all collections in one database +- ``Mongo::Collection``: Monitors changes to one collection -The following example opens a change stream on the ``restaurants`` collection -and outputs changes as they occur: +The following example opens a change stream on the ``restaurants`` collection, +converts the change stream to an ``Enumerator``, and outputs changes as they occur: .. literalinclude:: /includes/read/change-streams.rb :start-after: start-open-change-stream @@ -115,10 +118,10 @@ when update operations occur: :language: ruby :dedent: -Modify watch() Behavior ------------------------ +Modify watch Behavior +--------------------- -To modify the behavior of the ``watch()`` method, you can pass an options hash +To modify the behavior of the ``watch`` method, you can pass an options hash as a parameter to ``watch``. The following table describes some of the options that you can set: @@ -214,7 +217,7 @@ output: .. code-block:: none :copyable: false - :emphasize-lines: 3-6 + :emphasize-lines: 2-6 {"_id"=>{"_data"=>"..."}, "operationType"=>"update", "clusterTime"=> #<...1>, "wallTime"=>..., "fullDocument"=>{"_id"=>BSON::ObjectId('...'), @@ -244,6 +247,7 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: +- `Mongo::Collection::View::ChangeStream <{+api-root+}/Mongo/Collection/View/ChangeStream.html>`__ - `Mongo::Client::watch <{+api-root+}/Mongo/Client.html#watch-instance_method>`__ - `Mongo::Database::watch <{+api-root+}/Mongo/Database.html#watch-instance_method>`__ - `Mongo::Collection::watch <{+api-root+}/Mongo/Collection.html#watch-instance_method>`__ \ No newline at end of file From 803678f762b097a0304d433466a906e27cde7b3f Mon Sep 17 00:00:00 2001 From: norareidy Date: Fri, 17 Jan 2025 11:53:44 -0500 Subject: [PATCH 3/3] JB feedback --- source/includes/read/change-streams.rb | 9 +++------ source/read/change-streams.txt | 9 ++++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/source/includes/read/change-streams.rb b/source/includes/read/change-streams.rb index ae05b06d..410a8bee 100644 --- a/source/includes/read/change-streams.rb +++ b/source/includes/read/change-streams.rb @@ -17,8 +17,7 @@ # Monitors and prints changes to the "restaurants" collection # start-open-change-stream stream = collection.watch - enum = stream.to_enum - while (doc = enum.next) + stream.each do |doc| puts doc break if doc['operationType'] == 'invalidate' end @@ -36,8 +35,7 @@ # start-change-stream-pipeline pipeline = [{ '$match' => { 'operationType' => 'update' } }] stream = collection.watch(pipeline) - enum = stream.to_enum - while (doc = enum.next) + stream.each do |doc| puts doc break if doc['operationType'] == 'invalidate' end @@ -47,8 +45,7 @@ # start-change-stream-post-image options = { full_document: 'updateLookup' } stream = collection.watch([], options) - enum = stream.to_enum - while (doc = enum.next) + stream.each do |doc| puts doc break if doc['operationType'] == 'invalidate' end diff --git a/source/read/change-streams.txt b/source/read/change-streams.txt index c14ff5aa..efe2b4da 100644 --- a/source/read/change-streams.txt +++ b/source/read/change-streams.txt @@ -26,9 +26,8 @@ allows your application to subscribe to data changes on a collection, database, or deployment. When using the {+driver-short+}, you can call the ``watch`` method to return a -``Mongo::Collection::View::ChangeStream`` object. Then, you can convert the change -stream to an ``Enumerator`` object and iterate through its content to monitor data -changes, such as updates, insertions, and deletions. +``Mongo::Collection::View::ChangeStream`` object. Then, you can iterate through +its content to monitor data changes, such as updates, insertions, and deletions. Sample Data ~~~~~~~~~~~ @@ -61,8 +60,8 @@ stream monitors. You can call the ``watch`` method on the following objects - ``Mongo::Database``: Monitors changes to all collections in one database - ``Mongo::Collection``: Monitors changes to one collection -The following example opens a change stream on the ``restaurants`` collection, -converts the change stream to an ``Enumerator``, and outputs changes as they occur: +The following example opens a change stream on the ``restaurants`` collection +and outputs changes as they occur: .. literalinclude:: /includes/read/change-streams.rb :start-after: start-open-change-stream