MongoDB とドライバー
定義
db.collection.findOneAndUpdate( filter, update, options )filterおよびsort条件に基づいて単一のドキュメントを更新します。Tip
バージョン 8.0 の新機能:
updateOne()メソッドには、ユーザーが指定したソート順序で最初のドキュメントを更新するためのsortオプションが含まれています。次の値を返します。 Returns the original document by default. Returns the updated document if returnNewDocument is set to
trueor returnDocument is set toafter.
互換性
このメソッドは、次の環境でホストされている配置で使用できます。
- MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud
注意
This command is supported in all MongoDB Atlas clusters. For information on Atlas support for all commands, see Unsupported Commands.
MongoDB Enterprise: サブスクリプションベースの自己管理型 MongoDB バージョン
MongoDB Community: ソースが利用可能で、無料で使用できる自己管理型の MongoDB のバージョン
構文
db.collection.findOneAndUpdate() の形式は次のとおりです。
db.collection.findOneAndUpdate( <filter>, <update document or aggregation pipeline>, { writeConcern: <document>, projection: <document>, sort: <document>, maxTimeMS: <number>, upsert: <boolean>, returnDocument: <string>, returnNewDocument: <boolean>, collation: <document>, arrayFilters: [ <filterdocument1>, ... ] } )
パラメーター
db.collection.findOneAndUpdate() は以下のパラメーターを取ります。
Parameter | タイプ | 説明 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ドキュメント | |||||||||||||||||||
| ドキュメントまたは配列 | 更新ドキュメントまたは集計パイプライン。
| ||||||||||||||||||
| ドキュメント | 任意。書込み保証(write concern)を表現するドキュメント。デフォルトの書込み保証を使用する場合は省略します。 トランザクションで実行される場合、操作の書込み保証 (write concern)を明示的に設定しないでください。トランザクションで書込み保証を使用するには、「トランザクション書込み保証」を参照してください。 | ||||||||||||||||||
| ドキュメント | 任意。返すフィールドのサブセット。 返されたドキュメント内のすべてのフィールドを返すには、このパラメーターを省略します。 プロジェクションの引数がドキュメントでない場合、操作はエラーになります。 | ||||||||||||||||||
| ドキュメント | |||||||||||||||||||
| 数値 | 任意。 操作が 以内に完了する必要がある制限時間をミリ秒単位で指定します。 制限を超えた場合はエラーがスローされます。 | ||||||||||||||||||
| ブール値 | Optional. When
複数のアップサートを回避するため、 デフォルトは | ||||||||||||||||||
| string | Optional. Starting in
| ||||||||||||||||||
| ブール値 | 任意。 デフォルトは | ||||||||||||||||||
| ドキュメント | 任意。 任意。操作に使用する照合を指定します。 照合を指定すると、大文字・小文字やアクセント記号など、文字列を比較するための言語独自のルールを指定できます。 照合オプションの構文は次のとおりです。 照合を指定する場合、 照合が指定されていなくても、コレクションにデフォルトの照合が設定されている場合( コレクションにも操作にも照合が指定されていない場合、MongoDB では以前のバージョンで使用されていた単純なバイナリ比較によって文字列が比較されます。 1 つの操作に複数の照合は指定できません。たとえば、フィールドごとに異なる照合を指定できません。また、ソートと検索を一度に実行する場合、検索とソートで別の照合を使用できません。 | ||||||||||||||||||
| 配列 | 任意。配列フィールドの更新操作でどの配列要素を変更するかを決定するフィルター ドキュメントの配列。 アップデート ドキュメントでは、
アップデート ドキュメントには同じ識別子を複数回含めることができます。ただし、アップデート ドキュメント内の個別の識別子 ( ただし、次の例のように、単一のフィルター ドキュメント内の同じ識別子に複合条件を指定できます。 For examples, see Array Update Operations with
|
動作
パフォーマンス
再試行可能な書き込みでは、更新を実行する前に、findOneAndUpdate() メソッドを使用してドキュメント全体をレプリカセット内の各ノードの特別なサイドコレクションにコピーする必要があります。そのため、大きな文書や大きなレプリカセットを扱う場合、findOneAndUpdate() はコストが大きい操作になる可能性があります。
バージョン 8.0 の新機能:
ユーザー定義の順序付けで最初のドキュメントをより優れたパフォーマンスで更新するため、sort オプションを指定した db.collection.updateOne() メソッドを使用します。
ドキュメント一致
findOneAndUpdate() filterに一致するコレクション内の最初に一致するドキュメントを更新します。filterに一致するドキュメントがない場合、ドキュメントは更新されません。
sortパラメータを使用して、どのドキュメントを更新するかを制御することができます。
プロジェクション
重要
言語の整合性
find()とfindAndModify()のプロジェクションを集計の$projectステージと一貫性を持たせるために、
find()とfindAndModify()のプロジェクションは集計式と構文 を受け付けます。MongoDB は、プロジェクションに関して追加の制限を適用します。詳細については、「プロジェクションの制限」を参照してください。
projectionパラメータは次の形式のドキュメントを取ります。
{ field1 : <value>, field2 : <value> ... }
プロジェクション | 説明 |
|---|---|
| フィールドを包含することを指定します。プロジェクションの値としてゼロ以外の整数を指定した場合、その値を |
| フィールドの除外を指定します。 |
| |
| 配列プロジェクション 演算子( ビューには使用できません。 |
| プロジェクションを行ったフィールドの値を指定します。 集計式と構文の使用(リテラルと集計変数の使用を含む)では、新しいフィールドをプロジェクションしたり、既存のフィールドを新しい値でプロジェクションしたりできます。
|
埋め込みフィールド仕様
埋め込みドキュメント内のフィールドの場合は、次のいずれかを使用してフィールドを指定できます。
ドット表記の場合は次のようになります。
"field.nestedfield": <value>ネストされた形式の例
{ field: { nestedfield: <value> } }
_id フィールドプロジェクション
_id フィールドは、プロジェクションで _id: 0 を明示的に指定して抑制しない限り、返されるドキュメントにデフォルトで含まれます。
包含または除外
projection には、 _id フィールドを除いて、包含指定と除外指定の両方を含めることはできません。
フィールドを明示的に含めるプロジェクションでは、
_idフィールドだけが明示的に除外できる唯一のフィールドです。フィールドを明示的に除外するプロジェクションでは、
_idフィールドが明示的に包含できる唯一のフィールドですが、デフォルトで_idフィールドが含まれます。
シャーディングされたコレクション
シャーディングされたコレクション内のドキュメントには、シャードキー フィールドがない場合があります。シャードキーがないドキュメントを対象とするには、null の等価一致を別のフィルター条件(_id フィールド上と同様に)と組み合わせて使用できます。例:
{ _id: <value>, <shardkeyfield>: null } // _id of the document missing shard key
シャードキーの変更
シャードキー フィールドが不変の _id フィールドでない限り、ドキュメントのシャードキー値を更新できます。
警告
シャーディングされたコレクション内のドキュメントには、シャード キー フィールドがないことがあります。ドキュメントのシャード キーの値を変更するときに、誤ってシャード キーを削除しないように注意してください。
findOneAndUpdate()で既存のシャードキー値を変更するには:
必ず
mongos上で使用します。シャードに直接操作を実行しないでください。トランザクション内で、または 再試行可能な書き込みとして実行する必要があります。
完全なシャードキーに等価フィルターを含める必要があります。
欠落しているシャードキー
シャーディングされたコレクション内のドキュメントには、シャードキー フィールドがない場合があります。findOneAndUpdate()を使用してドキュメントにないシャードキーを設定するには、
必ず
mongos上で使用します。シャードに直接操作を実行しないでください。新しいシャードキー値が
nullでない場合は、トランザクション内で実行するか、再試行可能な書き込みとして実行する必要があります。完全なシャードキーに等価フィルターを含める必要があります。
Tip
欠落しているキー値は NULL 等価一致の一部として返されるため、NULL 値のキーが更新されないように、必要に応じて追加のクエリ条件(_id フィールドなど)を追加します。
トランザクション
findOneAndUpdate() は分散トランザクション内で使用できます。
重要
ほとんどの場合、分散トランザクションでは 1 つのドキュメントの書き込み (write) よりもパフォーマンス コストが高くなります。分散トランザクションの可用性は、効果的なスキーマ設計の代わりにはなりません。多くのシナリオにおいて、非正規化されたデータモデル(埋め込みドキュメントと配列)が引き続きデータやユースケースに最適です。つまり、多くのシナリオにおいて、データを適切にモデリングすることで、分散トランザクションの必要性を最小限に抑えることができます。
トランザクションの使用に関するその他の考慮事項(ランタイム制限や oplog サイズ制限など)については、「本番環境での考慮事項」も参照してください。
トランザクション内のアップサート
トランザクションがクロスシャード間書き込みトランザクション(write transaction)でない場合に、分散トランザクション内にコレクションとインデックスを作成できます。
findOneAndUpdate() upsert: true は、既存のコレクションまたは存在しないコレクションで実行できます。存在しないコレクションで実行すると、操作によってコレクションが作成されます。
書込み保証とトランザクション
トランザクションで実行される場合、操作の書込み保証 (write concern)を明示的に設定しないでください。トランザクションで書込み保証を使用するには、「トランザクション書込み保証」を参照してください。
Oplog エントリ
findOneAndUpdate() 操作によってドキュメントが正常に更新されると、その操作によって oplog(操作ログ)にエントリーが追加されます。操作が失敗した場合、または更新するドキュメントが見つからなかった場合は、その操作によって oplog にエントリーが追加されることはありません。
例
ドキュメントの更新
gradesコレクションには、以下と同様のドキュメントが含まれています。
{ _id: 6305, name : "A. MacDyver", "assignment" : 5, "points" : 24 }, { _id: 6308, name : "B. Batlock", "assignment" : 3, "points" : 22 }, { _id: 6312, name : "M. Tagnum", "assignment" : 5, "points" : 30 }, { _id: 6319, name : "R. Stiles", "assignment" : 2, "points" : 12 }, { _id: 6322, name : "A. MacDyver", "assignment" : 2, "points" : 14 }, { _id: 6234, name : "R. Stiles", "assignment" : 1, "points" : 10 }
次の操作では、name : R. Stilesにある最初のドキュメントを検索し、スコアを5増加させます。
db.grades.findOneAndUpdate( { "name" : "R. Stiles" }, { $inc: { "points" : 5 } } )
この操作は、更新前のオリジナルドキュメントを返します。
{ _id: 6319, name: "R. Stiles", "assignment" : 2, "points" : 12 }
returnNewDocumentが true の場合、操作は代わりに更新されたドキュメントを返します。
ドキュメントのソートと更新
gradesコレクションには、以下と同様のドキュメントが含まれています。
{ _id: 6305, name : "A. MacDyver", "assignment" : 5, "points" : 24 }, { _id: 6308, name : "B. Batlock", "assignment" : 3, "points" : 22 }, { _id: 6312, name : "M. Tagnum", "assignment" : 5, "points" : 30 }, { _id: 6319, name : "R. Stiles", "assignment" : 2, "points" : 12 }, { _id: 6322, name : "A. MacDyver", "assignment" : 2, "points" : 14 }, { _id: 6234, name : "R. Stiles", "assignment" : 1, "points" : 10 }
次の操作では、name : "A. MacDyver"にあるドキュメントを更新します。この操作では、一致するドキュメントを points の昇順でソートし、最もポイントが少ない一致するドキュメントを更新します。
db.grades.findOneAndUpdate( { "name" : "A. MacDyver" }, { $inc : { "points" : 5 } }, { sort : { "points" : 1 } } )
この操作は、更新前のオリジナルドキュメントを返します。
{ _id: 6322, name: "A. MacDyver", "assignment" : 2, "points" : 14 }
返されたドキュメントのプロジェクション
次の操作では、プロジェクションを使用して、返されたドキュメントの _id、points、assignment フィールドのみを表示します。
db.grades.findOneAndUpdate( { "name" : "A. MacDyver" }, { $inc : { "points" : 5 } }, { sort : { "points" : 1 }, projection: { "assignment" : 1, "points" : 1 } } )
この操作は、projectionドキュメントで指定されたフィールドと、プロジェクション ドキュメントで明示的に非表示にされていない(_id: 0)_idフィールドのみを含む元のドキュメントを返します。
{ "_id" : 6322, "assignment" : 2, "points" : 14 }
時間制限のあるドキュメントの更新
次の操作では、更新を完了するために 5 ミリ秒の時間制限が設定されます。
try { db.grades.findOneAndUpdate( { "name" : "A. MacDyver" }, { $inc : { "points" : 5 } }, { sort: { "points" : 1 }, maxTimeMS : 5 }; ); } catch(e){ print(e); }
操作が時間制限を超えた場合、以下が返されます。
Error: findAndModifyFailed failed: { "ok" : 0, "errmsg" : "operation exceeded time limit", "code" : 50 }
アップサートによるドキュメント更新
次の操作では、filterに一致するものがない場合に、upsertフィールドを使用して更新ドキュメントを挿入します。
try { db.grades.findOneAndUpdate( { "name" : "A.B. Abracus" }, { $set: { "name" : "A.B. Abracus", "assignment" : 5}, $inc : { "points" : 5 } }, { sort: { "points" : 1 }, upsert:true, returnNewDocument : true } ); } catch (e){ print(e); }
この操作では、以下を返します。
{ "_id" : ObjectId("5789249f1c49e39a8adc479a"), "name" : "A.B. Abracus", "assignment" : 5, "points" : 5 }
returnNewDocumentが false の場合、返す元のドキュメントがないため、操作ではnullを返します。
照合の指定
照合を指定すると、大文字・小文字やアクセント記号など、文字列を比較するための言語独自のルールを指定できます。
コレクション myCollは、次のドキュメントを含みます。
{ _id: 1, category: "café", status: "A" } { _id: 2, category: "cafe", status: "a" } { _id: 3, category: "cafE", status: "a" }
次の操作には照合オプションが含まれます。
db.myColl.findOneAndUpdate( { category: "cafe" }, { $set: { status: "Updated" } }, { collation: { locale: "fr", strength: 1 } } );
この操作を実行すると次のドキュメントが返されます。
{ "_id" : 1, "category" : "café", "status" : "A" }
次を使用して配列更新操作を行います: arrayFilters
注意
arrayFilters は、集計パイプラインが使用される更新では使用できません。
配列フィールドを更新するときに、どの配列要素を更新するかを決定するためのarrayFiltersを指定できます。
arrayFilters 条件に一致する要素を更新する
注意
arrayFilters は、集計パイプラインが使用される更新では使用できません。
次のドキュメントを使用してコレクション students を作成します。
db.students.insertMany( [ { "_id" : 1, "grades" : [ 95, 92, 90 ] }, { "_id" : 2, "grades" : [ 98, 100, 102 ] }, { "_id" : 3, "grades" : [ 95, 110, 100 ] } ] )
To modify all elements that are greater than or equal to 100 in the grades array, use the filtered positional operator $[<identifier>] with the arrayFilters option in the db.collection.findOneAndUpdate() method:
db.students.findOneAndUpdate( { grades: { $gte: 100 } }, { $set: { "grades.$[element]" : 100 } }, { arrayFilters: [ { "element": { $gte: 100 } } ] } )
この操作により 1 つのドキュメントの grades フィールドが更新され、操作後にコレクションには次のドキュメントが含まれます。
{ "_id" : 1, "grades" : [ 95, 92, 90 ] } { "_id" : 2, "grades" : [ 98, 100, 100 ] } { "_id" : 3, "grades" : [ 95, 110, 100 ] }
ドキュメントの配列の特定の要素を更新する
注意
arrayFilters は、集計パイプラインが使用される更新では使用できません。
次のドキュメントを使用してコレクション students2 を作成します。
db.students2.insertMany( [ { "_id" : 1, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 6 }, { "grade" : 85, "mean" : 90, "std" : 4 }, { "grade" : 85, "mean" : 85, "std" : 6 } ] }, { "_id" : 2, "grades" : [ { "grade" : 90, "mean" : 75, "std" : 6 }, { "grade" : 87, "mean" : 90, "std" : 3 }, { "grade" : 85, "mean" : 85, "std" : 4 } ] } ] )
次の操作では、_id フィールドが 1 に等しいドキュメントを検索し、フィルター処理された位置演算子 $[<identifier>] を arrayFilters とともに使用して、grades 配列内で grade が 85 以上の全要素の mean を変更します。
db.students2.findOneAndUpdate( { _id : 1 }, { $set: { "grades.$[elem].mean" : 100 } }, { arrayFilters: [ { "elem.grade": { $gte: 85 } } ] } )
この操作により 1 つのドキュメントの grades フィールドが更新され、操作後にコレクションには次のドキュメントが含まれます。
{ "_id" : 1, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 6 }, { "grade" : 85, "mean" : 100, "std" : 4 }, { "grade" : 85, "mean" : 100, "std" : 6 } ] } { "_id" : 2, "grades" : [ { "grade" : 90, "mean" : 75, "std" : 6 }, { "grade" : 87, "mean" : 90, "std" : 3 }, { "grade" : 85, "mean" : 85, "std" : 4 } ] }
更新に集計パイプラインを使用
db.collection.findOneAndUpdate() は、更新の集計パイプラインを受け入れることができます。このパイプラインには次のステージが含まれる可能性があります。
$addFieldsおよびそのエイリアス$set$replaceRootおよびそのエイリアス$replaceWith
集計パイプラインを使用すると、現在のフィールド値に基づいて条件付きのアップデートを表現したり、あるフィールドを他のフィールドの値を使用してアップデートするなど、より表現内容の多いアップデート ステートメントが可能になります。
たとえば、次のドキュメントを含むコレクション students2 を作成します。
db.students2.insertMany( [ { "_id" : 1, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 6 }, { "grade" : 85, "mean" : 90, "std" : 4 }, { "grade" : 85, "mean" : 85, "std" : 6 } ] }, { "_id" : 2, "grades" : [ { "grade" : 90, "mean" : 75, "std" : 6 }, { "grade" : 87, "mean" : 90, "std" : 3 }, { "grade" : 85, "mean" : 85, "std" : 4 } ] } ] )
次の操作では、_id フィールドが 1 に等しいドキュメントを検索し、集計パイプラインを使用して grades フィールドから新しいフィールド total を計算します。
db.students2.findOneAndUpdate( { _id : 1 }, [ { $set: { "total" : { $sum: "$grades.grade" } } } ], // The $set stage is an alias for ``$addFields`` stage { returnNewDocument: true } )
この操作では更新されたドキュメントを返します。
{ "_id" : 1, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 6 }, { "grade" : 85, "mean" : 90, "std" : 4 }, { "grade" : 85, "mean" :85, "std" : 6 } ], "total" : 250 }