Skip to content

Commit b989e3e

Browse files
author
Mike Dirolf
committed
improvements to the db.command api
1 parent d7ffcf3 commit b989e3e

File tree

8 files changed

+97
-80
lines changed

8 files changed

+97
-80
lines changed

gridfs/grid_file.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ def flush(self):
262262

263263
self.__flush_write_buffer()
264264

265-
md5 = self.__collection.database.command(SON([("filemd5", self.__id),
266-
("root", self.__collection.name)]))["md5"]
265+
md5 = self.__collection.database.command("filemd5", self.__id,
266+
root=self.__collection.name)["md5"]
267267

268268
grid_file = self.__collection.files.find_one({"_id": self.__id})
269269
grid_file["md5"] = md5

pymongo/collection.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,12 @@ def __create(self, options):
115115

116116
# Send size as a float, not an int/long. BSON can only handle 32-bit
117117
# ints which conflicts w/ max collection size of 10000000000.
118-
if options and "size" in options:
119-
options["size"] = float(options["size"])
120-
121-
command = SON({"create": self.__name})
122-
command.update(options)
123-
124-
self.__database.command(command)
118+
if options:
119+
if "size" in options:
120+
options["size"] = float(options["size"])
121+
self.__database.command("create", self.__name, **options)
122+
else:
123+
self.__database.command("create", self.__name)
125124

126125
def __getattr__(self, name):
127126
"""Get a sub-collection of this collection by name.
@@ -697,9 +696,7 @@ def drop_index(self, index_or_name):
697696

698697
self.__database.connection._purge_index(self.__database.name,
699698
self.__name, name)
700-
self.__database.command(SON([("deleteIndexes",
701-
self.__name),
702-
("index", name)]),
699+
self.__database.command("deleteIndexes", self.__name, index=name,
703700
allowable_errors=["ns not found"])
704701

705702
def index_information(self):
@@ -785,7 +782,7 @@ def group(self, key, condition, initial, reduce, finalize=None,
785782
if finalize is not None:
786783
group["finalize"] = Code(finalize)
787784

788-
return self.__database.command({"group":group})["retval"]
785+
return self.__database.command("group", group)["retval"]
789786

790787
def rename(self, new_name):
791788
"""Rename this collection.
@@ -809,11 +806,9 @@ def rename(self, new_name):
809806
if new_name[0] == "." or new_name[-1] == ".":
810807
raise InvalidName("collecion names must not start or end with '.'")
811808

812-
rename_command = SON([("renameCollection", self.__full_name),
813-
("to", "%s.%s" % (self.__database.name,
814-
new_name))])
815-
816-
self.__database.connection.admin.command(rename_command)
809+
new_name = "%s.%s" % (self.__database.name, new_name)
810+
self.__database.connection.admin.command("renameCollection",
811+
self.__full_name, to=new_name)
817812

818813
def distinct(self, key):
819814
"""Get a list of distinct values for `key` among all documents
@@ -863,11 +858,8 @@ def map_reduce(self, map, reduce, full_response=False, **kwargs):
863858
864859
.. mongodoc:: mapreduce
865860
"""
866-
command = SON([("mapreduce", self.__name),
867-
("map", map), ("reduce", reduce)])
868-
command.update(**kwargs)
869-
870-
response = self.__database.command(command)
861+
response = self.__database.command("mapreduce", self.__name,
862+
map=map, reduce=reduce, **kwargs)
871863
if full_response:
872864
return response
873865
return self.__database[response["result"]]

pymongo/connection.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -804,19 +804,19 @@ def copy_database(self, from_name, to_name,
804804

805805
database._check_name(to_name)
806806

807-
command = SON([("copydb", 1), ("fromdb", from_name), ("todb", to_name)])
807+
command = {"fromdb": from_name, "todb": to_name}
808808

809809
if from_host is not None:
810810
command["fromhost"] = from_host
811811

812812
if username is not None:
813-
nonce = self.admin.command(SON([("copydbgetnonce", 1),
814-
("fromhost", from_host)]))["nonce"]
813+
nonce = self.admin.command("copydbgetnonce",
814+
fromhost=from_host)["nonce"]
815815
command["username"] = username
816816
command["nonce"] = nonce
817817
command["key"] = helpers._auth_key(nonce, username, password)
818818

819-
return self.admin.command(command)
819+
return self.admin.command("copydb", **command)
820820

821821
def __iter__(self):
822822
return self

pymongo/cursor.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,20 +301,20 @@ def count(self, with_limit_and_skip=False):
301301
:meth:`~pymongo.cursor.Cursor.__len__` was deprecated in favor of
302302
calling :meth:`count` with `with_limit_and_skip` set to ``True``.
303303
"""
304-
command = SON([("count", self.__collection.name),
305-
("query", self.__spec),
306-
("fields", self.__fields)])
304+
command = {"query": self.__spec, "fields": self.__fields}
307305

308306
if with_limit_and_skip:
309307
if self.__limit:
310308
command["limit"] = self.__limit
311309
if self.__skip:
312310
command["skip"] = self.__skip
313311

314-
response = self.__collection.database.command(command, allowable_errors=["ns missing"])
315-
if response.get("errmsg", "") == "ns missing":
312+
r = self.__collection.database.command("count", self.__collection.name,
313+
allowable_errors=["ns missing"],
314+
**command)
315+
if r.get("errmsg", "") == "ns missing":
316316
return 0
317-
return int(response["n"])
317+
return int(r["n"])
318318

319319
def distinct(self, key):
320320
"""Get a list of distinct values for `key` among all documents
@@ -335,12 +335,13 @@ def distinct(self, key):
335335
if not isinstance(key, basestring):
336336
raise TypeError("key must be an instance of basestring")
337337

338-
command = SON([("distinct", self.__collection.name), ("key", key)])
339-
338+
options = {"key": key}
340339
if self.__spec:
341-
command["query"] = self.__spec
340+
options["query"] = self.__spec
342341

343-
return self.__collection.database.command(command)["values"]
342+
return self.__collection.database.command("distinct",
343+
self.__collection.name,
344+
**options)["values"]
344345

345346
def explain(self):
346347
"""Returns an explain plan record for this cursor.

pymongo/database.py

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -230,42 +230,70 @@ def _fix_outgoing(self, son, collection):
230230
def _command(self, command, allowable_errors=[], check=True, sock=None):
231231
warnings.warn("The '_command' method is deprecated. "
232232
"Please use 'command' instead.", DeprecationWarning)
233-
return self.command(command, check, allowable_errors, sock)
233+
return self.command(command, check=check,
234+
allowable_errors=allowable_errors, _sock=sock)
234235

235-
# TODO api could be nicer like take a verb and a subject and then kwargs
236-
# for options
237-
def command(self, command, check=True, allowable_errors=[], _sock=None):
236+
def command(self, command, value=1,
237+
check=True, allowable_errors=[], _sock=None, **kwargs):
238238
"""Issue a MongoDB command.
239239
240240
Send command `command` to the database and return the
241-
response. If `command` is an instance of :class:`str` then the
242-
command ``{command: 1}`` will be sent. Otherwise, `command`
243-
must be an instance of :class:`dict` and will be sent as is.
241+
response. If `command` is an instance of :class:`basestring`
242+
then the command {`command`: `value`} will be sent. Otherwise,
243+
`command` must be an instance of :class:`dict` and will be
244+
sent as is.
245+
246+
Any additional keyword arguments will be added to the final
247+
command document before it is sent.
248+
249+
For example, a command like ``{buildinfo: 1}`` can be sent
250+
using:
251+
252+
>>> db.command("buildinfo")
253+
254+
For a command where the value matters, like ``{collstats:
255+
collection_name}`` we can do:
256+
257+
>>> db.command("collstats", collection_name)
258+
259+
For commands that take additional arguments we can use
260+
kwargs. So ``{filemd5: object_id, root: file_root}`` becomes:
261+
262+
>>> db.command("filemd5", object_id, root=file_root)
244263
245264
:Parameters:
246265
- `command`: document representing the command to be issued,
247266
or the name of the command (for simple commands only).
248267
249268
.. note:: the order of keys in the `command` document is
250269
significant (the "verb" must come first), so commands
251-
which require multiple keys (eg, `findandmodify`)
252-
should use an instance of :class:`~pymongo.son.SON`
253-
instead of a Python `dict`.
270+
which require multiple keys (e.g. `findandmodify`)
271+
should use an instance of :class:`~pymongo.son.SON` or
272+
a string and kwargs instead of a Python `dict`.
254273
274+
- `value` (optional): value to use for the command verb when
275+
`command` is passed as a string
255276
- `check` (optional): check the response for errors, raising
256277
:class:`~pymongo.errors.OperationFailure` if there are any
257-
- `allowable_errors`: if `check` is ``True``, error messages in this
258-
list will be ignored by error-checking
278+
- `allowable_errors`: if `check` is ``True``, error messages
279+
in this list will be ignored by error-checking
280+
- `**kwargs` (optional): additional keyword arguments will
281+
be added to the command document before it is sent
259282
283+
.. versionchanged:: 1.5.1+
284+
Added the `value` argument for string commands, and keyword
285+
arguments for additional command options.
260286
.. versionchanged:: 1.5
261287
`command` can be a string in addition to a full document.
262288
.. versionadded:: 1.4
263289
264290
.. mongodoc:: commands
265291
"""
266292

267-
if isinstance(command, str):
268-
command = {command: 1}
293+
if isinstance(command, basestring):
294+
command = SON([(command, value)])
295+
296+
command.update(kwargs)
269297

270298
result = self["$cmd"].find_one(command, _sock=_sock,
271299
_must_use_master=True,
@@ -308,7 +336,7 @@ def drop_collection(self, name_or_collection):
308336
if name not in self.collection_names():
309337
return
310338

311-
self.command({"drop": unicode(name)})
339+
self.command("drop", unicode(name))
312340

313341
def validate_collection(self, name_or_collection):
314342
"""Validate a collection.
@@ -324,7 +352,7 @@ def validate_collection(self, name_or_collection):
324352
raise TypeError("name_or_collection must be an instance of "
325353
"(Collection, str, unicode)")
326354

327-
result = self.command({"validate": unicode(name)})
355+
result = self.command("validate", unicode(name))
328356

329357
info = result["result"]
330358
if info.find("exception") != -1 or info.find("corrupt") != -1:
@@ -339,7 +367,7 @@ def profiling_level(self):
339367
340368
.. mongodoc:: profiling
341369
"""
342-
result = self.command({"profile": -1})
370+
result = self.command("profile", -1)
343371

344372
assert result["was"] >= 0 and result["was"] <= 2
345373
return result["was"]
@@ -359,7 +387,7 @@ def set_profiling_level(self, level):
359387
if not isinstance(level, int) or level < 0 or level > 2:
360388
raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)")
361389

362-
self.command({"profile": level})
390+
self.command("profile", level)
363391

364392
def profiling_info(self):
365393
"""Returns a list containing current profiling information.
@@ -496,8 +524,7 @@ def authenticate(self, name, password):
496524
nonce = self.command("getnonce")["nonce"]
497525
key = helpers._auth_key(nonce, name, password)
498526
try:
499-
self.command(SON([("authenticate", 1), ("user", unicode(name)),
500-
("nonce", nonce), ("key", key)]))
527+
self.command("authenticate", user=unicode(name), nonce=nonce, key=key)
501528
return True
502529
except OperationFailure:
503530
return False
@@ -549,8 +576,7 @@ def eval(self, code, *args):
549576
if not isinstance(code, Code):
550577
code = Code(code)
551578

552-
command = SON([("$eval", code), ("args", list(args))])
553-
result = self.command(command)
579+
result = self.command("$eval", code, args=args)
554580
return result.get("retval", None)
555581

556582
def __call__(self, *args, **kwargs):

test/test_cursor.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -303,60 +303,59 @@ def test_kill_cursors(self):
303303
db = self.db
304304
db.drop_collection("test")
305305

306-
client_cursors = db.command({"cursorInfo": 1})["clientCursors_size"]
307-
by_location = db.command({"cursorInfo": 1})["byLocation_size"]
306+
client_cursors = db.command("cursorInfo")["clientCursors_size"]
307+
by_location = db.command("cursorInfo")["byLocation_size"]
308308

309309
test = db.test
310310
for i in range(10000):
311311
test.insert({"i": i})
312312

313313
self.assertEqual(client_cursors,
314-
db.command({"cursorInfo": 1})["clientCursors_size"])
314+
db.command("cursorInfo")["clientCursors_size"])
315315
self.assertEqual(by_location,
316-
db.command({"cursorInfo": 1})["byLocation_size"])
316+
db.command("cursorInfo")["byLocation_size"])
317317

318318
for _ in range(10):
319319
db.test.find_one()
320320

321321
self.assertEqual(client_cursors,
322-
db.command({"cursorInfo": 1})["clientCursors_size"])
322+
db.command("cursorInfo")["clientCursors_size"])
323323
self.assertEqual(by_location,
324-
db.command({"cursorInfo": 1})["byLocation_size"])
324+
db.command("cursorInfo")["byLocation_size"])
325325

326326
for _ in range(10):
327327
for x in db.test.find():
328328
break
329329

330330
self.assertEqual(client_cursors,
331-
db.command({"cursorInfo": 1})["clientCursors_size"])
331+
db.command("cursorInfo")["clientCursors_size"])
332332
self.assertEqual(by_location,
333-
db.command({"cursorInfo": 1})["byLocation_size"])
333+
db.command("cursorInfo")["byLocation_size"])
334334

335335
a = db.test.find()
336336
for x in a:
337337
break
338338

339-
self.assertNotEqual(
340-
client_cursors,
341-
db.command({"cursorInfo": 1})["clientCursors_size"])
339+
self.assertNotEqual(client_cursors,
340+
db.command("cursorInfo")["clientCursors_size"])
342341
self.assertNotEqual(by_location,
343-
db.command({"cursorInfo": 1})["byLocation_size"])
342+
db.command("cursorInfo")["byLocation_size"])
344343

345344
del a
346345

347346
self.assertEqual(client_cursors,
348-
db.command({"cursorInfo": 1})["clientCursors_size"])
347+
db.command("cursorInfo")["clientCursors_size"])
349348
self.assertEqual(by_location,
350-
db.command({"cursorInfo": 1})["byLocation_size"])
349+
db.command("cursorInfo")["byLocation_size"])
351350

352351
a = db.test.find().limit(10)
353352
for x in a:
354353
break
355354

356355
self.assertEqual(client_cursors,
357-
db.command({"cursorInfo": 1})["clientCursors_size"])
356+
db.command("cursorInfo")["clientCursors_size"])
358357
self.assertEqual(by_location,
359-
db.command({"cursorInfo": 1})["byLocation_size"])
358+
db.command("cursorInfo")["byLocation_size"])
360359

361360
def test_rewind(self):
362361
self.db.test.save({"x": 1})

test/test_database.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@ def test_errors(self):
188188
self.assertEqual(None, db.error())
189189
self.assertEqual(None, db.previous_error())
190190

191-
db.command({"forceerror": 1}, check=False)
191+
db.command("forceerror", check=False)
192192
self.assert_(db.error())
193193
self.assert_(db.previous_error())
194194

195-
db.command({"forceerror": 1}, check=False)
195+
db.command("forceerror", check=False)
196196
self.assert_(db.error())
197197
prev_error = db.previous_error()
198198
self.assertEqual(prev_error["nPrev"], 1)

test/test_master_slave_connection.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,10 @@ def test_kill_cursors(self):
184184

185185
def cursor_count():
186186
count = 0
187-
res = self.connection.master.test_pymongo.command({
188-
"cursorInfo": 1})
187+
res = self.connection.master.test_pymongo.command("cursorInfo")
189188
count += res["clientCursors_size"]
190189
for slave in self.connection.slaves:
191-
res = slave.test_pymongo.command({"cursorInfo": 1})
190+
res = slave.test_pymongo.command("cursorInfo")
192191
count += res["clientCursors_size"]
193192
return count
194193

0 commit comments

Comments
 (0)