MongoDB 聚合 $group 阶段已从外部创建的值/变量

Posted

技术标签:

【中文标题】MongoDB 聚合 $group 阶段已从外部创建的值/变量【英文标题】:MongoDB aggregation $group stage by already created values / variable from outside 【发布时间】:2021-02-28 15:21:34 【问题描述】:

成像我有一个对象数组,在aggregate 查询之前可用:

const groupBy = [
  
    realm: 1,
    latest_timestamp: 1318874398, //Date.now() values, usually different to each other
    item_id: 1234, //always the same
  ,
  
    realm: 2,
    latest_timestamp: 1312467986, //actually it's $max timestamp field from the collection
    item_id: 1234,
  ,
  
    realm: ..., //there are many of them
    latest_timestamp: ...,
    item_id: 1234,
  ,
  
    realm: 10,
    latest_timestamp: 1318874398, //but sometimes then can be the same
    item_id: 1234,
  ,
]

并使用以下架构收集 (example set available on MongoPlayground):

  
    realm: Number,
    timestamp: Number,
    item_id: Number,
    field: Number, //any other useless fields in this case
  

我的问题是,如何通过聚合框架使用已经可用的数据集(来自 groupBy)$group 集合中的值?

什么都试过了。

好的,让我们跳过废话,例如:

for (const element of groupBy) 
  //array of `find` queries

我当前的工作聚合查询是这样的:

      //first stage
      
         $match:  
           "item": 1234
           "realm" [1,2,3,4...,10]
         
      ,
      
        $group: 
          _id: 
            realm: '$realm',
          ,
          latest_timestamp: 
            $max: '$timestamp',
          ,
          data: 
            $push: '$$ROOT',
          ,
        ,
      ,
      
        $unwind: '$data',
      ,
      
        $addFields: 
          'data.latest_timestamp': 
            $cond: 
              if: 
                $eq: ['$data.timestamp', '$latest_timestamp'],
              ,
              then: '$latest_timestamp',
              else: '$$REMOVE',
            ,
          ,
        ,
      ,
      
        $replaceRoot: 
          newRoot: '$data',
        ,
      ,
      //At last, after this stages I can do useful job

但我发现它有点过时了,而且我已经听说使用[.mapReduce][1] 可以比这个查询更快地解决我的问题。 (但官方文档听起来并不乐观)是真的吗?

就目前而言,在开始处理(对我而言)有用的文档之前,我使用了 4 或 5 个阶段。

最近更新:

我检查了$facet 阶段,我发现它对这种特定情况感到好奇。可能会对我有所帮助。

物有所值:

在必要的阶段后收到文件后,我正在构建一个有代表性的聚类图,你可能也知道as a heatmap

之后,我逐个迭代每个文档(或对象数组)以找到它们正确的 xy 协调到位,应该是:

[
   
    x: x (number, actual $price), 
    y: y (number, actual $realm),
    value: price * quantity,
    quantity: sum_of_quantity_on_price_level
  
]

就目前而言,它是一个带有 for...loop 的旧代码,但在未来,我将使用 $facet => $bucket 运算符来完成这种工作。

【问题讨论】:

不确定我是否了解确切的问题。是不是这个查询太慢了,你正在寻找一个性能更好的解决方案? 好吧,在某种程度上,真正的问题不是查询本身的性能,而是我花费了大量时间和资源为集合中的每个$realm 查找$latest 时间戳10M+ 文档。实际上已经找到、存储并准备好服务,但不幸的是,我不知道如何在 聚合 $group 阶段使用它们。所以我想学习,有没有办法使用这些数据并扩展我对 MongoDB 本身的体验。因为我知道如何为$match 阶段“导入”变量,但不知道$group 啊,我明白了,您想使用已经存储在变量中的数据作为查询的起点,对吗?如果是这样,那么这取决于您拥有多少组以及您尝试对它们做什么。如果您想将处理拆分为两个并行管道(因此每个管道从同一点开始),您确实可以使用$facet @Avius,谢谢,我也发现它很有用。如果您以前使用过它,可以为我描述一下,如果我有大约 100 个左右的组,它们完全相同的查询,它“可以”使用吗? 那么,您想对这些组做什么?您能否更新您的问题,以便我们可以看到最终结果应该是什么样子?或许也可以添加//At last, after this stages I can do useful job 之后的代码。 【参考方案1】:

因此,我以另一种但相关的方式找到了我的问题的答案。

我正在考虑使用 $facet 运算符,老实说,它仍然是一个选项,但使用它,如下所示是一种不好的做法。

//building $facet query before aggregation

const ObjectQuery = 
for (const realm of realms) 
  Object.assign(ObjectQuery,  `$realm.name` : [ ... ] 


//mongoose query here
aggregation([
    $facet: ObjectQuery
  ,
  ...
])

所以,我选择了$project 阶段和$switch 运算符来过滤结果,例如$groups。

另外,使用MapReduce 也可以解决这个问题,但是出于某种原因,Mongo 官方文档recommends to avoid using it,并选择了聚合:$group 和 $merge 运算符。

【讨论】:

以上是关于MongoDB 聚合 $group 阶段已从外部创建的值/变量的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB的聚合操作

mongodb聚合命令

MongoDB 聚合比较:group()、$group 和 MapReduce

mongodb - 聚合因内存错误而失败

MongoDB 聚合管道(Aggregation Pipeline)

MongoDb 聚合在 $group 中使用 $sortByCount