按组分类的Concat字符串

时间:2017-10-20 03:06:01

标签: mongodb aggregation-framework concat

在我的查询中,我希望通过_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"

2 个答案:

答案 0 :(得分:2)

您可以使用聚合框架作为“两步”操作。这是首先通过$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 } }
)

当然,mapReduce_id的特定value形式的输出作为键组,但它基本上是输出。

我们使用[].concat.apply([],values.map(...)),因为“reducer”的输出可以是“分隔字符串”,因为mapReduce以大结果递增工作,因此reducer的输出可以在另一个传递中变为“输入” 。因此,我们需要预期会发生这种情况并相应地对待它。

答案 1 :(得分:0)

Mongo 4.4开始,$group阶段有了一个新的聚合运算符$accumulator,允许在文件分组时进行自定义累积:

// { "tag_id" : 1, "client_id" : "10001" }
// { "tag_id" : 1, "client_id" : "10002" }
// { "tag_id" : 2, "client_id" : "9999"  }
db.collection.aggregate([
  { $group: {
    _id: "$tag_id",
    client_id: {
      $accumulator: {
        accumulateArgs: ["$client_id"],
        init: function() { return [] },
        accumulate: function(ids, id) { return ids.concat(id) },
        merge: function(ids1, ids2) { return ids1.concat(ids2) },
        finalize: function(ids) { return ids.join(",") },
        lang: "js"
      }
    }
  }}
])
// { "_id" : 2, "client_id" : "9999" }
// { "_id" : 1, "client_id" : "10001,10002" }

累加器:

  • 累积在client_idaccumulateArgs)字段上
  • 被初始化为空数组(init
  • 通过将新的ID与已经看到的ID串联为新的ID(accumulatemerge)来积累
  • 最后将所有ID作为字符串(finalize