0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【MongoDB】MongoDB AtlasとPyMongoの初歩的な備忘録

Last updated at Posted at 2023-09-02

本記事は、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の接続情報取得まで

  1. Atlasアカウントを作成し、登録。Atlasにログインする。
  2. フリーのクラスターを作成し、デプロイする。
  3. IPホワイトリストに自分のIPアドレスを追加する。
  4. 手順2.で作成したクラスターにアクセスするためのユーザーを作成する。
  5. 接続情報を取得する。

上記箇条書きの内容について、以下補足。

手順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がインストールされていることを前提に説明します。インストールされていない場合は、以下コマンドを実行してください:

pymongoのインストール
python -m pip install pymongo

2-1 接続テスト

工程1でダウンロードした.pemファイルおよびmongodb+srv://...で始まる接続文字列が必要になります。この情報が無ければ以降の手順は実施できないので注意してください。

次のようなファイル構造を想定して紹介します:

ファイル構造
C:\TEST
├─ test.py (テスト用のpythonファイル)
└─ XXX.pem (ユーザー情報)

test.pyを以下のように作成します:

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 (環境ファイル)

そして、ファイルの中身を次のように設定します:

.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ファイルを読み込むためには

.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を作成していく予定です。

0
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?