我试图查询属性,该属性是对另一个模式和一些其他数据的引用数组。为了更好地澄清,这里是架构:
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
}
答案 0 :(得分:3)
你需要"项目"所有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()
语句中,但当然结果不再是" mongoose文档"因为.aggregate()
的一个方面是它可以改变"文件结构,因此mongoose"假设"就是这种情况,只返回一个"普通对象"。
这不是一个真正的问题,因为当你看到$project
阶段时,我们实际上是根据定义的模式要求文档中存在的所有相同字段。所以即使它只是一个普通的对象"没有问题"铸造"它回到了一个mongoose文件中。
这是.map()
的来源,因为它会返回一个已转换的"文档"的数组,这对下一阶段非常重要。
现在你打电话给Model.populate()
然后可以运行更多的"人口"在" mongoose文件数组"。
结果最终是你想要的。
这里真正改变的唯一事情就是聚合管道,所以为了简洁,所有这些都需要包含在内。
MongoDB 2.6 - 可以使用$map
和$setDifference
的组合过滤数组。结果是" set"但是,当mongoose默认在所有子文档数组上创建_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" }
}}
],
另一个替代方案是在"服务器"上做所有事情。代替。这是MongoDB 3.2及更高版本$lookup
的一个选项:
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()
可以高兴地这样做,因为它只是另一个"查询"并填写"文件"方便。
所以,如果你真的没有保存" bucketloads"通过删除原始文档数组中的其他数组成员来带宽,然后在后处理代码中将它们.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
}
)
答案 1 :(得分:-1)
exports.getStudentBy = async (req, res) => {
try {
// get search_criteria from query parameter
// build a query object with it
// send data to the frontend
const { search_field, search_value } = req.query;
const queryObj = {};
if (search_field !== '' && search_value !== '') {
queryObj[search_field] = search_value;
}
const student = await Student.find(queryObj);