improvements to the db.command api
This commit is contained in:
parent
d7ffcf3b65
commit
b989e3e34d
@ -262,8 +262,8 @@ class GridFile(object):
|
||||
|
||||
self.__flush_write_buffer()
|
||||
|
||||
md5 = self.__collection.database.command(SON([("filemd5", self.__id),
|
||||
("root", self.__collection.name)]))["md5"]
|
||||
md5 = self.__collection.database.command("filemd5", self.__id,
|
||||
root=self.__collection.name)["md5"]
|
||||
|
||||
grid_file = self.__collection.files.find_one({"_id": self.__id})
|
||||
grid_file["md5"] = md5
|
||||
|
||||
@ -115,13 +115,12 @@ class Collection(object):
|
||||
|
||||
# Send size as a float, not an int/long. BSON can only handle 32-bit
|
||||
# ints which conflicts w/ max collection size of 10000000000.
|
||||
if options and "size" in options:
|
||||
options["size"] = float(options["size"])
|
||||
|
||||
command = SON({"create": self.__name})
|
||||
command.update(options)
|
||||
|
||||
self.__database.command(command)
|
||||
if options:
|
||||
if "size" in options:
|
||||
options["size"] = float(options["size"])
|
||||
self.__database.command("create", self.__name, **options)
|
||||
else:
|
||||
self.__database.command("create", self.__name)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Get a sub-collection of this collection by name.
|
||||
@ -697,9 +696,7 @@ class Collection(object):
|
||||
|
||||
self.__database.connection._purge_index(self.__database.name,
|
||||
self.__name, name)
|
||||
self.__database.command(SON([("deleteIndexes",
|
||||
self.__name),
|
||||
("index", name)]),
|
||||
self.__database.command("deleteIndexes", self.__name, index=name,
|
||||
allowable_errors=["ns not found"])
|
||||
|
||||
def index_information(self):
|
||||
@ -785,7 +782,7 @@ class Collection(object):
|
||||
if finalize is not None:
|
||||
group["finalize"] = Code(finalize)
|
||||
|
||||
return self.__database.command({"group":group})["retval"]
|
||||
return self.__database.command("group", group)["retval"]
|
||||
|
||||
def rename(self, new_name):
|
||||
"""Rename this collection.
|
||||
@ -809,11 +806,9 @@ class Collection(object):
|
||||
if new_name[0] == "." or new_name[-1] == ".":
|
||||
raise InvalidName("collecion names must not start or end with '.'")
|
||||
|
||||
rename_command = SON([("renameCollection", self.__full_name),
|
||||
("to", "%s.%s" % (self.__database.name,
|
||||
new_name))])
|
||||
|
||||
self.__database.connection.admin.command(rename_command)
|
||||
new_name = "%s.%s" % (self.__database.name, new_name)
|
||||
self.__database.connection.admin.command("renameCollection",
|
||||
self.__full_name, to=new_name)
|
||||
|
||||
def distinct(self, key):
|
||||
"""Get a list of distinct values for `key` among all documents
|
||||
@ -863,11 +858,8 @@ class Collection(object):
|
||||
|
||||
.. mongodoc:: mapreduce
|
||||
"""
|
||||
command = SON([("mapreduce", self.__name),
|
||||
("map", map), ("reduce", reduce)])
|
||||
command.update(**kwargs)
|
||||
|
||||
response = self.__database.command(command)
|
||||
response = self.__database.command("mapreduce", self.__name,
|
||||
map=map, reduce=reduce, **kwargs)
|
||||
if full_response:
|
||||
return response
|
||||
return self.__database[response["result"]]
|
||||
|
||||
@ -804,19 +804,19 @@ class Connection(object): # TODO support auth for pooling
|
||||
|
||||
database._check_name(to_name)
|
||||
|
||||
command = SON([("copydb", 1), ("fromdb", from_name), ("todb", to_name)])
|
||||
command = {"fromdb": from_name, "todb": to_name}
|
||||
|
||||
if from_host is not None:
|
||||
command["fromhost"] = from_host
|
||||
|
||||
if username is not None:
|
||||
nonce = self.admin.command(SON([("copydbgetnonce", 1),
|
||||
("fromhost", from_host)]))["nonce"]
|
||||
nonce = self.admin.command("copydbgetnonce",
|
||||
fromhost=from_host)["nonce"]
|
||||
command["username"] = username
|
||||
command["nonce"] = nonce
|
||||
command["key"] = helpers._auth_key(nonce, username, password)
|
||||
|
||||
return self.admin.command(command)
|
||||
return self.admin.command("copydb", **command)
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
@ -301,9 +301,7 @@ class Cursor(object):
|
||||
:meth:`~pymongo.cursor.Cursor.__len__` was deprecated in favor of
|
||||
calling :meth:`count` with `with_limit_and_skip` set to ``True``.
|
||||
"""
|
||||
command = SON([("count", self.__collection.name),
|
||||
("query", self.__spec),
|
||||
("fields", self.__fields)])
|
||||
command = {"query": self.__spec, "fields": self.__fields}
|
||||
|
||||
if with_limit_and_skip:
|
||||
if self.__limit:
|
||||
@ -311,10 +309,12 @@ class Cursor(object):
|
||||
if self.__skip:
|
||||
command["skip"] = self.__skip
|
||||
|
||||
response = self.__collection.database.command(command, allowable_errors=["ns missing"])
|
||||
if response.get("errmsg", "") == "ns missing":
|
||||
r = self.__collection.database.command("count", self.__collection.name,
|
||||
allowable_errors=["ns missing"],
|
||||
**command)
|
||||
if r.get("errmsg", "") == "ns missing":
|
||||
return 0
|
||||
return int(response["n"])
|
||||
return int(r["n"])
|
||||
|
||||
def distinct(self, key):
|
||||
"""Get a list of distinct values for `key` among all documents
|
||||
@ -335,12 +335,13 @@ class Cursor(object):
|
||||
if not isinstance(key, basestring):
|
||||
raise TypeError("key must be an instance of basestring")
|
||||
|
||||
command = SON([("distinct", self.__collection.name), ("key", key)])
|
||||
|
||||
options = {"key": key}
|
||||
if self.__spec:
|
||||
command["query"] = self.__spec
|
||||
options["query"] = self.__spec
|
||||
|
||||
return self.__collection.database.command(command)["values"]
|
||||
return self.__collection.database.command("distinct",
|
||||
self.__collection.name,
|
||||
**options)["values"]
|
||||
|
||||
def explain(self):
|
||||
"""Returns an explain plan record for this cursor.
|
||||
|
||||
@ -230,17 +230,36 @@ class Database(object):
|
||||
def _command(self, command, allowable_errors=[], check=True, sock=None):
|
||||
warnings.warn("The '_command' method is deprecated. "
|
||||
"Please use 'command' instead.", DeprecationWarning)
|
||||
return self.command(command, check, allowable_errors, sock)
|
||||
return self.command(command, check=check,
|
||||
allowable_errors=allowable_errors, _sock=sock)
|
||||
|
||||
# TODO api could be nicer like take a verb and a subject and then kwargs
|
||||
# for options
|
||||
def command(self, command, check=True, allowable_errors=[], _sock=None):
|
||||
def command(self, command, value=1,
|
||||
check=True, allowable_errors=[], _sock=None, **kwargs):
|
||||
"""Issue a MongoDB command.
|
||||
|
||||
Send command `command` to the database and return the
|
||||
response. If `command` is an instance of :class:`str` then the
|
||||
command ``{command: 1}`` will be sent. Otherwise, `command`
|
||||
must be an instance of :class:`dict` and will be sent as is.
|
||||
response. If `command` is an instance of :class:`basestring`
|
||||
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,
|
||||
@ -248,15 +267,22 @@ class Database(object):
|
||||
|
||||
.. note:: the order of keys in the `command` document is
|
||||
significant (the "verb" must come first), so commands
|
||||
which require multiple keys (eg, `findandmodify`)
|
||||
should use an instance of :class:`~pymongo.son.SON`
|
||||
instead of a Python `dict`.
|
||||
which require multiple keys (e.g. `findandmodify`)
|
||||
should use an instance of :class:`~pymongo.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
|
||||
- `allowable_errors`: if `check` is ``True``, error messages
|
||||
in this list will be ignored by error-checking
|
||||
- `**kwargs` (optional): additional keyword arguments will
|
||||
be added to the command document before it is sent
|
||||
|
||||
.. versionchanged:: 1.5.1+
|
||||
Added the `value` argument for string commands, and keyword
|
||||
arguments for additional command options.
|
||||
.. versionchanged:: 1.5
|
||||
`command` can be a string in addition to a full document.
|
||||
.. versionadded:: 1.4
|
||||
@ -264,8 +290,10 @@ class Database(object):
|
||||
.. mongodoc:: commands
|
||||
"""
|
||||
|
||||
if isinstance(command, str):
|
||||
command = {command: 1}
|
||||
if isinstance(command, basestring):
|
||||
command = SON([(command, value)])
|
||||
|
||||
command.update(kwargs)
|
||||
|
||||
result = self["$cmd"].find_one(command, _sock=_sock,
|
||||
_must_use_master=True,
|
||||
@ -308,7 +336,7 @@ class Database(object):
|
||||
if name not in self.collection_names():
|
||||
return
|
||||
|
||||
self.command({"drop": unicode(name)})
|
||||
self.command("drop", unicode(name))
|
||||
|
||||
def validate_collection(self, name_or_collection):
|
||||
"""Validate a collection.
|
||||
@ -324,7 +352,7 @@ class Database(object):
|
||||
raise TypeError("name_or_collection must be an instance of "
|
||||
"(Collection, str, unicode)")
|
||||
|
||||
result = self.command({"validate": unicode(name)})
|
||||
result = self.command("validate", unicode(name))
|
||||
|
||||
info = result["result"]
|
||||
if info.find("exception") != -1 or info.find("corrupt") != -1:
|
||||
@ -339,7 +367,7 @@ class Database(object):
|
||||
|
||||
.. mongodoc:: profiling
|
||||
"""
|
||||
result = self.command({"profile": -1})
|
||||
result = self.command("profile", -1)
|
||||
|
||||
assert result["was"] >= 0 and result["was"] <= 2
|
||||
return result["was"]
|
||||
@ -359,7 +387,7 @@ class Database(object):
|
||||
if not isinstance(level, int) or level < 0 or level > 2:
|
||||
raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)")
|
||||
|
||||
self.command({"profile": level})
|
||||
self.command("profile", level)
|
||||
|
||||
def profiling_info(self):
|
||||
"""Returns a list containing current profiling information.
|
||||
@ -496,8 +524,7 @@ class Database(object):
|
||||
nonce = self.command("getnonce")["nonce"]
|
||||
key = helpers._auth_key(nonce, name, password)
|
||||
try:
|
||||
self.command(SON([("authenticate", 1), ("user", unicode(name)),
|
||||
("nonce", nonce), ("key", key)]))
|
||||
self.command("authenticate", user=unicode(name), nonce=nonce, key=key)
|
||||
return True
|
||||
except OperationFailure:
|
||||
return False
|
||||
@ -549,8 +576,7 @@ class Database(object):
|
||||
if not isinstance(code, Code):
|
||||
code = Code(code)
|
||||
|
||||
command = SON([("$eval", code), ("args", list(args))])
|
||||
result = self.command(command)
|
||||
result = self.command("$eval", code, args=args)
|
||||
return result.get("retval", None)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
|
||||
@ -303,60 +303,59 @@ class TestCursor(unittest.TestCase):
|
||||
db = self.db
|
||||
db.drop_collection("test")
|
||||
|
||||
client_cursors = db.command({"cursorInfo": 1})["clientCursors_size"]
|
||||
by_location = db.command({"cursorInfo": 1})["byLocation_size"]
|
||||
client_cursors = db.command("cursorInfo")["clientCursors_size"]
|
||||
by_location = db.command("cursorInfo")["byLocation_size"]
|
||||
|
||||
test = db.test
|
||||
for i in range(10000):
|
||||
test.insert({"i": i})
|
||||
|
||||
self.assertEqual(client_cursors,
|
||||
db.command({"cursorInfo": 1})["clientCursors_size"])
|
||||
db.command("cursorInfo")["clientCursors_size"])
|
||||
self.assertEqual(by_location,
|
||||
db.command({"cursorInfo": 1})["byLocation_size"])
|
||||
db.command("cursorInfo")["byLocation_size"])
|
||||
|
||||
for _ in range(10):
|
||||
db.test.find_one()
|
||||
|
||||
self.assertEqual(client_cursors,
|
||||
db.command({"cursorInfo": 1})["clientCursors_size"])
|
||||
db.command("cursorInfo")["clientCursors_size"])
|
||||
self.assertEqual(by_location,
|
||||
db.command({"cursorInfo": 1})["byLocation_size"])
|
||||
db.command("cursorInfo")["byLocation_size"])
|
||||
|
||||
for _ in range(10):
|
||||
for x in db.test.find():
|
||||
break
|
||||
|
||||
self.assertEqual(client_cursors,
|
||||
db.command({"cursorInfo": 1})["clientCursors_size"])
|
||||
db.command("cursorInfo")["clientCursors_size"])
|
||||
self.assertEqual(by_location,
|
||||
db.command({"cursorInfo": 1})["byLocation_size"])
|
||||
db.command("cursorInfo")["byLocation_size"])
|
||||
|
||||
a = db.test.find()
|
||||
for x in a:
|
||||
break
|
||||
|
||||
self.assertNotEqual(
|
||||
client_cursors,
|
||||
db.command({"cursorInfo": 1})["clientCursors_size"])
|
||||
self.assertNotEqual(client_cursors,
|
||||
db.command("cursorInfo")["clientCursors_size"])
|
||||
self.assertNotEqual(by_location,
|
||||
db.command({"cursorInfo": 1})["byLocation_size"])
|
||||
db.command("cursorInfo")["byLocation_size"])
|
||||
|
||||
del a
|
||||
|
||||
self.assertEqual(client_cursors,
|
||||
db.command({"cursorInfo": 1})["clientCursors_size"])
|
||||
db.command("cursorInfo")["clientCursors_size"])
|
||||
self.assertEqual(by_location,
|
||||
db.command({"cursorInfo": 1})["byLocation_size"])
|
||||
db.command("cursorInfo")["byLocation_size"])
|
||||
|
||||
a = db.test.find().limit(10)
|
||||
for x in a:
|
||||
break
|
||||
|
||||
self.assertEqual(client_cursors,
|
||||
db.command({"cursorInfo": 1})["clientCursors_size"])
|
||||
db.command("cursorInfo")["clientCursors_size"])
|
||||
self.assertEqual(by_location,
|
||||
db.command({"cursorInfo": 1})["byLocation_size"])
|
||||
db.command("cursorInfo")["byLocation_size"])
|
||||
|
||||
def test_rewind(self):
|
||||
self.db.test.save({"x": 1})
|
||||
|
||||
@ -188,11 +188,11 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertEqual(None, db.error())
|
||||
self.assertEqual(None, db.previous_error())
|
||||
|
||||
db.command({"forceerror": 1}, check=False)
|
||||
db.command("forceerror", check=False)
|
||||
self.assert_(db.error())
|
||||
self.assert_(db.previous_error())
|
||||
|
||||
db.command({"forceerror": 1}, check=False)
|
||||
db.command("forceerror", check=False)
|
||||
self.assert_(db.error())
|
||||
prev_error = db.previous_error()
|
||||
self.assertEqual(prev_error["nPrev"], 1)
|
||||
|
||||
@ -184,11 +184,10 @@ class TestMasterSlaveConnection(unittest.TestCase):
|
||||
|
||||
def cursor_count():
|
||||
count = 0
|
||||
res = self.connection.master.test_pymongo.command({
|
||||
"cursorInfo": 1})
|
||||
res = self.connection.master.test_pymongo.command("cursorInfo")
|
||||
count += res["clientCursors_size"]
|
||||
for slave in self.connection.slaves:
|
||||
res = slave.test_pymongo.command({"cursorInfo": 1})
|
||||
res = slave.test_pymongo.command("cursorInfo")
|
||||
count += res["clientCursors_size"]
|
||||
return count
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user