MongoDBのアグリゲートで一種のユニオンを実行する方法を知りたいです。コレクション内の次のドキュメントをイメージしてみましょう(構造は例のためです):
{
linkedIn: {
people : [
{
name : 'Fred'
},
{
name : 'Matilda'
}
]
},
Twitter: {
people : [
{
name : 'Hanna'
},
{
name : 'Walter'
}
]
}
}
TwitterとLinkedInの人々の和集合を返す集計を作成するにはどうすればよいですか?
{
{ name :'Fred', source : 'LinkedIn'},
{ name :'Matilda', source : 'LinkedIn'},
{ name :'Hanna', source : 'Twitter'},
{ name :'Walter', source : 'Twitter'},
}
これには、 aggregate メソッドを使用できるいくつかのアプローチがあります。
db.collection.aggregate([
// Assign an array of constants to each document
{ "$project": {
"linkedIn": 1,
"Twitter": 1,
"source": { "$cond": [1, ["linkedIn", "Twitter"],0 ] }
}},
// Unwind the array
{ "$unwind": "$source" },
// Conditionally Push the fields based on the matching constant
{ "$group": {
"_id": "$_id",
"data": { "$Push": {
"$cond": [
{ "$eq": [ "$source", "linkedIn" ] },
{ "source": "$source", "people": "$linkedIn.people" },
{ "source": "$source", "people": "$Twitter.people" }
]
}}
}},
// Unwind that array
{ "$unwind": "$data" },
// Unwind the underlying people array
{ "$unwind": "$data.people" },
// Project the required fields
{ "$project": {
"_id": 0,
"name": "$data.people.name",
"source": "$data.source"
}}
])
または、MongoDB 2.6のいくつかの演算子を使用した別のアプローチでは、次のようになります。
db.people.aggregate([
// Unwind the "linkedIn" people
{ "$unwind": "$linkedIn.people" },
// Tag their source and re-group the array
{ "$group": {
"_id": "$_id",
"linkedIn": { "$Push": {
"name": "$linkedIn.people.name",
"source": { "$literal": "linkedIn" }
}},
"Twitter": { "$first": "$Twitter" }
}},
// Unwind the "Twitter" people
{ "$unwind": "$Twitter.people" },
// Tag their source and re-group the array
{ "$group": {
"_id": "$_id",
"linkedIn": { "$first": "$linkedIn" },
"Twitter": { "$Push": {
"name": "$Twitter.people.name",
"source": { "$literal": "Twitter" }
}}
}},
// Merge the sets with "$setUnion"
{ "$project": {
"data": { "$setUnion": [ "$Twitter", "$linkedIn" ] }
}},
// Unwind the union array
{ "$unwind": "$data" },
// Project the fields
{ "$project": {
"_id": 0,
"name": "$data.name",
"source": "$data.source"
}}
])
そしてもちろん、ソースが何であるかを単に気にしない場合:
db.collection.aggregate([
// Union the two arrays
{ "$project": {
"data": { "$setUnion": [
"$linkedIn.people",
"$Twitter.people"
]}
}},
// Unwind the union array
{ "$unwind": "$data" },
// Project the fields
{ "$project": {
"_id": 0,
"name": "$data.name",
}}
])
その種の操作にはmap-reduceよりもaggregateの使用が推奨されるかどうかはわかりませんが、以下はあなたが求めていることを実行しています(.aggregate()関数で$ constをまったく問題なく使用できるかどうかはわかりません):
aggregate([
{ $project: { linkedIn: '$linkedIn', Twitter: '$Twitter', idx: { $const: [0,1] }}},
{ $unwind: '$idx' },
{ $group: { _id : '$_id', data: { $Push: { $cond:[ {$eq:['$idx', 0]}, { source: {$const: 'LinkedIn'}, people: '$linkedIn.people' } , { source: {$const: 'Twitter'}, people: '$Twitter.people' } ] }}}},
{ $unwind: '$data'},
{ $unwind: '$data.people'},
{ $project: { _id: 0, name: '$data.people.name', source: '$data.source' }}
])