MongoDB 将字符串数组聚合连接到单个字符串

MongoDB Aggregation join array of strings to single string

我们正在尝试 'join' 将字符串数组 'join' 聚合为单个字符串。

给定以下数据集:

集合 1:

{
  id: 1234,
  field: 'test'
}

集合 2:

{
  id: 1111,
  collection1_id: 1234,
  name: 'Max'
},
{
  id: 1112,
  collection1_id: 1234,
  name: 'Andy'
}

当前结果(经过查找等):

{
  id: 1234,
  field: 'test',
  collection2: ['Max', 'Andy'] 
}

想要的结果:

{
  id: 1234,
  field: 'test',
  collection2: 'Max, Andy'
}

是否可以通过某种方式将 'collection2' 连接到单个字符串?我们试过 $concat 但它只接受字符串。

要展平这个数组,您需要将进程转移到客户端。

mongo 将在新版本中提供一些新的扁平化选项,但据我所知,它将是算术选项(平均、最小、最大....)。

你走在正确的轨道上。

只需在您的 $project 阶段添加 $reduce over $concat

'collection2': {
    '$reduce': {
        'input': '$collection2',
        'initialValue': '',
        'in': {
            '$concat': [
                '$$value',
                {'$cond': [{'$eq': ['$$value', '']}, '', ', ']}, 
                '$$this']
        }
    }
}

注意:我们使用 $cond 来防止串联中出现前导 ,。 您也可以在 $reduce 之前使用 $substrCP 作为 $cond.

的替代方法

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

// { "collectionId" : 1234, "name" : "Max"  }
// { "collectionId" : 876,  "name" : "Rob"  }
// { "collectionId" : 1234, "name" : "Andy" }
db.collection.aggregate([
  { $group: {
    _id: "$collectionId",
    names: {
      $accumulator: {
        accumulateArgs: ["$name"],
        init: function() { return [] },
        accumulate: function(names, name) { return names.concat(name) },
        merge: function(names1, names2) { return names1.concat(names2) },
        finalize: function(names) { return names.join(",") },
        lang: "js"
      }
    }
  }}
])
// { "_id" : 876,  "names" : "Rob"      }
// { "_id" : 1234, "names" : "Max,Andy" }

累加器:

  • 场上累积 name (accumulateArgs)
  • 初始化为空数组(init)
  • 通过将新名称连接到已出现的名称(accumulatemerge)来累积
  • 最后将所有名称连接成一个字符串 (finalize)

有时最容易使用JavaScript:

db.getCollection('Collection1').aggregate([
{
   $lookup:
     {
       from: 'Collection2',
       localField: 'id',
       foreignField: 'collection1_id',
       as: 'col2'
     }
}]).map((e) => ({
  id: e.id,
  field: e.field,
 collection2: Array.isArray(e.col2) &&
   e.col2.reduce((arr, el) => {
     arr.push(el.name);
    return arr;
  }, []).join(', ')
}))