【问题标题】:Using mongo aggregate in meteor to total unlocked/locked in collection在流星中使用 mongo 聚合来解锁/锁定集合
【发布时间】:2015-08-11 01:57:16
【问题描述】:

所以,我有一个集合来存储与用户相关的文档,其结构如下:

{_id: "hofjew233332j4", userId: "fhewojfw34324", achievementUnlocked: true };

我想要做的是使用聚合和下划线能够按用户 ID 对文档进行分组,然后计算他们的记录中有多少百分比已解锁设置为 true,这样生成的文档将如下所示:

{_id: "fhewojfw34324(userId)", unlockPercentage: 40 (achievementUnlocked: true / all docs) }

我可以在只检索文档一次的情况下执行此操作吗?

【问题讨论】:

  • 虽然“可能”,但总的来说这是非常不明智的。但这也引出了一个问题,即“成就”似乎应该是相对较小的东西。为什么不将一个数组用于“解锁”,一个用于“锁定”并维护每个数组的计数值?这似乎是一个比使用单独的文档更实用的模型。它还可以省去汇总数据的麻烦,因为您将始终维护总数。
  • 我不确定您使用单独的文档是什么意思,但我宁愿不要用一个会不断变化的值来污染用户文档。成就实际上是一个相当大的数据集,每个用户可能包含 1000 条记录。
  • 那么它可能不应该包含 1000 条记录。把它分开。我已经用答案解释了我的方法。

标签: javascript mongodb meteor mongodb-query aggregation-framework


【解决方案1】:

按计数achievementUnlockedtrue 计数的第一组,然后在项目中用于计算percentage,如下聚合:

db.collectionName.aggregate([{
    "$group": {
        "_id": "$userId",
        "achievementUnlockedTrueCount": {
            "$sum": {
                "$cond": {
                    "if": {
                        "$eq": ["$achievementUnlocked", true] //count achievementUnlocked = true count 
                    },
                    "then": 1,
                    "else": 0
                }
            }
        },
        "totalCount": {
            "$sum": 1 // get total count of grouped documents 
        }
    }
}, {
    "$project": {
        "unlockPercentage": {
            "$multiply": [{
                "$divide": ["$achievementUnlockedTrueCount", "$totalCount"] //used this in project to caculate %
            }, 100]
        }
    }
}]).pretty()

【讨论】:

    【解决方案2】:

    我个人什至不会在这里进行聚合,因为数据似乎微不足道。为每个用户和/或“游戏”数据维护一组“锁定”和“解锁”成就会效率更高。

    拿一个这样的文件:

    {
        "_id": "hofjew233332j4", 
        "userId": "fhewojfw34324",
        "gameId": "v3XWHHvFSHwYxxk6H",
        "achievementsCount": 5,
        "locked": ["One","Two","Four","Five"],
        "lockedCount": 4,
        "unlocked": ["Three"]
        "unlockedCount": 1
    }
    

    因此,您通常会在此处使用所有“锁定”成就初始化每个用户和“游戏”,但在这种情况下,我们将显示一个已经在“解锁”中的成就。另请注意,“计数”字段反映了每个数组中存在的条目数。

    要“解锁”另一个成就,您只需执行更新以从“锁定”数组中删除并插入“解锁”数组,同时保持“计数”值:

    Achievements.update(
       { 
           "userId": "fhewojfw34324",
           "gameId": "v3XWHHvFSHwYxxk6H",
           "locked": "Four",
           "unlocked": { "$ne": "Four" }
       },
       {
           "$push": { "unlocked": "Four" },
           "$pull": { "locked": "Four" },
           "$inc": {
               "lockedCount": -1,
               "unlockedCount": 1
           }
       }
    )
    

    将文档更改为这种状态:

    {
        "_id": "hofjew233332j4", 
        "userId": "fhewojfw34324",
        "gameId": "v3XWHHvFSHwYxxk6H",
        "achievementsCount": 5,
        "locked": ["One","Two","Five"],
        "lockedCount": 3,
        "unlocked": ["Three","Four"]
        "unlockedCount": 2
    }
    

    这是一个非常简单的模式,因为每次更新都会维护正确的值和数据。如果您想要诸如“百分比”之类的信息,那么这很简单:

    Achievements.aggregate([
        { "$project": {
            "userId": 1,
            "gameId": 1,
            "percentUnlocked": { "$divide": [ "$unlockedCount", "$achivementsCount" ] }
    ])
    

    或者只是在客户端代码中应用该数学。

    此模型还使您可能想做的“真实”聚合变得更简单,信息范围也更广。此外,与需要将数据“累加”为一个单独的过程相比,进行计算要高效得多。

    【讨论】:

    • 很抱歉,您似乎在解决另一个问题。当我说在这种情况下无法绕过 1000 条记录时,我不需要更改整个数据结构并相信我。可以这么说,在您的结构中,每个文档都会非常大,并且会降低您在文档较少时可能增加的效率。
    • @TDmoneybanks 您在此处需要了解的是您“提出”最终需要遵循相同方法的内容。为了在一个查询中获得“总数”和“百分比”,您需要将所有内容填充到单个数组中,然后再次展开以进行其他计算,或接受多个查询。第一个是“不切实际”并且可能会突破限制,第二个是“缓慢”。重新考虑设计以适应您需要做的事情。我不会拿大约 1000 个,而且我也在展示一种明确的方法来“打破它”,这是你需要考虑的事情。
    • 感谢您的帮助,但我不认为是在同一页上。抱歉,如果我问的内容令人困惑或格式不正确。
    • @TDmoneybanks 这并不令人困惑。只是你以“错误的方式”处理这个问题,因此我在这里分享一些“智慧”并向你展示一些你可能没有考虑过的东西。使用true/false 值只需使用$cond 运算符将其转换为数字。我可以展示执行此操作的“错误方法”,但相反,我为您提供了一个“更好”的视角,您可以简单地维护每次更新的计数。比回顾汇总结果要快得多。
    • 我对你想说的话并不感到困惑。我告诉你,你对我所拥有的数据的了解远不及我,而且你的提议不适用于我的用例。
    猜你喜欢
    • 2016-01-11
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多