Or with If and In mongodb - mongodb

{
$cond: {
if: { $in: [`$$${field.fieldName}`, ['', null]] },
then: [],
else: `$$${field.fieldName}`,
},
},
In this condition, I also want to check the field.fieldName exits.
$exists: true,
then it will something like
if ( !filed.fieldName || field.fieldName === null || field.fieldName === '') then []
I am not finding any way to add $exits: true or false in this.
Please help if can add something to this. Or any alternate way to achieve ?

You can use $or
$ifNull
Evaluates an expression and returns the value of the expression if the expression evaluates to a non-null value. If the expression evaluates to a null value, including instances of undefined values or missing fields, returns the value of the replacement expression.
{ $ifNull: [ <expression>, <replacement-expression-if-null> ] }
{
$cond: {
if: {
$or : [
{ $in: [`$$${field.fieldName}`, ['', null]] },
{ $ifNull: [`$$${field.fieldName}`, ""] }
]},
then: [],
else: `$$${field.fieldName}`,
}
},

You just need to put $ifNull in else part, if it's not exists it will return [],
{
$cond: {
if: {
$in: [`$$${field.fieldName}`, ["", null]]
},
then: [],
else: { $ifNull: [`$$${field.fieldName}`, []] }
}
}
Playground
Input:
[
{ "key": null },
{ "key": "" },
{ "A": "a" }
]
Result:
[
{
"key": null,
"status": []
},
{
"key": "",
"status": []
},
{
"status": []
}
]
Second approach, if you want to add an existing condition in if part you can try condition with $or,
$type will return the field's data type the missing field type is "missing"
{
$cond: {
if: {
$or: [
{ $eq: [{ $type: `$$${field.fieldName}` }, "missing"] },
{ $in: [`$$${field.fieldName}`, ["", null]] }
]
},
then: [],
else: `$$${field.fieldName}`
}
}
Playground

Bear in mind, "field.fieldName exists" might be different to "field.fieldName is not null".
Consider special values like this:
db.collection.insertMany([
{ _id: 1, a: 1 },
{ _id: 2, a: '' },
{ _id: 3, a: undefined },
{ _id: 4, a: null },
{ _id: 5 }
])
db.collection.aggregate([
{
$set: {
type: { $type: "$a" },
ifNull: { $ifNull: ["$a", true] },
defined: { $ne: ["$a", undefined] },
existing: { $ne: [{ $type: "$a" }, "missing"] }
}
}
])
{ _id: 1, a: 1, type: double, ifNull: 1, defined: true, existing: true }
{ _id: 2, a: "", type: string, ifNull: "", defined: true, existing: true }
{ _id: 3, a: undefined, type: undefined, ifNull: true, defined: false, existing: true }
{ _id: 4, a: null, type: null, ifNull: true, defined: true, existing: true }
{ _id: 5, type: missing, ifNull: true, defined: false, existing: false }
So, condition could be this one, depending on your requirements:
{
$cond: {
if: { $ne: [{ $type: "$field.fieldName" }, "missing"] },
then: [],
else: "$field.fieldName",
}
}
For sake of completeness: With db.collection.find():
db.collection.find({ a: { $exists: false } })
{ _id: 5 }
db.collection.find({ a: { $exists: true} })
{ _id: 1, a: 1 },
{ _id: 2, a: '' },
{ _id: 3, a: undefined },
{ _id: 4, a: null }
db.collection.find({ a: null })
{ _id: 3, a: undefined },
{ _id: 4, a: null },
{ _id: 5 }
db.collection.find({ a: {$ne: null} })
{ _id: 1, a: 1 },
{ _id: 2, a: '' },
db.collection.find({ a: {$type: "null"} })
{ _id: 4, a: null }

Try this:
{
$cond: {
if: { $ne : [`$$${field.fieldName}`, undefined] },
then: [],
else: `$$${field.fieldName}`,
},
}

Related

MongoDb How to do complex aggregation to an order Model

Hello Guys This is my Order Model :
const OrderSchema = new Schema({
orderItems: {
name: { type: String, required: true },
qty: { type: Number, required: true },
image: { type: String, required: false },
price: { type: Number, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Product',
required: true,
},
},
totalPrice: { type: Number, required: true },
paymentMethod: { type: String, required: true },
paymentResult: {
status: { type: String, default: 'Waiting For Payment' }
},
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
isPaid: { type: Boolean, default: false },
paidAt: { type: Date },
OrderResult: {
status: { type: String, default: 'Waiting For Payment' }
},
isDelivered: { type: Boolean, default: false },
},{timestamps : true})
I did do This aggregation to get Users Orders Summary :
const usersOrderSummary = await Order.aggregate(
[
{$match:{} },
{$group: {_id:"$user", TotalSpent: {$sum:"$totalPrice"},TotalOrders: { $sum: 1 }}},
]
)
The Result Of this aggregation is an array of:
{
"usersOrderSummary": [
{
"_id": "6216687c0e0d9122f710a1a6",
"TotalSpent": 0.9,
"TotalOrders": 8
},
{
"_id": "628e4b96a7fd3bad9482a81c",
"TotalSpent": 9.18,
"TotalOrders": 53
}
]
}
I want your Help to do an aggregation to get this result:
I want to to do aggregation to collect the sum of completed orders and the sum of the amount that spent in this completed order
the condtion of completed order so i can only considered an order completed when
isDelivered:true
please check the schema above so it would be clear to know how order considered as completed
{
"usersOrderSummary": [
{
"_id": "6216687c0e0d9122f710a1a6",
"TotalSpent": 0.9,
"TotalOrders": 8,
"TotalCompletedOrderSpent": 0.1,
"TotalCompletedOrders": 1,
},
{
"_id": "628e4b96a7fd3bad9482a81c",
"TotalSpent": 9.18,
"TotalOrders": 53
"TotalCompletedOrderSpent": 4,
"TotalCompletedOrderSpent": 2,
}
]
}
Use $cond
db.collection.aggregate([
{
$match: {}
},
{
$group: {
_id: "$user",
TotalSpent: { $sum: "$totalPrice" },
TotalOrders: { $sum: 1 },
TotalCompletedOrderSpent: {
$sum: {
$cond: {
if: { $eq: [ "$isDelivered", true ] },
then: "$totalPrice",
else: 0
}
}
},
TotalCompletedOrders: {
$sum: {
$cond: {
if: { $eq: [ "$isDelivered", true ] },
then: 1,
else: 0
}
}
}
}
}
])
mongoplayground

mongodb can $unionWith use in $push

I want to get related data based on current item processing.
Sample:
[
{ field1: 1, field2: 2, value: 12 },
{ field1: 1, field2: 2, value: 21 },
{ field1: 1, value: 1 },
{ field2: 2, value: 2 },
{ field1: 2, field2: 3, value: 23 }
];
and result:
[
{
_id: { field1: 1, field2: 2 },
value: [12, 12],
relatedValue: [1, 2], // of item 1 and 2 because field 1 = 1 or field 2 = 2
},
];
Sample query:
db.collectionA.aggregate([
{
$match: { field1: 1 }
},
{
"$group":{
"_id":{
"field1":"$field1",
"field2":"$field2"
},
"alerts":{
"$push":{
"_id":"$_id",
"value":"$value",
"relatedData": {
"$unionWith": {
"coll": "collectionA",
"pipeline": [{
"$match": {
"$or": [
{ "field1": "$field1" },
{ "field2": "$field2" }
]
}
}]
}
}
}
}
}
}
])
I tried run this query but error, Please help me fix or give a solution
// Edited: value should be array because I want to group data by field1, field2 and push all value of group to an array
You're trying to use $unionWith within $group but it is a "pipeline stage" meaning it can't be used like that, the same way you can't use $group within a $group.
Additionally this stage is used to "union" two collections and not to populate data based on value matches ( which it seems you're trying to do here ), for this case you want to use $lookup, like so:
db.collection.aggregate([
{
$lookup: {
from: "collection",
let: {
field1: "$field1",
field2: "$field2",
docId: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$or: [
{
$eq: [
"$$field1",
"$field1"
]
},
{
$eq: [
"$$field2",
"$field2"
]
}
]
},
{
$ne: [
"$$docId",
"$_id"
]
}
]
}
}
},
{
$project: {
value: 1
}
}
],
as: "relatedData"
}
},
{
$group: {
_id: {
field1: "$field1",
field2: "$field2"
},
values: {
$push: "$value"
},
relatedValue: {
$push: {
$map: {
input: "$relatedData",
in: "$$this.value"
}
}
}
}
},
{
$project: {
field1: "$_id.field1",
field2: "$_id.field2",
values: 1,
relatedValues: {
"$setDifference": [
{
"$reduce": {
input: "$relatedValue",
initialValue: [],
in: {
"$setUnion": [
"$$this",
"$$value"
]
}
}
},
"$values"
]
}
}
}
])
Mongo Playground

About query array of Objects in mongoose

Test in mongoplayground
https://mongoplayground.net/p/c3UBL9JwX5u
expect
{
_id: "5df1e6f75de2b22f8e6c30e8",
t: [
{ name: "d1", tt: false },
{ name: "d2", tt: true }
]
}
This is my query
db.coll.find({
"t": {
$ne: {
"name": {
$regex: "d1",
$options: "g"
},
"tt": true
}
}
})
I got the wrong answer, So how to get the desired result ? Thanks !
t.name includes 'd1' and t.tt != true
t.name not includes 'd1'
I want got the result 1 or 2.
https://mongoplayground.net/p/CnGnSmRzXCB
db.coll.find({
$or: [
{
"t": {
$elemMatch: {
name: {
"$regex": "^d1$|d1"
},
tt: false
}
}
},
{
"t": {
$not: {
$elemMatch: {
name: {
$regex: "d1"
},
}
}
}
}
]
})

Aggregate and $ne Not Working as Expected

I'm trying the aggregation below, but do not appear to be getting the expected result using $ne and null.
I have tried other solutions like using a combination of $cond, $not, and $eq to no avail. Using $gt:[ "$unloadeddate", null] seems to give some results, but that does not seem to be proper syntax and I am concerned it's not trustworthy to run properly over entire dataset.
Also, querying as follows:
db.getCollection('esInvoices').find({"esBlendTickets.loadeddate":{$ne:null}})
... returns results so not sure why the same query in the aggregate function not working.
Any help is appreciated!!
The first part works...
"unloadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "$esBlendTickets.ticketdate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "$esDeliveryTickets.ticketdate"
}],
default: null
}
},
"loadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.loadeddate", null]
},
then: "$esBlendTickets.loadeddate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.loadeddate", null]
},
then: "$esDeliveryTickets.loadeddate"
}],
default: null
}
},
... but this second part (essentially the same logic except for the resulting value) does not work as expected...
"stagename": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}
Complete Aggregation:
db.esInvoices.aggregate([ {
$addFields: {
// A single invoice will not have both a blend ticket and delivery ticket associated so looping tough each case should work.
"unloadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "$esBlendTickets.ticketdate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "$esDeliveryTickets.ticketdate"
}],
default: null
}
},
"loadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.loadeddate", null]
},
then: "$esBlendTickets.loadeddate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.loadeddate", null]
},
then: "$esDeliveryTickets.loadeddate"
}],
default: null
}
},
"stagedate": "$InvoiceHeader.InvDate",
"stagename": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}
}}])
Think I just ran into the same problem you were having. Using Mongo 3.4 at the moment. From what I can tell the query $ne behaves differently from the aggregate $ne when you are comparing it to null. Threw me off for a little bit.
Specifically, a { $ne: [ '$field', null ] } predicate in the aggregate pipeline will return true when $field is undefined.
However, when using queries (not $aggregate), the { field: { $ne: null } predicate will return false for those same documents when $field is undefined.
My work-around was to use a $project with { field: { $ifNull: [ '$field': null ] } } in a prior step to turn undefined instances of that field into explicit nulls, which will then make the aggregate $ne work as I needed. For whatever reason, $ifNull works with null, undefined, and missing fields. Not sure why $ne is different.
Here's an example to reproduce.
db.test.insertMany([
{ a: 1, b: 'string' },
{ a: 2, b: null },
{ a: 3 },
])
db.test.find({ b: { $ne: null }}, { _id: 0 })
/*
returns:
{
"a" : 1.0,
"b" : "string"
}
*/
db.test.aggregate([
{ $project: {
_id: 0,
a: 1,
b: 1,
switched: { $switch: {
branches: [
{ case: { $ne: [ '$b', null ] }, then: 'cased' },
],
default: 'default',
}}
}}
])
/*
returns:
{
"a" : 1.0,
"b" : "string",
"switched" : "cased"
},
{
"a" : 2.0,
"b" : null,
"switched" : "default"
},
{
"a" : 3.0,
"switched" : "cased" <--
}
*/
The problem is missing fields are undefined while null fields are, well, nulls. When you write "$ne":[ "$esDeliveryTickets.ticketdate", null] you don't filter the former but only the latter.
But undefined is "smaller" than null. In order to filter them both you just need to make lte/gt instead of eq/ne. So this query returns all existing values:
See example:
db.test.insertOne(
{
"a" : 10,
"b" : null
})
...
db.getCollection('mytest').find( {},
{
"a" : { $lte : ["$a", null] },
"b" : { $lte : ["$b", null] },
"c" : { $lte : ["$c", null] },
})
// {
// "_id" : ObjectId("606724f38ec4d26b981b5a1c"),
// "a" : false,
// "b" : true,
// "c" : true
// }
In your case:
"stagename": { "$switch": {
branches:[ {
case: {
"$gt":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$gt":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}
and this one returns only missing (or null) values:
"stagename": { "$switch": {
branches:[ {
case: {
"$lte":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$lte":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}

Grouping and counting across documents?

I have a collection with documents similar to the following format:
{
departure:{name: "abe"},
arrival:{name: "tom"}
},
{
departure:{name: "bob"},
arrival:{name: "abe"}
}
And to get output like so:
{
name: "abe",
departureCount: 1,
arrivalCount: 1
},
{
name: "bob",
departureCount: 1,
arrivalCount: 0
},
{
name: "tom",
departureCount: 0,
arrivalCount: 1
}
I'm able to get the counts individually by doing a query for the specific data like so:
db.sched.aggregate([
{
"$group":{
_id: "$departure.name",
departureCount: {$sum: 1}
}
}
])
But I haven't figured out how to merge the arrival and departure name into one document along with counts for both. Any suggestions on how to accomplish this?
You should use a $map to split your doc into 2, then $unwind and $group..
[
{
$project: {
dep: '$departure.name',
arr: '$arrival.name'
}
},
{
$project: {
f: {
$map: {
input: {
$literal: ['dep', 'arr']
},
as: 'el',
in : {
type: '$$el',
name: {
$cond: [{
$eq: ['$$el', 'dep']
}, '$dep', '$arr']
}
}
}
}
}
},
{
$unwind: '$f'
}, {
$group: {
_id: {
'name': '$f.name'
},
departureCount: {
$sum: {
$cond: [{
$eq: ['$f.type', 'dep']
}, 1, 0]
}
},
arrivalCount: {
$sum: {
$cond: [{
$eq: ['$f.type', 'arr']
}, 1, 0]
}
}
}
}, {
$project: {
_id: 0,
name: '$_id.name',
departureCount: 1,
arrivalCount: 1
}
}
]