微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

node.js – 用于过滤数组和填充相关内容的Mongoose Query

我正在尝试查询属性,该属性是对另一个模式和一些其他数据的引用数组.为了更好地澄清,这是架构:

var orderSchema = new Schema({
        orderDate: Date,articles: [{
            article: {
                type: Schema.Types.ObjectId,ref: 'Article'
            },quantity: 'Number'
        }]
    }),Order = mongoose.model('Order',orderSchema);

虽然我设法成功查询参考,即:

Order.find({}).populate('articles.article',null,{
    price: {
        $lte: 500
    }
}).exec(function(err,data) {
    for (var order of data) {
        for (var article of order.articles) {
            console.log(article);
        }
    }
});

我有一些问题查询数量属性,即这不起作用:

Order.find({}).where({
    'articles.quantity': {
        $gte: 5
    }
}).populate('articles.article',{
    /*price: {
        $lte: 500
    }*/
}).exec(function(err,data) {
    for (var order of data) {
        for (var article of order.articles) {
            console.log(article);
        }
    }
});

甚至可以将查询基于数量?如果是这样,最好的方法是什么?

谢谢!

更新:

问题是,结果是一个完整的数组,或者什么都没有(请参阅更新的问题).我想只获得那些数量大于或等于5的记录.使用你的(和我的)方法,我得到根本没有记录(如果我设置$gte:5001)或两个记录(如果我设置$gte: 5000)

{
    "_id": ObjectId('56fe76c12f7174ac5018054f'),"orderDate": ISODate('2016-04-01T13:25:21.055Z'),"articles": [
        {
            "article": ObjectId('56fe76c12f7174ac5018054b'),"quantity": 5000,"_id": ObjectId('56fe76c12f7174ac50180551')
        },{
            "article": ObjectId('56fe76c12f7174ac5018054c'),"quantity": 1,"_id": ObjectId('56fe76c12f7174ac50180552')
        }
    ],"__v": 1
}

解决方法

你需要在这里“预测”匹配,因为所有MongoDB查询都会查找“文档”,其中“至少有一个元素”“大于”你要求的条件.

因此,过滤“数组”与您拥有的“查询”条件不同.

一个简单的“投影”只会将“第一个”匹配的项目返回到该条件.所以它可能不是你想要的,但作为一个例子:

Order.find({ "articles.quantity": { "$gte": 5 } })
    .select({ "articles.$": 1 })
    .populate({
        "path": "articles.article","match": { "price": { "$lte": 500 } }
    }).exec(function(err,orders) {
       // populated and filtered twice
    }
)

那种“有点”做你想要的,但问题实际上只会返回“articles”数组中的最多一个元素.

要正确执行此操作,您需要.aggregate()来过滤数组内容.理想情况下,这是通过MongoDB 3.2和$filter完成的.但是这里还有一种特殊的方法来.populate():

Order.aggregate(
    [
        { "$match": { "artciles.quantity": { "$gte": 5 } } },{ "$project": {
            "orderdate": 1,"articles": {
                "$filter": {
                    "input": "$articles","as": "article","cond": {
                       "$gte": [ "$$article.quantity",5 ]
                    }
                }
            },"__v": 1
        }}
    ],function(err,orders) {
        Order.populate(
            orders.map(function(order) { return new Order(order) }),{
                "path": "articles.article","match": { "price": { "$lte": 500 } }
            },orders) {
                // Now it's all populated and mongoose documents
            }
        )
    }
)

所以这里发生的是数组的实际“过滤”发生在.aggregate()语句中,但当然结果不再是“mongoose文档”,因为.aggregate()的一个方面是它可以“改变“文档结构,因此mongoose”假设“就是这种情况,只返回一个”普通对象“.

这不是一个真正的问题,因为当你看到$project阶段时,我们实际上要求根据定义的模式存在于文档中的所有相同字段.因此即使它只是一个“普通对象”,也没有问题将其“转换”回到一个mongoose文档中.

这是.map()的用武之地,因为它返回一个转换后的“文档”数组,这对下一个阶段很重要.

现在你调用Model.populate(),然后可以在“mongoose文档数组”上运行更多的“人口”.

结果最终是你想要的.

MongoDB早于3.2.x的旧版本

这里真正改变的唯一事情是聚合管道,所以为了简洁,所有这些都需要包含在内.

MongoDB 2.6 – 可以使用$map$setDifference的组合过滤数组.结果是“set”但是当认情况下mongoose在所有子文档数组上创建_id字段时这不是问题:

[
        { "$match": { "artciles.quantity": { "$gte": 5 } } },"articles": {
                "$setDiffernce": [
                   { "$map": {
                      "input": "$articles","in": {
                         "$cond": [
                             { "$gte": [ "$$article.price",5 ] },"$$article",false
                         ]
                      }
                   }},[false]
                ]
            },

较旧的修订必须使用$unwind

[
        { "$match": { "artciles.quantity": { "$gte": 5 } }},{ "$unwind": "$articles" },{ "$match": { "artciles.quantity": { "$gte": 5 } }},{ "$group": {
          "_id": "$_id","orderdate": { "$first": "$orderdate" },"articles": { "$push": "$articles" },"__v": { "$first": "$__v" }
        }}
    ],

$lookup替代品

另一种替代方法是在“服务器”上执行所有操作.这是MongoDB 3.2及更高版本$lookup一个选项:

Order.aggregate(
    [
        { "$match": { "artciles.quantity": { "$gte": 5 } }},"__v": 1
        }},{ "$lookup": {
            "from": "articles","localField": "articles.article","foreignField": "_id","as": "articles.article"
        }},{ "$unwind": "$articles.article" },"__v": { "$first": "$__v" }
        }},"cond": {
                       "$lte": [ "$$article.article.price",500 ]
                    }
                }
            },orders) {

    }
)

虽然这些只是简单的文档,但它与.populate()方法的结果相同.当然,如果你真的必须的话,你可以在所有情况下再次“演员”到mongoose文件.

“最短”的路径

这实际上可以追溯到原始语句,您基本上只是“接受”“查询”并不意味着“过滤”数组内容. .populate()可以愉快地这样做,因为它只是另一个查询”,并且方便地填充在“文档”中.

因此,如果您真的没有通过删除原始文档数组中的其他数组成员来保存bandwith的“bucketloads”,那么只需.filter()将它们放在后处理代码中:

Order.find({ "articles.quantity": { "$gte": 5 } })
    .populate({
        "path": "articles.article",orders) {
        orders = orders.filter(function(order) {
            order.articles = order.articles.filter(function(article) {
                return (
                    ( article.quantity >= 5 ) &&
                    ( article.article != null )
                )
            });
            return order.aricles.length > 0;
        })

        // orders has non matching entries removed            
    }
)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐