diff --git a/bson/dbref.py b/bson/dbref.py index f4395b76c..2edaf6902 100644 --- a/bson/dbref.py +++ b/bson/dbref.py @@ -42,7 +42,7 @@ class DBRef(object): - `**kwargs` (optional): additional keyword arguments will create additional, custom fields - .. mongodoc:: dbrefs + .. seealso:: The MongoDB documentation on `dbrefs `_. """ if not isinstance(collection, str): raise TypeError("collection must be an instance of str") diff --git a/bson/objectid.py b/bson/objectid.py index 6129df35b..faf8910ed 100644 --- a/bson/objectid.py +++ b/bson/objectid.py @@ -89,7 +89,7 @@ class ObjectId(object): :Parameters: - `oid` (optional): a valid ObjectId. - .. mongodoc:: objectids + .. seealso:: The MongoDB documentation on `ObjectIds`_. .. versionchanged:: 3.8 :class:`~bson.objectid.ObjectId` now implements the `ObjectID diff --git a/doc/Makefile b/doc/Makefile index 9fa6e3a48..d4bb2cbb9 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,89 +1,20 @@ -# Makefile for Sphinx documentation +# Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . BUILDDIR = _build -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest - +# Put it first so that "make" without argument is like "make help". help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -clean: - -rm -rf $(BUILDDIR)/* +.PHONY: help Makefile -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyMongo.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyMongo.qhc" - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/conf.py b/doc/conf.py index 4ce2b5bc0..facb74f47 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,8 +14,7 @@ import pymongo # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', - 'sphinx.ext.todo', 'doc.mongo_extensions', - 'sphinx.ext.intersphinx'] + 'sphinx.ext.todo', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/doc/examples/encryption.rst b/doc/examples/encryption.rst index ba07b3722..e86eb7733 100644 --- a/doc/examples/encryption.rst +++ b/doc/examples/encryption.rst @@ -16,7 +16,7 @@ level encryption supports workloads where applications must guarantee that unauthorized parties, including server administrators, cannot read the encrypted data. -.. mongodoc:: client-side-field-level-encryption +.. seealso:: The MongoDB documentation on `Client Side Field Level Encryption `_. Dependencies ------------ diff --git a/doc/examples/geo.rst b/doc/examples/geo.rst index 5caa36eaf..9fe62f910 100644 --- a/doc/examples/geo.rst +++ b/doc/examples/geo.rst @@ -10,7 +10,7 @@ Geospatial Indexing Example This example shows how to create and use a :data:`~pymongo.GEO2D` index in PyMongo. To create a spherical (earth-like) geospatial index use :data:`~pymongo.GEOSPHERE` instead. -.. mongodoc:: geo +.. seealso:: The MongoDB documentation on `Geospatial Indexes `_. Creating a Geospatial Index --------------------------- diff --git a/doc/examples/high_availability.rst b/doc/examples/high_availability.rst index 6a72ba75b..a5c252f8a 100644 --- a/doc/examples/high_availability.rst +++ b/doc/examples/high_availability.rst @@ -14,7 +14,7 @@ PyMongo makes working with `replica sets replica set and show how to handle both initialization and normal connections with PyMongo. -.. mongodoc:: rs +.. seealso:: The MongoDB documentation on `replication `_. Starting a Replica Set ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/make.bat b/doc/make.bat index 4ccc1590e..2119f5109 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -1,113 +1,35 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -set SPHINXBUILD=sphinx-build -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyMongo.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyMongo.ghc - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/doc/mongo_extensions.py b/doc/mongo_extensions.py deleted file mode 100644 index 47ea9b7c8..000000000 --- a/doc/mongo_extensions.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""MongoDB specific extensions to Sphinx.""" - -from docutils import nodes -from docutils.parsers import rst -from sphinx import addnodes - - -class mongodoc(nodes.Admonition, nodes.Element): - pass - - -class mongoref(nodes.reference): - pass - - -def visit_mongodoc_node(self, node): - self.visit_admonition(node, "seealso") - - -def depart_mongodoc_node(self, node): - self.depart_admonition(node) - - -def visit_mongoref_node(self, node): - atts = {"class": "reference external", - "href": node["refuri"], - "name": node["name"]} - self.body.append(self.starttag(node, 'a', '', **atts)) - - -def depart_mongoref_node(self, node): - self.body.append('') - if not isinstance(node.parent, nodes.TextElement): - self.body.append('\n') - - -class MongodocDirective(rst.Directive): - - has_content = True - required_arguments = 0 - optional_arguments = 0 - final_argument_whitespace = False - option_spec = {} - - def run(self): - node = mongodoc() - title = 'The MongoDB documentation on' - node += nodes.title(title, title) - self.state.nested_parse(self.content, self.content_offset, node) - return [node] - - -def process_mongodoc_nodes(app, doctree, fromdocname): - for node in doctree.traverse(mongodoc): - anchor = None - for name in node.parent.parent.traverse(addnodes.desc_signature): - anchor = name["ids"][0] - break - if not anchor: - for name in node.parent.traverse(nodes.section): - anchor = name["ids"][0] - break - for para in node.traverse(nodes.paragraph): - tag = str(list(para.traverse())[1]) - link = mongoref("", "") - link["refuri"] = "http://dochub.mongodb.org/core/%s" % tag - link["name"] = anchor - link.append(nodes.emphasis(tag, tag)) - new_para = nodes.paragraph() - new_para += link - node.replace(para, new_para) - - -def setup(app): - app.add_node(mongodoc, - html=(visit_mongodoc_node, depart_mongodoc_node), - latex=(visit_mongodoc_node, depart_mongodoc_node), - text=(visit_mongodoc_node, depart_mongodoc_node)) - app.add_node(mongoref, - html=(visit_mongoref_node, depart_mongoref_node)) - - app.add_directive("mongodoc", MongodocDirective) - app.connect("doctree-resolved", process_mongodoc_nodes) diff --git a/gridfs/__init__.py b/gridfs/__init__.py index c12f93f1d..2a637398e 100644 --- a/gridfs/__init__.py +++ b/gridfs/__init__.py @@ -17,7 +17,7 @@ The :mod:`gridfs` package is an implementation of GridFS on top of :mod:`pymongo`, exposing a file-like interface. -.. mongodoc:: gridfs +.. seealso:: The MongoDB documentation on `gridfs `_. """ from collections import abc @@ -63,7 +63,7 @@ class GridFS(object): `database` must use an acknowledged :attr:`~pymongo.database.Database.write_concern` - .. mongodoc:: gridfs + .. seealso:: The MongoDB documentation on `gridfs `_. """ if not isinstance(database, Database): raise TypeError("database must be an instance of Database") @@ -367,7 +367,7 @@ class GridFS(object): Removed the read_preference, tag_sets, and secondary_acceptable_latency_ms options. .. versionadded:: 2.7 - .. mongodoc:: find + .. seealso:: The MongoDB documentation on `find `_. """ return GridOutCursor(self.__collection, *args, **kwargs) @@ -452,7 +452,7 @@ class GridFSBucket(object): .. versionadded:: 3.1 - .. mongodoc:: gridfs + .. seealso:: The MongoDB documentation on `gridfs `_. """ if not isinstance(db, Database): raise TypeError("database must be an instance of Database") diff --git a/gridfs/grid_file.py b/gridfs/grid_file.py index 5a48fee17..e5ac598e7 100644 --- a/gridfs/grid_file.py +++ b/gridfs/grid_file.py @@ -816,7 +816,7 @@ class GridOutCursor(Cursor): .. versionadded 2.7 - .. mongodoc:: cursors + .. seealso:: The MongoDB documentation on `cursors `_. """ _disallow_transactions(session) collection = _clear_entity_type_registry(collection) diff --git a/pymongo/change_stream.py b/pymongo/change_stream.py index fe685694a..936059e14 100644 --- a/pymongo/change_stream.py +++ b/pymongo/change_stream.py @@ -64,7 +64,7 @@ class ChangeStream(object): :meth:`pymongo.mongo_client.MongoClient.watch` instead. .. versionadded:: 3.6 - .. mongodoc:: changeStreams + .. seealso:: The MongoDB documentation on `changeStreams `_. """ def __init__(self, target, pipeline, full_document, resume_after, max_await_time_ms, batch_size, collation, diff --git a/pymongo/client_session.py b/pymongo/client_session.py index 78410d325..7d9ce712f 100644 --- a/pymongo/client_session.py +++ b/pymongo/client_session.py @@ -37,7 +37,7 @@ the session are causally after previous read and write operations. Using a causally consistent session, an application can read its own writes and is guaranteed monotonic reads, even when reading from replica set secondaries. -.. mongodoc:: causal-consistency +.. seealso:: The MongoDB documentation on `causal-consistency `_. .. _transactions-ref: @@ -91,7 +91,7 @@ transaction. All subsequent operations that are part of the same transaction are routed to the same mongos server. When the transaction is completed, by running either commitTransaction or abortTransaction, the session is unpinned. -.. mongodoc:: transactions +.. seealso:: The MongoDB documentation on `transactions `_. .. _snapshot-reads-ref: diff --git a/pymongo/collection.py b/pymongo/collection.py index 7ddcb9699..68471c06c 100644 --- a/pymongo/collection.py +++ b/pymongo/collection.py @@ -138,7 +138,7 @@ class Collection(common.BaseObject): collection.__my_collection__ - .. mongodoc:: collections + .. seealso:: The MongoDB documentation on `collections `_. """ super(Collection, self).__init__( codec_options or database.codec_options, @@ -1376,8 +1376,7 @@ class Collection(common.BaseObject): expression object. Soft deprecated the ``manipulate`` option. - .. mongodoc:: find - + .. seealso:: The MongoDB documentation on `find `_. """ return Cursor(self, *args, **kwargs) @@ -1757,7 +1756,7 @@ class Collection(common.BaseObject): :meth:`create_index` no longer caches index names. Removed support for the drop_dups and bucket_size aliases. - .. mongodoc:: indexes + .. seealso:: The MongoDB documentation on `indexes `_. .. _wildcard index: https://docs.mongodb.com/master/core/index-wildcard/#wildcard-index-core """ @@ -2198,7 +2197,7 @@ class Collection(common.BaseObject): .. versionadded:: 3.6 - .. mongodoc:: changeStreams + .. seealso:: The MongoDB documentation on `changeStreams `_. .. _change streams specification: https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst diff --git a/pymongo/command_cursor.py b/pymongo/command_cursor.py index 6d0349a90..21822ac61 100644 --- a/pymongo/command_cursor.py +++ b/pymongo/command_cursor.py @@ -303,7 +303,7 @@ class RawBatchCommandCursor(CommandCursor): see :meth:`~pymongo.collection.Collection.aggregate_raw_batches` instead. - .. mongodoc:: cursors + .. seealso:: The MongoDB documentation on `cursors `_. """ assert not cursor_info.get('firstBatch') super(RawBatchCommandCursor, self).__init__( diff --git a/pymongo/cursor.py b/pymongo/cursor.py index 5608c71b2..596f06b66 100644 --- a/pymongo/cursor.py +++ b/pymongo/cursor.py @@ -146,7 +146,7 @@ class Cursor(object): Should not be called directly by application developers - see :meth:`~pymongo.collection.Collection.find` instead. - .. mongodoc:: cursors + .. seealso:: The MongoDB documentation on `cursors `_. """ # Initialize all attributes used in __del__ before possibly raising # an error to avoid attribute errors during garbage collection. @@ -494,7 +494,7 @@ class Cursor(object): :Parameters: - `limit`: the number of results to return - .. mongodoc:: limit + .. seealso:: The MongoDB documentation on `limit `_. """ if not isinstance(limit, int): raise TypeError("limit must be an integer") @@ -839,7 +839,7 @@ class Cursor(object): :meth:`~pymongo.database.Database.command` to run the explain command directly. - .. mongodoc:: explain + .. seealso:: The MongoDB documentation on `explain `_. """ c = self.clone() c.__explain = True @@ -1233,7 +1233,7 @@ class RawBatchCursor(Cursor): see :meth:`~pymongo.collection.Collection.find_raw_batches` instead. - .. mongodoc:: cursors + .. seealso:: The MongoDB documentation on `cursors `_. """ super(RawBatchCursor, self).__init__(*args, **kwargs) @@ -1250,7 +1250,7 @@ class RawBatchCursor(Cursor): def explain(self): """Returns an explain plan record for this cursor. - .. mongodoc:: explain + .. seealso:: The MongoDB documentation on `explain `_. """ clone = self._clone(deepcopy=True, base=Cursor(self.collection)) return clone.explain() diff --git a/pymongo/database.py b/pymongo/database.py index ea9d4a1dd..89a38a15a 100644 --- a/pymongo/database.py +++ b/pymongo/database.py @@ -73,7 +73,7 @@ class Database(common.BaseObject): :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the default) client.read_concern is used. - .. mongodoc:: databases + .. seealso:: The MongoDB documentation on `databases `_. .. versionchanged:: 3.2 Added the read_concern option. @@ -468,7 +468,7 @@ class Database(common.BaseObject): .. versionadded:: 3.7 - .. mongodoc:: changeStreams + .. seealso:: The MongoDB documentation on `changeStreams `_. .. _change streams specification: https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst @@ -585,7 +585,7 @@ class Database(common.BaseObject): BSON regular expression to a Python regular expression object. Added the `codec_options` parameter. - .. mongodoc:: commands + .. seealso:: The MongoDB documentation on `commands `_. """ if read_preference is None: read_preference = ((session and session._txn_read_preference()) diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 460861005..c12f0b9f4 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -494,7 +494,7 @@ class MongoClient(common.BaseObject): client to use Versioned API. See :ref:`versioned-api-ref` for details. - .. mongodoc:: connections + .. seealso:: The MongoDB documentation on `connections `_. .. versionchanged:: 4.0 Removed the ``waitQueueMultiple`` and ``socketKeepAlive`` keyword @@ -862,7 +862,7 @@ class MongoClient(common.BaseObject): .. versionadded:: 3.7 - .. mongodoc:: changeStreams + .. seealso:: The MongoDB documentation on `changeStreams `_. .. _change streams specification: https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst