MongoDb If and or statement Issue - mongodb

"$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"
]
}

Related

Mongo $IfNull doesn't do what it is expected to do

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 ?

Compare Size of Arrays Inside an Array of Objects

I want to find all documents where sCompetitions.length is greater than competitions.length.
Here's some sample documents document:
{
"_id" : ObjectId("59b28f432b4353d3f311dd1b"),
"name" : "Ford Focus RS 2008",
"requirements" : [
{
"rankType" : "D1",
"competitions" : [
ObjectId("59b151fd2b4353d3f3116827"),
ObjectId("59b151fd2b4353d3f3116829")
],
"sCompetitions" : [
"Rallye Monte-Carlo",
"Rally Sweden"
]
},
{
"rankType" : "A3",
"competitions" : [
ObjectId("59b151fd2b4353d3f3116f6b")
],
"sCompetitions" : [
"Rally Italia Sardegna",
"Neste Rally Finland"
]
}
]
},
{
"_id" : ObjectId("0000b28f432b4353f311dd1b"),
"name" : "Ford Focus RS 2012",
"requirements" : [
{
"rankType" : "D1",
"competitions" : [
ObjectId("59b151fd2b4353d3f3116827"),
ObjectId("59b151fd2b4353d3f3116829")
],
"sCompetitions" : [
"Rallye Monte-Carlo",
"Rally Sweden"
]
},
{
"rankType" : "A3",
"competitions" : [
ObjectId("59b151fd2b4353d3f3116f6b"),
ObjectId("59b151fd2b4353d3f3116f6b")
],
"sCompetitions" : [
"Rally Italia Sardegna",
"Neste Rally Finland"
]
}
]
}
So looking at the samples it would only return ObjectId("59b28f432b4353d3f311dd1b")
My problem is that requirements is an array by itself, so I would need to somehow iterate it
No need to "iterate". All you really need is an $anyElementTrue check after returning results from $map. And you can do this all inside a $redact action:
Model.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": "$requirements",
"as": "r",
"in": {
"$gt": [
{ "$size": "$$r.sCompetitions" },
{ "$size": "$$r.competitions" }
]
}
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
So it's a simple comparison by $size for each array element, and then if "any" of those elements is true, the document is "kept" or otherwise "pruned" from the results.

mongodb aggregate $redact to filter array elements

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.

MongoDb filter on value of the last element in an array

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"
}
}
}
]

Membership testing in conditional operator in mongo

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" }
}
}
}
}
}
}
])