当サイトは、アフィリエイト広告を利用しています
Dockerを使ってMongoDBコンテナを作成し、MongoDBを操作してみる
MongoDBの操作は
でがあるが、今回はmongoshで実行してみる
最終的にはPythonのpymongoなどを使って、プログラムから
操作することになるが、まずはMongoDBの仕組みや動きを見るために
mongoshで操作する
※プログラムで実装した後も確認等をするときに使えるので
mongo-expressでの操作方法に関しては下記記事で紹介している
MongoDBのデータ構造をつかむにはmongo-expressで見た方がわかりやすい
MongoDBはNoSQLは、従来のリレーショナルデータベース(RDB)とは
異なる方法でデータを処理・操作する
MongoDBはNoSQLのドキュメント指向型のDBで
JSONやXMLといったデータ形式で記述されたドキュメントの形でデータを管理する。
利点としてはJSONやXMLといったデータ形式でかつ階層構造を持たず、相互の関係が横並びに管理
されるため、複雑な構造を持つさまざまなデータを保存することができる
ドキュメント指向型のMongoDBではデータを
の単位で扱う
下記はMongoDBのデータをjson形式でexportしたもの。
// コレクション[//ドキュメント{// フィールド"_id": { "$oid": "6661ddc1ddb6cfaedf9ec54f" },"user_id": "1","name": "mori","age": 20},//ドキュメント{// フィールド"_id": { "$oid": "6661ddc1ddb6cfaedf9ec552" },"user_id": "4","name": "ayatuji","age": 99}]
データベース。
一番大きな単位。これはRDBと同じ
コレクションはRDBでいうことろのテーブルに相当する
ドキュメントの集まり
上記のMongoDBのデータサンプルの配列の部分
ドキュメントはRDBでいうことろのレコードに相当する
上記のMongoDBのデータサンプルの配列内のオブジェクトの部分
ドキュメントはRDBでいうことろのカラムに相当する
MongoDBのデータサンプルのオブジェクトの属性部分
mongosh(MongoDB Shell)は、MongoDB用のシェルのこと。
MongoDBデータベースに対してコマンドを実行し、データの操作やクエリの実行、
スクリプトの実行などを行うためのツール。
mongoshはMongoDB 4.4から正式に導入された
以前のバージョンのMongoDBでは、従来のmongoシェルが使われていたみたい
下記の環境で行う
WindowsでDocker for Windowsを使って行う
下記のような構成にする
.|-- docker-compose.yml`-- mongo|-- db`-- init`-- init-db.js
docker-composeを使って
を作ってデータを確認してみる
コンテナの設定をする
version: "3"services:# MongoDBコンテナmongodb:container_name: "mongodb"restart: alwaysimage: mongo:6.0.13ports:- "27017:27017"volumes:- ./mongo/init:/docker-entrypoint-initdb.d # 初期化スクリプト- mongoDataStore:/data/db # MongoDBのデータファイル# - ./mongo/db:/data/dbenvironment:MONGO_INITDB_ROOT_USERNAME: admin # MongoDBの管理者のユーザー名MONGO_INITDB_ROOT_PASSWORD: admin123 # MongoDBの管理者のパスワードMONGO_INITDB_DATABASE: mongotable # 初期DBの作成# 名前付きボリュームvolumes:mongoDataStore:name: mongoDataStore
MongoDBのデータの保存場所
名前付きボリュームではなく、バインドマウントにしたい場合はコメントアウト
している方を使う
docker-entrypoint-initdb.dにShell ScriptかJavaScriptで
書いたスクリプトがあれば、コンテナ起動時に実行してくれるのでホストから
バインドマウントしている
初期化時にユーザーを作りたい、またはDBに初期データを入れたい
時に使える
初期化スクリプト
// init-db.js// 管理者ユーザーでログインdb = db.getSiblingDB("admin");db.auth("admin", "admin123");// 新しいユーザーを作成db.createUser({user: "example_user",pwd: "password123",roles: [{ role: "readWrite", db: "mongotable" }],});// mongotable データベースに接続するdb = db.getSiblingDB("mongotable");// コレクションを作成するdb.createCollection("example_collection");// サンプルドキュメントを挿入するdb.example_collection.insertOne({key: "value",});print("Initialization completed.");
MongoDBはdbに少なくも1つのコレクションをがないとデータベースとして
認識しないので、MONGO_INITDB_DATABASEで設定している「mongotable」に
対して初期化スクリプトでコレクションを作成している。
※この初期化スクリプトがないとmongotableは作成されない
docker-compose.ymlで作成したMongoDBコンテナを
mongoshで操作してみる
mongodbコンテナにログインする
docker exec -it mongodb sh
MongoDBへのログインは下記で行う
# 構文mongosh mongodb://<username>:<password>@<host>:<port>/<authenticationDatabase># サンプルmongosh mongodb://admin:admin123@localhost:27017/admin
<username>
: MongoDBのユーザー名<password>
: ユーザーのパスワード<host>
: MongoDBサーバーのホスト名またはIPアドレス<port>
: MongoDBサーバーのポート番号<authenticationDatabase>
: 認証データベースの名前(省略可能)docker-compose.ymlのmongodbで設定した値を使う
# mongodbコンテナに入る$ docker exec -it mongodb sh# momgodbにログイン$ mongosh mongodb://admin:admin123@localhost:27017/adminCurrent Mongosh Log ID: 665f1afae7800cfcf66aa071Connecting to: mongodb://<credentials>@localhost:27017/admin?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.4Using MongoDB: 6.0.13Using Mongosh: 2.1.4For mongosh info see: https://docs.mongodb.com/mongodb-shell/------The server generated these startup warnings when booting2024-06-04T13:47:06.337+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem2024-06-04T13:47:07.606+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'2024-06-04T13:47:07.606+00:00: vm.max_map_count is too low------admin>
<authenticationDatabase>
でadminを指定しているので
adminデータベースに入っている
データベースに関連する操作をまとめる
dbの一覧を出力する
admin> show dbsadmin 100.00 KiBconfig 72.00 KiBlocal 72.00 KiBmongotable 40.00 KiB
初期化スクリプトで作ったmongotableが作成されている
操作したいdbへ切り替える
admin> use mongotableswitched to db mongotablemongotable>
dbを新規作成する
# db作成mongotable> use testdbswitched to db testdb# コレクション作成testdb> db.createCollection('COL');{ ok: 1 }# 一覧出力testdb> show dbsadmin 100.00 KiBconfig 108.00 KiBlocal 72.00 KiBmongotable 40.00 KiBtestdb 8.00 KiB
コレクションが無いとDB一覧には出ない
dbを削除する
# DBの削除testdb> db.dropDatabase(){ ok: 1, dropped: 'testdb' }# db一覧出力testdb> show dbsadmin 100.00 KiBconfig 108.00 KiBlocal 72.00 KiBmongotable 40.00 KiB
testdbが削除されている
コレクションに関する操作をまとめる
dbのコレクション一覧を出力する
mongotable> show collectionsexample_collection
初期化スクリプトで作ったコレクションが作成されている
db内にコレクションを作成する
# db移動admin> use mongotableswitched to db mongotable# コレクション作成mongotable> db.createCollection('USERS'){ ok: 1 }# コレクション一覧mongotable> show collectionsexample_collectionUSERS
USERSコレクションが作成される
コレクションを削除する
# コレクション削除mongotable> db.USERS.drop()true# コレクション一覧mongotable> show collectionsexample_collection
USERSコレクションが削除される
ドキュメントに基本的な操作をまとめる
先にデータベースとコレクションを作成しておく
# db作成admin> use SAMPLDBswitched to db SAMPLDB# コレクションを作成SAMPLDB> db.createCollection('users'){ ok: 1 }
条件で取得する場合はfindを使う
# 構文db.<db名>.find(# 抽出条件{ <field1>: <value1>, ...})# 実行文サンプルdb.users.find({age:50})# 件数制限をつける場合db.users.find({age:50}).limit(1)# 全件取得の場合db.users.find()
最初の1件だけ取得する場合はfindOneを使う
# 構文db.<db名>.findOne(# 抽出条件{ <field1>: <value1>, ...})# 実行文サンプルdb.users.findOne({age:50})
コレクションにあるドキュメント件数を取得する
# 構文db.<db名>.countDocuments()# 実行文サンプルdb.users.countDocuments()
ドキュメントを単数登録する場合は「insertOne」を使う
# 構文db.<db名>.insertOne( { <field1>: <value1>, ...})# 実行文db.users.insertOne({user_id: "1", name: "tujimura", age: 11})
実行してみる
// 実行前SAMPLDB> db.users.find()// 実行SAMPLDB> db.users.insertOne(... {user_id: "1", name: "tujimura", age: 11}... ){acknowledged: true,insertedId: ObjectId('66607a11ddb6cfaedf9ec50c')}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('66607a11ddb6cfaedf9ec50c'),user_id: '1',name: 'tujimura',age: 11}]
ドキュメントを複数登録する場合は「insertMany」を使う
# 構文db.<db名>.insertMany({ <field1>: <value1>, ...},{ <field1>: <value1>, ...})# 実行文サンプルdb.users.insertMany([{user_id: "2", name: "mori", age: 20},{user_id: "3", name: "shimada", age: 50}])
実行してみる
// 実行前SAMPLDB> db.users.find()// 実行SAMPLDB> db.users.insertMany([... {user_id: "2", name: "mori", age: 20},... {user_id: "3", name: "shimada", age: 50}... ]){acknowledged: true,insertedIds: {'0': ObjectId('66607ab3ddb6cfaedf9ec513'),'1': ObjectId('66607ab3ddb6cfaedf9ec514')}}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('66607ab3ddb6cfaedf9ec513'),user_id: '2',name: 'mori',age: 20},{_id: ObjectId('66607ab3ddb6cfaedf9ec514'),user_id: '3',name: 'shimada',age: 50}]
updateOneを使って単数更新する。
条件に合うドキュメント複数ある場合は
最初の一つのみ更新する
# 構文db.<db名>.updateOne(# 更新条件{ <field1>: <value1>, ...},# 更新内容{ $set: { <field1>: <value1>, ...} })# 実行文サンプルdb.users.updateOne({ age: 50 },{ $set: { name: 'kawahara' } })
実行してみる
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('66607e0fddb6cfaedf9ec52f'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('66607e0fddb6cfaedf9ec530'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('66607e0fddb6cfaedf9ec531'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('66607e0fddb6cfaedf9ec532'),user_id: '4',name: 'ayatuji',age: 99}]// 実行SAMPLDB> db.users.updateOne( { age: 50 }, { $set: { name: 'kawahara' } }){acknowledged: true,insertedId: null,matchedCount: 1,modifiedCount: 1,upsertedCount: 0}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('66607e0fddb6cfaedf9ec52f'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('66607e0fddb6cfaedf9ec530'),user_id: '2',name: 'kawahara',age: 50},{_id: ObjectId('66607e0fddb6cfaedf9ec531'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('66607e0fddb6cfaedf9ec532'),user_id: '4',name: 'ayatuji',age: 99}]
条件に一致するもの全て更新したい場合は「updateMany」を使う
# 構文db.<db名>.updateOne(# 更新条件{ <field1>: <value1>, ...},# 更新内容{ $set: { <field1>: <value1>, ...} })# 実行文サンプルdb.users.updateMany({ age: 50 },{ $set: { name: 'tanaka' } })
実行してみる
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('66607effddb6cfaedf9ec533'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('66607effddb6cfaedf9ec534'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('66607effddb6cfaedf9ec535'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('66607effddb6cfaedf9ec536'),user_id: '4',name: 'ayatuji',age: 99}]// 実行SAMPLDB> db.users.updateMany( { age: 50 }, { $set: { name: 'tanaka' } }){acknowledged: true,insertedId: null,matchedCount: 2,modifiedCount: 2,upsertedCount: 0}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('66607effddb6cfaedf9ec533'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('66607effddb6cfaedf9ec534'),user_id: '2',name: 'tanaka',age: 50},{_id: ObjectId('66607effddb6cfaedf9ec535'),user_id: '3',name: 'tanaka',age: 50},{_id: ObjectId('66607effddb6cfaedf9ec536'),user_id: '4',name: 'ayatuji',age: 99}]
複数更新されている
upsertを使うことで、存在する場合は更新、しない場合は追加することが
できる
# 構文# 単数の場合db.<db名>.updateOne(# 更新条件{ <field1>: <value1>, ...},# 更新内容{ $set: { <field1>: <value1>, ...} },# upsert判定{ upsert: true })# 複数の場合db.<db名>.updateMany(# 更新条件{ <field1>: <value1>, ...},# 更新内容{ $set: { <field1>: <value1>, ...} },# upsert判定{ upsert: true })# 実行文サンプル# 単数db.users.updateOne({ age: 100 },{ $set: { name: 'tomiyama' } },{ upsert: true });# 複数db.users.updateMany({ age: 100 },{ $set: { name: 'tomiyama' } },{ upsert: true });
実行してみる
まずは存在しない場合
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('6661d44eddb6cfaedf9ec537'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661d44eddb6cfaedf9ec538'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('6661d44eddb6cfaedf9ec539'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661d44eddb6cfaedf9ec53a'),user_id: '4',name: 'ayatuji',age: 99}]// 更新(存在しない)SAMPLDB> db.users.updateOne( { age: 100 }, { $set: { name: 'tomiyama' } }, { upsert: true });{acknowledged: true,insertedId: ObjectId('6661d477698538801e02f221'),matchedCount: 0,modifiedCount: 0,upsertedCount: 1}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('6661d44eddb6cfaedf9ec537'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661d44eddb6cfaedf9ec538'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('6661d44eddb6cfaedf9ec539'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661d44eddb6cfaedf9ec53a'),user_id: '4',name: 'ayatuji',age: 99},{_id: ObjectId('6661d477698538801e02f221'),age: 100,name: 'tomiyama'}]
次は更新する(存在する場合)
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('6661d51bddb6cfaedf9ec53b'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661d51bddb6cfaedf9ec53c'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('6661d51bddb6cfaedf9ec53d'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661d51bddb6cfaedf9ec53e'),user_id: '4',name: 'ayatuji',age: 99}]// 更新(存在する場合)SAMPLDB> db.users.updateOne( { age: 50 }, { $set: { name: 'tomiyama' } }, { upsert: true });{acknowledged: true,insertedId: null,matchedCount: 1,modifiedCount: 1,upsertedCount: 0}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('6661d51bddb6cfaedf9ec53b'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661d51bddb6cfaedf9ec53c'),user_id: '2',name: 'tomiyama',age: 50},{_id: ObjectId('6661d51bddb6cfaedf9ec53d'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661d51bddb6cfaedf9ec53e'),user_id: '4',name: 'ayatuji',age: 99}]
updateOneなので最初の1件のみ更新される
updateManyの場合は条件に一致するドキュメント全てが更新される
replaceはupdateと違い、ドキュメント自体を
置き換える。
※置き換え前のフィールドが置き換え後にない場合は消える
replaceOneなので最初の一つのみが置き換え対象となる
# 構文db.<db名>.replaceOne(# リプレイス条件{ <field1>: <value1>, ...},# リプレイス内容{ <field1>: <value1>, ...})# 実行文サンプルdb.users.replaceOne({ age: 50 },{ name: 'tanaka' })
実行してみる
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('6661d88bddb6cfaedf9ec543'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661d88bddb6cfaedf9ec544'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('6661d88bddb6cfaedf9ec545'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661d88bddb6cfaedf9ec546'),user_id: '4',name: 'ayatuji',age: 99}]// 実行SAMPLDB> db.users.replaceOne( { age: 50 }, { name: 'tanaka' }){acknowledged: true,insertedId: null,matchedCount: 1,modifiedCount: 1,upsertedCount: 0}// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('6661d88bddb6cfaedf9ec543'),user_id: '1',name: 'mori',age: 20},{ _id: ObjectId('6661d88bddb6cfaedf9ec544'), name: 'tanaka' },{_id: ObjectId('6661d88bddb6cfaedf9ec545'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661d88bddb6cfaedf9ec546'),user_id: '4',name: 'ayatuji',age: 99}]
条件に一致するドキュメント自体が置き換えられている
条件に合ったドキュメントのうち最初の1件を削除する
# 構文db.<db名>.deleteOne(# 削除条件{ <field1>: <value1>, ...})# 実行文サンプルdb.users.deleteOne({ age: 50 })
実行してみる
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('6661dcd0ddb6cfaedf9ec54b'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661dcd0ddb6cfaedf9ec54c'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('6661dcd0ddb6cfaedf9ec54d'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661dcd0ddb6cfaedf9ec54e'),user_id: '4',name: 'ayatuji',age: 99}]// 実行SAMPLDB> db.users.deleteOne( { age: 50 }){ acknowledged: true, deletedCount: 1 }// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('6661dcd0ddb6cfaedf9ec54b'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661dcd0ddb6cfaedf9ec54d'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661dcd0ddb6cfaedf9ec54e'),user_id: '4',name: 'ayatuji',age: 99}]
条件に一致するドキュメントの最初の1件が削除される
条件に合ったドキュメントを複数削除する
# 構文db.<db名>.deleteMany(# 削除条件{ <field1>: <value1>, ...})# 実行文サンプルdb.users.deleteMany({ age: 50 })
実行してみる
// 実行前SAMPLDB> db.users.find()[{_id: ObjectId('6661ddc1ddb6cfaedf9ec54f'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661ddc1ddb6cfaedf9ec550'),user_id: '2',name: 'shimada',age: 50},{_id: ObjectId('6661ddc1ddb6cfaedf9ec551'),user_id: '3',name: 'tuji',age: 50},{_id: ObjectId('6661ddc1ddb6cfaedf9ec552'),user_id: '4',name: 'ayatuji',age: 99}]// 実行SAMPLDB> db.users.deleteMany( { age: 50 }){ acknowledged: true, deletedCount: 2 }// 実行後SAMPLDB> db.users.find()[{_id: ObjectId('6661ddc1ddb6cfaedf9ec54f'),user_id: '1',name: 'mori',age: 20},{_id: ObjectId('6661ddc1ddb6cfaedf9ec552'),user_id: '4',name: 'ayatuji',age: 99}]
条件に一致するドキュメントがすべて削除されている
mongoshで操作してみることで、MongoDBがどんなデータを
持つのかイメージすることができた。
データの作成や更新は従来のRDBに比べると行いやすいく、データ自体も
かなり柔軟性が高いと感じた。
pythonからどのように使うのかについて調べたい。