【问题标题】:Partition of Data with MongoDB使用 MongoDB 进行数据分区
【发布时间】:2023-03-30 03:41:01
【问题描述】:

我有以下收藏

[
  {
    "setting": "Volume",
    "_id": ObjectId("5a934e000102030405000000"),
    "counting": 1
  },
  {
    "setting": "Brightness",
    "_id": ObjectId("5a934e000102030405000001"),
    "counting": 1
  },
  {
    "setting": "Contrast",
    "_id": ObjectId("5a934e000102030405000002"),
    "counting": 1
  },
  {
    "setting": "Contrast",
    "_id": ObjectId("5a934e000102030405000003"),
    "counting": 1
  },
  {
    "setting": "Contrast",
    "_id": ObjectId("5a934e000102030405000004"),
    "counting": 0
  },
  {
    "setting": "Sharpness",
    "_id": ObjectId("5a934e000102030405000005"),
    "counting": 1
  },
  {
    "setting": "Sharpness",
    "_id": ObjectId("5a934e000102030405000006"),
    "counting": 1
  },
  {
    "setting": "Language",
    "_id": ObjectId("5a934e000102030405000007"),
    "counting": 1
  },
  {
    "setting": "Language",
    "_id": ObjectId("5a934e000102030405000008"),
    "counting": 0
  }
]

现在我想通过setting group 并且只希望结果中的前两个数据在useless 中休息

所以我的输出应该在sort之后计数

[
  {
    "setting": "Contrast",
    "counting": 2
  },
  {
    "setting": "Sharpness",
    "counting": 2
  },
  {
    "setting": "Useless",
    "counting": 3
  }
]

【问题讨论】:

    标签: node.js mongodb mongoose mongodb-query aggregation-framework


    【解决方案1】:

    如果您能侥幸成功,那么最好将缩减后的结果“填充”到一个文档中,然后将$slice 前两个和$sum 其余部分:

    Model.aggregate([
      { "$group": {
        "_id": "$setting",
        "counting": { "$sum": "$counting" }
      }},
      { "$sort": { "counting": -1 } },
      { "$group": {
        "_id": null,
        "data": { "$push": "$$ROOT" }
      }},
      { "$addFields": {
         "data": {
           "$let": {
             "vars": { "top": { "$slice": ["$data", 0, 2 ] } },
             "in": {
               "$concatArrays": [
                 "$$top",
                 { "$cond": {
                   "if": { "$gt": [{ "$size": "$data" }, 2] },
                   "then": 
                     [{ 
                       "_id": "Useless",
                       "counting": {
                         "$sum": {
                           "$map": {
                             "input": {
                               "$filter": {
                                 "input": "$data",
                                 "cond": { "$not": { "$in": [ "$$this._id", "$$top._id" ] } }
                               }
                             },
                             "in": "$$this.counting"
                           }
                         }
                       }
                     }],
                   "else": []
                 }}
               ]
             }
           }
         }
      }},
      { "$unwind": "$data" },
      { "$replaceRoot": { "newRoot": "$data" } }
    ])
    

    如果它可能是一个非常“大”的结果甚至减少了,那么$limit 使用$facet 作为“其余”:

    Model.aggregate([
      { "$facet": {
        "top": [
          { "$group": {
            "_id": "$setting",
            "counting": { "$sum": "$counting" }
          }},
          { "$sort": { "counting": -1 } },
          { "$limit": 2 }
        ],
        "rest": [
          { "$group": {
            "_id": "$setting",
            "counting": { "$sum": "$counting" }
          }},
          { "$sort": { "counting": -1 } },
          { "$skip": 2 },
          { "$group": {
            "_id": "Useless",
            "counting": { "$sum": "$counting" }
          }}
        ]
      }},
      { "$project": {
        "data": {
          "$concatArrays": [
            "$top","$rest"
          ]
        }
      }},
      { "$unwind": "$data" },
      { "$replaceRoot": { "newRoot": "$data" } }
    ])
    

    甚至 $lookup 使用 MongoDB 3.6:

    Model.aggregate([
      { "$group": {
        "_id": "$setting",
        "counting": { "$sum": "$counting" }
      }},
      { "$sort": { "counting": -1 } },
      { "$limit": 2 },
      { "$group": {
        "_id": null,
        "top": { "$push": "$$ROOT" }   
      }},
      { "$lookup": {
        "from": "colllection",
        "let": { "settings": "$top._id" },
        "pipeline": [
          { "$match": {
            "$expr": {
              "$not": { "$in": [ "$setting", "$$settings" ] }
            }
          }},
          { "$group": {
            "_id": "Useless",
            "counting": { "$sum": "$counting" }
          }}
        ],
        "as": "rest"
      }},
      { "$project": {
        "data": {
          "$concatArrays": [ "$top", "$rest" ]
        }
      }},
      { "$unwind": "$data" },
      { "$replaceRoot": { "newRoot": "$data" } }
    ])
    

    实际上都几乎相同,并且都返回相同的结果:

    { "_id" : "Contrast", "counting" : 2 }
    { "_id" : "Sharpness", "counting" : 2 }
    { "_id" : "Useless", "counting" : 3 }
    

    如果对字段名称的控制对您来说非常重要,则可以选择在每个结尾处使用 $project 而不是 $replaceRoot。通常我只坚持$group 默认值


    如果您的 MongoDB 早于 3.4 并且生成的 "Useless" 余数实际上太大而无法使用第一种方法的任何变体,那么简单的 Promise 分辨率基本上就是答案,对于 aggregate 和另一个用于基本计数并简单地做数学:

    let [docs, count] = await Promise.all([
      Model.aggregate([
        { "$group": {
          "_id": "$setting",
          "counting": { "$sum": "$counting" }
        }},
        { "$sort": { "counting": -1 } },
        { "$limit": 2 },
      ]),
      Model.count().exec()
    ]);
    
    docs = [ 
      ...docs,
      { 
        "_id": "Useless",
        "counting": count - docs.reduce((o,e) => o + e.counting, 0)
      }
    ];
    

    或者没有async/await:

    Promise.all([
      Model.aggregate([
        { "$group": {
          "_id": "$setting",
          "counting": { "$sum": "$counting" }
        }},
        { "$sort": { "counting": -1 } },
        { "$limit": 2 },
      ]),
      Model.count().exec()
    ]).then(([docs, count]) => ([ 
      ...docs,
      { 
        "_id": "Useless",
        "counting": count - docs.reduce((o,e) => o + e.counting, 0)
      }
    ]).then( result => /* do something */ )
    

    这基本上是古老的“总页数”方法的一种变体,只需运行单独的查询来计算集合项。

    运行单独的请求通常是执行此操作的古老方法,并且通常性能最佳。其余解决方案主要针对“聚合技巧”,因为这是您所要求的,这就是您通过在同一事物上显示不同变体得到的答案。

    一个变体将所有结果放入一个文档中(在可能的情况下,当然是由于 BSON 限制),而其他变体基本上通过以不同的形式再次运行查询来改变“古老”的方法。 $facet 并联和$lookup 串联。

    【讨论】:

    • 是的,我通过promise 的最后两个查询得到了结果...但显然,当有大量数据时它们会花费时间???不是吗???
    • @AshishChoudhary 不要像这样留下多个 cmets。你可以问一个评论。我告诉你是因为在过去的几天里,你一直在做类似的事情,还有其他类似“这很棒”的问题和答案。评论用于澄清某些事情并在单个请求中发送。
    • @AshishChoudhary 您在问题中得到了澄清,因为这就是我们所做的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-08
    • 2023-03-17
    • 1970-01-01
    • 2022-07-29
    • 2011-01-04
    • 2015-10-20
    • 1970-01-01
    相关资源
    最近更新 更多