Этот пост является ответом на постоянный вопрос о драйверах MongoDB, который я получаю: «Поддерживает ли драйвер X функцию Y?» Ответ почти всегда «да», но вы не можете знать это, если не понимаете команды MongoDB.
Существует только четыре вида операций, которые драйвер MongoDB может выполнять на сервере: вставка, обновление, удаление, запрос и команды.
Почти два года назад моя коллега Кристина писала о « Почему командные помощники сосут », и она все еще права: если вы используете только удобные методы, не понимая объединяющую концепцию «команды», вы излишне привязаны к API конкретного драйвера и вы не знаете, как на самом деле работает MongoDB.
Итак, давайте сделаем поп-викторину:
-
Какие драйверы MongoDB поддерживают Aggregation Framework?
-
Какие поддерживают «групповые» операции?
-
Какие драйверы совместимы с функцией mapreduce MongoDB?
-
Какие драйверы позволяют запускать «считать» или «различать» в коллекции?
Если вы ответили «все из них», вы правы — каждый драйвер поддерживает команды, а все функции, о которых я спрашивал, — это команды.
Давайте рассмотрим три драйвера MongoDB для Python и покажем примеры использования distinct
команды в каждом.
PyMongo
У PyMongo есть два удобных метода для distinct
. Один в Collection
классе, другой на Cursor
:
>>> from pymongo import MongoClient >>> db = MongoClient().test >>> db.test_collection.distinct('my_key') [1.0, 2.0, 3.0] >>> db.test_collection.find().distinct('my_key') [1.0, 2.0, 3.0]
Но все это сводится к одной и той же команде MongoDB. Мы можем посмотреть его аргументы в Справочнике по командам MongoDB и увидеть, что отдельный принимает форму:
{ distinct: collection, key: <field>, query: <query> }
Итак, давайте использовать общий command
метод PyMongo для distinct
непосредственного запуска . Мы будем передавать collection
и key
аргументы и опускаем query
. Нам нужно использовать SON
класс PyMongo, чтобы убедиться, что мы передаем аргументы в правильном порядке:
>>> from bson import SON >>> db.command(SON([('distinct', 'test_collection'), ('key', 'my_key')])) {u'ok': 1.0, u'stats': {u'cursor': u'BasicCursor', u'n': 3, u'nscanned': 3, u'nscannedObjects': 3, u'timems': 0}, u'values': [1.0, 2.0, 3.0]}
Ответ в values
.
двигатель
Мой асинхронный драйвер для Tornado и MongoDB, называемый Motor , поддерживает аналогичные удобства для distinct
. У него есть оба MotorCollection.distinct
метода:
>>> from tornado.ioloop import IOLoop >>> from tornado import gen >>> import motor >>> from motor import MotorConnection >>> db = MotorConnection().open_sync().test >>> @gen.engine ... def f(): ... print (yield motor.Op(db.test_collection.distinct, 'my_key')) ... IOLoop.instance().stop() ... >>> f() >>> IOLoop.instance().start() [1.0, 2.0, 3.0]
… и MotorCursor.distinct
:
>>> @gen.engine ... def f(): ... print (yield motor.Op(db.test_collection.find().distinct, 'my_key')) ... IOLoop.instance().stop() ... >>> f() >>> IOLoop.instance().start() [1.0, 2.0, 3.0]
Опять же, это просто удобные альтернативы использованию MotorDatabase.command
:
>>> @gen.engine ... def f(): ... print (yield motor.Op(db.command, ... SON([('distinct', 'test_collection'), ('key', 'my_key')]))) ... IOLoop.instance().stop() ... >>> f() >>> IOLoop.instance().start() {u'ok': 1.0, u'stats': {u'cursor': u'BasicCursor', u'n': 3, u'nscanned': 3, u'nscannedObjects': 3, u'timems': 0}, u'values': [1.0, 2.0, 3.0]}
AsyncMongo
AsyncMongo — еще один драйвер для Tornado и MongoDB. Его интерфейс не так богат, как у Motor, поэтому я часто слышу такие вопросы, как «Поддерживает ли AsyncMongo distinct
? Поддерживает ли он aggregate
? Как насчет group
?» На самом деле, это те вопросы, которые вызвали этот пост. И, конечно, ответ — да, AsyncMongo поддерживает все команды:
>>> from tornado.ioloop import IOLoop >>> import asyncmongo >>> db = asyncmongo.Client( ... pool_id='mydb', host='127.0.0.1', port=27017, ... maxcached=10, maxconnections=50, dbname='test') >>> @gen.engine ... def f(): ... results = yield gen.Task(db.command, ... SON([('distinct', 'test_collection'), ('key', 'my_key')])) ... print results.args[0] ... IOLoop.instance().stop() ... >>> f() >>> IOLoop.instance().start() {u'ok': 1.0, u'stats': {u'cursor': u'BasicCursor', u'n': 3, u'nscanned': 3, u'nscannedObjects': 3, u'timems': 0}, u'values': [1.0, 2.0, 3.0]}
Исключения
Есть некоторые области, где драйверы действительно различаются, например, поддержка набора реплик или чтение настроек . Драйверы 10gen гораздо более последовательны, чем драйверы сторонних производителей. Но если основной операцией является команда, то все драйверы по сути одинаковы.
Итак, научитесь запускать команды
Поэтому в следующий раз, когда вы спросите: «Поддерживает ли драйвер X функцию Y», сначала проверьте, является ли команда Y командой, и найдите ее в справочнике команд . Скорее всего, он есть, и если это так, вы знаете, как его запустить.