Pythonでpymongoを利用してドキュメント指向データベースであるMongoDBを操作する方法について解説します。
ドキュメント指向データベース:MongoDB
MongoDB
MongoDBは、JSON形式のデータを蓄積できるデータベースで、ドキュメント指向データベースという分類になります。
データベースというとまず最初に思い浮かべるのはリレーショナルデータベース(RDB)かと思います。近年では、キー・バリュー型、列指向型、グラフデータベース等の様々なタイプのデータベースが登場しています。ドキュメント指向データベースもそういったデータベースの一種です。
ドキュメント指向データベースは、データを表形式で保存するリレーショナルデータベースとは違い、詳細なテーブル定義が必要ないことが大きな特徴です。リレーショナルデータベースは列が何で、列数がいくつでというような情報を明確に決めないとデータを蓄積できません。一方、ドキュメント指向データベースでは、JSONで表現できる形であれば任意の属性を追加でき、複雑な階層を持つデータも管理することができます。
また、大量データ処理のためにスケールアウトしやすい仕組みを持つ等の理由から近年注目されているデータベースです。
本記事では、クラウドで利用できるMongoDB Atlasを使用してMongoDBを利用する方法を紹介するとともに、Pythonのpymongoモジュールを利用してPythonプログラムからMongoDBを利用する方法について紹介します。
MongoDBの公式ページはこちらを参照してください。
MongoDBの利用方法
MongoDBはクラウドでのサービスであるMongoDB Atlasを提供しています。MongoDBをまず試してみたいという際には無料で簡単に使用することができます。
ここではMongoDB Atlasの利用方法を紹介します。まず、MongoDBの公式ページへアクセスし「無料で始める」をクリックします。※公式ページの画面構成は変わっている可能性があります。
MongoDB Atlasにサインアップ
Googleでサインアップするかメールアドレス等を入力して登録します。
サインアップするとプライバシーポリシー等の確認が出ますので確認し、acceptにチェックを入れて「Submit」をクリックします。
使用用途などを聞かれるためチェックを入れます。今回の場合は学習目的ということで「Learn MongoDB」、アプリケーションはBI(Business Intelligens)としておきました。また、preferred languageは「Python」としました。
MongoDBのデプロイ
次にデータベースをデプロイします。今回は学習目的のため無料のFreeで「Create」をクリックします。
その後、Share Clusterの設定になります。基本的には、デフォルト設定で進めていいと思いますが、それぞれ必要に応じて変更してください。今回はFreeのSharedを選びます。
クラウドプロバイダーをaws、Google Cloud、Azureから選ぶことができます。今回は「aws」「Tokyo」として進めます。
Cluster Tierという部分はFreeの選択のままとします。Freeはデータ数やネットワークパフォーマンス等制限がかかりますが、お試しで使用する分には十分かと思います。
その他の設定は特にいじらずに進めます。Cluster Nameはお好みがあれば変えても良いかと思います。
画面下の「Create Cluster」で作成します。
以上で、MongoDBの環境が準備が概ね完了します。
ユーザー設定およびネットワーク設定
次に、Sequrity Quickstart画面が表示されるかと思います。まずは、データベースへアクセスするためのユーザー名(username)とパスワード(Password)を設定します。入力したら「Create User」で作成します。
次にIPアドレス制限を追加します。これによりアクセスできるIPアドレスを制限できます。
現在使用しているIPを使いたい場合は「Add My Current IP Address」をクリックして設定してください(グローバルIPが固定でない場合は、変わった際にアクセスできなくなりますので注意してください)。「0.0.0.0/0」ですべてのIPを許可することも可能ですがセキュリティ上はおすすめできません。
このアクセス設定は、後でも変更できます。終わったら「Finish and Close」で設定を完了します。以下が表示されたらセキュリティ設定は完了です。「Go to Database」をクリックしましょう。
以下のような画面が表示されれば、MongoDB Atlasを使用する準備は完了します。
外部アプリケーションからの接続方法の確認
以降でPythonプログラムからMongoDBへアクセスする方法を紹介しますが、接続方法を確認しておきましょう。データベースの「Connect」をクリックします。
以下のような画面が出るので「Connect your application」をクリックします。
DRIVERとVERSIONを聞かれるので設定します。今回はPythonの接続を確認したいのでDRIVER「Python」、VERSION「3.6 or later」ということで選択しました。他の言語についても選ぶことができるので各言語ごとの接続方法を確認する際には選択してください。
「include full driver code example」をクリックするとPythonのコードとして埋め込むべきコードを表示してくれます。塗りつぶし部分は、上記で作成したユーザー名が入ります。また、<password>部分は上記で設定したパスワード、myFirstDatabase部分にはデフォルトの接続データベース名で置き換える形になります。
なお、この画面は接続方法を確認するためのもので何かを設定するような画面ではありません。確認が済んだら「Close」で画面を閉じましょう。
以降で、この接続文字列を使いつつPythonプログラムからMongoDBを利用するサンプルプログラムを紹介します。
pymongoを用いたMongoDBへのアクセス方法
MongoDBをPythonでアクセスして使用する場合には、pymongoモジュールを使用します。以降ではpymongoを使用したMongoDBのアクセス方法について紹介します。
pymongoの公式ドキュメントページはこちらを参考にしてください。
pymongoのインストール
Pythonでpymongoを使用するには、以下のようにpip installでインストールを実行します。
pip install pymongo
この段階で以下で紹介するソースコードを実行するとエラーが出るかと思います。dnspythonモジュールが不足しているというエラーです。
pymongo.errors.ConfigurationError: The "dnspython" module must be installed to use mongodb+srv:// URIs.
以下のpip installもあわせて実行してください。
pip install pymongo[srv]
以上で、pymongoを使用するための準備が整います。
pymongoによりMongoDBを操作するサンプルプログラム
MongoDBにPythonからアクセスするためのサンプルプログラムを以下に示します。
接続情報はmongodb.iniというファイルに記載して、configparserで読み込む形式にしています。以下の記載はusernameやpasswordのxxxxxx部分はそれぞれ登録した内容に変更してください。dbnameは今回はtest_dbというものを作成して使うことを想定して記載しています。その他の接続文字列も上記で確認した接続文字列にあわせて修正するようにしてください。
【設定ファイル】mongodb.ini
[MONGODB] uri_format = mongodb+srv://{username}:{password}@cluster0.bs2hs.mongodb.net/{dbname}?retryWrites=true&w=majority username = xxxxxxx password = xxxxxxxxxxxxxxxxxxx dbname = test_db
【サンプルプログラム】mongodb_sample.py
import configparser import datetime from pymongo import MongoClient def main(): # === mongodbの設定取得 config = configparser.ConfigParser() config.read('mongodb.ini') uri_format = config['MONGODB']['uri_format'] username = config['MONGODB']['username'] password = config['MONGODB']['password'] dbname = config['MONGODB']['dbname'] # 接続用情報を作成 uri = uri_format.format(username=username, password=password, dbname=dbname) # print(uri) # MongoDB Atlasに接続 client = MongoClient(uri) # テストのDBを作成 db = client['test_db'] # 登録する情報を作成 info1 = { 'name': 'taro', 'birthday': datetime.datetime.strptime('2020/01/01', '%Y/%m/%d'), 'city': 'nagoya', 'create_date': datetime.datetime.now() } info2 = { 'name': 'haruka', 'birthday': datetime.datetime.strptime('2020/12/01', '%Y/%m/%d'), 'city': 'tokyo', 'create_date': datetime.datetime.now() } # dbを登録する db_infos = db.infos db_infos.insert_one(info1) db_infos.insert_one(info2) print('=========== 検索') # 全てを検索する print('=== 全て検索') for info in db_infos.find(): print(info) # 属性で検索する print('=== 属性で検索') print(db_infos.find_one({'name': 'taro'})) # 時間で検索する print('=== 登録時間で検索') now_time = datetime.datetime.now() for info in db_infos.find({'create_date': {'$lt': now_time}}): print(info) print('========== 更新') db_infos.find_one_and_update( {'name': 'taro'}, {'$set': {'name': 'new_taro'}} ) print(db_infos.find_one({'name': 'new_taro'})) print('========== 削除') db_infos.delete_one({'name': 'new_taro'}) for info in db_infos.find(): print(info) if __name__ == '__main__': main()
【実行結果】 =========== 検索 === 全て検索 {'_id': ObjectId('626dc9094a12a276f38d078b'), 'name': 'taro', 'birthday': datetime.datetime(2020, 1, 1, 0, 0), 'city': 'nagoya', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)} {'_id': ObjectId('626dc90a4a12a276f38d078c'), 'name': 'haruka', 'birthday': datetime.datetime(2020, 12, 1, 0, 0), 'city': 'tokyo', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)} === 属性で検索 {'_id': ObjectId('626dc9094a12a276f38d078b'), 'name': 'taro', 'birthday': datetime.datetime(2020, 1, 1, 0, 0), 'city': 'nagoya', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)} === 登録時間で検索 {'_id': ObjectId('626dc9094a12a276f38d078b'), 'name': 'taro', 'birthday': datetime.datetime(2020, 1, 1, 0, 0), 'city': 'nagoya', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)} {'_id': ObjectId('626dc90a4a12a276f38d078c'), 'name': 'haruka', 'birthday': datetime.datetime(2020, 12, 1, 0, 0), 'city': 'tokyo', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)} ========== 更新 {'_id': ObjectId('626dc9094a12a276f38d078b'), 'name': 'new_taro', 'birthday': datetime.datetime(2020, 1, 1, 0, 0), 'city': 'nagoya', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)} ========== 削除 {'_id': ObjectId('626dc90a4a12a276f38d078c'), 'name': 'haruka', 'birthday': datetime.datetime(2020, 12, 1, 0, 0), 'city': 'tokyo', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)}
プログラムの説明は以降の節で説明します。
実行時にMongoDB Atlasの状態を確認する方法
上記サンプルプログラムは登録、更新、削除を一気に実行してしまうため、実行する際にはステップ実行をしつつ、MongoDBの状態を確認しながら見ていただくのがおすすめです。
MongoDBのデータ確認方法
MongoDB Atlasの管理画面から「Browse Collections」を開くとデータを参照できる画面が開きます。
以下は、サンプルプログラムを一度実行した後の実行結果です。test_dbというデータベースの下にinfosというコレクションが作成されています。また、QUERY RESULTSには登録したデータが表示されています。
サンプルプログラムをステップ実行しつつ画面を更新してもらえば状態の変化を確認しながら動作を確認できるかと思います。
MongoDBのデータの削除方法
管理画面からはデータベース、コレクション、レコードを直接削除することができます。それぞれにマウスカーソルを合わせるとゴミ箱ボタンが出るのでクリックします。
データベースとコレクションは削除の確認のため、それぞれデータベース名やコレクション名を入力しないと削除ができません。レコードは確認ボタンが出るだけでクリックすると削除ができます。
PythonからMongoDBへ接続する
ここからサンプルプログラムの詳細を説明していきます。MongoDBへアクセスするためには、pymongoモジュールからMongoClientをimportします。
from pymongo import MongoClient
以下の部分はmongodb.iniから接続情報を取得している部分です。MongoDBというよりconfigparserモジュールの使い方というところですので、configparserの使い方が分からない方は「configparserによるconfigファイル管理」も参考にしてください。
# === mongodbの設定取得 config = configparser.ConfigParser() config.read('mongodb.ini') uri_format = config['MONGODB']['uri_format'] username = config['MONGODB']['username'] password = config['MONGODB']['password'] dbname = config['MONGODB']['dbname']
MongoDBへの接続に関する部分は以下の部分です。uriは上記で確認した接続文字列を組み立てている部分です。printはコメントアウトしていますが、必要に応じてprintして接続文字列がどのようになるか確認してください。
# 接続用情報を作成 uri = uri_format.format(username=username, password=password, dbname=dbname) # print(uri) # MongoDB Atlasに接続 client = MongoClient(uri)
MongoClientに接続文字列を渡すことで以降の操作で使用するクライアントを作成することができます。
MongoDBのデータベース、コレクションの作成とデータ登録
次に今回のデータベースとコレクションを作成します。データベースやコレクションがない状態でも今回のプログラムを実行するとそれぞれ作成がされます。
なお、MongoDBではデータベースとコレクションという考え方があります。コレクションはRDBに慣れている人からするとテーブルと思ってもらうのが近いかと思います。
# テストのDBを作成 db = client['test_db'] # 登録する情報を作成 info1 = { 'name': 'taro', 'birthday': datetime.datetime.strptime('2020/01/01', '%Y/%m/%d'), 'city': 'nagoya', 'create_date': datetime.datetime.now() } info2 = { 'name': 'haruka', 'birthday': datetime.datetime.strptime('2020/12/01', '%Y/%m/%d'), 'city': 'tokyo', 'create_date': datetime.datetime.now() } # DBに登録する db_infos = db.infos db_infos.insert_one(info1) db_infos.insert_one(info2)
db = client[‘test_db’]の部分で、test_dbというデータベースが作られます。登録する情報は、辞書形式で用意します。今回はないですがリストなどを設定したり、階層構造を持たせることも可能です。今回はinfo1、info2というものを用意しました。
db_infos = db.infosの部分で、infosというコレクションが作成されます。その後、コレクションのinsert_oneに作成したデータを渡すことでデータを作成することができます。ここまで実行した状態でのMongoDBの状態は以下のようになるかと思います。
MongoDBの検索
MongoDBに登録したデータは、各属性や時間などを使って検索をすることができます。以下が検索している部分になります。
print('=========== 検索') # 全てを検索する print('=== 全て検索') for info in db_infos.find(): print(info) # 属性で検索する print('=== 属性で検索') print(db_infos.find_one({'name': 'taro'})) # 時間で検索する print('=== 登録時間で検索') now_time = datetime.datetime.now() for info in db_infos.find({'create_date': {'$lt': now_time}}): print(info)
上記のようにコレクションに対してfindとすることで一覧を検索できます。取得した値はforで順に取り出すことができます。
属性で検索したい場合は、find_oneに条件となる属性値を設定します。また、時間で検索したい場合には{‘create_date’: {‘$lt’: now_time}}の部分のように比較方法と時間を指定できます。$ltは「より小さい」を表します。$gtで「より大きい」等も指定できます。
MongoDBのデータ更新
MongoDBに登録したデータを更新する場合は、find_one_and_updateが使用できます。
print('========== 更新') db_infos.find_one_and_update( {'name': 'taro'}, {'$set': {'name': 'new_taro'}} ) print(db_infos.find_one({'name': 'new_taro'}))
find_one_and_updateでは、検索条件と設定する値を引数に指定します。設定する値は’$set’として指定します。
更新を実行した後のMongoDBの状態を見ると以下のようになっているかと思います。これまでのIDと同じデータのnameが”new_taro”に更新されていることが分かるかと思います。
MongoDBのデータ削除
データを削除する場合には、delete_oneを使用することができます。
print('========== 削除') db_infos.delete_one({'name': 'new_taro'}) for info in db_infos.find(): print(info)
delete_oneの引数には削除対象を検索するための情報を設定します。
この処理まで実行するとnew_taroのデータは削除され、以下のようなデータのみとなることが分かるかと思います。
【補足】接続でSSLエラーが発生する場合
MongoDB Atlasへ接続する際にSSLエラーが発生する場合があります。私が遭遇した例ではLet’s Encrpytoの証明書の有効期限切れが原因でした。「pymongoでMongoDB Atlasに接続した際にSSLエラーが出た場合の対処法」を参考にしてください。
【補足】_idで検索をする場合
PythonでMongoDBを使う方法を見てきました。MongoDBの検索について1点補足です。MongoDBでデータを登録すると_idという一意なIDが付与されます。この_idの文字列で検索をしたい場合には、少しだけ注意が必要です。以下のサンプルを見てください。
import configparser import datetime from bson.objectid import ObjectId from pymongo import MongoClient def main(): # === mongodbの設定取得 config = configparser.ConfigParser() config.read('mongodb.ini') uri_format = config['MONGODB']['uri_format'] username = config['MONGODB']['username'] password = config['MONGODB']['password'] dbname = config['MONGODB']['dbname'] # 接続用情報を作成 uri = uri_format.format(username=username, password=password, dbname=dbname) # MongoDB Atlasに接続 client = MongoClient(uri) # テストのDBを取得 db = client['test_db'] db_infos = db.infos # オブジェクトIDで検索する # search_id = '626dc90a4a12a276f38d078c' search_id = ObjectId('626dc90a4a12a276f38d078c') print(db_infos.find_one({'_id': search_id})) if __name__ == '__main__': main()
【実行結果】 {'_id': ObjectId('626dc90a4a12a276f38d078c'), 'name': 'haruka', 'birthday': datetime.datetime(2020, 12, 1, 0, 0), 'city': 'tokyo', 'create_date': datetime.datetime(2022, 5, 1, 8, 40, 57, 978000)}
※search_idの文字列は、登録したデータの_idをご確認いただき、変更して動かしてください。
_idで検索する場合には、文字列を指定するだけでは「None」となってしまいます。上記の検索部分を以下のように書き換えてもらうとNoneになることが確認できます。
# オブジェクトIDで検索する search_id = '626dc90a4a12a276f38d078c' # search_id = ObjectId('626dc90a4a12a276f38d078c') print(db_infos.find_one({'_id': search_id}))
これは_idが「bson.objectid.ObjectId」という型になっているためです。そのため、検索で使用する場合は、文字列をObjectIdに渡すことで変換してから検索に使用する必要があります。
_idでの検索時の注意事項として覚えておいていただけるとよいかと思います。