MongoDB 选择器变得非常复杂,尤其是当您使用JOIN
和其他精美关键字来自 mySQL 时。我尽力使这个问题的标题尽可能清楚,但却失败了。
例如,让MongoDB集合的文档具有以下模式:
{
_id : int
products : [
{
qte : int
status : string
},
{
qte : int
status : string
},
{
qte : int
status : string
},
...
]
}
我正在尝试运行db.collection.find({ })
查询,返回所有产品不将字符串“已完成”作为状态的文档。请注意products
数组的长度可变。
我们还可以说我们希望所有至少有一个产品的状态都没有“完成”的文档。
如果我将其作为 Javascript循环运行,我们会有以下内容:
// Will contain queried documents
var matches = new Array();
// The documents variable contains all documents of the collection
for (var i = 0, len = documents.length; i < len; i++) {
var match = false;
if (documents[i].products && documents[i].products.length !== 0) {
for (var j = 0; j < documents[i].products; j++) {
if (documents[i].products[j].status !== "finished") {
match = true;
break;
}
}
}
if (match) {
matches.push(documents[i]);
}
}
// The previous snippet was coded directly in the Stack Overflow textarea; I might have done nasty typos.
matches
数组将包含我正在寻找的文档。现在,我希望有一种类似于collection.find({"products.$.status" : {"$ne":"finished"}})
的方法,但是当我这样做时,MongoDB会讨厌我的脸。
此外,没有任何产品的文档需要被忽略,但我已经用$and
子句想出了这个。请注意,我需要返回整个文档,而不仅仅是产品数组。如果文档的产品未“完成”,则应存在整个文档。如果文档的所有产品都设置为“已完成”,则根本不会返回该文档。
MongoDB版本:3.2.4
示例
假设我们有一个包含三个文档的集合。
这个匹配,因为其中一个状态未“完成”。
{
_id : 1,
products : [
{
qte : 10,
status : "finished"
},
{
qte : 21,
status : "ongoing"
},
]
}
这不匹配,因为所有状态都设置为“已完成”
{
_id : 2,
products : [
{
qte : 35,
status : "finished"
},
{
qte : 210,
status : "finished"
},
{
qte : 2,
status : "finished"
},
]
}
这也不匹配,因为没有产品。如果products
字段未定义,它也不匹配。
{
_id : 3,
products : []
}
同样,如果我们在一个包含本例中三个文档的集合中运行查询,那么输出将为:
[
{
_id : 1,
products : [
{
qte : 10,
status : "finished"
},
{
qte : 21,
status : "ongoing"
},
]
}
]
只返回第一个文档,因为它至少有一个产品没有“已完成”状态,但最后两个没有进行剪切,因为他们要么将所有产品的状态设置为“已完成” “,或根本没有任何产品。
答案 0 :(得分:3)
尝试以下查询。它正在获取状态不等于"finished"
注意:此查询仅适用于MongoDB 3.2+
db.collection.aggregate([
{
$project:{
"projectid" : 1,
"campname" : 1,
"campstatus" : 1,
"clientid" : 1,
"paymentreq" : 1,
products:{
$filter:{
input:"$products",
as: "product",
cond:{$ne: ["$$product.status", "finished"]}
}
}
}
},
{
$match:{"products":{$gt: [0, {$size:"products"}]}}
}
])
答案 1 :(得分:0)
您需要.aggregate()
而不是.find()
。这是确定所有元素实际上不包含您想要的内容的唯一方法:
// Sample data
db.products.insertMany([
{ "products": [
{ "qte": 1 },
{ "status": "finished" },
{ "status": "working" }
]},
{ "products": [
{ "qte": 2 },
{ "status": "working" },
{ "status": "other" }
]}
])
然后使用$redact
进行聚合操作:
db.products.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": [
{ "$map": {
"input": "$products",
"as": "product",
"in": {
"$eq": [ "$$product.status", "finshed" ]
}
}}
]
},
"then": "$$PRUNE",
"else": "$$KEEP"
}
}}
])
或者您也可以使用较差和较慢的堂兄$where
db.products.find(function(){
return !this.products.some(function(product){
return product.status == "finished"
})
})
两者都只返回一个示例文档:
{
"_id" : ObjectId("56fb4791ae26432047413455"),
"products" : [
{
"qte" : 2
},
{
"status" : "working"
},
{
"status" : "other"
}
]
}
所以带$anyElementTrue
输入的$map
或.some()
基本上在这里做同样的事情并评估是否有任何匹配。您使用“否定”断言来“排除”实际找到匹配项的文档。