【问题标题】:mongodb grouping by multiple fields and counting each groupmongodb按多个字段分组并计算每个组
【发布时间】:2021-07-19 18:35:03
【问题描述】:

假设我们有一个包含以下字段的用户集合:

  • 年龄:可以是“年轻”或“老”
  • company_position:可以是“程序员”、“技术员”或“QA”
  • likes_cats: 布尔值
  • favourite_sport:可以是“网球”、“足球”或“无”

我想统计有多少老用户 |年轻,喜欢猫,喜欢网球或足球每个公司职位

我几乎得到了解决方案:

db.testing.aggregate([

    {
        $group: {
            _id: {
                age: '$age',
                company_position: '$company_position',
                likes_cats: '$likes_cats',
                favourite_sport: '$favourite_sport',
            },
            total: { $sum: 1 }
        }
    },
    
    {
        $group: {
            _id: '$_id.company_position',
            ages: {
                $push: {
                    age: '$_id.age',
                    total: '$total',
                }
            },
            likes_cats: {
                $push: {
                    age: '$_id.likes_cats',
                    total: '$total',
                }
            },
            favourite_sport: {
                $push: {
                    age: '$_id.favourite_sport',
                    total: '$total',
                }
            },   
        }
    }
])

此解决方案的问题是我将重复值推送到 ageslikes_catsfavourite_sport 字段中。如何在结果数组中没有重复值的情况下获得这样的结果?

{
    "job" : "technician",
    "ages" : [
        {
            "age" : "old",
            "total" : 3
        },
        {
            "age" : "young",
            "total" : 4
        },
    ],
    "likes_cats" : [
        {
            "like" : true,
            "total" : 3
        },
        {
            "like" : false,
            "total" : 1
        },
    ],
    "favourite_sport" : [
        {
            "age" : "tennis",
            "total" : 3
        },
        {
            "age" : "football",
            "total" : 4
        },      
        {
            "age" : "none",
            "total" : 4
        },
    ],
},

{ job: 'programmer', ... },
{ job: 'QA', ... },
....

我正在使用 Azure Cosmos DB 的 API for MongoDB,所以只有 mongodb 4.0 api is partially supported

这是游乐场的链接:

https://mongoplayground.net/p/wXpe9pqg1GV

【问题讨论】:

    标签: mongodb aggregation-framework


    【解决方案1】:

    在经历了集合、地图、过滤器和减少的地狱和火焰之后,我终于找到了解决方案。我之前差点搞定,但我不想做以下事情:

    
    db.testing.aggregate([
    
        {
            $group: {
                _id: {
                    age: '$age',
                    company_position: '$company_position',
                    likes_cats: '$likes_cats',
                    favourite_sport: '$favourite_sport',
                },
                total: { $sum: 1 }
            }
        },
    
        {
            $group: {
                _id: '$_id.company_position',
                ages_set: { $addToSet: '$_id.age' },
                ages: {
                    $push: {
                        id: '$_id.age',
                        total: { $sum: '$total' },
                    }
                },
                likes_cats_set: { $addToSet: '$_id.likes_cats' },
                likes_cats: {
                    $push: {
                        id: '$_id.likes_cats',
                        total: '$total',
                    }
                },
                favourite_sport_set: { $addToSet: '$_id.favourite_sport' },
                favourite_sport: {
                    $push: {
                        id: '$_id.favourite_sport',
                        total: '$total',
                    }
                },
            }
        },
    
        {
            $project: {
                ages: {
                    $map: {
                        input: '$ages_set',
                        as: 'uniqueId',
                        in: {
                            id: '$$uniqueId',
                            total: {
                                $reduce: {
                                    input: {
                                        $filter: {
                                            input: '$ages',
                                            as: 'filteringValue',
                                            cond: {
                                                $eq: ['$$filteringValue.id', '$$uniqueId']
                                            },
                                        }
                                    },
                                    initialValue: 0,
                                    in: {
                                        $sum: ['$$value', '$$this.total']
                                    }
                                }
                            }
                        }
                    }
                },
                likes_cats: {
                    $map: {
                        input: '$likes_cats_set',
                        as: 'uniqueId',
                        in: {
                            id: '$$uniqueId',
                            total: {
                                $reduce: {
                                    input: {
                                        $filter: {
                                            input: '$likes_cats',
                                            as: 'filteringValue',
                                            cond: {
                                                $eq: ['$$filteringValue.id', '$$uniqueId']
                                            },
                                        }
                                    },
                                    initialValue: 0,
                                    in: {
                                        $sum: ['$$value', '$$this.total']
                                    }
                                }
                            }
                        }
                    }
                },
                favourite_sport: {
                    $map: {
                        input: '$favourite_sport_set',
                        as: 'uniqueId',
                        in: {
                            id: '$$uniqueId',
                            total: {
                                $reduce: {
                                    input: {
                                        $filter: {
                                            input: '$favourite_sport',
                                            as: 'filteringValue',
                                            cond: {
                                                $eq: ['$$filteringValue.id', '$$uniqueId']
                                            },
                                        }
                                    },
                                    initialValue: 0,
                                    in: {
                                        $sum: ['$$value', '$$this.total']
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    ])
    
    

    这行得通,它基本上是在js中应用以下算法:

    const test = [
        {
            id: 1,
            total: 1,
        },
        {
            id: 2,
            total: 2,
        },
        {
            id: 3,
            total: 3,
        },
        {
            id: 2,
            total: 1,
        },
        {
            id: 1,
            total: 4,
        },
    ]
    
    const ids = [1, 2, 3]
    
    const result = ids.map((id) => {
        return {
            id,
            total: test
                .filter(function (value) {
                    if (value.id === id) {
                        return true
                    }
    
                    return false
                })
                .reduce((curr, next) => curr + next.total, 0),
        }
    })
    
    console.log(result)
    
    

    我不是这个解决方案的忠实拥护者,因为存在很大的错误空间。但它按预期工作。

    如果有人能提出更好的解决方案,我很乐意接受当前的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-20
      • 2014-11-05
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多