首页 > 解决方案 > Mongoose 选择子文档数小于 X 的记录

问题描述

我有 nodejs/mongodb 和 mongoose 设置。一切都是最后一个版本。

我在 NOSQL 中有点“全新”,并尝试使用 NOSQL 和 mongoose ORM 与这样的 SQL 查询重写:

select * from documents where count(select * from subdocuments where subdocuments.documentId = documents.id) = 5 order by documents.id desc limit 1

在我的情况下(嵌套集合),主要的集合结构是:

   documents: [{
         _id: ObjectId
         subdocuments: [{
              _id: ObjectId
         }]
   }]

documents.findOne所以我只需要使用mongoose 函数获取最后一个具有 count subdocuments = 5 的对象。

这是否可以使用 mongodb/mongoose 进行所有这些聚合?我找不到任何与我的案子有关的东西。

谢谢!

标签: node.jsmongodbmongoosemongodb-queryaggregation-framework

解决方案


有一个称为运算符$expr,它允许您在查询语言中使用聚合表达式。这在这里非常方便,您需要在查找查询中使用聚合运算符。

将您的查询分解成块以便理解,让我们进行检查

subdocuments.documentId = documents.id

在 mongo 中,这意味着迭代子文档数组以搜索满足上述条件的子文档,并且可以使用以下方式表示 $filter

{
    '$filter': {
        'input': '$subdocuments',
        'cond': { '$eq': ['$$this._id', '$_id'] }
    }
}

计数位通常用$size运算符表示,因此从上面获取结果,此查询

count(select * from subdocuments where subdocuments.documentId = documents.id)

被翻译成表达式

{
    '$size': {
        '$filter': {
            'input': '$subdocuments',
            'cond': { '$eq': ['$$this._id', '$_id'] }
        }
    }
}

使用比较运算符$gt满足条件

where count(select * from subdocuments where subdocuments.documentId = documents.id) > 5

作为

{
    '$gt': [
        { '$size': {
            '$filter': {
                'input': '$subdocuments',
                'cond': { '$eq': ['$$this._id', '$_id'] }
            }
        } },
        5
    ]
}

为了平等,用$eq

{
    '$eq': [
        { '$size': {
            '$filter': {
                'input': '$subdocuments',
                'cond': { '$eq': ['$$this._id', '$_id'] }
            }
        } },
        5
    ]
}

然后可以将上述内容find()与 $expr 一起用于查询

db.collection.find({
    '$expr': {
        '$eq': [
            { '$size': {
                '$filter': {
                    'input': '$subdocuments',
                    'cond': { '$eq': ['$$this._id', '$_id'] }
                }
            } },
            5
        ]
    }
})

排序和限制分别遵循 usingsort()limit()cursor 方法

db.collection.find({
    '$expr': {
        '$eq': [
            { '$size': {
                '$filter': {
                    'input': '$subdocuments',
                    'cond': { '$eq': ['$$this._id', '$_id'] }
                }
            } },
            5
        ]
    }
}).sort({ '_id': -1 }).limit(1)

此查询也有一个聚合变体。使用聚合框架,管道中使用的表达式是相似的。

查找部分在$match管道步骤中完成:

db.collection.aggregate([
    { '$match': {
        '$expr': {
            '$eq': [
                { '$size': {
                    '$filter': {
                        'input': '$subdocuments',
                        'cond': { '$eq': ['$$this._id', '$_id'] }
                    }
                } },
                5
            ]
        }
    } }
])

排序和限制分别在流水线阶段$sort$limit流水线阶段进行

db.collection.aggregate([
    { '$match': {
        '$expr': {
            '$eq': [
                { '$size': {
                    '$filter': {
                        'input': '$subdocuments',
                        'cond': { '$eq': ['$$this._id', '$_id'] }
                    }
                } },
                5
            ]
        }
    } },
    { '$sort': { '_id': 1 } },
    { '$limit': 1 }
])

推荐阅读