美文网首页QUANTAXIS
async-mongo: motor

async-mongo: motor

作者: yutiansut | 来源:发表于2017-10-24 01:01 被阅读26次

    .. currentmodule:: motor.motor_asyncio

    Tutorial: Using Motor With :mod:asyncio

    .. These setups are redundant because I can't figure out how to make doctest
    run a common setup before the setup for the two groups. A "testsetup:: *"
    is the obvious answer, but it's run after group-specific setup.

    .. testsetup:: before-inserting-2000-docs

    import pymongo
    import motor.motor_asyncio
    import asyncio
    from asyncio import coroutine
    db = motor.motor_asyncio.AsyncIOMotorClient().test_database

    .. testsetup:: after-inserting-2000-docs

    import pymongo
    import motor.motor_asyncio
    import asyncio
    from asyncio import coroutine
    db = motor.motor_asyncio.AsyncIOMotorClient().test_database
    pymongo.MongoClient().test_database.test_collection.insert_many(
    [{'i': i} for i in range(2000)])

    .. testcleanup:: *

    import pymongo
    pymongo.MongoClient().test_database.test_collection.delete_many({})

    A guide to using MongoDB and asyncio with Motor.

    .. contents::

    Tutorial Prerequisites

    You can learn about MongoDB with the MongoDB Tutorial_ before you learn Motor.

    Using Python 3.4 or later, do::

    $ python3 -m pip install motor

    This tutorial assumes that a MongoDB instance is running on the
    default host and port. Assuming you have downloaded and installed <http://docs.mongodb.org/manual/installation/>_ MongoDB, you
    can start it like so:

    .. code-block:: bash

    $ mongod

    .. _pip: http://www.pip-installer.org/en/latest/installing.html

    .. _MongoDB Tutorial: http://docs.mongodb.org/manual/tutorial/getting-started/

    Object Hierarchy

    Motor, like PyMongo, represents data with a 4-level object hierarchy:

    • :class:~motor.motor_asyncio.AsyncIOMotorClient
      represents a mongod process, or a cluster of them. You explicitly create one
      of these client objects, connect it to a running mongod or mongods, and
      use it for the lifetime of your application.
    • :class:~motor.motor_asyncio.AsyncIOMotorDatabase: Each mongod has a set of databases (distinct
      sets of data files on disk). You can get a reference to a database from a
      client.
    • :class:~motor.motor_asyncio.AsyncIOMotorCollection: A database has a set of collections, which
      contain documents; you get a reference to a collection from a database.
    • :class:~motor.motor_asyncio.AsyncIOMotorCursor: Executing :meth:~motor.motor_asyncio.AsyncIOMotorCollection.find on
      an :class:~motor.motor_asyncio.AsyncIOMotorCollection gets an :class:~motor.motor_asyncio.AsyncIOMotorCursor, which
      represents the set of documents matching a query.

    Creating a Client

    You typically create a single instance of :class:~motor.motor_asyncio.AsyncIOMotorClient at the time your
    application starts up.

    .. doctest:: before-inserting-2000-docs

    import motor.motor_asyncio
    client = motor.motor_asyncio.AsyncIOMotorClient()

    This connects to a mongod listening on the default host and port. You can
    specify the host and port like:

    .. doctest:: before-inserting-2000-docs

    client = motor.motor_asyncio.AsyncIOMotorClient('localhost', 27017)

    Motor also supports connection URIs_:

    .. doctest:: before-inserting-2000-docs

    client = motor.motor_asyncio.AsyncIOMotorClient('mongodb://localhost:27017')

    Connect to a replica set like:

    client = motor.motor_asyncio.AsyncIOMotorClient('mongodb://host1,host2/?replicaSet=my-replicaset-name')

    .. _connection URIs: http://docs.mongodb.org/manual/reference/connection-string/

    Getting a Database

    A single instance of MongoDB can support multiple independent
    databases <http://docs.mongodb.org/manual/reference/glossary/#term-database>_.
    From an open client, you can get a reference to a particular database with
    dot-notation or bracket-notation:

    .. doctest:: before-inserting-2000-docs

    db = client.test_database
    db = client['test_database']

    Creating a reference to a database does no I/O and does not require an
    await expression.

    Getting a Collection

    A collection <http://docs.mongodb.org/manual/reference/glossary/#term-collection>_
    is a group of documents stored in MongoDB, and can be thought of as roughly
    the equivalent of a table in a relational database. Getting a
    collection in Motor works the same as getting a database:

    .. doctest:: before-inserting-2000-docs

    collection = db.test_collection
    collection = db['test_collection']

    Just like getting a reference to a database, getting a reference to a
    collection does no I/O and doesn't require an await expression.

    Inserting a Document

    As in PyMongo, Motor represents MongoDB documents with Python dictionaries. To
    store a document in MongoDB, call :meth:~AsyncIOMotorCollection.insert_one in an
    await expression:

    .. doctest:: before-inserting-2000-docs

    async def do_insert():
    ... document = {'key': 'value'}
    ... result = await db.test_collection.insert_one(document)
    ... print('result %s' % repr(result.inserted_id))
    ...

    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_insert())
    result ObjectId('...')

    .. mongodoc:: insert

    .. doctest:: before-inserting-2000-docs
    :hide:

    Clean up from previous insert

    pymongo.MongoClient().test_database.test_collection.delete_many({})
    <pymongo.results.DeleteResult ...>

    Using native coroutines

    Starting in Python 3.5, you can define a native coroutine_ with async def
    instead of the coroutine decorator. Within a native coroutine, wait
    for an async operation with await instead of yield:

    .. doctest:: before-inserting-2000-docs

    async def do_insert():
    ... for i in range(2000):
    ... result = await db.test_collection.insert_one({'i': i})
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_insert())

    Within a native coroutine, the syntax to use Motor with Tornado or asyncio
    is often identical.

    .. _native coroutine: https://www.python.org/dev/peps/pep-0492/

    Getting a Single Document With find_one

    Use :meth:~motor.motor_asyncio.AsyncIOMotorCollection.find_one to get the first document that
    matches a query. For example, to get a document where the value for key "i" is
    less than 1:

    .. doctest:: after-inserting-2000-docs

    async def do_find_one():
    ... document = await db.test_collection.find_one({'i': {'$lt': 1}})
    ... pprint.pprint(document)
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_find_one())
    {'_id': ObjectId('...'), 'i': 0}

    The result is a dictionary matching the one that we inserted previously.

    .. note:: The returned document contains an "_id", which was
    automatically added on insert.

    .. mongodoc:: find

    Querying for More Than One Document

    Use :meth:~motor.motor_asyncio.AsyncIOMotorCollection.find to query for a set of documents.
    :meth:~motor.motor_asyncio.AsyncIOMotorCollection.find does no I/O and does not require an await
    expression. It merely creates an :class:~motor.motor_asyncio.AsyncIOMotorCursor instance. The query is
    actually executed on the server when you call :meth:~motor.motor_asyncio.AsyncIOMotorCursor.to_list
    or execute an async for loop.

    To find all documents with "i" less than 5:

    .. doctest:: after-inserting-2000-docs

    async def do_find():
    ... cursor = db.test_collection.find({'i': {'$lt': 5}}).sort('i')
    ... for document in await cursor.to_list(length=100):
    ... pprint.pprint(document)
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_find())
    {'_id': ObjectId('...'), 'i': 0}
    {'_id': ObjectId('...'), 'i': 1}
    {'_id': ObjectId('...'), 'i': 2}
    {'_id': ObjectId('...'), 'i': 3}
    {'_id': ObjectId('...'), 'i': 4}

    A length argument is required when you call to_list to prevent Motor
    from buffering an unlimited number of documents.

    async for

    
    You can handle one document at a time in an ``async for`` loop:
    
    .. doctest:: after-inserting-2000-docs
    
      >>> async def do_find():
      ...     c = db.test_collection
      ...     async for document in c.find({'i': {'$lt': 2}}):
      ...         pprint.pprint(document)
      ...
      >>> loop = asyncio.get_event_loop()
      >>> loop.run_until_complete(do_find())
      {'_id': ObjectId('...'), 'i': 0}
      {'_id': ObjectId('...'), 'i': 1}
    
    You can apply a sort, limit, or skip to a query before you begin iterating:
    
    .. doctest:: after-inserting-2000-docs
    
      >>> async def do_find():
      ...     cursor = db.test_collection.find({'i': {'$lt': 5}})
      ...     # Modify the query before iterating
      ...     cursor.sort('i', -1).limit(2).skip(2)
      ...     async for document in cursor:
      ...         pprint.pprint(document)
      ...
      >>> loop = asyncio.get_event_loop()
      >>> loop.run_until_complete(do_find())
      {'_id': ObjectId('...'), 'i': 2}
      {'_id': ObjectId('...'), 'i': 1}
    
    The cursor does not actually retrieve each document from the server
    individually; it gets documents efficiently in `large batches`_.
    
    .. _`large batches`: https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/#cursor-batches
    
    Iteration in Python 3.3 and 3.4
    

    In Python versions without async for, handle one document at a time with
    :attr:~motor.motor_asyncio.AsyncIOMotorCursor.fetch_next
    and :meth:~motor.motor_asyncio.AsyncIOMotorCursor.next_object:

    .. doctest:: after-inserting-2000-docs

    @coroutine
    ... def do_find():
    ... cursor = db.test_collection.find({'i': {'$lt': 5}})
    ... while (yield from cursor.fetch_next):
    ... document = cursor.next_object()
    ... pprint.pprint(document)
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_find())
    {'_id': ObjectId('...'), 'i': 0}
    {'_id': ObjectId('...'), 'i': 1}
    async def do_find():
    ... cursor = db.test_collection.find({'i': {'$lt': 5}})
    ... # Modify the query before iterating
    ... cursor.sort('i', -1).limit(2).skip(2)
    ... async for document in cursor:
    ... pprint.pprint(document)
    ...
    >>> loop = asyncio.get_event_loop()
    >>> loop.run_until_complete(do_find())
    {'_id': ObjectId('...'), 'i': 2}
    {'id': ObjectId('...'), 'i': 1}

    The cursor does not actually retrieve each document from the server
    individually; it gets documents efficiently in large batches
    .

    .. _large batches: https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/#cursor-batches

    Iteration in Python 3.3 and 3.4
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    In Python versions without async for, handle one document at a time with
    :attr:~motor.motor_asyncio.AsyncIOMotorCursor.fetch_next
    and :meth:~motor.motor_asyncio.AsyncIOMotorCursor.next_object:

    .. doctest:: after-inserting-2000-docs

    >>> @coroutine
    ... def do_find():
    ... cursor = db.test_collection.find({'i': {'$lt': 5}})
    ... while (yield from cursor.fetch_next):
    ... document = cursor.next_object()
    ... pprint.pprint(document)
    ...
    >>> loop = asyncio.get_event_loop()
    >>> loop.run_until_complete(do_find())
    {'_id': ObjectId('...'), 'i': 0}
    {'_id': ObjectId('...'), 'i': 1}
    {'_id': ObjectId('...'), 'i': 2}
    {'_id': ObjectId('...'), 'i': 3}
    {'_id': ObjectId('...'), 'i': 4}

    Counting Documents

    Use :meth:~motor.motor_asyncio.AsyncIOMotorCursor.count to determine the number of documents in
    a collection, or the number of documents that match a query:

    .. doctest:: after-inserting-2000-docs

    async def do_count():
    ... n = await db.test_collection.find().count()
    ... print('%s documents in collection' % n)
    ... n = await db.test_collection.find({'i': {'$gt': 1000}}).count()
    ... print('%s documents where i > 1000' % n)
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_count())
    2000 documents in collection
    999 documents where i > 1000

    :meth:~motor.motor_asyncio.AsyncIOMotorCursor.count uses the count command internally; we'll
    cover commands_ below.

    .. seealso:: Count command <http://docs.mongodb.org/manual/reference/command/count/>_

    Updating Documents

    :meth:~motor.motor_asyncio.AsyncIOMotorCollection.replace_one changes a document. It requires two
    parameters: a query that specifies which document to replace, and a
    replacement document. The query follows the same syntax as for :meth:find or
    :meth:find_one. To replace a document:

    .. doctest:: after-inserting-2000-docs

    async def do_replace():
    ... coll = db.test_collection
    ... old_document = await coll.find_one({'i': 50})
    ... print('found document: %s' % pprint.pformat(old_document))
    ... _id = old_document['_id']
    ... result = await coll.replace_one({'_id': _id}, {'key': 'value'})
    ... print('replaced %s document' % result.modified_count)
    ... new_document = await coll.find_one({'_id': id})
    ... print('document is now %s' % pprint.pformat(new_document))
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_replace())
    found document: {'id': ObjectId('...'), 'i': 50}
    replaced 1 document
    loop.run_until_complete(do_count())
    2000 documents in collection
    999 documents where i > 1000

    :meth:~motor.motor_asyncio.AsyncIOMotorCursor.count uses the count command internally; we'll
    cover commands
    below.

    .. seealso:: Count command <http://docs.mongodb.org/manual/reference/command/count/>


    Updating Documents
    ------------------

    :meth:~motor.motor_asyncio.AsyncIOMotorCollection.replace_one changes a document. It requires two
    parameters: a query that specifies which document to replace, and a
    replacement document. The query follows the same syntax as for :meth:find or
    :meth:find_one. To replace a document:

    .. doctest:: after-inserting-2000-docs

    >>> async def do_replace():
    ... coll = db.test_collection
    ... old_document = await coll.find_one({'i': 50})
    ... print('found document: %s' % pprint.pformat(old_document))
    ... _id = old_document['_id']
    ... result = await coll.replace_one({'_id': _id}, {'key': 'value'})
    ... print('replaced %s document' % result.modified_count)
    ... new_document = await coll.find_one({'_id': _id})
    ... print('document is now %s' % pprint.pformat(new_document))
    ...
    >>> loop = asyncio.get_event_loop()
    >>> loop.run_until_complete(do_replace())
    found document: {'_id': ObjectId('...'), 'i': 50}
    replaced 1 document
    document is now {'_id': ObjectId('...'), 'key': 'value'}

    You can see that :meth:replace_one replaced everything in the old document
    except its _id with the new document.

    Use :meth:~motor.motor_asyncio.AsyncIOMotorCollection.update_one with MongoDB's modifier operators to
    update part of a document and leave the
    rest intact. We'll find the document whose "i" is 51 and use the $set
    operator to set "key" to "value":

    .. doctest:: after-inserting-2000-docs

    async def do_update():
    ... coll = db.test_collection
    ... result = await coll.update_one({'i': 51}, {'$set': {'key': 'value'}})
    ... print('updated %s document' % result.modified_count)
    ... new_document = await coll.find_one({'i': 51})
    ... print('document is now %s' % pprint.pformat(new_document))
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_update())
    updated 1 document
    document is now {'_id': ObjectId('...'), 'i': 51, 'key': 'value'}

    "key" is set to "value" and "i" is still 51.

    :meth:update_one only affects the first document it finds, you can
    update all of them with :meth:update_many::

    await coll.update_many({'i': {'$gt': 100}},
                           {'$set': {'key': 'value'}})
    

    .. mongodoc:: update

    Deleting Documents

    :meth:~motor.motor_asyncio.AsyncIOMotorCollection.delete_many takes a query with the same syntax as
    :meth:~motor.motor_asyncio.AsyncIOMotorCollection.find.
    :meth:delete_many immediately removes all matching documents.

    .. doctest:: after-inserting-2000-docs

    async def do_delete_many():
    ... coll = db.test_collection
    ... n = await coll.count()
    ... print('%s documents before calling delete_many()' % n)
    ... result = await db.test_collection.delete_many({'i': {'$gte': 1000}})
    ... print('%s documents after' % (await coll.count()))
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_delete_many())
    2000 documents before calling delete_many()
    1000 documents after

    .. mongodoc:: remove

    Commands

    Besides the "CRUD" operations--insert, update, delete, and find--all other
    operations on MongoDB are commands. Run them using
    the :meth:~motor.motor_asyncio.AsyncIOMotorDatabase.command method on :class:~motor.motor_asyncio.AsyncIOMotorDatabase:

    .. doctest:: after-inserting-2000-docs

    from bson import SON
    async def use_count_command():
    ... response = await db.command(SON([("count", "test_collection")]))
    ... print('response: %s' % pprint.pformat(response))
    ...
    loop = asyncio.get_event_loop()
    loop.run_until_complete(use_count_command())
    response: {'n': 1000, 'ok': 1.0...}

    Since the order of command parameters matters, don't use a Python dict to pass
    the command's parameters. Instead, make a habit of using :class:bson.SON,
    from the bson module included with PyMongo::

    await db.command(SON([("distinct", "test_collection"), ("key", "my_key"]))
    

    Many commands have special helper methods, such as
    :meth:~motor.motor_asyncio.AsyncIOMotorDatabase.create_collection or
    :meth:~motor.motor_asyncio.AsyncIOMotorCollection.aggregate, but these are just conveniences atop
    the basic :meth:command method.

    .. mongodoc:: commands

    .. _example-web-application-aiohttp:

    A Web Application With aiohttp_

    Let us create a web application using aiohttp_, a popular HTTP package for
    asyncio. Install it with::

    python3 -m pip install aiohttp

    We are going to make a trivial web site with two pages served from MongoDB.
    To begin:

    .. literalinclude:: examples/aiohttp_example.py
    :language: python3
    :start-after: setup-start
    :end-before: setup-end

    The AsyncIOMotorClient constructor does not actually connect to MongoDB.
    The client connects on demand, when you attempt the first operation.
    We create it and assign the "test" database's handle to db.

    The setup_db coroutine drops the "pages" collection (plainly, this code is
    for demonstration purposes), then inserts two documents. Each document's page
    name is its unique id, and the "body" field is a simple HTML page. Finally,
    setup_db returns the database handle.

    We'll use the setup_db coroutine soon. First, we need a request handler
    that serves pages from the data we stored in MongoDB.

    .. literalinclude:: examples/aiohttp_example.py
    :language: python3
    :start-after: handler-start
    :end-before: handler-end

    We start the server by running setup_db and passing the database handle
    to an :class:aiohttp.web.Application:

    .. literalinclude:: examples/aiohttp_example.py
    :language: python3
    :start-after: main-start
    :end-before: main-end

    Note that it is a common mistake to create a new client object for every
    request; this comes at a dire performance cost. Create the client
    when your application starts and reuse that one client for the lifetime
    of the process. You can maintain the client by storing a database handle
    from the client on your application object, as shown in this example.

    Visit localhost:8080/pages/page-one and the server responds "Hello!".
    At localhost:8080/pages/page-two it responds "Goodbye." At other URLs it
    returns a 404.

    The complete code is in the Motor repository in examples/aiohttp_example.py.

    .. _aiohttp: https://aiohttp.readthedocs.io/

    See also the :doc:examples/aiohttp_gridfs_example.

    Further Reading

    The handful of classes and methods introduced here are sufficient for daily
    tasks. The API documentation for :class:~motor.motor_asyncio.AsyncIOMotorClient, :class:~motor.motor_asyncio.AsyncIOMotorDatabase,
    :class:~motor.motor_asyncio.AsyncIOMotorCollection, and :class:~motor.motor_asyncio.AsyncIOMotorCursor provides a
    reference to Motor's complete feature set.

    Learning to use the MongoDB driver is just the beginning, of course. For
    in-depth instruction in MongoDB itself, see The MongoDB Manual_.

    相关文章

      网友评论

        本文标题:async-mongo: motor

        本文链接:https://www.haomeiwen.com/subject/efnzuxtx.html