概要
タイトルの通りです。
最近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/部分一致/範囲検索)
(よって省略...)