Tweak spec test because pymongo unpins cursors eagerly after errors. Tweak spec test for PoolClearedEvent ordering when MongoDB handshake fails (see DRIVERS-1785). Only skip killCursors for some error codes. Rely on SDAM error handling to close the connection after a state change error. Add service_id to various events. Retain reference to pinned sockets to prevent premptive closure by CPython's cyclic GC.
1079 lines
44 KiB
Python
1079 lines
44 KiB
Python
# 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.
|
|
|
|
"""Database level operations."""
|
|
|
|
import warnings
|
|
|
|
from bson.codec_options import DEFAULT_CODEC_OPTIONS
|
|
from bson.dbref import DBRef
|
|
from bson.son import SON
|
|
from pymongo import auth, common
|
|
from pymongo.aggregation import _DatabaseAggregationCommand
|
|
from pymongo.change_stream import DatabaseChangeStream
|
|
from pymongo.collection import Collection
|
|
from pymongo.command_cursor import CommandCursor
|
|
from pymongo.errors import (CollectionInvalid,
|
|
InvalidName)
|
|
from pymongo.message import _first_batch
|
|
from pymongo.read_preferences import ReadPreference
|
|
|
|
|
|
_INDEX_REGEX = {"name": {"$regex": r"^(?!.*\$)"}}
|
|
|
|
|
|
def _check_name(name):
|
|
"""Check if a database name is valid.
|
|
"""
|
|
if not name:
|
|
raise InvalidName("database name cannot be the empty string")
|
|
|
|
for invalid_char in [' ', '.', '$', '/', '\\', '\x00', '"']:
|
|
if invalid_char in name:
|
|
raise InvalidName("database names cannot contain the "
|
|
"character %r" % invalid_char)
|
|
|
|
|
|
class Database(common.BaseObject):
|
|
"""A Mongo database.
|
|
"""
|
|
|
|
def __init__(self, client, name, codec_options=None, read_preference=None,
|
|
write_concern=None, read_concern=None):
|
|
"""Get a database by client and name.
|
|
|
|
Raises :class:`TypeError` if `name` is not an instance of
|
|
:class:`basestring` (:class:`str` in python 3). Raises
|
|
:class:`~pymongo.errors.InvalidName` if `name` is not a valid
|
|
database name.
|
|
|
|
:Parameters:
|
|
- `client`: A :class:`~pymongo.mongo_client.MongoClient` instance.
|
|
- `name`: The database name.
|
|
- `codec_options` (optional): An instance of
|
|
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
|
default) client.codec_options is used.
|
|
- `read_preference` (optional): The read preference to use. If
|
|
``None`` (the default) client.read_preference is used.
|
|
- `write_concern` (optional): An instance of
|
|
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
|
default) client.write_concern is used.
|
|
- `read_concern` (optional): An instance of
|
|
:class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the
|
|
default) client.read_concern is used.
|
|
|
|
.. mongodoc:: databases
|
|
|
|
.. versionchanged:: 3.2
|
|
Added the read_concern option.
|
|
|
|
.. versionchanged:: 3.0
|
|
Added the codec_options, read_preference, and write_concern options.
|
|
:class:`~pymongo.database.Database` no longer returns an instance
|
|
of :class:`~pymongo.collection.Collection` for attribute names
|
|
with leading underscores. You must use dict-style lookups instead::
|
|
|
|
db['__my_collection__']
|
|
|
|
Not:
|
|
|
|
db.__my_collection__
|
|
"""
|
|
super(Database, self).__init__(
|
|
codec_options or client.codec_options,
|
|
read_preference or client.read_preference,
|
|
write_concern or client.write_concern,
|
|
read_concern or client.read_concern)
|
|
|
|
if not isinstance(name, str):
|
|
raise TypeError("name must be an instance of str")
|
|
|
|
if name != '$external':
|
|
_check_name(name)
|
|
|
|
self.__name = name
|
|
self.__client = client
|
|
|
|
@property
|
|
def client(self):
|
|
"""The client instance for this :class:`Database`."""
|
|
return self.__client
|
|
|
|
@property
|
|
def name(self):
|
|
"""The name of this :class:`Database`."""
|
|
return self.__name
|
|
|
|
def with_options(self, codec_options=None, read_preference=None,
|
|
write_concern=None, read_concern=None):
|
|
"""Get a clone of this database changing the specified settings.
|
|
|
|
>>> db1.read_preference
|
|
Primary()
|
|
>>> from pymongo import ReadPreference
|
|
>>> db2 = db1.with_options(read_preference=ReadPreference.SECONDARY)
|
|
>>> db1.read_preference
|
|
Primary()
|
|
>>> db2.read_preference
|
|
Secondary(tag_sets=None)
|
|
|
|
:Parameters:
|
|
- `codec_options` (optional): An instance of
|
|
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
|
default) the :attr:`codec_options` of this :class:`Collection`
|
|
is used.
|
|
- `read_preference` (optional): The read preference to use. If
|
|
``None`` (the default) the :attr:`read_preference` of this
|
|
:class:`Collection` is used. See :mod:`~pymongo.read_preferences`
|
|
for options.
|
|
- `write_concern` (optional): An instance of
|
|
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
|
default) the :attr:`write_concern` of this :class:`Collection`
|
|
is used.
|
|
- `read_concern` (optional): An instance of
|
|
:class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the
|
|
default) the :attr:`read_concern` of this :class:`Collection`
|
|
is used.
|
|
|
|
.. versionadded:: 3.8
|
|
"""
|
|
return Database(self.client,
|
|
self.__name,
|
|
codec_options or self.codec_options,
|
|
read_preference or self.read_preference,
|
|
write_concern or self.write_concern,
|
|
read_concern or self.read_concern)
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, Database):
|
|
return (self.__client == other.client and
|
|
self.__name == other.name)
|
|
return NotImplemented
|
|
|
|
def __ne__(self, other):
|
|
return not self == other
|
|
|
|
def __hash__(self):
|
|
return hash((self.__client, self.__name))
|
|
|
|
def __repr__(self):
|
|
return "Database(%r, %r)" % (self.__client, self.__name)
|
|
|
|
def __getattr__(self, name):
|
|
"""Get a collection of this database by name.
|
|
|
|
Raises InvalidName if an invalid collection name is used.
|
|
|
|
:Parameters:
|
|
- `name`: the name of the collection to get
|
|
"""
|
|
if name.startswith('_'):
|
|
raise AttributeError(
|
|
"Database has no attribute %r. To access the %s"
|
|
" collection, use database[%r]." % (name, name, name))
|
|
return self.__getitem__(name)
|
|
|
|
def __getitem__(self, name):
|
|
"""Get a collection of this database by name.
|
|
|
|
Raises InvalidName if an invalid collection name is used.
|
|
|
|
:Parameters:
|
|
- `name`: the name of the collection to get
|
|
"""
|
|
return Collection(self, name)
|
|
|
|
def get_collection(self, name, codec_options=None, read_preference=None,
|
|
write_concern=None, read_concern=None):
|
|
"""Get a :class:`~pymongo.collection.Collection` with the given name
|
|
and options.
|
|
|
|
Useful for creating a :class:`~pymongo.collection.Collection` with
|
|
different codec options, read preference, and/or write concern from
|
|
this :class:`Database`.
|
|
|
|
>>> db.read_preference
|
|
Primary()
|
|
>>> coll1 = db.test
|
|
>>> coll1.read_preference
|
|
Primary()
|
|
>>> from pymongo import ReadPreference
|
|
>>> coll2 = db.get_collection(
|
|
... 'test', read_preference=ReadPreference.SECONDARY)
|
|
>>> coll2.read_preference
|
|
Secondary(tag_sets=None)
|
|
|
|
:Parameters:
|
|
- `name`: The name of the collection - a string.
|
|
- `codec_options` (optional): An instance of
|
|
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
|
default) the :attr:`codec_options` of this :class:`Database` is
|
|
used.
|
|
- `read_preference` (optional): The read preference to use. If
|
|
``None`` (the default) the :attr:`read_preference` of this
|
|
:class:`Database` is used. See :mod:`~pymongo.read_preferences`
|
|
for options.
|
|
- `write_concern` (optional): An instance of
|
|
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
|
default) the :attr:`write_concern` of this :class:`Database` is
|
|
used.
|
|
- `read_concern` (optional): An instance of
|
|
:class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the
|
|
default) the :attr:`read_concern` of this :class:`Database` is
|
|
used.
|
|
"""
|
|
return Collection(
|
|
self, name, False, codec_options, read_preference,
|
|
write_concern, read_concern)
|
|
|
|
def create_collection(self, name, codec_options=None,
|
|
read_preference=None, write_concern=None,
|
|
read_concern=None, session=None, **kwargs):
|
|
"""Create a new :class:`~pymongo.collection.Collection` in this
|
|
database.
|
|
|
|
Normally collection creation is automatic. This method should
|
|
only be used to specify options on
|
|
creation. :class:`~pymongo.errors.CollectionInvalid` will be
|
|
raised if the collection already exists.
|
|
|
|
Options should be passed as keyword arguments to this method. Supported
|
|
options vary with MongoDB release. Some examples include:
|
|
|
|
- "size": desired initial size for the collection (in
|
|
bytes). For capped collections this size is the max
|
|
size of the collection.
|
|
- "capped": if True, this is a capped collection
|
|
- "max": maximum number of objects if capped (optional)
|
|
|
|
See the MongoDB documentation for a full list of supported options by
|
|
server version.
|
|
|
|
:Parameters:
|
|
- `name`: the name of the collection to create
|
|
- `codec_options` (optional): An instance of
|
|
:class:`~bson.codec_options.CodecOptions`. If ``None`` (the
|
|
default) the :attr:`codec_options` of this :class:`Database` is
|
|
used.
|
|
- `read_preference` (optional): The read preference to use. If
|
|
``None`` (the default) the :attr:`read_preference` of this
|
|
:class:`Database` is used.
|
|
- `write_concern` (optional): An instance of
|
|
:class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the
|
|
default) the :attr:`write_concern` of this :class:`Database` is
|
|
used.
|
|
- `read_concern` (optional): An instance of
|
|
:class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the
|
|
default) the :attr:`read_concern` of this :class:`Database` is
|
|
used.
|
|
- `collation` (optional): An instance of
|
|
:class:`~pymongo.collation.Collation`.
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `**kwargs` (optional): additional keyword arguments will
|
|
be passed as options for the create collection command
|
|
|
|
.. versionchanged:: 3.11
|
|
This method is now supported inside multi-document transactions
|
|
with MongoDB 4.4+.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. versionchanged:: 3.4
|
|
Added the collation option.
|
|
|
|
.. versionchanged:: 3.0
|
|
Added the codec_options, read_preference, and write_concern options.
|
|
|
|
.. versionchanged:: 2.2
|
|
Removed deprecated argument: options
|
|
"""
|
|
with self.__client._tmp_session(session) as s:
|
|
# Skip this check in a transaction where listCollections is not
|
|
# supported.
|
|
if ((not s or not s.in_transaction) and
|
|
name in self.list_collection_names(
|
|
filter={"name": name}, session=s)):
|
|
raise CollectionInvalid("collection %s already exists" % name)
|
|
|
|
return Collection(self, name, True, codec_options,
|
|
read_preference, write_concern,
|
|
read_concern, session=s, **kwargs)
|
|
|
|
def aggregate(self, pipeline, session=None, **kwargs):
|
|
"""Perform a database-level aggregation.
|
|
|
|
See the `aggregation pipeline`_ documentation for a list of stages
|
|
that are supported.
|
|
|
|
Introduced in MongoDB 3.6.
|
|
|
|
.. code-block:: python
|
|
|
|
# Lists all operations currently running on the server.
|
|
with client.admin.aggregate([{"$currentOp": {}}]) as cursor:
|
|
for operation in cursor:
|
|
print(operation)
|
|
|
|
All optional `aggregate command`_ parameters should be passed as
|
|
keyword arguments to this method. Valid options include, but are not
|
|
limited to:
|
|
|
|
- `allowDiskUse` (bool): Enables writing to temporary files. When set
|
|
to True, aggregation stages can write data to the _tmp subdirectory
|
|
of the --dbpath directory. The default is False.
|
|
- `maxTimeMS` (int): The maximum amount of time to allow the operation
|
|
to run in milliseconds.
|
|
- `batchSize` (int): The maximum number of documents to return per
|
|
batch. Ignored if the connected mongod or mongos does not support
|
|
returning aggregate results using a cursor.
|
|
- `collation` (optional): An instance of
|
|
:class:`~pymongo.collation.Collation`.
|
|
|
|
The :meth:`aggregate` method obeys the :attr:`read_preference` of this
|
|
:class:`Database`, except when ``$out`` or ``$merge`` are used, in
|
|
which case :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`
|
|
is used.
|
|
|
|
.. note:: This method does not support the 'explain' option. Please
|
|
use :meth:`~pymongo.database.Database.command` instead.
|
|
|
|
.. note:: The :attr:`~pymongo.database.Database.write_concern` of
|
|
this collection is automatically applied to this operation.
|
|
|
|
:Parameters:
|
|
- `pipeline`: a list of aggregation pipeline stages
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `**kwargs` (optional): See list of options above.
|
|
|
|
:Returns:
|
|
A :class:`~pymongo.command_cursor.CommandCursor` over the result
|
|
set.
|
|
|
|
.. versionadded:: 3.9
|
|
|
|
.. _aggregation pipeline:
|
|
https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline
|
|
|
|
.. _aggregate command:
|
|
https://docs.mongodb.com/manual/reference/command/aggregate
|
|
"""
|
|
with self.client._tmp_session(session, close=False) as s:
|
|
cmd = _DatabaseAggregationCommand(
|
|
self, CommandCursor, pipeline, kwargs, session is not None,
|
|
user_fields={'cursor': {'firstBatch': 1}})
|
|
return self.client._retryable_read(
|
|
cmd.get_cursor, cmd.get_read_preference(s), s,
|
|
retryable=not cmd._performs_write,
|
|
pin=self.client._should_pin_cursor(s))
|
|
|
|
def watch(self, pipeline=None, full_document=None, resume_after=None,
|
|
max_await_time_ms=None, batch_size=None, collation=None,
|
|
start_at_operation_time=None, session=None, start_after=None):
|
|
"""Watch changes on this database.
|
|
|
|
Performs an aggregation with an implicit initial ``$changeStream``
|
|
stage and returns a
|
|
:class:`~pymongo.change_stream.DatabaseChangeStream` cursor which
|
|
iterates over changes on all collections in this database.
|
|
|
|
Introduced in MongoDB 4.0.
|
|
|
|
.. code-block:: python
|
|
|
|
with db.watch() as stream:
|
|
for change in stream:
|
|
print(change)
|
|
|
|
The :class:`~pymongo.change_stream.DatabaseChangeStream` iterable
|
|
blocks until the next change document is returned or an error is
|
|
raised. If the
|
|
:meth:`~pymongo.change_stream.DatabaseChangeStream.next` method
|
|
encounters a network error when retrieving a batch from the server,
|
|
it will automatically attempt to recreate the cursor such that no
|
|
change events are missed. Any error encountered during the resume
|
|
attempt indicates there may be an outage and will be raised.
|
|
|
|
.. code-block:: python
|
|
|
|
try:
|
|
with db.watch(
|
|
[{'$match': {'operationType': 'insert'}}]) as stream:
|
|
for insert_change in stream:
|
|
print(insert_change)
|
|
except pymongo.errors.PyMongoError:
|
|
# The ChangeStream encountered an unrecoverable error or the
|
|
# resume attempt failed to recreate the cursor.
|
|
logging.error('...')
|
|
|
|
For a precise description of the resume process see the
|
|
`change streams specification`_.
|
|
|
|
:Parameters:
|
|
- `pipeline` (optional): A list of aggregation pipeline stages to
|
|
append to an initial ``$changeStream`` stage. Not all
|
|
pipeline stages are valid after a ``$changeStream`` stage, see the
|
|
MongoDB documentation on change streams for the supported stages.
|
|
- `full_document` (optional): The fullDocument to pass as an option
|
|
to the ``$changeStream`` stage. Allowed values: 'updateLookup'.
|
|
When set to 'updateLookup', the change notification for partial
|
|
updates will include both a delta describing the changes to the
|
|
document, as well as a copy of the entire document that was
|
|
changed from some time after the change occurred.
|
|
- `resume_after` (optional): A resume token. If provided, the
|
|
change stream will start returning changes that occur directly
|
|
after the operation specified in the resume token. A resume token
|
|
is the _id value of a change document.
|
|
- `max_await_time_ms` (optional): The maximum time in milliseconds
|
|
for the server to wait for changes before responding to a getMore
|
|
operation.
|
|
- `batch_size` (optional): The maximum number of documents to return
|
|
per batch.
|
|
- `collation` (optional): The :class:`~pymongo.collation.Collation`
|
|
to use for the aggregation.
|
|
- `start_at_operation_time` (optional): If provided, the resulting
|
|
change stream will only return changes that occurred at or after
|
|
the specified :class:`~bson.timestamp.Timestamp`. Requires
|
|
MongoDB >= 4.0.
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `start_after` (optional): The same as `resume_after` except that
|
|
`start_after` can resume notifications after an invalidate event.
|
|
This option and `resume_after` are mutually exclusive.
|
|
|
|
:Returns:
|
|
A :class:`~pymongo.change_stream.DatabaseChangeStream` cursor.
|
|
|
|
.. versionchanged:: 3.9
|
|
Added the ``start_after`` parameter.
|
|
|
|
.. versionadded:: 3.7
|
|
|
|
.. mongodoc:: changeStreams
|
|
|
|
.. _change streams specification:
|
|
https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst
|
|
"""
|
|
return DatabaseChangeStream(
|
|
self, pipeline, full_document, resume_after, max_await_time_ms,
|
|
batch_size, collation, start_at_operation_time, session,
|
|
start_after)
|
|
|
|
def _command(self, sock_info, command, slave_ok=False, value=1, check=True,
|
|
allowable_errors=None, read_preference=ReadPreference.PRIMARY,
|
|
codec_options=DEFAULT_CODEC_OPTIONS,
|
|
write_concern=None,
|
|
parse_write_concern_error=False, session=None, **kwargs):
|
|
"""Internal command helper."""
|
|
if isinstance(command, str):
|
|
command = SON([(command, value)])
|
|
|
|
command.update(kwargs)
|
|
with self.__client._tmp_session(session) as s:
|
|
return sock_info.command(
|
|
self.__name,
|
|
command,
|
|
slave_ok,
|
|
read_preference,
|
|
codec_options,
|
|
check,
|
|
allowable_errors,
|
|
write_concern=write_concern,
|
|
parse_write_concern_error=parse_write_concern_error,
|
|
session=s,
|
|
client=self.__client)
|
|
|
|
def command(self, command, value=1, check=True,
|
|
allowable_errors=None, read_preference=None,
|
|
codec_options=DEFAULT_CODEC_OPTIONS, session=None, **kwargs):
|
|
"""Issue a MongoDB command.
|
|
|
|
Send command `command` to the database and return the
|
|
response. If `command` is an instance of :class:`basestring`
|
|
(:class:`str` in python 3) then the command {`command`: `value`}
|
|
will be sent. Otherwise, `command` must be an instance of
|
|
:class:`dict` and will be sent as is.
|
|
|
|
Any additional keyword arguments will be added to the final
|
|
command document before it is sent.
|
|
|
|
For example, a command like ``{buildinfo: 1}`` can be sent
|
|
using:
|
|
|
|
>>> db.command("buildinfo")
|
|
|
|
For a command where the value matters, like ``{collstats:
|
|
collection_name}`` we can do:
|
|
|
|
>>> db.command("collstats", collection_name)
|
|
|
|
For commands that take additional arguments we can use
|
|
kwargs. So ``{filemd5: object_id, root: file_root}`` becomes:
|
|
|
|
>>> db.command("filemd5", object_id, root=file_root)
|
|
|
|
:Parameters:
|
|
- `command`: document representing the command to be issued,
|
|
or the name of the command (for simple commands only).
|
|
|
|
.. note:: the order of keys in the `command` document is
|
|
significant (the "verb" must come first), so commands
|
|
which require multiple keys (e.g. `findandmodify`)
|
|
should use an instance of :class:`~bson.son.SON` or
|
|
a string and kwargs instead of a Python `dict`.
|
|
|
|
- `value` (optional): value to use for the command verb when
|
|
`command` is passed as a string
|
|
- `check` (optional): check the response for errors, raising
|
|
:class:`~pymongo.errors.OperationFailure` if there are any
|
|
- `allowable_errors`: if `check` is ``True``, error messages
|
|
in this list will be ignored by error-checking
|
|
- `read_preference` (optional): The read preference for this
|
|
operation. See :mod:`~pymongo.read_preferences` for options.
|
|
If the provided `session` is in a transaction, defaults to the
|
|
read preference configured for the transaction.
|
|
Otherwise, defaults to
|
|
:attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`.
|
|
- `codec_options`: A :class:`~bson.codec_options.CodecOptions`
|
|
instance.
|
|
- `session` (optional): A
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `**kwargs` (optional): additional keyword arguments will
|
|
be added to the command document before it is sent
|
|
|
|
.. note:: :meth:`command` does **not** obey this Database's
|
|
:attr:`read_preference` or :attr:`codec_options`. You must use the
|
|
`read_preference` and `codec_options` parameters instead.
|
|
|
|
.. note:: :meth:`command` does **not** apply any custom TypeDecoders
|
|
when decoding the command response.
|
|
|
|
.. note:: If this client has been configured to use MongoDB Versioned
|
|
API (see :ref:`versioned-api-ref`), then :meth:`command` will
|
|
automactically add API versioning options to the given command.
|
|
Explicitly adding API versioning options in the command and
|
|
declaring an API version on the client is not supported.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. versionchanged:: 3.0
|
|
Removed the `as_class`, `fields`, `uuid_subtype`, `tag_sets`,
|
|
and `secondary_acceptable_latency_ms` option.
|
|
Removed `compile_re` option: PyMongo now always represents BSON
|
|
regular expressions as :class:`~bson.regex.Regex` objects. Use
|
|
:meth:`~bson.regex.Regex.try_compile` to attempt to convert from a
|
|
BSON regular expression to a Python regular expression object.
|
|
Added the `codec_options` parameter.
|
|
|
|
.. versionchanged:: 2.7
|
|
Added `compile_re` option. If set to False, PyMongo represented BSON
|
|
regular expressions as :class:`~bson.regex.Regex` objects instead of
|
|
attempting to compile BSON regular expressions as Python native
|
|
regular expressions, thus preventing errors for some incompatible
|
|
patterns, see `PYTHON-500`_.
|
|
|
|
.. versionchanged:: 2.3
|
|
Added `tag_sets` and `secondary_acceptable_latency_ms` options.
|
|
.. versionchanged:: 2.2
|
|
Added support for `as_class` - the class you want to use for
|
|
the resulting documents
|
|
|
|
.. _PYTHON-500: https://jira.mongodb.org/browse/PYTHON-500
|
|
|
|
.. mongodoc:: commands
|
|
"""
|
|
if read_preference is None:
|
|
read_preference = ((session and session._txn_read_preference())
|
|
or ReadPreference.PRIMARY)
|
|
with self.__client._socket_for_reads(
|
|
read_preference, session) as (sock_info, slave_ok):
|
|
return self._command(sock_info, command, slave_ok, value,
|
|
check, allowable_errors, read_preference,
|
|
codec_options, session=session, **kwargs)
|
|
|
|
def _retryable_read_command(self, command, value=1, check=True,
|
|
allowable_errors=None, read_preference=None,
|
|
codec_options=DEFAULT_CODEC_OPTIONS, session=None, **kwargs):
|
|
"""Same as command but used for retryable read commands."""
|
|
if read_preference is None:
|
|
read_preference = ((session and session._txn_read_preference())
|
|
or ReadPreference.PRIMARY)
|
|
|
|
def _cmd(session, server, sock_info, slave_ok):
|
|
return self._command(sock_info, command, slave_ok, value,
|
|
check, allowable_errors, read_preference,
|
|
codec_options, session=session, **kwargs)
|
|
|
|
return self.__client._retryable_read(
|
|
_cmd, read_preference, session)
|
|
|
|
def _list_collections(self, sock_info, slave_okay, session,
|
|
read_preference, **kwargs):
|
|
"""Internal listCollections helper."""
|
|
|
|
coll = self.get_collection(
|
|
"$cmd", read_preference=read_preference)
|
|
if sock_info.max_wire_version > 2:
|
|
cmd = SON([("listCollections", 1),
|
|
("cursor", {})])
|
|
cmd.update(kwargs)
|
|
with self.__client._tmp_session(
|
|
session, close=False) as tmp_session:
|
|
cursor = self._command(
|
|
sock_info, cmd, slave_okay,
|
|
read_preference=read_preference,
|
|
session=tmp_session)["cursor"]
|
|
cmd_cursor = CommandCursor(
|
|
coll,
|
|
cursor,
|
|
sock_info.address,
|
|
session=tmp_session,
|
|
explicit_session=session is not None)
|
|
else:
|
|
match = _INDEX_REGEX
|
|
if "filter" in kwargs:
|
|
match = {"$and": [_INDEX_REGEX, kwargs["filter"]]}
|
|
dblen = len(self.name.encode("utf8") + b".")
|
|
pipeline = [
|
|
{"$project": {"name": {"$substr": ["$name", dblen, -1]},
|
|
"options": 1}},
|
|
{"$match": match}
|
|
]
|
|
cmd = SON([("aggregate", "system.namespaces"),
|
|
("pipeline", pipeline),
|
|
("cursor", kwargs.get("cursor", {}))])
|
|
cursor = self._command(sock_info, cmd, slave_okay)["cursor"]
|
|
cmd_cursor = CommandCursor(coll, cursor, sock_info.address)
|
|
cmd_cursor._maybe_pin_connection(sock_info)
|
|
return cmd_cursor
|
|
|
|
def list_collections(self, session=None, filter=None, **kwargs):
|
|
"""Get a cursor over the collectons of this database.
|
|
|
|
:Parameters:
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `filter` (optional): A query document to filter the list of
|
|
collections returned from the listCollections command.
|
|
- `**kwargs` (optional): Optional parameters of the
|
|
`listCollections command
|
|
<https://docs.mongodb.com/manual/reference/command/listCollections/>`_
|
|
can be passed as keyword arguments to this method. The supported
|
|
options differ by server version.
|
|
|
|
:Returns:
|
|
An instance of :class:`~pymongo.command_cursor.CommandCursor`.
|
|
|
|
.. versionadded:: 3.6
|
|
"""
|
|
if filter is not None:
|
|
kwargs['filter'] = filter
|
|
read_pref = ((session and session._txn_read_preference())
|
|
or ReadPreference.PRIMARY)
|
|
|
|
def _cmd(session, server, sock_info, slave_okay):
|
|
return self._list_collections(
|
|
sock_info, slave_okay, session, read_preference=read_pref,
|
|
**kwargs)
|
|
|
|
return self.__client._retryable_read(
|
|
_cmd, read_pref, session,
|
|
pin=self.client._should_pin_cursor(session))
|
|
|
|
def list_collection_names(self, session=None, filter=None, **kwargs):
|
|
"""Get a list of all the collection names in this database.
|
|
|
|
For example, to list all non-system collections::
|
|
|
|
filter = {"name": {"$regex": r"^(?!system\\.)"}}
|
|
db.list_collection_names(filter=filter)
|
|
|
|
:Parameters:
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `filter` (optional): A query document to filter the list of
|
|
collections returned from the listCollections command.
|
|
- `**kwargs` (optional): Optional parameters of the
|
|
`listCollections command
|
|
<https://docs.mongodb.com/manual/reference/command/listCollections/>`_
|
|
can be passed as keyword arguments to this method. The supported
|
|
options differ by server version.
|
|
|
|
.. versionchanged:: 3.8
|
|
Added the ``filter`` and ``**kwargs`` parameters.
|
|
|
|
.. versionadded:: 3.6
|
|
"""
|
|
if filter is None:
|
|
kwargs["nameOnly"] = True
|
|
else:
|
|
# The enumerate collections spec states that "drivers MUST NOT set
|
|
# nameOnly if a filter specifies any keys other than name."
|
|
common.validate_is_mapping("filter", filter)
|
|
kwargs["filter"] = filter
|
|
if not filter or (len(filter) == 1 and "name" in filter):
|
|
kwargs["nameOnly"] = True
|
|
|
|
return [result["name"]
|
|
for result in self.list_collections(session=session, **kwargs)]
|
|
|
|
def drop_collection(self, name_or_collection, session=None):
|
|
"""Drop a collection.
|
|
|
|
:Parameters:
|
|
- `name_or_collection`: the name of a collection to drop or the
|
|
collection object itself
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
|
|
.. note:: The :attr:`~pymongo.database.Database.write_concern` of
|
|
this database is automatically applied to this operation when using
|
|
MongoDB >= 3.4.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. versionchanged:: 3.4
|
|
Apply this database's write concern automatically to this operation
|
|
when connected to MongoDB >= 3.4.
|
|
|
|
"""
|
|
name = name_or_collection
|
|
if isinstance(name, Collection):
|
|
name = name.name
|
|
|
|
if not isinstance(name, str):
|
|
raise TypeError("name_or_collection must be an instance of str")
|
|
|
|
with self.__client._socket_for_writes(session) as sock_info:
|
|
return self._command(
|
|
sock_info, 'drop', value=name,
|
|
allowable_errors=['ns not found', 26],
|
|
write_concern=self._write_concern_for(session),
|
|
parse_write_concern_error=True,
|
|
session=session)
|
|
|
|
def validate_collection(self, name_or_collection,
|
|
scandata=False, full=False, session=None,
|
|
background=None):
|
|
"""Validate a collection.
|
|
|
|
Returns a dict of validation info. Raises CollectionInvalid if
|
|
validation fails.
|
|
|
|
See also the MongoDB documentation on the `validate command`_.
|
|
|
|
:Parameters:
|
|
- `name_or_collection`: A Collection object or the name of a
|
|
collection to validate.
|
|
- `scandata`: Do extra checks beyond checking the overall
|
|
structure of the collection.
|
|
- `full`: Have the server do a more thorough scan of the
|
|
collection. Use with `scandata` for a thorough scan
|
|
of the structure of the collection and the individual
|
|
documents.
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `background` (optional): A boolean flag that determines whether
|
|
the command runs in the background. Requires MongoDB 4.4+.
|
|
|
|
.. versionchanged:: 3.11
|
|
Added ``background`` parameter.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. _validate command: https://docs.mongodb.com/manual/reference/command/validate/
|
|
"""
|
|
name = name_or_collection
|
|
if isinstance(name, Collection):
|
|
name = name.name
|
|
|
|
if not isinstance(name, str):
|
|
raise TypeError("name_or_collection must be an instance of str or "
|
|
"Collection")
|
|
|
|
cmd = SON([("validate", name),
|
|
("scandata", scandata),
|
|
("full", full)])
|
|
if background is not None:
|
|
cmd["background"] = background
|
|
|
|
result = self.command(cmd, session=session)
|
|
|
|
valid = True
|
|
# Pre 1.9 results
|
|
if "result" in result:
|
|
info = result["result"]
|
|
if info.find("exception") != -1 or info.find("corrupt") != -1:
|
|
raise CollectionInvalid("%s invalid: %s" % (name, info))
|
|
# Sharded results
|
|
elif "raw" in result:
|
|
for _, res in result["raw"].items():
|
|
if "result" in res:
|
|
info = res["result"]
|
|
if (info.find("exception") != -1 or
|
|
info.find("corrupt") != -1):
|
|
raise CollectionInvalid("%s invalid: "
|
|
"%s" % (name, info))
|
|
elif not res.get("valid", False):
|
|
valid = False
|
|
break
|
|
# Post 1.9 non-sharded results.
|
|
elif not result.get("valid", False):
|
|
valid = False
|
|
|
|
if not valid:
|
|
raise CollectionInvalid("%s invalid: %r" % (name, result))
|
|
|
|
return result
|
|
|
|
def _current_op(self, include_all=False, session=None):
|
|
"""Helper for running $currentOp."""
|
|
cmd = SON([("currentOp", 1), ("$all", include_all)])
|
|
with self.__client._socket_for_writes(session) as sock_info:
|
|
if sock_info.max_wire_version >= 4:
|
|
return self.__client.admin._command(
|
|
sock_info, cmd, codec_options=self.codec_options,
|
|
session=session)
|
|
else:
|
|
spec = {"$all": True} if include_all else {}
|
|
return _first_batch(sock_info, "admin", "$cmd.sys.inprog",
|
|
spec, -1, True, self.codec_options,
|
|
ReadPreference.PRIMARY, cmd,
|
|
self.client._event_listeners)
|
|
|
|
def current_op(self, include_all=False, session=None):
|
|
"""**DEPRECATED**: Get information on operations currently running.
|
|
|
|
Starting with MongoDB 3.6 this helper is obsolete. The functionality
|
|
provided by this helper is available in MongoDB 3.6+ using the
|
|
`$currentOp aggregation pipeline stage`_, which can be used with
|
|
:meth:`aggregate`. Note that, while this helper can only return
|
|
a single document limited to a 16MB result, :meth:`aggregate`
|
|
returns a cursor avoiding that limitation.
|
|
|
|
Users of MongoDB versions older than 3.6 can use the `currentOp command`_
|
|
directly::
|
|
|
|
# MongoDB 3.2 and 3.4
|
|
client.admin.command("currentOp")
|
|
|
|
Or query the "inprog" virtual collection::
|
|
|
|
# MongoDB 2.6 and 3.0
|
|
client.admin["$cmd.sys.inprog"].find_one()
|
|
|
|
:Parameters:
|
|
- `include_all` (optional): if ``True`` also list currently
|
|
idle operations in the result
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
|
|
.. versionchanged:: 3.9
|
|
Deprecated.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. _$currentOp aggregation pipeline stage: https://docs.mongodb.com/manual/reference/operator/aggregation/currentOp/
|
|
.. _currentOp command: https://docs.mongodb.com/manual/reference/command/currentOp/
|
|
"""
|
|
warnings.warn("current_op() is deprecated. See the documentation for "
|
|
"more information",
|
|
DeprecationWarning, stacklevel=2)
|
|
return self._current_op(include_all, session)
|
|
|
|
def profiling_level(self, session=None):
|
|
"""**DEPRECATED**: Get the database's current profiling level.
|
|
|
|
Starting with PyMongo 3.12, this helper is obsolete. Instead, users
|
|
can run the `profile command`_, using the :meth:`command`
|
|
helper to get the current profiler level. Running the
|
|
`profile command`_ with the level set to ``-1`` returns the current
|
|
profiler information without changing it::
|
|
|
|
res = db.command("profile", -1)
|
|
profiling_level = res["was"]
|
|
|
|
The format of ``res`` depends on the version of MongoDB in use.
|
|
|
|
Returns one of (:data:`~pymongo.OFF`,
|
|
:data:`~pymongo.SLOW_ONLY`, :data:`~pymongo.ALL`).
|
|
|
|
:Parameters:
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
|
|
.. versionchanged:: 3.12
|
|
Deprecated.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. mongodoc:: profiling
|
|
.. _profile command: https://docs.mongodb.com/manual/reference/command/profile/
|
|
"""
|
|
warnings.warn("profiling_level() is deprecated. See the documentation "
|
|
"for more information",
|
|
DeprecationWarning, stacklevel=2)
|
|
result = self.command("profile", -1, session=session)
|
|
|
|
assert result["was"] >= 0 and result["was"] <= 2
|
|
return result["was"]
|
|
|
|
def set_profiling_level(self, level, slow_ms=None, session=None,
|
|
sample_rate=None, filter=None):
|
|
"""**DEPRECATED**: Set the database's profiling level.
|
|
|
|
Starting with PyMongo 3.12, this helper is obsolete. Instead, users
|
|
can directly run the `profile command`_, using the :meth:`command`
|
|
helper, e.g.::
|
|
|
|
res = db.command("profile", 2, filter={"op": "query"})
|
|
|
|
:Parameters:
|
|
- `level`: Specifies a profiling level, see list of possible values
|
|
below.
|
|
- `slow_ms`: Optionally modify the threshold for the profile to
|
|
consider a query or operation. Even if the profiler is off queries
|
|
slower than the `slow_ms` level will get written to the logs.
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `sample_rate` (optional): The fraction of slow operations that
|
|
should be profiled or logged expressed as a float between 0 and 1.
|
|
- `filter` (optional): A filter expression that controls which
|
|
operations are profiled and logged.
|
|
|
|
Possible `level` values:
|
|
|
|
+----------------------------+------------------------------------+
|
|
| Level | Setting |
|
|
+============================+====================================+
|
|
| :data:`~pymongo.OFF` | Off. No profiling. |
|
|
+----------------------------+------------------------------------+
|
|
| :data:`~pymongo.SLOW_ONLY` | On. Only includes slow operations. |
|
|
+----------------------------+------------------------------------+
|
|
| :data:`~pymongo.ALL` | On. Includes all operations. |
|
|
+----------------------------+------------------------------------+
|
|
|
|
Raises :class:`ValueError` if level is not one of
|
|
(:data:`~pymongo.OFF`, :data:`~pymongo.SLOW_ONLY`,
|
|
:data:`~pymongo.ALL`).
|
|
|
|
.. versionchanged:: 3.12
|
|
Added the ``sample_rate`` and ``filter`` parameters.
|
|
Deprecated.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. mongodoc:: profiling
|
|
.. _profile command: https://docs.mongodb.com/manual/reference/command/profile/
|
|
"""
|
|
warnings.warn("set_profiling_level() is deprecated. See the "
|
|
"documentation for more information",
|
|
DeprecationWarning, stacklevel=2)
|
|
|
|
if not isinstance(level, int) or level < 0 or level > 2:
|
|
raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)")
|
|
|
|
if slow_ms is not None and not isinstance(slow_ms, int):
|
|
raise TypeError("slow_ms must be an integer")
|
|
|
|
if sample_rate is not None and not isinstance(sample_rate, float):
|
|
raise TypeError(
|
|
"sample_rate must be a float, not %r" % (sample_rate,))
|
|
|
|
cmd = SON(profile=level)
|
|
if slow_ms is not None:
|
|
cmd['slowms'] = slow_ms
|
|
if sample_rate is not None:
|
|
cmd['sampleRate'] = sample_rate
|
|
if filter is not None:
|
|
cmd['filter'] = filter
|
|
self.command(cmd, session=session)
|
|
|
|
def profiling_info(self, session=None):
|
|
"""**DEPRECATED**: Returns a list containing current profiling
|
|
information.
|
|
|
|
Starting with PyMongo 3.12, this helper is obsolete. Instead, users
|
|
can view the database profiler output by running
|
|
:meth:`~pymongo.collection.Collection.find` against the
|
|
``system.profile`` collection as detailed in the `profiler output`_
|
|
documentation::
|
|
|
|
profiling_info = list(db["system.profile"].find())
|
|
|
|
:Parameters:
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
|
|
.. versionchanged:: 3.12
|
|
Deprecated.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
|
|
.. mongodoc:: profiling
|
|
.. _profiler output: https://docs.mongodb.com/manual/reference/database-profiler/
|
|
"""
|
|
warnings.warn("profiling_info() is deprecated. See the "
|
|
"documentation for more information",
|
|
DeprecationWarning, stacklevel=2)
|
|
|
|
return list(self["system.profile"].find(session=session))
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
raise TypeError("'Database' object is not iterable")
|
|
|
|
next = __next__
|
|
|
|
def dereference(self, dbref, session=None, **kwargs):
|
|
"""Dereference a :class:`~bson.dbref.DBRef`, getting the
|
|
document it points to.
|
|
|
|
Raises :class:`TypeError` if `dbref` is not an instance of
|
|
:class:`~bson.dbref.DBRef`. Returns a document, or ``None`` if
|
|
the reference does not point to a valid document. Raises
|
|
:class:`ValueError` if `dbref` has a database specified that
|
|
is different from the current database.
|
|
|
|
:Parameters:
|
|
- `dbref`: the reference
|
|
- `session` (optional): a
|
|
:class:`~pymongo.client_session.ClientSession`.
|
|
- `**kwargs` (optional): any additional keyword arguments
|
|
are the same as the arguments to
|
|
:meth:`~pymongo.collection.Collection.find`.
|
|
|
|
.. versionchanged:: 3.6
|
|
Added ``session`` parameter.
|
|
"""
|
|
if not isinstance(dbref, DBRef):
|
|
raise TypeError("cannot dereference a %s" % type(dbref))
|
|
if dbref.database is not None and dbref.database != self.__name:
|
|
raise ValueError("trying to dereference a DBRef that points to "
|
|
"another database (%r not %r)" % (dbref.database,
|
|
self.__name))
|
|
return self[dbref.collection].find_one(
|
|
{"_id": dbref.id}, session=session, **kwargs)
|