按组连接字符串

shi*_*wan 9 group-by concat mongodb aggregation-framework

我想对记录进行分组并_id通过组合client_id值来创建一个字符串。

以下是我的文档示例:

{
  "_id" : ObjectId("59e955e633d64c81875bfd2f"),
  "tag_id" : 1,
  "client_id" : "10001"
}
{
  "_id" : ObjectId("59e955e633d64c81875bfd30"),
  "tag_id" : 1,
  "client_id" : "10002"
}
Run Code Online (Sandbox Code Playgroud)

我想要这个输出:

{
  "_id" : 1
  "client_id" : "10001,10002"
}
Run Code Online (Sandbox Code Playgroud)

Nei*_*unn 5

您可以使用聚合框架作为“两步”操作来完成此操作。首先通过$pushwith$group管道将项目累积到数组中,然后在最终投影中对生成的数组使用$concatwith :$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"]
            }
          }
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

我们$cond在这里还应用以避免在结果中将空字符串与逗号连接起来,因此它看起来更像是一个分隔列表。

仅供参考,有一个 JIRA 问题SERVER-29339确实要求$reduce将其实现为累加器表达式,以允许它直接在$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(",") }
  )
)
Run Code Online (Sandbox Code Playgroud)

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 } }
)
Run Code Online (Sandbox Code Playgroud)

当然,它以和作为键集的特定mapReduce形式输出,但它基本上是输出。_idvalue

我们[].concat.apply([],values.map(...))之所以使用“reducer”,是因为“reducer”的输出可以是“定界字符串”,因为mapReduce以增量方式处理大结果,因此“reducer”的输出可以成为另一遍的“输入”。因此,我们需要预料到这种情况可能会发生,并相应地对待它。