I have a collection "structure" with a document :
{
"case" : {
"version" : 3,
"key" : 1
},
"salle" : {
"version" : 2,
"key" : 1
}
}
And I try a $cond aggregation with $ifNull operator :
db.structure.aggregate([
{
"structure" : {
"$cond": {
"if": {
"$ifNull": ["$salle", true]
},
"then": "$case.key",
"else": "$salle.key"
}
}
}
])
The trouble is that $ifNull always return true and displays $case.key, while $salle is not null
The most surprising is that I get the right response with $not operator :
db.structure.aggregate([
{
"structure" : {
"$cond": {
"if": {
"$not": {
"$ifNull": ["$salle", false]
}
},
"then": "$case.key",
"else": "$salle.key"
}
}
}
])
Can anyone can explain me what's going on ?
Related
I'm using following aggregated query to get results (let say query1):
db.fb.aggregate(
[
{
$addFields : { noOfLikes : { $sum : { $map : { input : "$facebookEvents", as : "f", in : {$cond : [ { $eq : ["$$f.type" , "like"] }, 1, 0 ]} }}}}
},
{
$match : {"noOfLikes" : {$gte : 2}}
}
]
)
and another query to get some other results (let's say query2):
db.fb.aggregate(
[
{ $match : { author : "dave" } },
{ $match : { test : "test1" } }
]
)
Is it possible to get query1 OR query2 using a single aggregate query? In other words, I want to get results that match either query1 or query2 using a single query. Appreciate any help
mongo version: 3.4.4
Within a single aggregate query, you would need to run your pipeline as follows:
db.fb.aggregate([
{
"$addFields": {
"noOfLikes": {
"$sum" : {
"$map": {
"input": "$facebookEvents",
"as": "f",
"in": {
"$cond": [{ "$eq": ["$$f.type" , "like"] }, 1, 0 ]
}
}
}
}
}
},
{
"$match" : {
"$or": [
{ "noOfLikes" : { "$gte" : 2 } },
{ "author": "dave", "test": "test1" }
]
}
}
])
or using $redact as:
db.fb.aggregate([
{
"$redact": {
"$cond": [
{
"$or": [
{ "$gte": [
{
"$sum" : {
"$map": {
"input": "$facebookEvents",
"as": "f",
"in": {
"$cond": [
{ "$eq": ["$$f.type" , "like"] },
1,
0
]
}
}
}
}, 2 ]
},
{
"$and": [
{ "$eq": ["$author", "dave"] },
{ "$eq": ["$test", "test1"] }
]
}
]
},
"$$KEEP",
"$$PRUNE"
]
}
}
])
You can use $facet
db.fb.aggregate(
{
$facet : {
byLikes : [
{ $addFields : { noOfLikes : { $sum : { $map : { input : "$facebookEvents", as : "f", in : {$cond : [ { $eq : ["$$f.type" , "like"] }, 1, 0 ]} }}}} },
{ $match : {"noOfLikes" : {$gte : 2}}}
],
byAuthor : [
{ $match : { author : "dave" } },
{ $match : { test : "test1" } }
]
}
}
)
"$redact": {
"$cond": {
"if": {
"$eq": [ { "$arrayElemAt": [ "$messages.type", -1 ] }, "A" ]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}
On the above code, i need to add 2 more condition in if statements but not able to add them.
i.e (messages.type = 'A' or messages.type = 'D') && (messages.sender=61)
Please help
you can use $cond with $and like this:
{$cond : [{$and : [{$eq : [1,1]},{$eq : [2,2]},{$eq : [3,3]}]},"yes","no"]}
returns "yes"
{$cond : [{$and : [{$eq : [2,1]},{$eq : [2,2]},{$eq : [3,3]}]},"yes","no"]}
returns "no"
edit:
As per the query in the OP (note this is untested):
"$redact":{
$cond : [
{
$and : [
{
$or : [
{
$eq : [
"$messages.type",
"A"
]
},
{
$eq : [
"$messages.type",
"D"
]
},
]
},
{
$eq : [
"$messages.sender",
61
]
}
]
},
"$$KEEP",
"$$PRUNE"
]
}
In mongodb 3.4.5 think below simple collection ooo:
{ "id" : 1, "to" : [ { "id" : 2 }, { "id" : 4, "valid" : true } ] }
Want to $redact to if valid not true:
db.ooo.aggregate([{ "$redact": {
"$cond": {
"if": { "$ne": [ "$valid", true] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}])
Result:
{ "id" : 1, "to" : [ { "id" : 2 } ] }
But when changed condition to if valid is true:
db.ooo.aggregate([{ "$redact": {
"$cond": {
"if": { "$eq": [ "$valid", true] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}])
The result is empty.
The $redact walks the whole documents from top level then downwards, the "$valid": true part cannot pass on top level (top level have no valid field), then the $$ROOT be $$PRUNEd, and nothing returned.
The solution (skip this and see below updated):
db.ooo.aggregate([{ "$redact": {
"$cond": {
"if": { $or: ["$to", {"$eq": [ "$valid", true]}] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}])
UPDATE:
based on #neil's comment, I corrected the answer:
db.ooo.aggregate([{ "$redact": {
"$cond": {
"if": { $or: ["$to", {"$eq": [ "$valid", true]}] },
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}])
And the expected result shown:
{ "id" : 1, "to" : [ { "id" : 4, "valid" : true } ] }
That is the { "id" : 2 } be pruned.
The following query is not valid. Can anyone please point me to the error?
I am trying to filter on all documents where the last item in the Progress array has a given state - for example "Done".
db.logdata.aggregate(
[
{ "$match": { "ProcessingInfo.Progress" : { "$exists": true } } },
{ "$redact":
{
"$cond": {
"if": { "$eq": [ { "$arrayElemAt": [ "$ProcessingInfo.Progress.State", -1 ], "Done" } ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}
}
]
)
Sample document (which should be matched - because the last State in Progress array is "InProgress"):
{
"_id" : ObjectId("578fa85bb29339a1fa6d6109"),
"ProcessingInfo" : {
"DateStarted" : ISODate("2016-08-06T16:55:58.294+0000"),
"Attempt" : NumberInt(1),
"LastState" : "Failed",
"Progress" : [
{
"State" : "Failed",
"StateDescription" : ""
},
{
"State" : "Success",
"StateDescription" : ""
},
{
"State" : "Done",
"StateDescription" : ""
},
{
"State" : "InProgress",
"StateDescription" : ""
}
]
}
}
To kind of "circumvent" this problem I have an extra field in the document root "LastState" - is that maybe the way to go (complexity-wise)?
Your query has a little syntax error: your "Done" should be in the $eq array, not in the object containing $arrayElemAt definition. The following query (optimised to filter out documents without the expected State anywhere in the ProcessInfo.Progress array thanks to what Styvane suggested) should return your example document:
[
{ "$match": { "ProcessingInfo.Progress.State" : "InProgress" } },
{ "$redact":
{
"$cond": {
"if": { "$eq": [ { "$arrayElemAt": [ "$ProcessingInfo.Progress.State", -1 ] }, "InProgress" ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}
}
]
How can I use $in (like) operator inside $cond? For example, inside a "project" stage I want to create a new field called "category" that is conditional on some other fields (for grouping later): it's value would be catA if "test_value" is in the list [1,9] else "catB". For small list I can use set of $eq inside "$or".
"category": {
"$cond": {
"if" :{
"$or" : [
{ "$eq" : ["$test_value", 1]},
{"$eq" : ["$test_value", 9] }
]
},
"then": {
"$literal" : "catA"
},
"else": {
"$literal" : "catB"
}
}
Is there any way to use something like:
"if" :{
"$in" : {"$test_value", [1,9]}
},
"then": {
"$literal" : "catA"
},
"else": {
"$literal" : "catB"
}
}
I am using mongoengine, and above syntax is not allowed.
You can do intersection of the field and the list of expected values for that field using $setIntersection and check the length of the result. If the length is bigger that 0 then the field has value from the list of expected values.
Try the following query:
db.collection.aggregate([
{
$project: {
category: {
$let: {
vars: {
resultSize: {
$size: { $setIntersection: [["$test_value"], [1, 9]] }
}
},
in: {
"$cond": {
"if": { "$gt" : ["$$resultSize", 0] },
"then": { "$literal" : "catA" },
"else": { "$literal" : "catB" }
}
}
}
}
}
}
])