对子文档进行分组/计数,同时保留根字段

2023-12-22

在 mongodb 中,经过一些 $match 和 $project 后,我​​得到以下 2 个文档。我正在尝试弄清楚如何将每个事件的每个组中每个团队的状态列表分组/计数在一起。简而言之,我需要知道每个州有多少支球队(0、1 或 2)。我从以下文档开始。

{ 
    "_id" : "event1", 
    "groups" : [
        {
            "_id" : "group1", 
            "wlActive" : true, 
            "teams" : [
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(0)}, 
                {"state" : NumberInt(0)} 
            ]
        }, 
        {
            "_id" : "group2", 
            "wlActive" : false, 
            "teams" : [
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(0)}, 
                {"state" : NumberInt(0)} 
            ]
        }
    ]
},
{ 
    "_id" : "event2", 
    "groups" : [
        {
            "_id" : "group3", 
            "wlActive" : true, 
            "teams" : [
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(0)}, 
                {"state" : NumberInt(0)} 
            ]
        }, 
        {
            "_id" : "group4",
            "wlActive" : false, 
            "teams" : [
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(2)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(1)}, 
                {"state" : NumberInt(0)}, 
                {"state" : NumberInt(0)} 
            ]
        }
    ]
}

我希望最终得到的是这样的:

{ 
    "_id" : "event1", 
    "groups" : [
        {
            "_id" : "group1", 
            "wlActive" : true, 
            "states":[
                {"state":NumberInt(2), count:2},
                {"state":NumberInt(1), count:3},
                {"state":NumberInt(0), count:2}
            }
        }, 
        {
            "_id" : "group2", 
            "wlActive" : false, 
            "states":[
                {"state":NumberInt(2), count:2},
                {"state":NumberInt(1), count:3},
                {"state":NumberInt(0), count:2}
            }
        }
    ]
},
{ 
    "_id" : "event2", 
    "groups" : [
        {
            "_id" : "group3", 
            "wlActive" : true, 
            "states":[
                {"state":NumberInt(2), count:2},
                {"state":NumberInt(1), count:3},
                {"state":NumberInt(0), count:2}
            }
        }, 
        {
            "_id" : "group4",
            "wlActive" : false, 
            "states":[
                {"state":NumberInt(2), count:2},
                {"state":NumberInt(1), count:3},
                {"state":NumberInt(0), count:2}
            }
        }
    ]
}

它不需要完全是这样,但只要我可以获得每个团队状态的计数,并为每个组保留诸如“wlActive”之类的字段。我在这里见过类似的例子,但我似乎无法解决这个问题。


实际上你可以用$addFields https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/ or $project https://docs.mongodb.com/manual/reference/operator/aggregation/project/

db.collection.aggregate([
  { "$addFields": {
    "groups": {
      "$map": {
        "input": "$groups",
        "in": {
          "$mergeObjects": [
            "$$this",
            { "teams": {
              "$reduce": {
                "input": "$$this.teams",
                "initialValue": [ ],
                "in": {
                  "$cond": {
                    "if": { 
                      "$ne": [ { "$indexOfArray":  ["$$value.state", "$$this.state"] }, -1 ]
                    },
                    "then": {
                      "$concatArrays": [
                        { "$filter": {
                          "input": "$$value",
                          "as": "v",
                          "cond": { "$ne": [ "$$v.state", "$$this.state" ]  }
                        }},
                        [{
                          "state": "$$this.state",
                          "count": { "$sum": [
                            { "$arrayElemAt": [
                              "$$value.count",
                              { "$indexOfArray": ["$$value.state", "$$this.state" ] }
                            ]},
                            1
                          ]}
                        }]
                      ]
                    },
                    "else": {
                      "$concatArrays": [
                        "$$value",
                        [{ "state": "$$this.state", "count": 1 }]
                      ]
                    }
                  }
                }
              }
            }}
          ]
        }
      }
    }
  }}
])

这非常复杂并且基本上使用$reduce https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/“内联”作为替代$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/管道运营商。

The $reduce https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/是工作的主要部分,因为它使用键上的“分组”总计来迭代每个数组项“减少”到另一个数组。它通过寻找值来做到这一点state在当前减少的结果内通过$indexOfArray https://docs.mongodb.com/manual/reference/operator/aggregation/indeOfArray/。当找不到某些东西时(-1返回)它通过以下方式附加到当前结果$concatArrays https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/与一个新的state and count of 1。这是else case.

当某事is found ( the thencase ),我们通过以下方式从结果数组中删除匹配的元素$filter https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ and 连接来自匹配索引的新元素$indexOfArray https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfArray/并使用提取值$arrayElemAt https://docs.mongodb.com/manual/reference/operator/aggregation/arrayElemAt/。这给出了当前count使用添加的匹配元素的$sum https://docs.mongodb.com/manual/reference/operator/aggregation/sum/为了增加计数1.

当然,传统上你可以这样做$unwind https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/ and $group https://docs.mongodb.com/manual/reference/operator/aggregation/group/声明:

db.collection.aggregate([
  { "$unwind": "$groups" },
  { "$unwind": "$groups.teams" },
  { "$group": {
    "_id": {
      "_id": "$_id",
      "gId": "$groups._id",
      "wlActive": "$groups.wlActive",
      "state": "$groups.teams.state"
    },
    "count": { "$sum": 1 }
  }},
  { "$sort": { "_id": -1, "count": -1 } },
  { "$group": {
    "_id": {
      "_id": "$_id._id",
      "gId": "$_id.gId",
      "wlActive": "$_id.wlActive",
    },
    "teams": { "$push": { "state": "$_id.state", "count": "$count" } }
  }},
  { "$group": {
    "_id": "$_id._id",
    "groups": {
      "$push": {
        "_id": "$_id.gId",
        "wlActive": "$_id.wlActive",
        "teams": "$teams"
      }
    }
  }}
])

Here $unwind https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/习惯于“压扁”将数组内容放入单独的文档中。你这样做到teams水平和$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/ on the 复合键其中标识独特性下降到state level.

由于所有文档详细信息都是初始文档的一部分$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/键,您删除的级别“独特性” so teams变成一个数组使用$push https://docs.mongodb.com/manual/reference/operator/aggregation/push/。为了回到原来的文档形式,另一个$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/是在原来的基础上完成的_id文件的价值和$push https://docs.mongodb.com/manual/reference/operator/aggregation/push/重建了groups array.

那个形式大概是"easier"理解,但它确实需要更长的时间来运行并且需要更多的资源。第一种形式是optimal因为你实际上不需要$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/在现有文档中,您通常应该避免$unwind https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/除非绝对必要。即分组state across 所有文件是必要的,但在单个文档中则不是。

无论哪种方式基本上都会返回相同的结果:

{
        "_id" : "event1",
        "groups" : [
                {
                        "_id" : "group1",
                        "wlActive" : true,
                        "teams" : [
                                {
                                        "state" : 2,
                                        "count" : 2
                                },
                                {
                                        "state" : 1,
                                        "count" : 3
                                },
                                {
                                        "state" : 0,
                                        "count" : 2
                                }
                        ]
                },
                {
                        "_id" : "group2",
                        "wlActive" : false,
                        "teams" : [
                                {
                                        "state" : 2,
                                        "count" : 2
                                },
                                {
                                        "state" : 1,
                                        "count" : 3
                                },
                                {
                                        "state" : 0,
                                        "count" : 2
                                }
                        ]
                }
        ]
}
{
        "_id" : "event2",
        "groups" : [
                {
                        "_id" : "group3",
                        "wlActive" : true,
                        "teams" : [
                                {
                                        "state" : 2,
                                        "count" : 2
                                },
                                {
                                        "state" : 1,
                                        "count" : 3
                                },
                                {
                                        "state" : 0,
                                        "count" : 2
                                }
                        ]
                },
                {
                        "_id" : "group4",
                        "wlActive" : false,
                        "teams" : [
                                {
                                        "state" : 2,
                                        "count" : 2
                                },
                                {
                                        "state" : 1,
                                        "count" : 3
                                },
                                {
                                        "state" : 0,
                                        "count" : 2
                                }
                        ]
                }
        ]
}

不管它的价值如何,因为这并不是真正的“聚合”跨文档的任何内容,您都可以简单地返回所有数据并在客户端代码中“聚合”数组项。

作为 mongo shell 示例:

db.collection.find().map(doc => Object.assign({}, doc, {
  _id: doc._id,
  groups: doc.groups.map(g => Object.assign({}, g, {
    _id: g._id,
    wlActive: g.wlActive,
    teams: ((input) => {
      var obj = input.reduce((o, e) => 
      (o.hasOwnProperty(e.state)) ? 
        Object.assign({} , o, { [e.state]: o[e.state]+1 })
        : Object.assign({}, o, { [e.state]: 1 }),  {});
      return Object.keys(obj)
        .map(k => ({ state: parseInt(k), count: obj[k] }))
        .sort((a,b) => b.state - a.state);
    })(g.teams)
  }))
}))
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对子文档进行分组/计数,同时保留根字段 的相关文章

随机推荐

  • TFS 团队构建日志单元测试结果

    我们正在使用 TFS 2010 Team Build 它会在构建过程中自动运行我们的单元测试 如果单击 查看日志 我可以看到运行的测试列表和每个测试结果 成功 失败 如果发生失败 我想查看测试结果输出 以便开发人员可以找出失败的原因是什么
  • 使用 extern 和 #include 头文件有什么区别?

    我开始质疑 extern 关键字的有用性 该关键字用于访问其他模块 其他文件中 中的变量 函数 当我们使用 include 预处理器导入带有变量 函数原型或函数 变量定义的头文件时 我们不是在做同样的事情吗 extern之所以需要 是因为它
  • javascript location.href onchange 事件监听器?

    我想在你离开页面时显示一条消息 不是烦人的警报 只是一些告诉你等待的html 在考虑它时我面临着某些困难 当用户按下 Stop 时 浏览器 取消导航离开 行动 我想要发送消息 离开 每当单击任何链接时 都会出现该消息 当单击的链接打开另一个
  • Windows 7 中“ruby.exe 未被识别为内部或外部命令”

    我的操作系统是Windows 7 我正要连接我的本地MySQL数据库与Heroku共享数据库 在某一时刻 我得到了libmysql dll文件丢失的错误 所以我搜索并下载了dll文件并将其保存在ruby中 bin 目录 当我再次准备连接时
  • 混合效应逻辑回归

    我正在尝试在 python 中实现混合效应逻辑回归 作为比较 我正在使用glmer函数从lme4R 中的包 我发现statsmodels模块有一个BinomialBayesMixedGLM应该能够适合这样的模型 然而 我遇到了很多问题 我找
  • QTimer线程亲和性

    在文档中它说 在多线程应用程序中 您可以使用QTimer在任何线程中 有一个事件循环 要从非 GUI 线程启动事件循环 请使用QThread exec Qt 使用定时器的线程亲和性来确定 哪个线程将发出timeout 信号 正因为如此 你
  • 位置:固定导致元素比浏览器宽

    谁能告诉我为什么position fixed会导致元素比浏览器或页面上的其他内容更宽并导致水平滚动 这是代码 超文本标记语言
  • 如果我想要像 bootstrap vue 和 vuetify 这样的 2 个 css 框架,请使用同一个项目

    我想要 2 个 CSS 框架使用我现有的 nuxtjs 项目 有人质疑为什么要使用 2 个 CSS 框架一个 vuejs 项目 回答这个问题 我想要我的网站网页版和移动版 我使用网页版是 boostrapvue 它对于网页版来说很棒 或者
  • 使用 FileManager.default.currentDirectoryPath 时,Swift 5.2 出现分段错误:11

    我有一个类似于以下内容的自定义 Swift 脚本 usr bin env xcrun sdk macosx swift import Foundation let path FileManager default currentDirect
  • 当两个路由指向同一模型时,设计资源名称出现问题

    我的 api v1 和 v2 有两条路由指向相同的 Devise 模型 路由如下 scope api do namespace v1 do devise for users controllers gt registrations gt d
  • 如何一次性设置所有formik值?

    我有一个formik用于编辑来自 API 端点的数据的表单 目前我正在重新初始化useEffect像这样挂钩 React useEffect gt initialValues first name address first name in
  • 找不到与给定名称匹配的资源:attr 'android:tabLayout'

    我想通过用我自己的样式替换样式来自定义选项卡小部件 我要更换的项目如下
  • 类型或命名空间 不存在

    好吧 我之前已经遇到过这个问题一百万次了 而且之前已经被回答过 100 万 1 次了 然而 再一次 我有 3 个项目 A B 和 C 每个项目都有一个 DLL 每个项目都是 Net 4 0 不是客户端构建 完整的4 0 项目 C 引用 A
  • 为什么 CHECKSUM_AGG() 对于完全不同的输入值返回相同的值?

    我真的不明白怎么办CHECKSUM AGG 有效 尽管我知道它是通过使用异或来构建的 这解释了为什么它会返回0当您传递相等的整数时 但是 为什么我在以下 SQL 中得到相同的聚合校验和 其中输入值是唯一的 DECLARE test1 TAB
  • 为什么 10 的幂以科学记数法打印为 5 次方?

    我想知道 10 的幂是否与控制台中科学计数法的打印相关以及如何相关 我搜索了 R 文档 但没有找到任何相关的内容 或者我真正理解的内容 首先 我的scipen and digits设置是 unlist options scipen digi
  • 寻求 2D 版本的 approxfun()

    我正在 R 中寻找 2D 版本stats approxfun 可以生成插值function来自 x y z 数据集的 f x y 我在包裹中没有找到akima或其他地方 我会尝试重新适应interp surfaceR 包中的函数fields
  • 我可以在黄瓜中使用带有示例的背景吗?

    我想通过一个示例来说明所有场景的第一步 但所有场景的第一步都是相同的 只有一个元素不同 所以我想在后台完成所有这些步骤 并想添加带有背景的示例 简而言之 假设我想导航到 facebook twitter google 和 youtube 页
  • Quartz调度器和OSGI

    我有一个 OSGI 调度程序包 其中包含 Quartz Scheduler Jar 该捆绑包仅向其他捆绑包公开一个应用程序接口 并且当注册新作业时 它将被包装到临时作业 实现 StatefulJob 中并使用调度程序进行调度 这样我就不必公
  • 使用unix_socket登录MySQL帐户

    我已经创建了一个从机器 A 到机器 B 的转发器 通过 SSH 机器 B 有一个正在运行的 MySQL 实例 其 root 帐户使用unix socket验证 User Host plugin root localhost unix soc
  • 对子文档进行分组/计数,同时保留根字段

    在 mongodb 中 经过一些 match 和 project 后 我 得到以下 2 个文档 我正在尝试弄清楚如何将每个事件的每个组中每个团队的状态列表分组 计数在一起 简而言之 我需要知道每个州有多少支球队 0 1 或 2 我从以下文档