[NeDBを使い方]データベースのドキュメントをアップデートしよう。
NeDBはJavaScriptで作られたデータベースです。 Node.js、nw.js、エレクトロン、ブラウザ等で動かすことができます。 Github NeDBはドキュメント型データベースでJSONをそのまま保存できます。 そのため、JavaScriptのプログラムと親和性が高く、データの取得、利用が簡単にできます。 また、NeDBはMongoDBと互換性があり、一つのデータベースはMongoDBのコレクションと対応しています。 対応は下記の通りです。 – データベース→コレクション – データロウ→ドキュメント 今回は、NeDBデータベースにデータをアップデートする方法について書いていきます。
テスト用のデータベース
本記事で使うカウント対象のデータベースとドキュメントをインサートして準備します。 記事内で検索条件指定で得られたドキュメントを説明のために、_idフィールドの値を使います。 例:〇〇という検索条件ため、結果はid1,id2,id3のドキュメントの配列になります。export function getTestDatabase() { let db = new Datastore(); db.insert([ { _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false }, { _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true }, { _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false }, { _id: 'id4', planet: 'Omicron Persia 8', system: 'futurama', inhabited: true }, { _id: 'id6', fruits: ['apple', 'orange', 'pear'] } { _id: 'id7', name: 'Name', value: 5 } ], function (err, newDoc) { console.log('err', err); console.log('newDoc', newDoc); }) return db }
ドキュメントの基本的なアップデート
ドキュメントをアップデートするにはDatastore#update(query, update, options, callback)を使う必要があります。 Datastore#updateはコレクション内の全てのドキュメントからクエリに適合したドキュメントを更新します。ドキュメントの_idフィールドをアップデートすることはできません。 Datastore#updateは下記の引数を持ちます。- query:アップデートの対象ドキュメントの条件を設定します。Datastor#findやDatastor#findOneと同様の構文を使用することができます。
- update:ドキュメントのアップデート内容を指定します。アップデートは「ドキュメント全体をアップデートするドキュメントリプレース」と「ドキュメントのフィールドを指定してアップデートするフィールド修飾子アップデート」のどちらかを指定します。ドキュメントリプレースの場合、クエリを満たすドキュメントがリプレースされます。また、フィールド修飾子アップデートの場合、クエリを満たすドキュメントのフィールドをフィールド修飾子の意図する方法でアップデートします。フィールドが存在しない時はフィールドを作成してアップデートします。有効なフィールド修飾子は下記の通りです。
- $set:フィールドの値を変更します。
- $unset:フィールドを削除します。
- $inc:フィールドの値をインクリメントします。
- options:アップデート時のオプションを設定できます。下記の3つのフラグがあります。
- multi:trueの場合、フィールド修飾子アップデートが有効になり、ドキュメントのフィールドを更新します。falseの場合、ドキュメントリプレースが有効になります。デフォルトはfalseが有効になっています。
- upsert:trueの場合、クエリに適合するドキュメントがない時に新しくドキュメントをインサートします。デフォルトはfalseになっています。 フィールド修飾子を使用しない場合はドキュメントがインサートされます。 フィールド修飾子を使用する場合はフィールド修飾子のクエリからフィールド修飾子が全て除去されたドキュメントがインサートされます。
returnUpdatedDocs:tureかつupsertでない場合、索結果としてアップデートの対象になったドキュメントがアップデートされたドキュメント配列が検返却されるようになります。また、アップデートされたドキュメント配列は実際にアップデートされたかどうかは保証しません。デフォルトはfalseで、MongoDB-compatible互換ではありません。
callback:アップデート処理のコールバックを設定します。コールバック関数の引数は下記の通りです。
- err:エラー情報
- numAffected:変更ドキュメント数
- affectedDocuments:変更ドキュメント
- upsert:アップサートされたかどうか
また、上記の引数に設定される値は場合によって異なるので注意が必要です。 – アップサートの場合、affectedDocumentsはインサートされたドキュメントを含む配列となり、upsertはtrueがセットされています。 – アップデートかつreturnUpdatedDocsフラグがfalseの場合,affectedDocumentsはセットされません。 – アップデートかつreturnUpdatedDocsフラグがtrueかつmultiがfalseの場合,affectedDocumentsはアップデートされたドキュメントがセットされます。 – アップデートかつreturnUpdatedDocsフラグがtrueかつmultiがtrueの場合,affectedDocumentsはアップデートされたドキュメントの配列がセットされます。 *このAPIはv1.7.4とv1.8の間で変更されています。どう変わったかはログを参照してください。
export function updateData() { let db = getTestDatabase() // ドキュメントを別のドキュメントにリプレースします。 db.update({ planet: 'Jupiter' }, { planet: 'Pluton'}, {}, function (err, numReplaced) { // クエリに合うid3のドキュメントだけをリプレースします。numReplacedは1がセットされています。 // リプレース後のドキュメントは{ _id: 'id3', planet: 'Pluton' }になります。 // systemフィールドとinhabitedフィールドが消えていることに注意してください。また、_idは変更されません。 console.log('err', err); console.log('numReplaced', numReplaced); }); // $set修飾子を使うことでフィールドの値をアップデートします。 db.update({ system: 'solar' }, { $set: { system: 'solar system' } }, { multi: true }, function (err, numReplaced) { // クエリに合うid1,id,2id3のドキュメントのsystemフィールドをアップデートします。 // numReplacedは3がセットされています。 console.log('err', err); console.log('numReplaced', numReplaced); }); // 存在しないフィールドにサブドキュメントを設定し、サブドキュメント内の値をドット記法で設定します。 db.update({ planet: 'Mars' }, { $set: { 'data.satellites': 2, 'data.red': true } }, {}, function () { // id1のドキュメントはアップデート後下記のようになります。 // { _id: 'id1', system: 'solar', inhabited: false, data: { satellites: 2, red: true }} // サブドキュメントのフィールドを変更する場合は必ずドット記法を使用しないといけません。 // オブジェクト記法を使った場合、単純なサブドキュメントのリプレースになり、もともとのサブドキュメントが持っていたフィールドと値が失われます。 db.update({ planet: 'Mars' }, { $set: { data: { satellites: 3 } } }, {}, function () { // id1のドキュメントはアップデート後下記のようになります。 // { _id: 'id1', system: 'solar', inhabited: false, data: { satellites: 3 }} // dataフィールドのサブドキュメントをリプレースしたため、data.redフィールドの値が失われています。 }); }); // フィールドの削除を行います。 db.update({ planet: 'Mars' }, { $unset: { planet: true } }, {}, function () { // id1のドキュメントのplanetフィールドは削除されます。 // また、ドット記法を使うことでネストされたフィールドに対しても削除を行うことができます。 }); // アップサートオプションを有効にすることでドキュメントのアップサート機能を有効にします。 db.update({ planet: 'Pluton' }, { planet: 'Pluton', inhabited: false }, { upsert: true }, function (err, numReplaced, upsert) { // クエリに合うドキュメントが無いため、ドキュメント{ _id: 'id5', planet: 'Pluton', inhabited: false }がインサートをされます。 // numReplacedは1になり,upsertは{ _id: 'id5', planet: 'Pluton', inhabited: false }となります。 console.log('err', err); console.log('numReplaced', numReplaced); console.log('upsert', upsert); }); // アップサート機能を有効にした時にフィールド修飾子を使うと、フィールド修飾子を除去した状態のドキュメントがインサートされます。。 db.update({ planet: 'Pluton' }, { $inc: { distance: 38 } }, { upsert: true }, function (err, numReplaced, upsert) { // クエリに合うドキュメントが無いため、ドキュメント{ _id: 'id5', planet: 'Pluton', distance: 38 }がインサートされます。 // numReplacedは1になり,upsertは { _id: 'id5', planet: 'Pluton', distance: 38 }となります。 console.log('err', err); console.log('numReplaced', numReplaced); console.log('upsert', upsert); }); }
ドキュメントのアレイフィールドに対するアップデート
フィールド修飾子には配列に適用できる特別な修飾子があります。 使用できるフィールド修飾子は下記の通りです、- $push:配列の末尾に値を追加します。
- $pop:配列の先頭(-1)または、末尾(1)から値を取り出します。
- $addToSet:配列の一意性を保って値を追加します。すでに同じ値が含まれる場合はドキュメントは変更されません。
- $pull:配列から「指定した値」、または「クエリを満たす値」を削除します。
- $each:$pushと$addToSetに複数の値を渡すときに使います。
- $slice:$pushと$eachによってできる結果の配列のサイズを制限することができます。0,正数,負数を指定することができます。
- 0:空の配列になります。
- 正数:先頭からn番目までの配列になります。
- 負数:末尾からn番目までの配列になります。
export function updateArray() { let db = getTestDatabase() // $pushを使って配列の末尾に新しい要素を追加します。 db.update({ _id: 'id6' }, { $push: { fruits: 'banana' } }, {}, function () { // アップデート後はid6は['apple', 'orange', 'pear', 'banana']になります。 }); // $popは配列から要素を削除します。1を指定すると末尾から、-1を指定すると先頭から削除します。 db.update({ _id: 'id6' }, { $pop: { fruits: 1 } }, {}, function () { // 1を指定しているので末尾を削除されます。フルーツは['apple', 'orange']となります。 // { $pop: { fruits: -1 } }のように$pop修飾子に-1を指定した場合は先頭が削除され、フルーツは['orange', 'pear']となります。 }); // $addToSetは配列の中に重複する値がない場合に配列に要素を追加します。 // 等価性の確認はディープチェックになります。(例:$addToSetはドキュメントを追加する場合も配列内に同じ内容のドキュメントがある場合も追加は行いません。) // しかし、既に配列内で重複が発生しているかどうかについてはチェックはしません。 db.update({ _id: 'id6' }, { $addToSet: { fruits: 'apple' } }, {}, function () { // フルーツフィールドは変更されません。 // 配列に含まれない物('banana'等)を指定すると追加されます。 }); // $pullは配列の中から一致する値またはクエリを全て削除します。 db.update({ _id: 'id6' }, { $pull: { fruits: 'apple' } }, {}, function () { // アップデートしたあとはのフルーツの配列は['orange', 'pear']となります。 }); // クエリに適合した値を削除します。$inを使うと配列に含まれる値を全て削除します。 // db.update({ _id: 'id6' }, { $pull : { fruits: $in : ['apple', 'pear'] } }, {}, function () { // // アップデートしたあとはのフルーツの配列は['orange']となります。 // }); // $eachは$pushや$addToSetを複数の値を適用します。 db.update({ _id: 'id6' }, { $push: { fruits: { $each: ['banana', 'orange'] } } }, {}, function () { // アップデート後のフルーツフィールドの配列は['apple', 'orange', 'pear', 'banana', 'orange']となります。 }); // $sliceは$pushと$eachによってできる結果の配列のサイズを制限することができます。0,正数,負数を指定することができます。 // - 0:空の配列になります。 // - 正数:先頭からn番目までの配列になります。 // - 負数:末尾からn番目までの配列になります。 db.update({ _id: 'id6' }, { $push: { fruits: { $each: ['banana'], $slice: 2 } } }, {}, function () { // アップデート後のフルーツフィールドの配列は['apple', 'orange']となります。 }); }
$min/$max
$minと$maxは与えられた値が条件にあった場合だけ値を更新します。 正直queryとの使い分けがわからない。- $min:フィールドの現在値が指定した値より小さい場合はアップデートします。
- $max:フィールドの現在値が指定した値より大きい場合はアップデートします。
export function updateMaxMin() { let db = getTestDatabase() // 現在の値と指定した値を比較してアップデートするかを判定します。$minなので小さいかどうかを判定します。 db.update({ _id: 'id7' }, { $min: { value: 2 } }, {}, function () { // ドキュメントは{ _id: 'id', name: 'Name', value: 2 }にアップデートされます。 }); db.update({ _id: 'id7' }, { $min: { value: 8 } }, {}, function () { // ドキュメントはアップデートされません。 }); }