mongodb aggregate pipeline if else to execute one of the query - mongodb

My objects looks like the following
{
id: 1,
a: "val7f",
b: "online",
c: "reg",
d: "retd"
},
{
id: 2,
a: "val6f",
b: "online",
c: "reg",
d: "new"
},
{
id: 3,
a: "val6f",
b: "offline",
c: "non-reg",
d: "new"
}
I want to build a query in the following manner.
if (a=="val7f" && b=="offline" && c="reg") has objs > 1 return result Objs
else if(a=="val7f" && b=="offline" && c="reg") has objs > 1 return result objs
etc.
I built the following query but values displayed are from else query even if there are results for the if case:
db.Collection.aggregate([
{ $redact: {
$cond: {
if: {
$and: [
{ $eq: [ "a", "val7f" ] },
{ $eq: [ "b", "offline" ] },
{ $eq: [ "c", "reg" ] }
]
},
then: "$$DESCEND",
else: {
$cond: {
if: {
$and: [
{ $eq: [ "$a", "val6f"] },
{ $eq: [ "b", "offline"] },
{ $eq: [ "c", "reg"] }
]
},
else: "$$PRUNE"
}
}
}
} }
]);
Summarised as follows
I've put condition as follows
if: {
$and: [
{ $eq: [ "a", "val7f" ] },
{ $eq: [ "b", "offline" ] },
{ $eq: [ "c", "reg" ] }
]
}
if this has result say
{
id: 6,
a: "val7f",
b: "online",
c: "reg",
d: "retd"
}
it should return the above document.
in case there are no document matching criteria
else case should execute
if: {
$and: [
{ $eq: [ "$a", "val6f"] },
{ $eq: [ "b", "offline"] },
{ $eq: [ "c", "reg"] }
]
}
and if there is match for this say
{
id: 11,
a: "val6f",
b: "offline",
c: "reg",
d: "new"
}
In my case, for the query built above I'm getting both the documents in the output shell.

Related

Mongoose fails to updateMany with $switch operator

I tried to use an example in the official document of mongodb,
db.students3.updateMany({ },
[
{ $set: { grade: { $switch: {
branches: [
{ case: { $gte: [ "$average", 90 ] }, then: "A" },
{ case: { $gte: [ "$average", 80 ] }, then: "B" },
{ case: { $gte: [ "$average", 70 ] }, then: "C" },
{ case: { $gte: [ "$average", 60 ] }, then: "D" }
],
default: "F"
} } } }
])
Property grade is defined as Number type. I got the error, when tried to update all documents in the database students3,
CastError: Cast to Number failed for value "{ '$switch': { branches: [ [Object] ] } }" (type Object) at path "grade"
Could someone explain the error?
Thanks.
You have to $trunc first, then average field would be created.
{ $set: { average : { $trunc: [ { $avg: "$tests" }, 0 ] }, modified: "$$NOW" } }
complete update
db.collection.update({},
[
{
$set: {
average: { $trunc: [ { $avg: "$tests" }, 0 ] },
modified: "$$NOW"
}
},
{
$set: {
grade: {
$switch: {
branches: [
{ case: { $gte: [ "$average", 90 ] }, then: "A" },
{ case: { $gte: [ "$average", 80 ] }, then: "B" },
{ case: { $gte: [ "$average", 70 ] }, then: "C" },
{ case: { $gte: [ "$average", 60 ] }, then: "D" }
],
default: "F"
}
}
}
}
])
mongoplayground

How can I exclude results that contain a specific element from grouped results?

A: It should be output how many _ids are included by date grouped by date.
B: The number of elements in details in A.
If it has element, count 1. not 0. If the document is as follows, the value counted after excluding from A becomes B
{
_id: ObjectId
details: array //no elements
createdAt: Date
}
C: The count of B becomes C, except when there are specific details.slaesManagerIds among B.
details.salesManagerIds is provided as an array.
For examples,
[ObjecttId("612f57184205db63a3396a9e"), ObjectId("612cb021278f621a222087d7")]
I made query as follows.
https://mongoplayground.net/p/6sBxAmO_31y
It goes well until B. How can I write a query to get C ?
If you write and execute a query that can obtain C through the link above, you should get the following result.
[
{
"A": 2,
"B": 1,
"C": 1,
"_id": "2018-05-19"
},
{
"A": 3,
"B": 3,
"C": 1,
"_id": "2018-05-18"
}
]
use $filter
db.collection.aggregate([
{
$group: {
_id: {
$dateToString: {
format: "%Y-%m-%d",
date: "$createdAt"
}
},
A: {
$sum: 1
},
B: {
$sum: {
$cond: [
{
$and: [
{
$isArray: "$details"
},
{
$gt: [
{
$size: "$details"
},
0
]
}
]
},
1,
0
]
}
},
C: {
$sum: {
$cond: [
{
$and: [
{
$isArray: "$details"
},
{
$gt: [
{
$size: "$details"
},
0
]
},
{
$gt: [
{
$size: {
$filter: {
input: "$details",
as: "d",
cond: {
$and: [
{
$not: [
{
$in: [
"$$d.salesManagerId",
[
ObjectId("612f57184205db63a3396a9e"),
ObjectId("612cb021278f621a222087d7")
]
]
}
]
}
]
}
}
}
},
0
]
}
]
},
1,
0
]
}
}
}
},
{
$sort: {
_id: -1
}
}
])
mongoplayground

How to add a sub-document field with a condition in MongoDB?

I am trying to add a new subField with a condition.
In the case the field already exists, I don't overwrite it.
In the case the condition is not fulfilled, I don't want to add the parent object.
Here is my collection :
{type: "A", object: {a: "", b: "foo"}},
{type: "A", object: {a: ""}},
{type: "A"},
{type: "B"}
Here is my aggregate :
{
$addFields: {
"object.b": {
$cond: {
if: {$eq: ["$type","A"]},
then: {$ifNull: ["$object.b", "bar"]},
else: "$DROP"
}
}
}
}
$DROP is not an aggregate command, but in the else case I don't want to add the new field.
It will not create the b field, but the parent object remains.
Here is my current result :
{type: "A", "object": {a: "", b: "foo"}},
{type: "A", "object": {a: "", b: "bar"}},
{type: "A", "object": {b: "bar"}},
{type: "B", "object": {}},
Here is what I want :
{type: "A", object: {a: "", b: "foo"}},
{type: "A", object: {a: "", b: "bar"}},
{type: "A", object: {b: "bar"}},
{type: "B"}
Your help will be highly appreciated.
This aggregate query will give you the desired result:
db.collection.aggregate([
{
$addFields: {
object: {
$cond: {
if: { $eq: [ "$type", "A" ] },
then: {
$mergeObjects: [
"$object",
{ b: { $ifNull: [ "$object.b", "bar" ] } }
]
},
else: "$$REMOVE"
}
}
}
}
])
Note the $$REMOVE is a aggregation system variable.
When a $set adds a path, all path is added, even if you end up to $$REMOVE this will affect only the last of the path, the rest would be already added see example
Query
$set with switch case starting from object
if object doesn't exist
if type A add {object :{"b" : "bar"}}
else $$REMOVE
$type = "A" AND (not-value "$object.b")
add {"b" : "bar"} (this case covers also the case object:null)
else
keep old value (another type, or b had value)
*Maybe it could be smaller but we check many things(see the example for all cases of data)
object exists/null/not exists
type A/not
b exists/null/value
Test code here
db.collection.aggregate([
{
"$set": {
"object": {
"$switch": {
"branches": [
{
"case": {
"$eq": [
{
"$type": "$object"
},
"missing"
]
},
"then": {
"$cond": [
{
"$eq": [
"$type",
"A"
]
},
{
"b": "bar"
},
"$$REMOVE"
]
}
},
{
"case": {
"$and": [
{
"$eq": [
"$type",
"A"
]
},
{
"$or": [
{
"$eq": [
"$object.b",
null
]
},
{
"$eq": [
{
"$type": "$object.b"
},
"missing"
]
}
]
}
]
},
"then": {
"$mergeObjects": [
"$object",
{
"b": "bar"
}
]
}
}
],
"default": "$object"
}
}
}
}
])

Query MongoDb aggregate join two collections

I need help for querying mongoDb
So I have two collections like
Collection A:
{someField: "123", anotherField: "456"},
{someField: "1234", anotherField: "4567"}
Collection B
{someField: "123", otherField: "789"}
with Query:
db.A.aggregate([
{
$lookup:
{
from: "B",
let: { someField: "$someField", otherField: "$otherField" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$someField", "$$someField" ] },
{ $eq: [ "$otherField", "789" ] }
]
}
}
},
],
as: "B"
}
}
])
I get all collection A, with B empty in {someField: "1234", anotherField: "4567"}
What I want to achieve is like:
{someField: "123", anotherField: "456", b: {someField: "123", otherField: "789"}}
Thank you in advance
you only need to declare $someField in the let section.
db.collectionA.aggregate([
{
$lookup: {
from: 'collectionB',
let: { some_field: '$someField' },
pipeline: [
{ $match: {
$expr: {
$and: [
{ $eq: [ "$someField", "$$some_field" ] },
{ $eq: [ "$otherField", "789" ] }
]
}
}
}
],
as: 'B'
}
},
{
$match: {
$expr: {
$gt: [ { $size: "$B" }, 0 ]
}
}
}
])
https://mongoplayground.net/p/RTiUMWl8QaX
This is how I removed the empty B array documents:
db.A.aggregate( [
{
$lookup: {
from: "B",
localField: "someField",
foreignField: "someField",
as: "B"
}
},
{
$addFields: {
B: {
$filter: {
input: "$B",
cond: {
$eq: [ "$$this.otherField", "789" ]
}
}
}
}
},
{
$match: {
$expr: {
$gt: [ { $size: "$B" }, 0 ]
}
}
}
] ).pretty()

How to get several combinations of documents where sums of properties reach a certain value in Mongodb?

If we imagine this kind of document structure :
[
{
id: 1,
name: "",
values : {
a: 24,
b: 42
}
},
{
id: 2,
name: "",
values : {
a: 43,
b: 53
}
},
{
id: 3,
name: "",
values : {
a: 33,
b: 25
}
},
{
id: 4,
name: "",
values : {
a: 89,
b: 2
}
}
// ...
]
Is it possible to get one or more lists of documents where, for example, the sum of the $.values.a equals 100 and the sum of the $.values.b equals 120? Or if not is it possible to sort the bests fits with a kind of threshold?
For example, the best output can be something like that :
[
{
id: 1,
name: "",
values : {
a: 24,
b: 42
}
},
{
id: 2,
name: "",
values : {
a: 43,
b: 53
}
},
{
id: 3,
name: "",
values : {
a: 33,
b: 25
}
}
]
There is no any native implementation...
But, You can have desired results if your data meets some requirements:
You collection has no too much data (this solution scales badly)
Your id field is unique
Your collection has index for id field
Explanation
We sort by id
With $lookup with the same collection (it's important ´id´ to be indexed) and pick next 10 documents for the current document L i=(Doc i+1 ... Doc i+11)
With $reduce, we count from i ... i+n untill a > 100 and b > 120
With $facet, we separate lists which meets exactly a=100, b=120 results (equals) and threshold (+- 10 for values.a and values.b)
Last steps, if any equals exists, we ignore threshold. Otherwise, we take threshold.
db.collection.aggregate([
{
$sort: {
id: 1
}
},
{
$lookup: {
from: "collection",
let: {
id: "$id"
},
pipeline: [
{
$sort: {
id: 1
}
},
{
$match: {
$expr: {
$gt: [
"$id",
"$$id"
]
}
}
},
{
$limit: 10
}
],
as: "bucket"
}
},
{
$replaceRoot: {
newRoot: {
$reduce: {
input: "$bucket",
initialValue: {
a: "$values.a",
b: "$values.b",
data: [
{
_id: "$_id",
id: "$id",
name: "$name",
values: "$values"
}
]
},
in: {
a: {
$add: [
"$$value.a",
{
$cond: [
{
$and: [
{
$lt: [
"$$value.a",
100
]
},
{
$lt: [
"$$value.b",
120
]
}
]
},
"$$this.values.a",
0
]
}
]
},
b: {
$add: [
"$$value.b",
{
$cond: [
{
$and: [
{
$lt: [
"$$value.a",
100
]
},
{
$lt: [
"$$value.b",
120
]
}
]
},
"$$this.values.b",
0
]
}
]
},
data: {
$concatArrays: [
"$$value.data",
{
$cond: [
{
$and: [
{
$lt: [
"$$value.a",
100
]
},
{
$lt: [
"$$value.b",
120
]
}
]
},
[
"$$this"
],
[]
]
}
]
}
}
}
}
}
},
{
$facet: {
equals: [
{
$match: {
a: 100,
b: 120
}
}
],
threshold: [
{
$match: {
a: {
$gte: 90,
$lt: 110
},
b: {
$gte: 110,
$lt: 130
}
}
}
]
}
},
{
$project: {
result: {
$cond: [
{
$gt: [
{
$size: "$equals"
},
0
]
},
"$equals",
"$threshold"
]
}
}
},
{
$unwind: "$result"
}
])
MongoPlayground