[英]Is there a way to query documents in mongoDB/mongoose to match ALL objects in an array property WHERE the datetime is greater than a specified date
[英]Match Documents where all array members do not contain a value
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"
},
]
}
]
只返回第一個文檔,因為它至少有一個產品沒有“已完成”狀態,但最后兩個沒有進行切割,因為他們要么將所有產品的狀態設置為“已完成”,或者根本沒有任何產品。
嘗試以下查詢。 它正在獲取狀態不等於"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"}]}}
}
])
你需要.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"
}
]
}
因此帶有$map
輸入的$anyElementTrue
或.some()
基本上在這里做同樣的事情,並評估是否有任何匹配。 您使用“否定”斷言來“排除”實際找到匹配項的文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.