LoginSignup
3
1

More than 3 years have passed since last update.

[Python3 / MongoDB] pymongoの処理呼出しを軽くまとめる

Last updated at Posted at 2019-12-08

概要

タイトルの通りです。

最近MongoDBを使い始めたのだけれど、情報が色々なサイトに散らばっていて困っていたので、「自分が普段使いそうなものをまとめておく(`・ω・´)キリッ」ということを目指した記事になっています。

Versions

$ python -V
Python 3.7.1

$ pip list | grep pymongo
pymongo                  3.9.0

公式チュートリアル

https://api.mongodb.com/python/current/tutorial.html
私の記事なぞ見なくても、これがある....英語だが

モジュールのimport

from pymongo import MongoClient, DESCENDING, ASCENDING

ここに書いているimport文は実行済みのものとして、以下の記事を書いていきます。

インスタンス作成

# これからこれを使って行くよ
>>> m_client = MongoClient()

使えそうなメソッドを列挙してみる

どばー

>>> dir(m_client)
['HOST', 'PORT', '_BaseObject__codec_options', '_BaseObject__read_concern', '_BaseObject__read_preference', '_BaseObject__write_concern', '_MongoClient__all_credentials', '_MongoClient__cursor_manager', '_MongoClient__default_database_name', '_MongoClient__index_cache', '_MongoClient__index_cache_lock', '_MongoClient__kill_cursors_queue', '_MongoClient__lock', '_MongoClient__options', '_MongoClient__start_session', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_cache_credentials', '_cache_index', '_cached', '_close_cursor', '_close_cursor_now', '_constructor_args', '_database_default_options', '_encrypter', '_end_sessions', '_ensure_session', '_event_listeners', '_get_server_session', '_get_socket', '_get_topology', '_is_writable', '_kill_cursors', '_kill_cursors_executor', '_process_periodic_tasks', '_process_response', '_purge_credentials', '_purge_index', '_read_preference_for', '_repr_helper', '_reset_server', '_reset_server_and_request_check', '_retry_with_session', '_retryable_read', '_retryable_write', '_return_server_session', '_run_operation_with_response', '_select_server', '_send_cluster_time', '_server_property', '_slaveok_for_server', '_socket_for_reads', '_socket_for_writes', '_tmp_session', '_topology', '_topology_settings', '_write_concern_for', 'address', 'arbiters', 'close', 'close_cursor', 'codec_options', 'database_names', 'drop_database', 'event_listeners', 'fsync', 'get_database', 'get_default_database', 'is_locked', 'is_mongos', 'is_primary', 'kill_cursors', 'list_database_names', 'list_databases', 'local_threshold_ms', 'max_bson_size', 'max_idle_time_ms', 'max_message_size', 'max_pool_size', 'max_write_batch_size', 'min_pool_size', 'next', 'nodes', 'primary', 'read_concern', 'read_preference', 'retry_reads', 'retry_writes', 'secondaries', 'server_info', 'server_selection_timeout', 'set_cursor_manager', 'start_session', 'unlock', 'watch', 'write_concern']

1. 最低限の基本処理

DB名を列挙

>>> m_client.list_database_names()
['admin', 'candles', 'config', 'local'] # 私のmongodbにあったDB

# どっちでも同じ結果よ~
>>> m_client.database_names()
['admin', 'candles', 'config', 'local']

DBを作成

なんと不要
指定したDBが存在しなくても、そこにデータを投入するとDBも一緒に作成されます!
便利だけど、ちょっと怖い...?

Collectionを作成

これも不要
mongodbでは、DBの中にCollectionという枠があり、その中にレコードを挿入していくのですが、レコードinsertの時点で存在しないCollectionは自動で作成されます。

※MongoDBに挿入されるのは、レコードではなくて本来はドキュメントと呼ばれているが、1件ずつ挿入する単位としてよく使われるレコードという呼称を本記事では使用します

これも便利だけどちょっと怖いですね。
(Collection指定が間違っててもエラーが出ないかも)

レコードの挿入(insert)

インスタンス.db名.collection名.insert_one({'hoge': 'ほげ'})
のような書き方になります。
※上で書いた通り、DB / Collectionともに未作成であっても、データを挿入すれば勝手に作成されます。


>>> m_client.db_name.collection_name.insert_one({'hoge': 'ほげ1'})
<pymongo.results.InsertOneResult object at 0x7fb567329448>

# insertメソッドもあるけど、使うと怒られる
>>> m_client.db_name.collection_name.insert({'hoge': 'ほげ2'})
__main__:1: DeprecationWarning: insert is deprecated. Use insert_one or insert_many instead.
ObjectId('5dec7ab6f8f8434dbcab979a')

これで、 {'hoge': 'ほげ'} が2件insertされた。

選択(select *)

>>> m_client.db_name.collection_name.find()
<pymongo.cursor.Cursor object at 0x7fb56730ddd8> # また君か...

# listでくくって配列化すると中身が見える
>>> list(m_client.db_name.collection_name.find())
[
    {'_id': ObjectId('5dec7ab6f8f8434dbcab979a'), 'hoge': 'ほげ1'},
    {'_id': ObjectId('5dec7ae3f8f8434dbcab979b'), 'hoge': 'ほげ2'}
]

さっき入れた ほげ が2件入っている。

選択(1件だけselect)

>>> m_client.db_name.collection_name.find_one()
{'_id': ObjectId('5dec7ab6f8f8434dbcab979a'), 'hoge': 'ほげ1'}

条件を指定して選択(select * where ~)

>>> list(m_client.db_name.collection_name.find({'hoge': 'ほげ2'}))
[
    {'_id': ObjectId('5dec7ab6f8f8434dbcab979a'), 'hoge': 'ほげ2'}
]
# 'hoge'列の値が 'ほげ2' になっているデータが他にもあれば全て取得される


# find_oneなら1件だけ選択
>>> m_client.db_name.collection_name.find_one({'hoge': 'ほげ'})
{'_id': ObjectId('5dec7ae3f8f8434dbcab979b'), 'hoge': 'ほげ'}

削除(delete)

# 条件指定で最初に見つかったレコードが削除される
>>> m_client.db_name.collection_name.delete_one({'hoge': 'ほげ2'})
<pymongo.results.DeleteResult object at 0x7fb567344208>

2. もうちょっと頑張ってみる

DBを列挙?

# これだけだと中身が見えない....
>>> m_client.list_databases()
<pymongo.command_cursor.CommandCursor object at 0x7fb56730dcf8>

# ので、配列化する
>>> list(m_client.list_databases())
[
    {'name': 'admin', 'sizeOnDisk': 102400.0, 'empty': False}, 
    {'name': 'candles', 'sizeOnDisk': 1875968.0, 'empty': False},
    {'name': 'config', 'sizeOnDisk': 98304.0, 'empty': False},
    {'name': 'local', 'sizeOnDisk': 73728.0, 'empty': False}
]

何に使うのかは不明....(´・ω・`)

'_id'を指定して挿入

mongodbは、レコードのinsert時に、各レコードに対して自動的にユニークな '_id' を割り振ってくれる。

が、'_id' を自分で指定したいときはこうする。

>>> m_client.db_name.collection_name.insert({
    '_id': 'idだよ',
    'hoge': 'ほげ3'
})

# すると、こうなる
>>> list(m_client.your_db_name.your_collection_name.find())
[
    {'_id': ObjectId('5dec7ab6f8f8434dbcab979a'), 'hoge': 'ほげ1'},
    {'_id': ObjectId('5dec7ae3f8f8434dbcab979b'), 'hoge': 'ほげ'},
    {'_id': 'idだよ', 'hoge': 'ほげ3'}
]

# 日時とか入れるといいのかな?と思ってます
>>> import datetime
>>> m_client.db_name.collection_name.insert_one({'_id': datetime.datetime.now(), 'hoge': 'ほげ4'})
>>> list(m_client.db_name.collection_name.find())
[
    {'_id': ObjectId('5dec7ab6f8f8434dbcab979a'), 'hoge': 'ほげ1'},
    {'_id': ObjectId('5dec7ae3f8f8434dbcab979b'), 'hoge': 'ほげ'},
    {'_id': 'idだよ', 'hoge': 'ほげ3'},
    {'_id': datetime.datetime(2019, 12, 8, 14, 58, 31, 579000), 'hoge': 'ほげ4'}
]

複数件のレコードをinsert(bulk_insert)

insert_manyというメソッドに、データを配列形式で渡せばよい

# 実行したらこんな感じ(今回は実行してない)
m_client.db_name.collection_name.insert_many([
    {'hoge': '六角形'},
    {'hoge': 'ヘキサゴン'}
])

Collection / DB の削除

# Collection削除
>>> m_client.db_name.collection_name.drop()
>>> list(m_client.db_name.collection_name.find())
[]

# ↑ 1件もヒットしなくなった

# Collectionが一つもなくなったら、自動的にDBもなくなった!
>>> list(m_client.list_database_names())
['admin', 'candles', 'config', 'local']

選択結果をソート(order_by)

# とりあえず、いっぱいレコードをinsert(5~6回実行)
>>> m_client.db_name.collection_name.insert_one({'_id': datetime.datetime.now(), 'hoge': 'ほげ'})

# 降順ソート(新しい日時が一番上になる)
>>> list(m_client.db_name.collection_name.find().sort('_id', DESCENDING))
[
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 56, 625000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 984000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 528000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 121000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 54, 696000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 9, 36, 233000), 'hoge': 'ほげ'}
]

# 昇順ソート(古い日時が一番上になる)
>>> list(m_client.db_name.collection_name.find().sort('_id', ASCENDING))
[
    {'_id': datetime.datetime(2019, 12, 8, 15, 9, 36, 233000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 54, 696000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 121000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 528000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 984000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 56, 625000), 'hoge': 'ほげ'}
]

# 実はこれでも行ける(こっちの方が好き)
>>> list(m_client.db_name.collection_name.find(sort=[('_id', DESCENDING)]))

特定列に対し範囲指定をして選択

範囲指定に使う識別子は大体これ

以上 以下 未満
識別子 $gte $gt $lte $lt

※詳しくは ここ(MongoDB公式: comparison-expression-operators) みてくださいな...

# 特定の値以上(その日時以降)
>>> list(m_client.db_name.collection_name.find(filter={
    '_id':{'$gte': datetime.datetime(2019, 12, 8, 15, 10, 55, 528000)}
}))
[
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 528000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 984000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 56, 625000), 'hoge': 'ほげ'}
]

# 特定の値を超えた値(その日時を除いて、その日時以降)
>>> list(m_client.db_name.collection_name.find(filter={
    '_id':{'$gt': datetime.datetime(2019, 12, 8, 15, 10, 55, 528000)}
}))
[
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 984000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 56, 625000), 'hoge': 'ほげ4'}
]

# 特定の値から特定の値まで
>>> list(m_client.db_name.collection_name.find(filter={
    '_id':{
        '$gte': datetime.datetime(2019, 12, 8, 15, 10, 55, 121000),
        '$lte':  datetime.datetime(2019, 12, 8, 15, 10, 55, 984000)
    }
}))
[
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 121000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 528000), 'hoge': 'ほげ'},
    {'_id': datetime.datetime(2019, 12, 8, 15, 10, 55, 984000), 'hoge': 'ほげ'}
]

$gt$lt は逆の意味になります

その他、条件指定を組み合わせて選択

ここが詳しいですね!
pymongoを使った様々な検索条件(AND/OR/部分一致/範囲検索)

(よって省略...)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1