【问题标题】:How to find array greater than document size in MongoDB如何在MongoDB中找到大于文档大小的数组
【发布时间】:2021-02-17 06:56:24
【问题描述】:

我有如下架构

[
{
 id:"111"
 tags:[222,333,444,555]
},
{
 id: "222"
 tags:[312,345,534]
},
{
 id:"333"
 tags:[111,222,333,444,555]
},

]

我想在聚合管道中找到所有标签数组大小大于$match 返回的文档大小的文档,所以在上面的例子中。文档数为 3,所以我想返回标签数组大小大于 3 的所有文档

[
{
 id:"111"
 tags:[222,333,444,555]
},
{
 id:"333"
 tags:[111,222,333,444,555]
},

]

我正在使用聚合管道来处理其他信息,我被困在如何存储文档大小以便我可以找到所有大于文档大小的标签

下面是我正在使用的查询,我想在聚合中一次调用

.aggregate([
        
   {
      "$match":{
         "ids":{
            "$in":[
               "111",
               "222",
                "333"
            ]
         }
      }
   })]

【问题讨论】:

  • 不,它没有静态大小,在我的情况下,数组的大小取决于“匹配”返回的文档大小聚合
  • 您的意思是集合大小(文档数),如集合中的文档数?
  • 是的,聚合管道中$match返回的文档数

标签: node.js mongodb


【解决方案1】:

你可以试试,

  • $match你的条件
  • $group by null 并创建 root 文档数组并获取 count 中的根文档数
  • $unwind解构root数组
  • $matchtags大小和count大于或不使用$expr表达式匹配
  • $replaceRoot 替换根目录下的 root 对象
db.collection.aggregate([
  { $match: { id: { $in: ["111", "222", "333"] } } },
  {
    $group: {
      _id: null,
      root: { $push: "$$ROOT" },
      count: { $sum: 1 }
    }
  },
  { $unwind: "$root" },
  { $match: { $expr: { $gt: [{ $size: "$root.tags" }, "$count"] } } },
  { $replaceRoot: { newRoot: "$root" } }
])

Playground


第二个选项:

  • 前 2 个阶段 $match$group 都与上述查询相同,
  • $project 过滤根数组匹配条件如果tags 的大小和count 大于或不大于,这将返回过滤的根数组
  • $unwind解构根数组
  • $replaceRoot 将根对象替换为根
db.collection.aggregate([
  { $match: { id: { $in: ["111", "222", "333"] } } },
  {
    $group: {
      _id: null,
      root: { $push: "$$ROOT" },
      count: { $sum: 1 }
    }
  },
  {
    $project: {
      root: {
        $filter: {
          input: "$root",
          cond: { $gt: [{ $size: "$$this.tags" }, "$count"] }
        }
      }
    }
  },
  { $unwind: "$root" },
  { $replaceRoot: { newRoot: "$root" } }
])

Playground

如果您愿意,您可以跳过$unwind$replaceRoot 阶段,因为此查询始终返回根目录中的一个文档,因此您可以像result[0]['root'] 这样轻松访问,您可以节省2 个阶段的处理和执行时间。

【讨论】:

    【解决方案2】:

    您可以使用$facet 获取两个流,即一个带有过滤文档的流,以及使用$count 的计数。生成的流然后可以 与$filter 进一步聚合如下以获得所需的结果

    db.getCollection('collection').aggregate([
        { '$facet': {
            'counts': [
                { '$match': { 'id': { '$in': ['111', '222', '333'] } } }, 
                { '$count': "numberOfMatches" }
            ],
            'docs': [
                { '$match': { 'id': { '$in': ['111', '222', '333'] } } }, 
            ]  
        } },
        {  '$project': {
            'result': {
                '$filter': {
                    'input': '$docs',
                    'cond': {
                        '$gt': [
                            { '$size': '$$this.tags' },
                            { '$arrayElemAt': ['$counts.numberOfMatches', 0] }
                        ]
                    }
                }
            } 
        } }
    ])
    

    【讨论】:

      【解决方案3】:

      Facet 帮你解决这个问题。

      • $facet 有助于对传入的文档进行分类。我们使用totalDoc 统计文档,allDocuments 获取所有文档
      • $arrayElemAt 有助于从totalDoc 获取第一个对象,我们已经知道只有一个对象应该在totalDoc 中。因为我们分组的时候使用_id:null
      • $unwind 有助于解构 allDocuments 数组

      这里是代码

      db.collection.aggregate([
        {
          $facet: {
            totalDoc: [
              {
                $group: {
                  _id: null,
                  count: {
                    $sum: 1
                  }
                }
              }
            ],
            allDocuments: [
              {
                $project: {
                  tags: 1
                }
              }
            ]
          }
        },
        {
          $addFields: {
            totalDoc: {
              "$arrayElemAt": [
                "$totalDoc",
                0
              ]
            }
          }
        },
        {
          $unwind: "$allDocuments"
        },
        {
          $addFields: {
            sizeGtDoc: {
              $gt: [
                {
                  $size: "$allDocuments.tags"
                },
                "$totalDoc.count"
              ]
            }
          }
        },
        {
          $match: {
            sizeGtDoc: true
          }
        },
        {
          "$replaceRoot": {
            "newRoot": "$allDocuments"
          }
        }
      ])
      

      工作Mongo playground

      【讨论】:

      • 这似乎可行,但查询对于小任务来说太大了,如果我没有得到任何更短的答案,我会接受这个答案
      • 好的,没问题。我也在检查做空这个
      猜你喜欢
      • 2020-12-12
      • 2020-11-18
      • 2013-06-01
      • 2012-05-13
      • 1970-01-01
      • 2015-09-21
      • 2015-05-03
      • 1970-01-01
      • 2012-09-21
      相关资源
      最近更新 更多