本記事は、MongoDB AtlasとPythonの接続方法をさらっと紹介し、PyMongoのよく使いそうな関数をまとめるといった備忘録になります。メモ書きを記事にしたといった方が簡潔な説明ですねw。
なお、MongoDB Atlasにログインするなどの工程はこちらでは紹介しません。
今回の記事を作成するにあたって、以下の文献を参考にしました。
1- MongoDB Atlasの操作
説明はこちらからになります。(コマンドラインツールの方法はいつか機会があれば。)
1-1 MongoDB Atlasとは
MongoDB Atlas provides an easy way to host and manage your data in the cloud. This tutorial guides you through creating an Atlas cluster, connecting to it, and loading sample data.
→クラウド上でデータをホストおよび管理する方法を提供するサービス。
You can get started with Atlas through the
Atlas CLI or the Atlas User Interface.
→AtlasのUIでの方法を紹介。
1-2 MongoDB Atlasの接続情報取得まで
- Atlasアカウントを作成し、登録。Atlasにログインする。
- フリーのクラスターを作成し、デプロイする。
- IPホワイトリストに自分のIPアドレスを追加する。
- 手順2.で作成したクラスターにアクセスするためのユーザーを作成する。
- 接続情報を取得する。
上記箇条書きの内容について、以下補足。
手順2.について(初めてクラスターを作成する場合):
- 「Data Services」タブの「DEPLOYMENT>Database」にアクセスし、画面右上の「+ Create」ボタンを押下。
- Cloud Providerを選択。(AWS、Google Cloud、Azureが選択可能)
- Regionを選択。(ASIA>TokyoまたはASIA>Osakaなど)
- Clusterの詳細設定を選択。(必要があれば。)
- 上記内容で設定し、問題なければ「Create Cluster」を押下。(無料枠であることを念のため確認。)
- クラスターの作成が始まる。
※無料枠のクラスターが作成済みの状態で手順2.を実施すると有料であることを目の当たりにするので注意。
手順3.について:
- 「Data Services」タブの「SECURITY>Network Access」にアクセスし、「IP Access List」の「+ ADD IP ADDRESS」ボタンを押下。
- モーダル画面が表示されるので、クラスターにアクセスして問題ないIPアドレスを入力し、「Confirm」ボタンを押下。
- IPリストの変更・削除は同タブ内で実施可能。
手順4.について:
- 「Data Services」タブの「SECURITY>Database Access」にアクセスし、「Database Users」タブの「+ ADD NEW DATABASE USER」ボタンを押下。
- 「Authentication Method」を選択。(Password、Certificate、AWS IAMが選択可能。本記事ではCertificateを選択した場合を想定し、紹介する。)
- 「Common Name」にユーザー名を入力する。
- 「Download certificate when user is added」を「クリック」する。(作成時にクリックしなくても後ほどファイルの取得は可能)
- 「Select the certificate expiration for this user」で「certificate」の有効期限を選択。(3カ月,6か月,12カ月,24カ月を選択可能。今回は3カ月を選択。)
- 「Database User Privileges>Built-in Role」からユーザーの権限を選択。(Atlas admin、Read and write to any database、Only read any databaseが選択可能。データベースの読み書きの権限が必要なのでRead and write to any databaseを選択。)
- 「Add User」ボタンを押下。
- 同タブ内で作成したユーザーが表示されていることを確認。(具体的にはAuthentication MethodとMongoDB Rolesを確認。)また、CertificateKeyが記されたファイルがダウンロードされていることも確認。(接続に必要な情報です。もし、ファイルのダウンロードができなかった場合は、同一ユーザーのファイル有効期限を再度設定し、ファイルをダウンロードしてください。)
手順5.について:
- 「Data Services」タブの「DEPLOYMENT>Database」にアクセスし、作成済みのクラスターを確認。
- 「Connect」ボタンを押下し、「Connect to your application>Drivers」を押下。(ほかにCompassやShellなどのアクセス方法がありますが、興味があればそちらを試してみるとよいかと思います。)
- DriverおよびVersionを選択する。(driverはPythonのほかにJava、Node.js、PHPなどが選択可能。versionは最新のものが推奨されている。)
- Install your driverでは最新のdriverを選択した場合
python -m pip install pymongo
と表示されていると思うので、これをコマンドラインで実行し、インストール。 - 接続情報にユーザーの検証情報を使用する。手順4.で検証方法を「Certificate」にした場合は、「X.509」にチェックをつける。(「Password」にした場合は、接続情報にユーザー名とパスワードを入力する必要があるので注意。)
- 接続情報が記された「mongodb+srv://....」で始まる文字列をメモする。
Atlas上で行う操作は以上になります。
なお、「Data Services」タブのDEPLOYMENT>Database>Browse Collections」でDatabaseおよびCollections、Documentの作成が可能なのでUI上で操作したい場合はこちらから試してみるのもありです。
ちなみに、MongoDBとRDB(MySQL、PostgreSQLなど)の概念の対応表は次の通り:
MongoDB | RDB |
---|---|
Database | Database |
Collection | Table |
Document | Record |
2 MongoDB AtlasとPythonの接続
MongoDB Atlasでの作業後、Pythonにはすでにpymongo
がインストールされていることを前提に説明します。インストールされていない場合は、以下コマンドを実行してください:
python -m pip install pymongo
2-1 接続テスト
工程1でダウンロードした.pem
ファイルおよびmongodb+srv://...
で始まる接続文字列が必要になります。この情報が無ければ以降の手順は実施できないので注意してください。
次のようなファイル構造を想定して紹介します:
C:\TEST
├─ test.py (テスト用のpythonファイル)
└─ XXX.pem (ユーザー情報)
test.py
を以下のように作成します:
from pymongo import MongoClient
# 以下uriに格納する値が接続文字列。
uri = "mongodb+srv://<CONNECTION_STRINGS>"
client = MongoClient(uri,tls=True,tlsCertificateKeyFile='<PATH_TO_CERTIFICATE>')
db = client['testDB']
collection = db['testCol']
doc_count = collection.count_documents({})
print(doc_count)
上記コードを実行し、0
という値が表示されれば接続成功です。エラーが生じた場合は
-
pymongo
がインストール済みか -
uri
の値が正しいか -
<PATH_TO_CERTIFICATE>
の指定は正しいか
といったことを確認してください。
ちなみに上記コードを簡単に説明すると、pymongo.MongoClient
でMongoDBへの接続を実施。client
はMongoDBのクラスターに対してDatabaseの作成や削除といったアクションを行うことが可能です。
例えば、client['testDB']
はクラスター内にtestDB
という名前でDatabaseを作成し、db['testCol']
はtestDB
内にtestCol
という名前のCollectionを作成します。collection.count_documents({})
はCollection内のDocumentの数を計上しており、現在、Documentはなにも追加していないはずなので、doc_count=0
となります。
2-2 CertificateKeyFileについて
接続テストでは接続文字列をコードにべた書きしていますが、実際の運用を考えればあまり好ましい書き方ではありません。そこでPython-dotenvを紹介します。
このPythonモジュールは.env
ファイルの値をkeyとvalueのペアで読み込むことができ、さらに環境変数として設定することも可能です。
このモジュールをインストールするには次のコードを実行します:
pip install python-dotenv
まずは.env
ファイルを配置します:
C:\TEST
├─ test.py (テスト用のpythonファイル)
├─ XXX.pem (ユーザー情報)
└─ .env (環境ファイル)
そして、ファイルの中身を次のように設定します:
PASSWORD='AIUEO'
USERNAME='TEST_USER'
MONGODB_URI='mongodb+srv://<CONNECTION_STRINGS>'
MONGODB_URI_PASSWORD='mongodb+srv://${USERNAME}:${PASSWORD}@<CONNECTION_STRINGS>'
ここで、${KEY}
はプレースホルダーとしての役割を持ち、例えば、KEY=VALUE
と定義すればこのファイルから値を読み込んだとき該当のプレースホルダーはVALUE
に置換されます。
次に、上記.env
ファイルを読み込むためには
import os
from dotenv import dotenv_values
dirname = os.path.dirname(__file__)
path = os.path.join(dirname, '.env')
config = dotenv_values(dotenv_path=path)
print(config)
とし、このコードを実行すると、
OrderedDict([('PASSWORD', 'AIUEO'), ('USERNAME', 'TEST_USER'), ('MONGODB_URI', 'mongodb+srv://<CONNECTION_STRINGS>'), ('MONGODB_URI_PASSWORD', 'mongodb+srv://TEST_USER:AIUEO@<CONNECTION_STRINGS>')])
が返されます。あとは、config.get(KEY, 'DEFAULT_VALUE')
等で値にアクセスできます。例えば、
config['USERNAME'] # TEST_USER
config.get('PASSWORD', '') # AIUEO
config.get('MONGODE_URI', '') # mongodb+srv://<CONNECTION_STRINGS>
といった感じです。
2-3 よく使いそうなAPI
※雑多にメモしているので徐々に整理していく予定。(並べる順番など)
▼Making a Connection with MongoClient
MongoDBに接続する:
from pymongo import MongoClient
client = MongoClient("")
▼Getting a Database
PyMongoでDBを作成するには属性にアクセスする形式で:
db = client.test_database
または
db = client["test_database"]
DBの一覧を取得するには次のようにする:
client.list_database_names()
▼Getting a Collection
CollectionはMongoDB内に格納されたDocumentの集団で、RDBにおけるテーブルと等価な概念とみなされる。
Databaseにアクセスする方法と同じ形式で:
collection = db.test_collection
または
collection = db["test_collection"]
DB内のCollectionの一覧を取得するには次のようにする:
db.list_collection_names()
▼Documents
MongoDBにおけるDataはJSON形式のドキュメントを使用して表現される。
例えば次のようなもの:
import datetime
post = {
"author": "Mike",
"text": "My first blog post!",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.now(tz=datetime.timezone.utc),
}
▼Inserting a Document
Collectionの中にDocumentを挿入するため位はinsert_one()
メソッドを使用する:
posts = db.posts
post_id = posts.insert_one(post).inserted_id
※Documentが挿入された際に自動的に_id
という特殊なキーが挿入される。
_id
の値はCollection間でユニークでなければならない。
Database内のCollectionのリストを確認するためには次のようにする:
db.list_collection_names()
▼Getting a Single Document With find_one()
find_one()
メソッドは最も基本的なクエリの形式でクエリにマッチしたDocumentを1つ返す。マッチしない場合はNone
を返す。
例えば次の通り:
posts.find_one()
posts.find_one({"author": "Mike"})
posts.find_one({"author": "Eliot"}) <- return None
▼Querying By ObjectId
_id
の値はObjectIdであり、str型の値ではない。
例えば、
posts.find_one({"_id": post_id})
は値を返すが、
post_id_as_str = str(post_id)
posts.find_one({"_id": post_id_as_str})
はNone
を返す。そのため、必要に応じてstring型からObjectIdへの変換が必要な場合がある。
そのような場合は次のようにする:
from bson.objectId import ObjectId
def get(post_id):
document = client.db.collection.find_one({"_id": ObjectId(post_id)})
▼Bulk Inserts
リスト型にしたDocumentを挿入するためにはinsert_many()
メソッドを使用する:
new_posts = [
{
"author": "Mike",
"text": "Another post!",
"tags": ["bulk", "insert"],
"date": datetime.datetime(2009, 11, 12, 11, 14),
},
{
"author": "Eliot",
"title": "MongoDB is fun",
"text": "and pretty easy too!",
"date": datetime.datetime(2009, 11, 10, 10, 45),
},
]
result = posts.insert_many(new_posts)
inserted_ids = result.inserted_ids
※new_posts内のスキーマの構造は異なることに注意。
▼Querying for More Than One Document
1つ以上のDocumentを取得するためにはfind()
メソッドを使用する。
find()
はCursor
インスタンスを返す:
for post in posts.find():
print(post)
for post in posts.find({"author": "Mike"}):
print(post)
▼Replace a Single Document Matching The Filter
Document:{'x': 1, '_id': ObjectId('54f4c5befba5220aa4d6dee7')}
の'x':1
を'y':1
に置換したい場合、次のようにする:
result = db.test_collection.replace_one({'x':1}, {'y':1})
第1引数がfilterにあたり、このfilterとマッチしたDocumentを第2引数で置換する。
さらに、upsert
オプションをTrueにした場合、filterにマッチするDocumentがない場合新しくDocumentを追加する。
result = db.test_collection.replace_one({'z':1, {'z':1}}, True)
▼Update a Single Document Matching The Filter
Document: {'x': 1, '_id': 0}
の'x'の値を更新したい場合、次のようにする:
result = db.test_collection.update_one({'x':1}, {'$inc': {'x':3}})
第1引数のfilterとマッチしたDocumentの値を第2引数の値に更新する。
上記例では、'x'の値に3追加した、{'x': 4, '_id': 0}にDocumentが更新される。
さらに、upsert
オプションをTrueにした場合、filterにマッチするDocumentがない場合新しくDocumentを追加する。
result = db.test_collection.update_one({'x':-10}, {'$inc': {'x':3}}, True)
上記例では、第1引数にマッチするDocumentがないので、新しくDocumentが追加される:
{'_id': ObjectId('626a678eeaa80587d4bb3fb7'), 'x': -7}
▼Update One or More Documents That Match The Filter
update_many()
メソッドを使用することで、filterと合致したDocumentを第2引数で更新する。
例えば、Documentに{'x': 1, '_id': 0},{'x': 1, '_id': 1},{'x': 1, '_id': 2}があるとき、
result = db.test_collection.update_many({'x':1}, {'$inc': {'x':3}})
とすると、Documentは{'x': 4, '_id': 0},{'x': 4, '_id': 1},{'x': 4, '_id': 2}に更新される。
▼Delete a Single Document Mathching The Filter
filterにマッチしたDocumentを1つ削除するには例えば、
Documentが{'x': 1, '_id': 0},{'x': 1, '_id': 1},{'x': 1, '_id': 2}のとき次のようにする:
result = db.test_collection.delete_one({'x': 1})
doc_count = db.test_collection.count_documents({'x': 1}) # 2
▼Delete One or More Documents That Match The Filter
filterにマッチしたDocumentを1つまたは複数削除するには例えば、
Documentが{'x': 1, '_id': 0},{'x': 1, '_id': 1},{'x': 1, '_id': 2}のとき次のようにする:
result = db.test_collection.delete_many({'x': 1})
doc_count = db.test_collection.count_documents({'x': 1}) # 0
▼Get a List of Distinct Values For Key Among All Documents in This Collection
result = db.test_collection.distinct('x', {'x': 1})
▼Counting
クエリにマッチしたDocumentの数を知りたいとき、count_documents()
メソッドを使用する。
以下は、Collection内のDocumentの数を取得する:
posts.count_documents({})
posts.count_documents({"author": "Mike"})
▼Alias for drop_collection()
db.test_collection.drop()
OR
db.drop_collection("test_collection")
▼Rename this Collection
db.test_collection.rename("sample_collection")
▼Get The Options Set on This Collection
db.test_collection.options()
▼Range Queries
MongoDBではadvanced queriesをサポートしている。(https://www.mongodb.com/docs/manual/reference/operator/)
例えば、ある日時よりも古いポストを取得し、"author"によって取得結果をソートするには
d = datetime.datetime(2009,11,12,12)
for post in posts.find({"date": {"$lt": d}}).sort("author):
print(post)
ここで$lt
はLess Thanを意味するクエリ。
▼Indexing
インデックスを追加することはクエリの速度の向上やクエリやDocument格納機能を付与し得る。
例えば、unique indexを作成する:
result = db.profiles.create_index(["user_id", pymongo.ASCENDING], unique=True)
sorted(list(db.profiles.index_information())) # ['id', 'user_id_1']
インデックスは自動生成される_id
と上記で作成したようなuser_id
の2種類がある。
インデックスにより既にCollection内に存在しているuser_id
を挿入することを防ぐことができる。
例えば、[{"user_id": 110, "name": "Luke"},{"user_id": 111, "name": "Ziltoid"},]がすでにCollection内に挿入済みであるとする
このとき、
{"user_id": 112, "name": "Drew"}は挿入可能であるが、{"user_id": 111, "name": "Tommy"}
は既にuser_idが存在しているので挿入ができない。
3- まとめ
MongoDB Atlasのクラスターで接続情報を取得し、PyMongo
をインストールし、このモジュールを用いてPythonとクラスターを接続テストを行いました。
次はFAST API
を使って、APIを作成していく予定です。