Этот пост является ответом на постоянный вопрос о драйверах 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 командой, и найдите ее в справочнике команд . Скорее всего, он есть, и если это так, вы знаете, как его запустить.