MongoDB聚合:将数组属性展平为根数组

Posted

技术标签:

【中文标题】MongoDB聚合:将数组属性展平为根数组【英文标题】:MongoDB aggregation: flatten array property into root array 【发布时间】:2021-01-25 02:42:57 【问题描述】:

我正在开发一个使用$graphLookUp 递归查找cmets 线程的功能,我几乎拥有它。 (虽然有点复杂,但它确实有效!)

我需要的最后一步是: 不要将嵌套的 posteriorThread 作为根数组 ($$ROOT) 的属性,而是将其合并到根本身。

聚合代码:
const posteriorThread = await Comment.aggregate([
  
    $match: 
      _id: post.threadDescendant
    
  ,

  
    $graphLookup: 
      from: 'baseposts',
      startWith: '$threadDescendant',
      connectFromField: 'threadDescendant',
      connectToField: '_id',
      as: 'posteriorThread'
    
  ,

  
    $unwind: '$posteriorThread'
  ,

  
    $sort:  'posteriorThread.depth': 1 
  ,

  
    $group: 
      _id: '$_id',
      posteriorThread:  $push: '$posteriorThread' ,
      root:  $first: '$$ROOT' 
    
  ,

  
    $project: 
      'root.posteriorThread': 0
    
  ,

  
    $replaceRoot: 
      newRoot: 
        $mergeObjects: [
          
            posteriorThread: '$posteriorThread'
          ,
          '$root'
        ]
      
    
  
]);
电流输出
 OUTPUT: posteriorThread
[
  
    _id: '5f7eab40575e6fc56ee07604',
    onModel: 'BasePost',
    depth: 1,
    user: '5f5da45245c07cc06e51b09f',
    text: 'thread 0',
    isThread: true,
    threadDescendant: '5f7eabad575e6fc56ee07607',
    posteriorThread: [
      
        _id: '5f7eabad575e6fc56ee07607',
        onModel: 'Comment',
        depth: 2,
        user: '5f5da45245c07cc06e51b09f',
        text: 'thread 1',
        isThread: true,
        threadDescendant: '5f7eac82575e6fc56ee07609'
      ,
      
        _id: '5f7eac82575e6fc56ee07609',
        onModel: 'Comment',
        depth: 3,
        user: '5f5da45245c07cc06e51b09f',
        text: 'thread 2',
        isThread: true
      
    ]
  
];

期望的输出
OUTPUT: posteriorThread 
[
  
    _id: '5f7eab40575e6fc56ee07604',
    onModel: 'BasePost',
    depth: 1,
    user: '5f5da45245c07cc06e51b09f',
    text: 'thread 0',
    isThread: true,
    threadDescendant: '5f7eabad575e6fc56ee07607'
  ,
  
    _id: '5f7eabad575e6fc56ee07607',
    onModel: 'Comment',
    depth: 2,
    user: '5f5da45245c07cc06e51b09f',
    text: 'thread 1',
    isThread: true,
    threadDescendant: '5f7eac82575e6fc56ee07609'
  ,
  
    _id: '5f7eac82575e6fc56ee07609',
    onModel: 'Comment',
    depth: 3,
    user: '5f5da45245c07cc06e51b09f',
    text: 'thread 2',
    isThread: true
  
];

我可以在常规 js 中聚合后完成此操作,但我更愿意在聚合中完成所有操作。 需要替换的部分是 mergeObjects 位并替换为其他内容或 group 聚合并采取不同的策略,但我不确定该放置什么。 另外,如果您有任何其他建议来使这个更清洁,我会全力以赴。

提前致谢。

【问题讨论】:

【参考方案1】:

真的很有挑战性。至少对我来说。而且非常有趣的案例。让我们试试我的解决方案。希望它有效..

db.test.aggregate([

    // PREViosU STEPS YOU ALREADY DID

    
        $group: 
            _id: "$_id",
            items: $push: "$$ROOT",
            subItems: $first: "$posteriorThread"
        
    ,
    
        $project: 
            "items.posteriorThread": 0
        
    ,
    
        $addFields: 
            allItems: 
                $concatArrays: ["$items", "$subItems"]
            
        
    ,
    
        $group: 
            _id: null,
            mergedItems: $push: "$allItems"
        
    ,
    
        $unwind: "$mergedItems"
    ,
    
        $unwind: "$mergedItems"
    ,
    
        $replaceRoot: 
          newRoot: "$mergedItems"
        
    

])

【讨论】:

嘿,您的解决方案不起作用,因为 subItems 不是数组,因此无法连接,但它确实让我更接近了!我现在基本上有了它,但是$replaceRoot 仍然给我留下了一个嵌套数组,但这没什么大不了的,因为我可以在聚合后得到正确的解决方案。 如果您想查看我的最终解决方案,请查看我的答案。 很高兴听到您找到了解决方案。我不知道为什么我的解决方案不起作用。我实际上是在处理您的当前输出。这意味着您必须使用您已经执行的步骤,然后在现有阶段之后添加我的管道阶段。在您的当前输出中,后线程是一个项目数组。所以它应该奏效了。无论如何,我实际上将您的当前输出存储到一个集合中,然后我尝试了我的步骤。它给了我结果。无论如何,这个问题真的很有趣.. 嗯,这很奇怪。是的,subItems 使用 $first 返回一个对象,所以当我转到 $concat 时,它会抛出错误,类似于“无法连接和对象”。但是,是的,我认为添加一个简单的线程功能将是几行代码,它变成了一整天的聚合和错误!再次感谢你的帮助;它真的让我走上了正确的道路。如果您想复制并粘贴我的答案,我会给您答案。 哈哈..不不。并不需要。请。我很高兴它真的有帮助!谢谢你的提问。老实说,我不知道该怎么做,这对我来说也是实验性的。问题陈述很有趣。所以我开始研究它,我也从中吸取了一些教训。【参考方案2】:

感谢@Sunil K Samanta 引导我朝着正确的方向前进。这不是最漂亮的解决方案,但它确实给了我正确的解决方案。

const posteriorThread = await Comment.aggregate([
  
    $match: 
      _id: post.threadDescendant
    
  ,

  
    $graphLookup: 
      from: 'baseposts',
      startWith: '$threadDescendant',
      connectFromField: 'threadDescendant',
      connectToField: '_id',
      as: 'posteriorThread'
    
  ,

  
    $unwind: '$posteriorThread'
  ,

  
    $sort:  'posteriorThread.depth': 1 
  ,

  
    $group: 
      _id: '$_id',
      items:  $push: '$$ROOT.posteriorThread' ,
      root:  $push: '$$ROOT' ,
    ,
  ,

  
    $project: 
      items: 1,
      root:  $slice: ['$$ROOT.root', 0, 1] ,
    ,
  ,

  
    $project: 
      'root.posteriorThread': 0,
    ,
  ,

  
    $addFields: 
      allItems: 
        $concatArrays: ['$root', '$items'],
      ,
    ,
  ,

  
    $replaceRoot: 
      newRoot: 
        full_posterior: '$$ROOT.allItems',
      ,
    ,
  ,
])
)[0].full_posterior;

【讨论】:

[0].full_posterior 是我们首先要避免的

以上是关于MongoDB聚合:将数组属性展平为根数组的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB 聚合与嵌套的对象属性数组与日期

MongoDB聚合在根中创建内部数组

将二维数组保存到 MongoDb 的最佳方法

使用聚合和查找 mongodb 从对象数组中获取最小值

使用聚合和查找 mongodb 从对象数组中获取最小值

如何在 mongodb 聚合中展开嵌套对象数组?