Mongoose在所有文档中获取数组字段中的项目数

时间:2018-04-19 04:21:51

标签: mongodb mongoose

使用aggregate或map / reduce的新功能。我希望所有文档中名为“tags”的数组字段中的项目数。我正在使用猫鼬。谢谢!

示例:

doc 1

tags: ['A', 'B', 'C', 'D']

doc 2

tags: ['A', 'B']

doc 3

tags: ['D']

结果:{A: 2, B: 2, C: 1, D: 2}

还要计算总数组项数,以便计算百分比(例如,A = 2/7)

1 个答案:

答案 0 :(得分:1)

尽我所能,我可以做到这样的事情:

db.getCollection('tags').aggregate([
  { "$unwind": "$tags" },
  { "$group": {
    "_id": "$tags",
    "count": { "$sum": 1 }  
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
    "_id": null,
    "tags": { "$push": { "k": "$_id", "v": "$count" } }  
  }},
  { "$replaceRoot": {
    "newRoot": { 
      "$mergeObjects": [ 
        { "$arrayToObject": "$tags" },
        { "total": { "$sum": "$tags.v" } }
      ]
    }
  }}
])

如果你没有MongoDB 3.6功能,你可以简单地转换输出:

db.getCollection('tags').aggregate([
  { "$unwind": "$tags" },
  { "$group": {
    "_id": "$tags",
    "count": { "$sum": 1 }  
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
    "_id": null,
    "tags": { "$push": { "k": "$_id", "v": "$count" } }  
  }}
]).map( d => 
  Object.assign(
    d.tags.reduce((acc,curr) => Object.assign(acc, { [curr.k]: curr.v }), {}),
    { total: d.tags.reduce((acc,curr) => acc + curr.v, 0) }
  )
)

无论哪种方式都会产生:

{
    "A" : 2.0,
    "B" : 2.0,
    "C" : 1.0,
    "D" : 2.0,
    "total" : 7.0
}

如果你想要"百分比"在那里,您可以发布处理输出,因为所有数字已经存在,或者只是改变处理。

对于MongoDB 3.6

db.getCollection('tags').aggregate([
  { "$unwind": "$tags" },
  { "$group": {
    "_id": "$tags",
    "count": { "$sum": 1 }  
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
    "_id": null,
    "tags": { "$push": { "k": "$_id", "v": "$count" } } ,
    "total": { "$sum": "$count" }
  }},
  { "$replaceRoot": {
    "newRoot": { 
      "$mergeObjects": [ 
        { "$arrayToObject": {
          "$map": {
            "input": "$tags",
            "in": {
              "k": "$$this.k",
              "v": {
                "count": "$$this.v",
                "perc": { "$multiply": [{ "$divide": [ "$$this.v", "$total" ] }, 100 ] }   
              }
            }   
          }
        }},
        { "total": "$total" }
      ]
    }
  }}
])

或更低版本,仍在处理输出:

db.getCollection('tags').aggregate([
  { "$unwind": "$tags" },
  { "$group": {
    "_id": "$tags",
    "count": { "$sum": 1 }  
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
    "_id": null,
    "tags": { "$push": { "k": "$_id", "v": "$count" } },
    "total": { "$sum": "$count" } 
  }}
]).map( d =>
  Object.assign(
    d.tags.reduce((acc,curr) => 
      Object.assign(acc, { [curr.k]: { count: curr.v, perc: curr.v / d.total  * 100 } }),
      {}
    ),
    { total: d.total }
  )
)

输出包括每个分组键上的两个键:

{
    "A" : {
        "count" : 2.0,
        "perc" : 28.5714285714286
    },
    "B" : {
        "count" : 2.0,
        "perc" : 28.5714285714286
    },
    "C" : {
        "count" : 1.0,
        "perc" : 14.2857142857143
    },
    "D" : {
        "count" : 2.0,
        "perc" : 28.5714285714286
    },
    "total" : 7.0
}

请注意,MongoDB shell对现代JavaScript功能有一点限制。在现代JavaScript环境中,例如最近发布的Nodejs,您可以将转换缩短一点:

result.map(({ _id, tags, ...d }) =>
  ({
    ..tags.reduce((acc,curr) => 
      ({ ...acc, [curr.k]: { count: curr.v, perc: curr.v / d.total  * 100 }),
      {}
    ),
    ...d
  })
)