MongoDBでCRUD操作を実行する方法
著者は、 Open Internet / Free Speech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
MongoDB は、ドキュメント形式のデータを保存および処理するために使用される永続的なドキュメント指向データベースです。 他のデータベース管理システムと同様に、MongoDBでは、次の4つの基本的なタイプのデータ操作を通じてデータを管理および操作できます。
- Create操作。データベースへのデータの書き込みが含まれます。
- Read操作。データベースにクエリを実行してデータベースからデータを取得します。
- Update操作。データベースにすでに存在するデータを変更します
- Delete操作。データベースからデータを完全に削除します。
これらの4つの操作は、まとめてCRUD操作と呼ばれます。
このチュートリアルでは、新しいMongoDBドキュメントを作成し、後でそれらを取得してデータを読み取る方法の概要を説明します。 また、ドキュメント内のデータを更新する方法、およびドキュメントが不要になったときにドキュメントを削除する方法についても説明します。
前提条件
このチュートリアルに従うには、次のものが必要です。
sudo
権限を持つ通常の非rootユーザーと、UFWで構成されたファイアウォールを備えたサーバー。 このチュートリアルは、Ubuntu 20.04を実行しているサーバーを使用して検証されており、Ubuntu20.04のこの初期サーバーセットアップチュートリアルに従ってサーバーを準備できます。- サーバーにインストールされているMongoDB。 これを設定するには、 Ubuntu20.04にMongoDBをインストールする方法に関するチュートリアルに従ってください。
- 認証を有効にして管理ユーザーを作成することにより、サーバーのMongoDBインスタンスを保護します。 このようにMongoDBを保護するには、 Ubuntu20.04でMongoDBを保護する方法に関するチュートリアルに従ってください。
- このチュートリアル全体で使用されるMongoDBシェルの基本的な知識。 MongoDBシェルの使用方法については、チュートリアル MongoDBShellの使用方法に従ってください。
注:サーバーの構成、インストール、およびMongoDBの安全なインストールの方法に関するリンクされたチュートリアルは、Ubuntu20.04を参照しています。 このチュートリアルは、基盤となるオペレーティングシステムではなく、MongoDB自体に焦点を当てています。 通常、認証が有効になっている限り、オペレーティングシステムに関係なく、すべてのMongoDBインストールで機能します。
ステップ1—MongoDBサーバーに接続する
このガイドでは、MongoDBシェルを使用してMongoDBと対話します。 MongoDBでCRUD操作を実行して実行するには、最初にMongoDBシェルを開いてMongoDBデータベースに接続する必要があります。
MongoDBインスタンスがリモートサーバーで実行されている場合は、ローカルマシンからそのサーバーにSSHで接続します。
ssh sammy@your_server_ip
次に、MongoDBシェルを開いて、MongoDBインストールに接続します。 データの書き込みと読み取りの権限を持つMongoDBユーザーとして接続してください。 前提条件のMongoDBセキュリティチュートリアルに従った場合、そのガイドのステップ1で作成した管理ユーザーとして接続できます。
mongo -u AdminSammy -p --authenticationDatabase admin
ユーザーのパスワードを入力すると、端末プロンプトが大なり記号(>
)に変わります。 これは、シェルが接続先のMongoDBサーバーのコマンドを受け入れる準備ができたことを意味します。
注:新しい接続では、MongoDBシェルはデフォルトでtest
データベースに自動的に接続します。 このデータベースを安全に使用して、MongoDBとMongoDBシェルを試すことができます。
または、別のデータベースに切り替えて、このチュートリアルに記載されているすべてのサンプルコマンドを実行することもできます。 別のデータベースに切り替えるには、use
コマンドに続けて、データベースの名前を実行します。
use database_name
MongoDBシェルを使用してMongoDBサーバーに接続したので、新しいドキュメントの作成に進むことができます。
ステップ2—ドキュメントの作成
このガイドの後のステップで読み取り、更新、および削除を練習できるデータを用意するために、このステップでは、MongoDBでデータドキュメントを作成する方法に焦点を当てます。
MongoDBを使用して、世界中の有名な歴史的建造物のディレクトリを構築および管理していると想像してみてください。 このディレクトリには、各記念碑の名前、国、都市、地理的な場所などの情報が保存されます。
このディレクトリ内のドキュメントは、ギザの大ピラミッドを表すこの例と同様の形式に従います。
ギザのピラミッド
{ "name": "The Pyramids of Giza", "city": "Giza", "country": "Egypt", "gps": { "lat": 29.976480, "lng": 31.131302 } }
このドキュメントは、すべてのMongoDBドキュメントと同様に、BSONで記述されています。 BSONは、人間が読める形式のJSONのバイナリ形式です。 BSONまたはJSONドキュメントのすべてのデータは、field: value
の形式をとるフィールドと値のペアとして表されます。
このドキュメントは4つのフィールドで構成されています。 最初は記念碑の名前で、次に都市と国が続きます。 これらの3つのフィールドすべてに文字列が含まれています。 gps
と呼ばれる最後のフィールドは、記念碑のGPS位置を詳細に示すネストされたドキュメントです。 この場所は、緯度と経度の座標のペアで構成され、それぞれlat
フィールドとlng
フィールドで表され、それぞれが浮動小数点値を保持します。
注: MongoDBドキュメントの構造について詳しくは、概念記事ドキュメント指向データベースの概要をご覧ください。
insertOne
メソッドを使用して、このドキュメントをmonuments
という新しいコレクションに挿入します。 その名前が示すように、insertOne
は、一度に複数のドキュメントを作成するのではなく、個々のドキュメントを作成するために使用されます。
MongoDBシェルで、次の操作を実行します。
db.monuments.insertOne( { "name": "The Pyramids of Giza", "city": "Giza", "country": "Egypt", "gps": { "lat": 29.976480, "lng": 31.131302 } } )
このinsertOne
メソッドを実行する前に、monuments
コレクションを明示的に作成していないことに注意してください。 MongoDBを使用すると、存在しないコレクションに対してコマンドを自由に実行できます。欠落しているコレクションは、最初のオブジェクトが挿入されたときにのみ作成されます。 この例のinsertOne()
メソッドを実行すると、ドキュメントがコレクションに挿入されるだけでなく、コレクションが自動的に作成されます。
MongoDBは、insertOne
メソッドを実行し、ギザのピラミッドを表す要求されたドキュメントを挿入します。 操作の出力は、正常に実行されたことを通知し、新しいドキュメント用に自動的に生成されたObjectId
も提供します。
Output{ "acknowledged" : true, "insertedId" : ObjectId("6105752352e6d1ebb7072647") }
MongoDBでは、コレクション内の各ドキュメントには、主キーとして機能する一意の_id
フィールドが必要です。 _id
フィールドを含めて、各ドキュメントの_id
フィールドが一意であることを確認する限り、独自に選択した値を指定できます。 ただし、新しいドキュメントで_id
フィールドが省略されている場合、MongoDBは_id
フィールドの値としてオブジェクト識別子(ObjectId
オブジェクトの形式)を自動的に生成します。
monuments
コレクションのオブジェクト数を確認することで、ドキュメントが挿入されたことを確認できます。
db.monuments.count()
このコレクションに挿入したドキュメントは1つだけなので、count
メソッドは1
を返します。
Output1
このようにドキュメントを1つずつ挿入すると、複数のドキュメントを作成する場合にすぐに面倒になります。 MongoDBは、1回の操作で複数のドキュメントを挿入するために使用できるinsertMany
メソッドを提供します。
次のコマンド例を実行します。このコマンドは、insertMany
メソッドを使用して、6つの有名なモニュメントをmonuments
コレクションに挿入します。
db.monuments.insertMany([ {"name": "The Valley of the Kings", "city": "Luxor", "country": "Egypt", "gps": { "lat": 25.746424, "lng": 32.605309 }}, {"name": "Arc de Triomphe", "city": "Paris", "country": "France", "gps": { "lat": 48.873756, "lng": 2.294946 }}, {"name": "The Eiffel Tower", "city": "Paris", "country": "France", "gps": { "lat": 48.858093, "lng": 2.294694 }}, {"name": "Acropolis", "city": "Athens", "country": "Greece", "gps": { "lat": 37.970833, "lng": 23.726110 }}, {"name": "The Great Wall of China", "city": "Huairou", "country": "China", "gps": { "lat": 40.431908, "lng": 116.570374 }}, {"name": "The Statue of Liberty", "city": "New York", "country": "USA", "gps": { "lat": 40.689247, "lng": -74.044502 }} ])
6つのドキュメントを囲む角かっこ([
および]
)に注意してください。 これらの括弧は、ドキュメントの配列を示します。 角かっこ内には、コンマで区切られた複数のオブジェクトが次々に表示されます。 MongoDBメソッドが複数のオブジェクトを必要とする場合は、このような配列の形式でオブジェクトのリストを提供できます。
MongoDBは、新しく挿入されたオブジェクトごとに1つずつ、いくつかのオブジェクト識別子で応答します。
Output{ "acknowledged" : true, "insertedIds" : [ ObjectId("6105770952e6d1ebb7072648"), ObjectId("6105770952e6d1ebb7072649"), ObjectId("6105770952e6d1ebb707264a"), ObjectId("6105770952e6d1ebb707264b"), ObjectId("6105770952e6d1ebb707264c"), ObjectId("6105770952e6d1ebb707264d") ] }
monuments
コレクションのオブジェクト数を確認することで、ドキュメントが挿入されたことを確認できます。
db.monuments.count()
これらの6つの新しいドキュメントを追加した後、このコマンドの期待される出力は7
です。
Output7
これで、2つの別々の挿入方法を使用して、いくつかの有名なモニュメントを表す多数のドキュメントを作成しました。 次に、MongoDBのfind()
メソッドで挿入したデータを読み取ります。
ステップ3—ドキュメントを読む
コレクションにいくつかのドキュメントが保存されたので、データベースにクエリを実行してこれらのドキュメントを取得し、データを読み取ることができます。 この手順では、最初に特定のコレクション内のすべてのドキュメントをクエリする方法の概要を説明し、次にフィルターを使用して取得したドキュメントのリストを絞り込む方法について説明します。
前の手順を完了すると、monuments
コレクションに挿入された有名なモニュメントを説明する7つのドキュメントが作成されます。 find()
メソッドを使用すると、1回の操作で7つのドキュメントすべてを取得できます。
db.monuments.find()
このメソッドを引数なしで使用すると、フィルタリングは適用されず、MongoDBに、指定されたコレクションmonuments
で使用可能なすべてのオブジェクトを返すように要求します。 MongoDBは次の出力を返します。
Output{ "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 } } { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } } { "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } } { "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 } } { "_id" : ObjectId("6105770952e6d1ebb707264b"), "name" : "Acropolis", "city" : "Athens", "country" : "Greece", "gps" : { "lat" : 37.970833, "lng" : 23.72611 } } { "_id" : ObjectId("6105770952e6d1ebb707264c"), "name" : "The Great Wall of China", "city" : "Huairou", "country" : "China", "gps" : { "lat" : 40.431908, "lng" : 116.570374 } } { "_id" : ObjectId("6105770952e6d1ebb707264d"), "name" : "The Statue of Liberty", "city" : "New York", "country" : "USA", "gps" : { "lat" : 40.689247, "lng" : -74.044502 } }
MongoDBシェルは、7つのドキュメントすべてを1つずつ完全に出力します。 これらの各オブジェクトには、定義していない_id
プロパティがあることに注意してください。 前述のように、_id
フィールドはそれぞれのドキュメントの主キーとして機能し、前の手順でinsertMany
メソッドを実行したときに自動的に作成されました。
MongoDBシェルからのデフォルトの出力はコンパクトで、各ドキュメントのフィールドと値が1行に出力されます。 これは、特に複数のフィールドまたはネストされたドキュメントを含むオブジェクトでは読みにくくなる可能性があります。
find()
メソッドの出力を読みやすくするために、次のようなpretty
印刷機能を使用できます。
db.monuments.find().pretty()
今回は、MongoDBシェルがドキュメントを複数行に印刷し、それぞれにインデントを付けます。
Output{ "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 } } { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } } . . .
前の2つの例では、find()
メソッドが引数なしで実行されたことに注意してください。 どちらの場合も、コレクションからすべてのオブジェクトを返しました。 クエリにフィルタを適用して、結果を絞り込むことができます。
前の例から、MongoDBが王家の谷にObjectId("6105770952e6d1ebb7072648")
の値を持つオブジェクト識別子を自動的に割り当てたことを思い出してください。 オブジェクト識別子は、ObjectId("")
内の16進文字列だけでなく、ObjectId
オブジェクト全体(オブジェクト識別子を格納するためにMongoDBで使用される特別なデータ型)です。
次のfind()
メソッドは、クエリフィルタードキュメントを引数として受け入れることにより、単一のオブジェクトを返します。 クエリフィルタードキュメントは、コレクションに挿入するドキュメントと同じ構造に従い、フィールドと値で構成されますが、代わりにクエリ結果をフィルターするために使用されます。
この例で使用されているクエリフィルタードキュメントには、_id
フィールドが含まれており、値として The Valley of theKings'オブジェクト識別子が含まれています。 独自のデータベースでこのクエリを実行するには、強調表示されたオブジェクト識別子を、独自のmonuments
コレクションに保存されているドキュメントの1つに置き換えてください。
db.monuments.find({"_id": ObjectId("6105770952e6d1ebb7072648")}).pretty()
この例のクエリフィルタードキュメントは、等式条件を使用します。つまり、クエリは、ドキュメントで指定されたものと一致するフィールドと値のペアを持つすべてのドキュメントを返します。 基本的に、この例では、find()
メソッドに、_id
の値がObjectId("6105770952e6d1ebb7072648")
と等しいドキュメントのみを返すように指示しています。
このメソッドを実行した後、MongoDBは要求されたオブジェクト識別子に一致する単一のオブジェクトを返します。
Output{ "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } }
ドキュメントの他のフィールドでも品質条件を使用できます。 説明のために、フランスで記念碑を検索してみてください。
db.monuments.find({"country": "France"}).pretty()
このメソッドは2つのモニュメントを返します。
Output{ "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } } { "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 } }
クエリフィルタードキュメントは非常に強力で柔軟性があり、コレクションドキュメントに複雑なフィルターを適用できます。
コレクションをクエリするさまざまな方法の詳細については、クエリの作成方法チュートリアルをご覧ください。
–>
ステップ4—ドキュメントの更新
MongoDBのようなドキュメント指向データベース内のドキュメントは、時間の経過とともに変化するのが一般的です。 場合によっては、アプリケーションの要件の変化に合わせて構造を進化させる必要があります。そうしないと、データ自体が変化する可能性があります。 この手順では、個々のドキュメントのフィールド値を変更したり、コレクション内のすべてのドキュメントに新しいフィールドを追加したりして、既存のドキュメントを更新する方法に焦点を当てます。
insertOne()
およびinsertMany()
メソッドと同様に、MongoDBには、単一のドキュメントまたは複数のドキュメントを一度に更新できるメソッドが用意されています。 これらの更新メソッドとの重要な違いは、新しいドキュメントを作成するときに、メソッド引数としてドキュメントデータを渡すだけでよいことです。 コレクション内の既存のドキュメントを更新するには、更新するドキュメントを指定する引数も渡す必要があります。
ユーザーがこれを実行できるようにするために、MongoDBは、更新メソッドで、前の手順でドキュメントを検索および取得するために使用したものと同じクエリフィルタードキュメントメカニズムを使用します。 ドキュメントの取得に使用できるクエリフィルタードキュメントを使用して、更新するドキュメントを指定することもできます。
凱旋門の名前を凱旋門のフルネームに変更してみてください。 これを行うには、単一のドキュメントを更新するupdateOne()
メソッドを使用します。
db.monuments.updateOne( { "name": "Arc de Triomphe" }, { $set: { "name": "Arc de Triomphe de l'Étoile" } } )
updateOne
メソッドの最初の引数は、前の手順で説明したように、単一の等式条件を持つクエリフィルタードキュメントです。 この例では、{ "name": "Arc de Triomphe" }
は、Arc de Triomphe
の値を保持するname
キーを持つドキュメントを検索します。 ここでは、任意の有効なクエリフィルタードキュメントを使用できます。
2番目の引数は更新ドキュメントであり、更新中に適用する必要がある変更を指定します。 更新ドキュメントは、キーとしての更新演算子と、値としての各演算子のパラメータで構成されています。 この例では、使用される更新演算子は$set
です。 ドキュメントフィールドを新しい値に設定する役割を果たし、新しいフィールド値を持つJSONオブジェクトが必要です。 ここで、set: { "name": "Arc de Triomphe de l'Étoile" }
は、フィールドname
の値をArc de Triomphe de l'Étoile
に設定するようにMongoDBに指示します。
このメソッドは、クエリフィルタードキュメントによって1つのオブジェクトが検出され、1つのオブジェクトが正常に更新されたことを示す結果を返します。
Output{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
注:ドキュメントクエリフィルターが単一のドキュメントを選択するのに十分正確でない場合、updateOne()
は、複数の結果から返された最初のドキュメントのみを更新します。
更新が機能したかどうかを確認するには、France
に関連するすべてのモニュメントを取得してみてください。
db.monuments.find({"country": "France"}).pretty()
今回、このメソッドは Arc de Triomphe を返しますが、フルネームは更新操作によって変更されています。
Output{ "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe de l'Étoile", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } } . . .
複数のドキュメントを変更するには、代わりにupdateMany()
メソッドを使用できます。
例として、誰がエントリを作成したかについての情報がないことに気づき、データベースに各記念碑を追加した作成者の功績を認めたいとします。 これを行うには、monuments
コレクションの各ドキュメントに新しいeditor
フィールドを追加します。
次の例には、空のクエリフィルタードキュメントが含まれています。 空のクエリドキュメントを含めることにより、この操作はコレクション内のすべてのドキュメントに一致し、updateMany()
メソッドは各ドキュメントに影響します。 更新ドキュメントは、各ドキュメントに新しいeditor
フィールドを追加し、それにSammy
の値を割り当てます。
db.monuments.updateMany( { }, { $set: { "editor": "Sammy" } } )
このメソッドは、次の出力を返します。
Output{ "acknowledged" : true, "matchedCount" : 7, "modifiedCount" : 7 }
この出力は、7つのドキュメントが一致し、7つも変更されたことを通知します。
変更が適用されたことを確認します。
db.monuments.find().pretty()
Output{ "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 }, "editor" : "Sammy" } { "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 }, "editor" : "Sammy" } . . .
返されたすべてのドキュメントには、editor
という新しいフィールドがSammy
に設定されています。 $set
更新演算子に存在しないフィールド名を指定することにより、更新操作は一致するすべてのドキュメントに欠落しているフィールドを作成し、新しい値を適切に設定します。
$set
を最も頻繁に使用しますが、MongoDBでは他の多くの更新演算子を使用できるため、ドキュメントのデータと構造に複雑な変更を加えることができます。 これらの更新演算子の詳細については、MongoDBの主題に関する公式ドキュメントを参照してください。
ステップ5—ドキュメントを削除する
データベース内のデータが古くなり、削除する必要がある場合があります。 Mongoの更新および挿入操作と同様に、クエリフィルタードキュメントと一致する最初のドキュメントのみを削除するdeleteOne()
メソッドと、複数のオブジェクトを削除するdeleteMany()
メソッドがあります。一度に。
これらの方法の使用を練習するには、最初に変更した凱旋門記念碑を削除してみてください。
db.monuments.deleteOne( { "name": "Arc de Triomphe de l'Étoile" } )
このメソッドには、前の更新と取得の例のようなクエリフィルタードキュメントが含まれていることに注意してください。 以前と同様に、有効なクエリを使用して、削除するドキュメントを指定できます。
MongoDBは次の結果を返します。
Output{ "acknowledged" : true, "deletedCount" : 1 }
ここで、結果は、プロセスで削除されたドキュメントの数を示しています。
フランスの記念碑を照会して、ドキュメントが実際にコレクションから削除されているかどうかを確認します。
db.monuments.find({"country": "France"}).pretty()
今回は、凱旋門を削除したため、このメソッドは単一の記念碑エッフェル塔のみを返します。
Output{ "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 }, "editor" : "Sammy" }
複数のドキュメントを一度に削除する方法を説明するために、Sammy
がエディターであったすべての記念碑ドキュメントを削除します。 以前にSammy
をすべてのモニュメントのエディターとして指定したため、これによりコレクションが空になります。
db.monuments.deleteMany( { "editor": "Sammy" } )
今回、MongoDBは、このメソッドが6つのドキュメントを削除したことを通知します。
Output{ "acknowledged" : true, "deletedCount" : 6 }
monuments
コレクションが空になったことを確認するには、その中のドキュメントの数を数えます。
db.monuments.count()
Output0
コレクションからすべてのドキュメントを削除したので、このコマンドは0
の期待される出力を返します。
結論
この記事を読むことで、CRUD操作の概念( C reate、 R ead、 U pdate、 D elete)に慣れました。 —データ管理の4つの重要なコンポーネント。 これで、MongoDBデータベースに新しいドキュメントを挿入したり、既存のドキュメントを変更したり、コレクションにすでに存在するドキュメントを取得したり、必要に応じてドキュメントを削除したりできます。
ただし、このチュートリアルでは、クエリフィルタリングの基本的な方法を1つだけ取り上げていることに注意してください。 MongoDBは、複雑な基準に対して関心のあるドキュメントを正確に選択できる堅牢なクエリシステムを提供します。 より複雑なクエリの作成について詳しくは、主題に関する公式のMongoDBドキュメントを確認することをお勧めします。