我想按 _id
对记录进行分组,并通过组合 client_id
值创建一个字符串。
以下是我的文档示例:
{
"_id" : ObjectId("59e955e633d64c81875bfd2f"),
"tag_id" : 1,
"client_id" : "10001"
}
{
"_id" : ObjectId("59e955e633d64c81875bfd30"),
"tag_id" : 1,
"client_id" : "10002"
}
我想要这样的输出:
{
"_id" : 1
"client_id" : "10001,10002"
}
最佳答案
您可以将聚合框架作为“两步”操作来完成。这是先通过 $push
将项目累加到数组中使用 $group
管道,然后使用 $concat
与 $reduce
在最终投影中生成的阵列上:
db.collection.aggregate([
{ "$group": {
"_id": "$tag_id",
"client_id": { "$push": "$client_id" }
}},
{ "$addFields": {
"client_id": {
"$reduce": {
"input": "$client_id",
"initialValue": "",
"in": {
"$cond": {
"if": { "$eq": [ "$$value", "" ] },
"then": "$$this",
"else": {
"$concat": ["$$value", ",", "$$this"]
}
}
}
}
}
}}
])
我们还申请$cond
此处是为了避免在结果中将空字符串与逗号连接起来,因此它看起来更像是一个分隔列表。
仅供引用,存在 JIRA 问题 SERVER-29339这确实要求 $reduce
将作为 accumulator expression 实现允许它直接在 $group
中使用流水线阶段。不太可能很快发生,但理论上它会取代 $push
在上面并使操作成为单个管道阶段。建议语法示例在 JIRA 问题上。
如果你没有 $reduce
(需要 MongoDB 3.4)然后只对游标进行后处理:
db.collection.aggregate([
{ "$group": {
"_id": "$tag_id",
"client_id": { "$push": "$client_id" }
}},
]).map( doc =>
Object.assign(
doc,
{ "client_id": doc.client_id.join(",") }
)
)
这会导致使用 mapReduce
执行此操作的另一种选择如果你真的必须:
db.collection.mapReduce(
function() {
emit(this.tag_id,this.client_id);
},
function(key,values) {
return [].concat.apply([],values.map(v => v.split(","))).join(",");
},
{ "out": { "inline": 1 } }
)
这当然以 _id
和 value
作为键集的特定 mapReduce
形式输出,但它基本上是输出。
我们使用 [].concat.apply([],values.map(...))
因为“reducer”的输出可以是一个“定界字符串”,因为 mapReduce
以增量方式处理大量结果,因此 reducer 的输出可以在另一遍中变成“输入”。因此,我们需要预料到这会发生并相应地对待它。
关于mongodb - 按组连接字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46841825/