当前位置: 首页 > 面试题库 >

猫鼬查询以过滤数组并填充相关内容

汤承允
2023-03-14
问题内容

我试图查询该属性,该属性是对另一个架构的引用和一些其他数据的数组。为了更好的说明,以下是模式:

    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);
        }
    }
});

我在查询quantity属性时遇到了一些问题,即这不起作用:

Order.find({}).where({
    'articles.quantity': {
        $gte: 5
    }
}).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);
        }
    }
});

甚至有可能基于查询quantity吗?如果是这样,最好的方法是什么?

谢谢!

更新:

问题是,结果要么是完整的数组,要么什么都不是(请参阅更新的问题)。我只想获取数量等于或大于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 } }
            },
            function(err,orders) {
                // now it's all populated and mongoose documents
            }
        )
    }
)

因此,这里发生的是.aggregate()语句中实际发生的数组“过滤”
,但是当然,此操作的结果不再是“猫鼬文档”,因为它的一个方面.aggregate()是它可以“更改”文档结构,并且因此,猫鼬“假定”是这种情况,并且仅返回“普通对象”。

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

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

现在调用Model.populate(),然后可以在“猫鼬文档数组”上运行其他“填充”。

结果最终就是您想要的。

MongoDB低于3.2.x的版本

唯一真正改变的是聚合管道,因此,为了简洁起见,所有这些都需要包括在内。

MongoDB 2.6-
可以使用$map和组合过滤数组$setDifference。结果是一个“集合”,但是当猫鼬_id默认情况下在所有子文档数组上创建一个字段时,这不是问题:

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

的旧版本必须使用$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替代

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

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

    }
)

尽管这些只是简单的文档,但其结果与您从该.populate()方法中获得的结果相同。当然,如果确实需要,在所有情况下,您都可以随时再次再次“广播”到猫鼬文档中。

“最短”路径

这实际上可以追溯到原始语句,在该语句中,您基本上只是“接受”“查询”并不是要“过滤”数组内容。该.populate()可happilly这样做becuse它只是一个“查询”,并在方便的“文档”馅。

因此,如果您真的没有通过删除原始文档数组中的其他数组成员来节省带宽的“负担”,那么请.filter()在后期处理代码中将它们删除:

Order.find({ "articles.quantity": { "$gte": 5 } })
    .populate({
        "path": "articles.article",
        "match": { "price": { "$lte": 500 } }
    }).exec(function(err,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            
    }
)


 类似资料:
  • 问题内容: 一般而言,我对Mongoose和MongoDB还是很陌生,所以我很难确定是否可以进行以下操作: 有更好的方法吗? 编辑 如有任何混淆,我们深表歉意。我想做的是获取所有包含有趣标签或政治标签的商品。 编辑 没有where子句的文档: 使用where子句,我得到一个空数组。 问题答案: 对于大于3.2的现代MongoDB,您可以在大多数情况下用作替代。这也有实际上做加盟,而不是什么“在服务

  • 问题内容: 假设以下3个模型: 当我查询汽车时,我可以填充零件: 猫鼬是否有办法在所有汽车的嵌套零件对象中填充otherIds。 我可能可以遍历每辆车并尝试填充: 问题是我必须使用一个像async这样的库来对每个对象进行填充调用,然后等到所有操作完成后再返回。 可以在不循环所有汽车的情况下做? 问题答案: 更新: 请参阅以获取在Mongoose 4中添加的更紧凑的版本。摘要如下: 猫鼬3及以下:

  • 问题内容: 我在用猫鼬填充后无法通过在文档内部匹配的值来查询文档。 我的架构是这样的: 我想让所有用户拥有例如类型为“ Gmail”的电子邮件。 以下查询返回空结果: 我不得不像这样过滤JS中的结果: 有没有办法从猫鼬直接查询类似的东西? 问题答案: @Jason Cust 已经很好地解释了它- 在这种情况下,通常最好的解决方案是更改架构,以防止通过存储在单独集合中的文档的属性进行查询。 不过,这

  • 问题内容: 如何在示例文档中填充“组件”: 这是我的JS,可从Mongoose获取文档: 问题答案: 猫鼬4.5支持此 您可以加入不止一个深层次

  • 如何在示例文档中填充“组件”: 这是我的JS,我在这里通过Mongoose获取文档:

  • 问题内容: 我将MongoDB和Mongoose用作我的ODM,并且试图在同一条语句中使用和进行查询。 这是我的简单文档模型: 我只是想获取一个用户的每条消息,并按与他交谈的每个用户分组。我这样尝试过: 但是,不幸的是,我的模型没有方法。您有什么解决方案可以使它正常工作吗? 谢谢。 问题答案: 使用$ lookup的示例填充,lookup填充为数组,因此$ unwind。