【问题标题】:Get count of Array Sizes by Ranges按范围获取数组大小的计数
【发布时间】:2017-12-03 19:23:07
【问题描述】:

我有一个集合(我们称之为“AwesomeCollection”),其中包含如下所示的文档:

{
  "_id" : "someID",
  "DateAdded" : ISODate("2017-06-08T22:35:43.517Z"),
  "Info" : {
    "size" : NumberLong(32530454),
    "filtype" : "APK"
  },
  "ARRAY_NODE" : [{
      "key1" : "val1"
    }, {
      "key2" : "val2"
    }, {
      "key3" : "val3"
    }]
}

ARRAY_NODE 是一个数组字段,可以包含 1 到 200 个项目。

我想编写一个查询,返回 AwesomeCollection 中每个类别的文档计数:

  1. 其中 ARRAY_NODE 大小
  2. 其中 ARRAY_NODE 50 =
  3. 其中 ARRAY_NODE 100 =
  4. 其中 ARRAY_NODE 150 =

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    您希望在聚合管道中使用 $switch 语句:

    db.collection.aggregate([
      { "$group": {
        "_id": {
          "$let": {
            "vars": {
              "length": { 
                "$cond": {
                  "if": { "$isArray": "$ARRAY_NODE" },
                  "then": { "$size": "$ARRAY_NODE" },
                  "else": 0
                }
              }
            },
            "in": {
              "$switch": {
                "branches": [
                  { "case": { "$lt": [ "$$length", 50 ] }, "then": "< 50" },
                  { "case": { "$lt": [ "$$length", 100 ] }, "then": ">= 50 < 100" },
                  { "case": { "$lt": [ "$$length", 150 ] }, "then": ">= 100 < 150" },
                  { "case": { "$lt": [ "$$length", 200 ] }, "then": ">= 150 < 200" },
                ],
                "default": "> 200"
              }
            }
          }
        },
        "count": { "$sum": 1 }
      }}
    ])
    

    如图所示,我们通过在$let 中首先声明来缩短语法,以便为每个计数获取数组的$size

    有一个$bucket 聚合管道阶段基本上是创建类似语句的“快捷方式”,但它的用法是“方便”,因此它的输出并不完全相同:

    db.collection.aggregate([
      { "$bucket": {
        "groupBy": { 
          "$cond": {
            "if": { "$isArray": "$ARRAY_NODE" },
            "then": { "$size": "$ARRAY_NODE" },
            "else": 0
          }
        },
        "boundaries": [0, 49, 99, 149, 199],
        "default": "> 200"
      }}
    ])
    

    如果您确实需要完整的标签,请使用带有$switch 的完整表单。另请注意,“范围”当然也是一种便利功能,因此遵循它们自己的严格逻辑。当您的逻辑不同时,最好还是写完整的$switch

    还要注意,如果文档中实际上没有这样的字段,则需要在早期版本中使用$isArray$ifNull(如下所示),以便进行逻辑测试以返回默认值0 表示“长度”。否则 $size 运算符,它期望一个数组将产生错误:

    $size 的参数必须是数组,但类型为:EOO

    逻辑处理或返回一个空数组到$size,如The argument to $size must be an Array, but was of type: EOO的答案所示:

    这总是并且仍然可以使用$cond 运算符,但只是作为“三元”运算符的语法是嵌套的,而不是$switch 的简洁形式

    db.collection.aggregate([
      { "$group": {
        "_id": {
          "$let": {
            "vars": {
              "length": { 
                "$cond": {
                  "if": { "$ifNull": [ "$ARRAY_NODE", false ] },
                  "then": { "$size": "$ARRAY_NODE" },
                  "else": 0
                }
              }
            },
            "in": {
              "$cond": {
                "if": { "$lt": [ "$$length", 50 ] },
                "then": "< 50",
                "else": {
                  "$cond": {
                    "if": { "$lt": [ "$$length", 100 ] },
                    "then": "> 50 < 100",
                    "else": {
                      "$cond": {
                        "if": { "$lt": [ "$$length", 150 ] },
                        "then": ">100 < 150",
                        "else": {
                          "$cond": {
                            "if": { "$lt": [ "$$length", 200 ] },
                            "then": "> 150 < 200",
                            "else": "> 200"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    ])
    

    作为演示。将一些文档插入到具有不同数组大小的集合中:

    // Insert documents with arrays of given lengths
    db.collection.insertMany(
      [
        5,40,      //        < 50  count 2
        70,        //  >= 50 < 100 count 1
        120,130,   // >= 100 < 150 count 2
        170,       // >= 150 < 200 count 1
        210        // > 200        count 1
      ].map( n => 
        ({ "ARRAY_NODE": Array.apply(null,Array(n)).map(() => ({})) }) )
    )
    

    然后运行任何聚合语句以产生结果:

    {  "_id" : "< 50", "count" : 2  }
    {  "_id" : ">= 50 < 100", "count" : 1 }
    {  "_id" : ">= 100 < 150", "count" : 2 }
    {  "_id" : ">= 150 < 200", "count" : 1 }
    {  "_id" : "> 200", "count" : 1 }
    

    【讨论】:

    • 好的,如果我想检查更多条件怎么办?例如添加日期:{$gt: ISODate('2017-06-20')}。将在聚合中的哪个位置插入?
    • @IsaacBok 如果您还有其他问题,那么Ask a new Question 并使其清晰且独立。可以使用$match 将常规查询选择添加为初始管道阶段。
    • 我得到 { $err: \"The argument to $size must be an Array, but was of type: EOO\", code: 17124} 即使 ARRAY_NODE 是一个数组...跨度>
    • @IsaacBok 实际上这意味着它不是一个数组,因此您的某些文档中可能没有这样的数组,我们当然可以将其过滤掉。请参阅 $ifNull$cond 的其他用法,以使用 0 替换缺失的数组以了解大小。
    • @IsaacBok 你很沉默。你在这里的个人资料显示了一些不被接受的答案的糟糕历史,所以我觉得你需要一些刺激。您已获得符合“您实际提出的问题”规范的答案。问题中的文档显示了一个数组,我还向您展示了如何处理缺少该数组的文档。您有两种版本的声明,一种用于最新版本的 MongoDB,另一种与旧版本兼容。两者都产生了要求的结果。
    猜你喜欢
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多