按范围计算阵列大小

时间:2017-06-29 22:56:16

标签: mongodb mongodb-query aggregation-framework

我有一个集合(我们称之为'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大小< 50
  2. 其中ARRAY_NODE 50 =<尺寸< 100
  3. 其中ARRAY_NODE 100 =<尺寸< 150
  4. 其中ARRAY_NODE 150 =<尺寸< 200

1 个答案:

答案 0 :(得分: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聚合管道阶段基本上是一个&#34;快捷方式&#34;用于创建类似的声明,但它的使用是方便的&#34;所以输出不完全相同:

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的完整表单。另请注意&#34;范围&#34;当然也是一种便利功能,因此遵循他们自己严格的逻辑。当你的逻辑不同时,最好再写$switch

另请注意,如果文档中实际不存在此类字段,则必须在早期版本中使用$isArray$ifNull(如稍后所示)才能对逻辑进行逻辑测试返回默认值0以指示&#34;长度&#34;。否则,期望数组的$size运算符将产生错误:

  

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

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

使用$cond运算符始终可以实现这一点,但它的语法只是&#34;三元&#34;运算符是嵌套的,而不是$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 }