You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB聚合计算餐厅支付极值两国人数占比方案咨询

MongoDB餐厅消费统计比值计算方案

db format

people集合数据格式示例

{
  _id: ObjectId("576865c1bab6cf2f2fb39d8f"),
  firstName: 'Zofia',
  lastName: 'Wojciechowska',
  sex: 'female',
  address: {
    country: 'Poland',
    city: 'Warsaw',
    postalCode: '05-075',
    street: 'Wilcza'
  },
  birthDate: ISODate("1974-11-18T12:11:00.000Z"),
  payments: [
    { category: 'relax', name: 'holidays', amount: 282.46 },
    { category: 'food', name: 'store', amount: 15.18 },
    { category: 'food', name: 'store', amount: 64.09 },
    { category: 'food', name: 'restaurant', amount: 26.32 },
    { category: 'health', name: 'pharmacy', amount: 53.27 },
    { category: 'food', name: 'restaurant', amount: 43.56 },
    { category: 'health', name: 'doctor', amount: 142.27 },
    { category: 'relax', name: 'holidays', amount: 959.59 },
    { category: 'health', name: 'pharmacy', amount: 61.14 },
    { category: 'health', name: 'doctor', amount: 102.29 }
  ],
  wealth: {
    bankAccounts: [ { bank: 'Pekao SA', balance: 18643.26 } ],
    realEstates: [
      { type: 'flat', worth: 599000 },
      { type: 'flat', worth: 684000 }
    ],
    market: { stocks: [], bonds: [] },
    credits: [
      {
        bank: 'Bank Zachodni WBK',
        type: 'mortgage',
        value: 499985.3
      }
    ]
  }
}

注:上述样例文档存在字段截断,不影响聚合逻辑编写

需求说明

  • 基础需求:计算两类国家的人员数量比值
    • Max国家:payments.name = 'restaurant' 支付记录平均金额最高的国家
    • Min国家:payments.name = 'restaurant' 支付记录平均金额最低的国家
  • 进阶需求:计算比值时,Max国家仅统计个人餐厅类支付金额高于Min国家餐厅类支付均值的人员,Min国家统计规则不变。

原有基础代码存在的问题

之前写的聚合逻辑有两个核心错误,导致无法实现进阶需求:

  1. 第二次$group的_id字段取值错误,上一步分组输出已经没有$address.country字段,会导致分组无法拿到全局的Max、Min国家
  2. 没有预留Max国家人员的金额过滤逻辑,无法筛选出符合进阶要求的统计人数

原有代码如下:

db.people.aggregate([ 
    { $unwind: "$payments" }, 
    { $match: { "payments.name": "restaurant" } }, 
    { $group: { 
        _id: { country: "$address.country" }, 
        avgPayment: { $avg: "$payments.amount" },
        noPerson: { $sum: 1}
        } }, 
    { $sort : {"avgPayment": -1}},
    { $group: {
        _id: { country: "$address.country" },
        max:  {$first: "$$ROOT"},
        min: { $last: "$$ROOT"}
    }},
    { $project: {res: {$divide: [ "$max.noPerson","$min.noPerson"
    ]},
    _id: 0}}
])

可直接运行的实现方案

用$facet单阶段完成多维度统计,避免多次扫表,逻辑如下:

db.people.aggregate([
  // 拆分支付数组,仅保留餐厅消费记录
  { $unwind: "$payments" },
  { $match: { "payments.name": "restaurant" } },
  {
    $facet: {
      // 分支1:计算每个国家餐厅消费均值,定位Max、Min国家
      countryStat: [
        {
          $group: {
            _id: "$address.country",
            avgRestaurantPay: { $avg: "$payments.amount" }
          }
        },
        { $sort: { avgRestaurantPay: -1 } },
        {
          $group: {
            _id: null,
            maxCountry: { $first: "$$ROOT" },
            minCountry: { $last: "$$ROOT" }
          }
        }
      ],
      // 分支2:保留所有餐厅消费记录的核心字段用于后续人数统计
      userPayRecords: [
        {
          $project: {
            country: "$address.country",
            payAmount: "$payments.amount"
          }
        }
      ]
    }
  },
  // 展开计算结果,关联国家统计值到每条消费记录
  { $unwind: "$countryStat" },
  { $unwind: "$userPayRecords" },
  {
    $replaceRoot: {
      newRoot: { $mergeObjects: ["$userPayRecords", "$countryStat"] }
    }
  },
  // 按规则统计有效人数
  {
    $group: {
      _id: null,
      // Min国家人数:所有属于Min国家的餐厅消费记录数
      minCountryTotal: {
        $sum: { $cond: [{ $eq: ["$country", "$minCountry._id"] }, 1, 0] }
      },
      // Max国家有效人数:属于Max国家,且单笔餐厅消费额高于Min国家餐厅消费均值
      maxCountryValid: {
        $sum: {
          $cond: [
            {
              $and: [
                { $eq: ["$country", "$maxCountry._id"] },
                { $gt: ["$payAmount", "$minCountry.avgRestaurantPay"] }
              ]
            },
            1,
            0
          ]
        }
      }
    }
  },
  // 计算最终比值,输出辅助校验字段
  {
    $project: {
      ratio: { $divide: ["$maxCountryValid", "$minCountryTotal"] },
      maxCountryName: "$maxCountry._id",
      minCountryName: "$minCountry._id",
      minCountryAvgPay: "$minCountry.avgRestaurantPay",
      _id: 0
    }
  }
])

逻辑说明

  • 用$facet在一次聚合扫描中同时完成国家维度均值计算、个人消费记录留存,性能比多段聚合高
  • 修正了原代码二次分组的_id错误,统一按_id:null分组拿到全局最高、最低均值的国家信息
  • 统计Max国家人数时增加双重判断:国籍属于Max国家+个人餐厅消费额大于Min国家餐厅消费均值,完全符合进阶需求
  • 输出结果额外返回Max/Min国家名称、Min国家餐厅消费均值,方便结果校验

内容的提问来源于stack exchange,提问作者An Minh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 03:42:31