次の例では、"Algorithms in C++"
は2回存在します。
$unset
修飾子は特定のフィールドを削除できますが、フィールドからエントリを削除するにはどうすればよいですか?
{
"_id" : ObjectId("4f6cd3c47156522f4f45b26f"),
"favorites" : {
"books" : [
"Algorithms in C++",
"The Art of Computer Programming",
"Graph Theory",
"Algorithms in C++"
]
},
"name" : "robert"
}
あなたがしなければならないことは、重複するタグを検出してカウントするためにmap reduceを使用することです..次に、_$set
_を使用して{ "_id" : ObjectId("4f6cd3c47156522f4f45b26f"),
に基づいて本全体を置き換えます
これはここで何度も議論されています..ご覧ください
mongodbのインデックス付き列で重複を見つける高速な方法
http://csanz.posterous.com/look-for-duplicates-using-mongodb-mapreduce
MongoDB 2.2以降、これを実現するために$unwind
、$group
、および$project
ステージで 集約フレームワーク を使用できます。
db.users.aggregate([{$unwind: '$favorites.books'},
{$group: {_id: '$_id',
books: {$addToSet: '$favorites.books'},
name: {$first: '$name'}}},
{$project: {'favorites.books': '$books', name: '$name'}}
])
$project
集計フィールドはネストできないため、$group
でfavorites
フィールドの名前を変更する必要があることに注意してください。
最も簡単な解決策は、 setUnion (Mongo 2.6+)を使用することです。
db.users.aggregate([
{'$addFields': {'favorites.books': {'$setUnion': ['$favorites.books', []]}}}
])
@kynanの answer のアイデアに基づいているが、他のすべてのフィールドを明示的に指定せずに保持する別の(より長い)バージョン(Mongo 3.4+):
> db.users.aggregate([
{'$unwind': {
'path': '$favorites.books',
// output the document even if its list of books is empty
'preserveNullAndEmptyArrays': true
}},
{'$group': {
'_id': '$_id',
'books': {'$addToSet': '$favorites.books'},
// arbitrary name that doesn't exist on any document
'_other_fields': {'$first': '$$ROOT'},
}},
{
// the field, in the resulting document, has the value from the last document merged for the field. (c) docs
// so the new deduped array value will be used
'$replaceRoot': {'newRoot': {'$mergeObjects': ['$_other_fields', "$$ROOT"]}}
},
// this stage wouldn't be necessary if the field wasn't nested
{'$addFields': {'favorites.books': '$books'}},
{'$project': {'_other_fields': 0, 'books': 0}}
])
{ "_id" : ObjectId("4f6cd3c47156522f4f45b26f"), "name" : "robert", "favorites" :
{ "books" : [ "The Art of Computer Programmning", "Graph Theory", "Algorithms in C++" ] } }
Mongo 4.4
以降、 $function
集計演算子を使用すると、カスタムjavascript関数を適用して、MongoDBクエリ言語でサポートされていない動作を実装できます。
たとえば、配列から重複を削除するには、次のようにします。
// {
// "favorites" : { "books" : [
// "Algorithms in C++",
// "The Art of Computer Programming",
// "Graph Theory",
// "Algorithms in C++"
// ]},
// "name" : "robert"
// }
db.collection.aggregate(
{ $set:
{ "favorites.books":
{ $function: {
body: function(books) { return books.filter((v, i, a) => a.indexOf(v) === i) },
args: ["$favorites.books"],
lang: "js"
}}
}
}
)
// {
// "favorites" : { "books" : [
// "Algorithms in C++",
// "The Art of Computer Programming",
// "Graph Theory"
// ]},
// "name" : "robert"
// }
これには次の利点があります。
$unwind
ステージと$group
ステージの組み合わせよりも効率的です。$function
は3つのパラメータを取ります:
body
。これは適用する関数であり、そのパラメーターは変更する配列です。args
。これには、body
関数がパラメーターとして受け取るレコードのフィールドが含まれます。私たちの場合は"$favorites.books"
です。lang
は、body
関数が記述されている言語です。現在、js
のみが利用可能です。