シェア

はじめに

MongoDBを使用する場合、ほとんどの時間を何らかの形でドキュメントの管理に費やすことになります。新しいドキュメントを作成してコレクションに追加する場合でも、ドキュメントを取得する場合でも、データを更新する場合でも、古いアイテムを削除する場合でも、ドキュメントはMongoDBモデルの中心にあります。

このガイドでは、MongoDBドキュメントとは何かを説明し、ドキュメント中心の環境を管理するために知っておく必要のある一般的な操作について説明します。

MongoDBドキュメントとは?

MongoDBでは、データベースとコレクション内のすべてのデータはドキュメントに格納されます。コレクションはデフォルトで必須のスキーマを指定しないため、コレクション内のドキュメントは任意に複雑な構造を持つことができ、兄弟ドキュメントで使用される形式と一致する必要はありません。これにより、非常に優れた柔軟性が提供され、アプリケーションの要件の変化に応じてスキーマを有機的に発展させることができます。

MongoDBドキュメント自体は、BSONデータシリアライゼーション形式を使用しています。これは、JSON JavaScript Object Notationのバイナリ表現です。これにより、プログラムでクエリおよび操作できる定義済みのデータ型を持つ組織化された構造が提供されます。

BSONドキュメントは、キーと値のペアを含む一対の中括弧({})で表されます。BSONでは、これらのデータ対はフィールドとそのとして知られています。フィールドが最初にきて、文字列で表されます。値は、有効なBSONデータ型にすることができます。コロン(:)は、フィールドをその値から分離します。コンマは、各フィールドと値のペアを互いに分離するために使用されます。

例として、MongoDBが理解できる有効なBSONドキュメントを次に示します。

{
_id: 80380,
vehicle_type: "car",
mileage: 7377.80,
color: "blue",
markets: [
"US",
"UK"
],
options: {
transmission: "automatic",
num_doors: 4,
power_windows: true
}
}

ここでは、いくつかの型を確認できます。

  • _idは整数です
  • vehicle_typecolorは文字列です
  • mileageは浮動小数点数です
  • marketsは文字列の配列です
  • optionsには、文字列、整数、およびブール値で構成される値を持つネストされたドキュメントが含まれています

この柔軟性により、ドキュメントはデータを格納するための非常に柔軟な媒体です。新しいフィールドを簡単に追加したり、ドキュメントを相互に埋め込んだり、構造の複雑さが格納されるデータと正確に一致したりします。

新しいドキュメントを作成する方法

新しいドキュメントを作成するには、作成したドキュメントを格納するデータベースに切り替えます。この記事では、デモンストレーションの目的でschoolデータベースを使用します。

use school

ドキュメントを挿入するコレクションも選択する必要があります。データベースと同様に、ドキュメントを挿入するコレクションを明示的に作成する必要はありません。MongoDBは、最初のデータが書き込まれるときに自動的に作成します。この例では、studentsというコレクションを使用します。

ドキュメントの格納場所がわかったので、次のいずれかの方法を使用して新しいドキュメントを挿入できます。

insert()メソッドの使用

insert()メソッドを使用すると、1つまたは複数のドキュメントを呼び出されたコレクションに挿入できます。

単一のドキュメントを挿入するには、コレクションで呼び出すことによって、ドキュメントをメソッドに渡します。ここでは、Ashleyという名前の学生の新しいドキュメントを挿入します。

db.students.insert(
{
first_name: "Ashley",
last_name: "Jenkins",
dob: new Date("January 08, 2003"),
grade_level: 8
}
)
WriteResult({ "nInserted" : 1 })

一度に複数のドキュメントを挿入する場合は、ドキュメントをinsert()に渡す代わりに、ドキュメントの配列を渡します。BrianとLeahという名前の学生の新しいドキュメントを2つ追加できます。

db.students.insert(
[
{
first_name: "Brian",
last_name: "McMantis",
dob: new Date("September 18, 2010"),
grade_level: 2
},
{
first_name: "Leah",
last_name: "Drake",
dob: new Date("October 03, 2009")
}
]
)
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})

バルク書き込み操作を実行したため、戻り値は、前に見たWriteResultオブジェクトではなく、BulkWriteResultになります。

insert()メソッドは柔軟性がありますが、多くのMongoDBドライバーで非推奨となり、次の2つのメソッドが推奨されています。

insertOne()メソッドの使用

insertOne()メソッドは、単一のドキュメントを挿入するために使用できます。insert()メソッドとは異なり、一度に1つのドキュメントしか挿入できないため、動作が少し予測しやすくなります。

構文は、単一のドキュメントを追加するためにinsert()を使用する場合と同じです。Naomiという名前の別の学生を追加できます。

db.students.insertOne(
{
first_name: "Naomi",
last_name: "Pyani"
}
)
{
"acknowledged" : true,
"insertedId" : ObjectId("60e877914655cbf49ff7cb86")
}

insert()とは異なり、insertOne()メソッドは、いくつかの追加の有用な情報を含むドキュメントを返します。書き込みがクラスターによって承認されたことを確認し、オブジェクトIDが含まれています(オブジェクトIDは、こちらでIDを提供しなかったためドキュメントに割り当てられました)。

insertMany()メソッドの使用

複数のドキュメントを一度に挿入する場合のシナリオをカバーするために、insertMany()メソッドが推奨されるようになりました。insert()で複数のドキュメントを挿入する場合と同様に、insertMany()はドキュメントの配列を受け取ります。

Jasmine、Michael、Toniという名前の3人の新しい学生を追加できます。

db.students.insertMany(
[
{
first_name: "Jasmine",
last_name: "Took",
dob: new Date("April 11, 2011")
},
{
first_name: "Michael",
last_name: "Rodgers",
dob: new Date("February 25, 2008"),
grade_level: 6
},
{
first_name: "Toni",
last_name: "Fowler"
}
]
)
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("60e8792d4655cbf49ff7cb87"),
ObjectId("60e8792d4655cbf49ff7cb88"),
ObjectId("60e8792d4655cbf49ff7cb89")
]
}

insertOne()と同様に、insertMany()は書き込みを承認し、挿入されたドキュメントに割り当てられたIDを含む配列を提供するドキュメントを返します。

既存のドキュメントをクエリする方法

ドキュメントのクエリは、それ自体が記事になるほど広範なトピックです。さまざまなタイプのドキュメントを取得するためのクエリをどのように作成するかについての詳細は、MongoDB内のデータのクエリに関するガイドをご覧ください。

詳細は上記のリンク先の記事に譲るのが最適ですが、MongoDBがドキュメントをクエリするために提供しているメソッドについては少なくとも説明できます。MongoDBからドキュメントをフェッチする主な方法は、問題のコレクションでfind()メソッドを呼び出すことです。

たとえば、studentsからすべてのドキュメントを収集するには、引数なしでfind()を呼び出すことができます。

db.students.find()
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

出力をより読みやすくするために、find()の後にpretty()メソッドをチェーンすることもできます。

db.<collection>.find().pretty()
{
"_id" : ObjectId("60e8743b4655cbf49ff7cb83"),
"first_name" : "Ashley",
"last_name" : "Jenkins",
"dob" : ISODate("2003-01-08T00:00:00Z"),
"grade_level" : 8
}
{
"_id" : ObjectId("60e875d54655cbf49ff7cb84"),
"first_name" : "Brian",
"last_name" : "McMantis",
"dob" : ISODate("2010-09-18T00:00:00Z"),
"grade_level" : 2
}
{
"_id" : ObjectId("60e875d54655cbf49ff7cb85"),
"first_name" : "Leah",
"last_name" : "Drake",
"dob" : ISODate("2009-10-03T00:00:00Z")
}
{
"_id" : ObjectId("60e877914655cbf49ff7cb86"),
"first_name" : "Naomi",
"last_name" : "Pyani"
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb87"),
"first_name" : "Jasmine",
"last_name" : "Took",
"dob" : ISODate("2011-04-11T00:00:00Z")
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb88"),
"first_name" : "Michael",
"last_name" : "Rodgers",
"dob" : ISODate("2008-02-25T00:00:00Z"),
"grade_level" : 6
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb89"),
"first_name" : "Toni",
"last_name" : "Fowler"
}

_idフィールドが各ドキュメントに追加されていることがわかります。MongoDBでは、コレクション内の各ドキュメントに一意の_idが必要です。オブジェクトの作成時に指定しない場合、自動的に追加されます。このIDを使用して、単一のオブジェクトを確実に取得できます。

db.students.find(
{
_id : ObjectId("60e8792d4655cbf49ff7cb89")
}
)
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

データのクエリに関するさまざまな方法の詳細については、上記のリンク先の記事をご覧ください。

既存のドキュメントを更新する方法

データベースの多くのユースケースまたはほとんどのユースケースでは、データベース内の既存のデータを変更できる必要があります。新しい値を反映するためにフィールドを更新する必要がある場合や、既存のドキュメントに追加情報が利用可能になったときに追加する必要がある場合があります。

MongoDBでは、既存のドキュメントを更新するために、いくつかの関連するメソッドを使用しています。

  • updateOne():指定されたフィルターに基づいて、コレクション内の単一のドキュメントを更新します。
  • updateMany():指定されたフィルターに一致するコレクション内の複数のドキュメントを更新します。
  • replaceOne():指定されたフィルターに基づいて、コレクション内のドキュメント全体を置き換えます。

これらの各バリアントを使用して、さまざまなタイプの更新を実行する方法について説明します。

更新演算子

ドキュメントを更新するための各メソッドを見ていく前に、利用可能な更新演算子のいくつかを見ていく必要があります。

  • $currentDate:フィールドの値を現在の日付に設定します(日付またはタイムスタンプ型)。
    • 構文:{ $currentDate: { <field>: <type>, ... } }
  • $inc:フィールドの値を設定された量だけインクリメントします。
    • 構文:{ $inc: { <field>: <amount>, ... } }
  • $min:指定された値が現在の値よりも小さい場合に、フィールドの値を更新します。
    • 構文:{ $min: { <field>: <value>, ... } }
  • $max:指定された値が現在の値よりも大きい場合に、フィールドの値を更新します。
    • 構文:{ $max: { <field>: <value>, ... } }
  • $mul:フィールドの値に指定された数値を乗算して更新します。
    • 構文:{ $mul: { <field>: <value>, ... } }
  • $rename:フィールド名を新しい識別子に変更します。
    • 構文:{ $rename: { <field>: <new_name>, ... } }
  • $set:フィールドの値を指定された値に置き換えます。
    • 構文:{ $set: { <field>: value, ... } }
  • $setOnInsert:アップサート操作中に、新しいドキュメントが作成されている場合はフィールドの値を設定し、それ以外の場合は何も実行しません。
    • 構文:{ $setOnInsert: { <field>: <value>, ... } }
  • $unset:ドキュメントからフィールドを削除します。
    • 構文:{ $unset: { <field>: "", ... } }
  • $:クエリを満たす最初の配列要素のプレースホルダー。
    • 構文:{ <update_operator>: {<array>.$: <value> } }
  • $[]:クエリを満たすすべての配列要素のプレースホルダー。
    • 構文:{ <update_operator>: { <array>.$[]: <value> } }
  • $addToSet:値がまだ存在しない場合に、配列に値を追加します。
    • 構文:{ $addToSet: { <field>: <value>, ... } }
  • $pop:配列の最初または最後の要素を削除します。
    • 構文:{ $pop: { <field>: (-1 or 1), ... } }
  • $pull:条件に一致する配列のすべての要素を削除します。
    • 構文:{ $pull: { <field>: <condition>, ... } }
  • $push:値を配列に追加します。
    • 構文:{ $push: { <field>: <value>, ... } }
  • $pullAll:指定されたすべての要素を配列から削除します。
    • 構文:{ $pullAll: { <field>: [ <value>, ... ], ...} }
  • $each$addToSetおよび$push演算子を変更して、配列を単一の要素としてではなく、配列の各要素を追加するようにします。
    • 構文:{ <update_operator>: { <field>: { $each: [ <value>, ... ] }, ... } }
  • $position$eachとともに使用され、$push演算子が挿入する位置を指定します。
    • 構文:{ $push: { <field>: { $each: [ <value>, ... ], $position: <num> } } }
  • $slice$eachおよび$pushとともに使用され、配列内の要素の合計数を制限します。
    • 構文:{ $push: { <field>: { $each: [ <value>, ... ], $slice: <num> } } }
  • $sort$eachおよび$pushとともに使用され、配列要素をソートします。
    • 構文:{ $push: { <field>: { $each: [ <value>, ... ], $sort: <sort_order> } } }

これらのさまざまな更新演算子を使用すると、ドキュメントのさまざまなフィールドをさまざまな方法で更新できます。

コレクション内の単一のドキュメントを更新する

MongoDBのupdateOne()メソッドは、コレクション内の単一のドキュメントを更新するために使用されます。このメソッドは、2つの必須引数と、オプションの引数を指定するドキュメントを受け取ります。

最初の引数は、ドキュメントを選択するために使用されるフィルター条件を指定するドキュメントです。updateOne()メソッドはコレクション内で最大で1つのドキュメントを変更するため、フィルター条件を満たす最初のドキュメントが使用されます。

2番目の引数は、実行する必要がある更新操作を指定します。上記の更新操作は、ここで指定して、一致するドキュメントの内容を変更できます。

3番目の引数は、メソッドの動作を変更するためのさまざまなオプションのドキュメントです。最も重要な潜在的な値は次のとおりです。

  • upsert:フィルターが既存のドキュメントと一致しない場合に新しいドキュメントを挿入することにより、操作をアップサートプロシージャに変えます。
  • collation:操作に適用する必要がある言語固有のルールを定義するドキュメント。

例として、_idフィールドでフィルタリングして、正しいドキュメントをターゲットにしていることを確認することで、単一の学生レコードを更新できます。grade_levelを新しい値に設定できます。

db.students.updateOne(
{ _id: ObjectId("60e8792d4655cbf49ff7cb89") },
{ $set: { grade_level: 3 } }
)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

コレクション内の複数のドキュメントを更新する

MongoDBのupdateMany()メソッドは、updateOne()メソッドと同様に機能しますが、最初のマッチの後に停止するのではなく、指定されたフィルターに一致するドキュメントをすべて更新します。

updateMany()構文はupdateOne()構文と完全に一致するため、唯一の違いは操作のスコープです。

例として、teachersコレクションドキュメントのsubjects配列内の "composition" のすべてのインスタンスを "writing" に変更する場合は、次のようなものを使用できます。

db.teachers.updateMany(
{ subject: "composition" },
{ $set: { "subjects.$": "writing" } }
)
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 3 }

ドキュメントを確認すると、"composition" の各インスタンスが "writing" に置き換えられているはずです。

db.teachers.find()
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Nancy", "last_name" : "Smith", "subjects" : [ "vocabulary", "pronunciation" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "writing" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "writing", "grammar" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bad"), "first_name" : "Rebecca", "last_name" : "Carrie", "subjects" : [ "grammar", "literature" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "writing", "grammar", "vocabulary", "pronunciation" ] }

ドキュメントを置き換える

replaceOne()メソッドは、updateOne()メソッドと同様に機能しますが、個々のフィールドを更新するのではなく、ドキュメント全体を置き換えます。構文は、前の2つのコマンドと同じです。

たとえば、ナンシー・スミスが学校を辞めて、文学を教えるクララ・ニューマンという教師に置き換える場合は、次のように入力できます。

db.teachers.replaceOne(
{
$and: [
{ first_name: "Nancy" },
{ last_name: "Smith" }
]
},
{
first_name: "Clara",
last_name: "Newman",
subjects: [ "literature" ]
}
)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

一致したドキュメントが削除され、指定されたドキュメントに置き換えられていることがわかります。

db.teachers.find()
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Clara", "last_name" : "Newman", "subjects" : [ "literature" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "writing" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "writing", "grammar" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bad"), "first_name" : "Rebecca", "last_name" : "Carrie", "subjects" : [ "grammar", "literature" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "writing", "grammar", "vocabulary", "pronunciation" ] }

ドキュメントを削除する方法

コレクションからドキュメントを削除することも、ドキュメントのライフサイクルの一部です。ドキュメントを削除するには、deleteOne()またはdeleteMany()メソッドを使用できます。これらは同じ構文を持ち、操作するドキュメントの数のみが異なります。

ほとんどの場合、これらのメソッドのいずれかでドキュメントを削除するために行う必要があるのは、削除するドキュメントをどのように選択するかを指定するフィルタードキュメントをメソッドに提供することだけです。deleteOne()メソッドは、フィルターが生成する一致の数に関係なく、最大で1つのドキュメントを削除しますが、deleteMany()メソッドは、フィルター条件に一致するすべてのドキュメントを削除します。

たとえば、単一の学生を削除するには、_idを提供して明示的に一致させることができます。

db.students.deleteOne({
_id: ObjectId("60e8792d4655cbf49ff7cb87")
})
{ "acknowledged" : true, "deletedCount" : 1 }

学年が割り当てられていない学生を削除する場合は、代わりにdeleteMany()メソッドを使用できます。

db.students.deleteMany({
grade_level: { $eq: null }
})
{ "acknowledged" : true, "deletedCount" : 2 }

確認すると、残りのすべての学生に学年が割り当てられているはずです。

db.students.find()
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler", "grade_level" : 3 }

結論

ドキュメントの作成、クエリ、更新、削除の方法を学ぶことで、MongoDB内でドキュメントを日常的に効果的に管理するために必要なスキルが身につきます。さまざまなドキュメントおよびコレクションメソッドと、情報の一致と変更を可能にする演算子に慣れることで、データベースシステムが理解できる複雑な思考を表現できます。

FAQ

MongoDBにおける埋め込みドキュメント、またはネストされたドキュメントとは、内部に別のドキュメントを含むドキュメントのことです。

以下は埋め込みドキュメントの例です。ここでは、address(追加の中括弧でサブドキュメントとして示される)がuserレコードでアクセスできます。

db.user.findOne({_id: 111111})
{
_id: 111111,
email: “email@example.com”,
name: {given: “Jane”, family: “Han”},
address: {
street: “111 Elm Street”,
city: “Springfield”,
state: “Ohio”,
country: “US”,
zip: “00000”,
}
}

MongoDBのドキュメントの最大サイズは16メガバイトです。

この制限は、単一のドキュメントが過剰な量のRAMや、伝送中に過剰な量の帯域幅を使用しないようにするために役立ちます。

16MBを超えるドキュメントを保存するために、MongoDBはGridFS APIを提供しています。

ドキュメントを削除するには、deleteOne()またはdeleteMany()メソッドを使用できます。これらは構文は同じで、操作するドキュメントの数だけが異なります。

単一のドキュメントを削除する場合、特定の_idを持つドキュメントを削除するための基本的な構文は次のようになります。

db.students.deleteOne({
_id: ObjectId("60e8792d4655cbf49ff7cb87")
})

また、特定の条件に一致する多数のドキュメントを削除する場合、構文は同様になります。

db.students.deleteMany({
grade_level: { $eq: null }
})

MongoDBでドキュメントを修正するには、いくつかの関連する更新メソッドがあります。

具体的には、ドキュメントに新しいデータを追加するには、$addToSet 更新演算子を使用できます。この演算子は、値がまだ存在しない場合に限り、ドキュメントの配列に値を追加します。

MongoDBで1つのドキュメントを別のドキュメントと明示的に比較するための特定の方法はありません。ドキュメントのフィールド間の等価性を演算子と比較するクエリを構成することで実行できます。

比較は、集計パイプラインを構成することによっても行うことができます。この方法では、次のステージを作成できます。

  • 複数のドキュメントから値をグループ化する
  • グループ化されたデータに対して操作を実行して、単一の結果を返す
  • 時間の経過に伴うデータ変更を分析する
著者について
Justin Ellingwood

ジャスティン・エリングウッド

ジャスティンは2013年からデータベース、Linux、インフラストラクチャ、および開発者ツールについて執筆しています。彼は現在、妻と2匹のウサギと一緒にベルリンに住んでいます。彼は通常、三人称で書く必要がないため、関係者全員にとって安心です。