how to add total wasted days in my query mongodb - mongodb

i have following query i want to count totalwasteddays please help me this. where i wrong in total line number. i am new in mongodb please help me this i am very thankful
error in this line TotalWastedDays: { $add: $toInt:"$none",$toInt:"$fade",$toInt:"$Torn" },
db.getCollection('campaigns').aggregate([
{ $match: { _id: ObjectId("5b49d08db8695590d4ea7204") } },
{ $project: { doc: "$$ROOT" } },
{ $unwind: '$doc.assets' } ,
{ $project: {
assets: '$doc.assets',
none: { $filter : { input: '$doc.assets.outdoor_tracking', as: 'outdoor_tracking', cond: { $eq: ['$$outdoor_tracking.issue_type','None'] } } },
fade: { $filter : { input: '$doc.assets.outdoor_tracking', as: 'outdoor_tracking', cond: { $eq: ['$$outdoor_tracking.issue_type','Fade'] } } },
Torn: { $filter : { input: '$doc.assets.outdoor_tracking', as: 'outdoor_tracking', cond: { $eq: ['$$outdoor_tracking.issue_type','Torn'] } } },
}
},
{
$project: {
assets:1,
none: { $size: { "$ifNull": ["$none", []] } },
fade: { $size: { "$ifNull": ["$fade", []] } },
Torn: { $size: { "$ifNull": ["$Torn", []] } },
},
$project: {
assets:1,
none:1 ,
fade:1 ,
Torn:1 ,
TotalWastedDays: { $add: $toInt:"$none",$toInt:"$fade",$toInt:"$Torn" },
}
}
])

Modify
{ $add: $toInt:"$none",$toInt:"$fade",$toInt:"$Torn" }
To
{ $add: [{$toInt:"$none"},{$toInt:"$fade"},{$toInt:"$Torn"}] }

Related

Extract value from an array of objects in mongodb aggregate

I am new to mongodb,
I'm trying to extract expectedDeliveryTime from the following data:
{
"from": "Giza",
"delivery_rule": [
{
"to": "Giza",
"expectedDeliveryTime": 3
},
{
"to": "Riyadh",
"expectedDeliveryTime": 2
}
]
}
I am trying to fetch expectedDeliveryTime WHERE from='Giza' and to='Riyadh'
MySQL equivalent would be SELECT expectedDeliveryTime FROM delivery_rules AS d WHERE d.from='Giza' AND d.to='Giza'
Below is part of my code
{
$lookup: {
from: 'Setting',
pipeline: [
{
$match: {
$expr: { $eq: ['$name', 'delivery_rules'] },
},
}
],
as: 'delivery_rules',
}
},
{
$addFields: {
delivery_rules: "$delivery_rules.value"
}
},
{ $unwind: '$delivery_rules' },
{
$addFields: {
delivery_rules: {
$filter: {
input: "$delivery_rules",
as: "rule",
cond: {
$eq: [
"$$rule.from",
"Giza"
]
},
}
}
}
},
{
$group: {
expectedDeliveryTime: { $first: '$delivery_rules' },
}
},
{
$project: {
_id: 0,
expectedDeliveryTime: 1,
}
}
You can solve this using simply $match and $unwind
db.collection.aggregate([
{
"$match": {
"from": "Giza"
}
},
{
"$unwind": "$delivery_rule"
},
{
"$match": {
"delivery_rule.to": "Riyadh"
}
},
{
$project: {
_id: 0,
expectedDeliveryTime: "$delivery_rule.expectedDeliveryTime"
}
}
])
Here is the Mongo playground for your reference.

Project one field from specific object element in an array mongodb

I have a collection:
{
"_id": 1,
"_deleted": false,
"customFields": [{
"fieldName": "sapID",
"value": ""
}, {
"fieldName": "salesTerritory",
"value": ""
}, {
"fieldName": "clientType",
"value": "Corporate"
}],
}
How can I project(aggregate) only the value field of the element with fieldName = "clientType":
db.collection.aggregate(
{
$project:{value:<code>}
}
)
I tried $filter but it does not work
db.collection.aggregate([
{
$project: {
"customFields.value": 1
}
}
])
mongoplayground
db.collection.aggregate([
{
$set: {
customFields: {
$map: {
input: "$customFields",
as: "c",
in: {
$cond: {
if: { "$eq": [ "$$c.fieldName", "clientType" ] },
then: { value: "$$c.value" },
else: "$$c"
}
}
}
}
}
}
])
mongoplayground
What about this?
db.collection.aggregate([
{
$project: {
customFields: {
$filter: {
input: "$customFields",
$cond: { $eq: ["$$this.fieldName", "clientType"] }
}
}
}
}
])
Mongo Playground

Mongodb loop through every distinct values and select tags using aggregate (facet)

I have collection like this:
{
"labels": [{
"description": "Dog"
}, {
"description": "Red"
}, {
"description": "XXX"
}]
}
{
"labels": [{
"description": "Cat"
}, {
"description": "XXX"
}, {
"description": "Yellow"
}]
}
{
"labels": [{
"description": "Dog"
}, {
"description": "Red"
}, {
"description": "Yellow"
}]
}
{
"labels": [{
"description": "Bird"
}, {
"description": "XXX"
}, {
"description": "XXX"
}]
}
I want to filter for example only "Red" and "Yellow" colors from ALL elements and output document like this:
// because "Dog" appears 2 times so total = 2
{
description: "Dog",
total: 2,
colors: [
{ "_id": "Red", total: 2 },
{ "_id": "Yellow", total: 1 }
]
}
{
description: "Cat",
total: 1,
colors: [
{ "_id": "Yellow", total: 1 }
]
}
{
description: "Bird",
total: 1,
colors: []
}
{
description: "Red",
total: 2,
colors: [
{ _id: "Yellow", total: 1 }
]
}
{
description: "XXX",
total: 4,
colors: [
{ _id: "Yellow", total: 1 }
]
}
I can do this by using collection.distinct('labels.description') and then iterating through every single element + make a separate collection.count({ 'labels.description': 'Dog' }) like this:
for (...)
db.collection.aggregate([
{
"$match": {
"labels.description": valueFromLoop // (e.g. Dog)
}
},
{ $unwind : "$labels" },
{
"$group": {
"_id": "$labels.description",
"count": { "$sum": 1 }
}
},
{
"$match": {
"$or": [
{ "_id": "Red" },
{ "_id": "Yellow" }
]
}
},
{
"$sort": {
"count": -1
}
}
])
I want to do this in a single aggregation or mapReduce so that I could easily output it to new collection using $out instead of using Bulk operations separately, however I don't know if it's possible.
Try this:
let filter = ["Red", "Yellow"];
db.testcollection.aggregate([
{
$addFields: { bkp: "$labels" }
},
{ $unwind: "$labels" },
{
$addFields: {
bkp: {
$filter: {
input: "$bkp",
as: "item",
cond: {
$and: [
{ $ne: ["$$item.description", "$labels.description"] },
{ $in: ["$$item.description", filter] }
]
}
}
}
}
},
{
$unwind: {
path: "$bkp",
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: {
key1: "$labels.description",
key2: { $ifNull: ["$bkp.description", false] }
},
total: { $sum: 1 }
}
},
{
$group: {
_id: "$_id.key1",
description: { $first: "$_id.key1" },
total: {
$sum: {
$cond: {
if: { $first: [["$_id.key2"]] },
then: 1,
else: "$total"
}
}
},
colors: {
$push: {
$cond: {
if: { $first: [["$_id.key2"]] },
then: {
_id: "$_id.key2",
total: "$total"
},
else: "$$REMOVE"
}
}
}
}
},
{ $project: { _id: 0 } }
]);
For some reason with code from both answers it does not count all tags properly.
I'm posting what works:
db.collection.aggregate([
{
$project: {
labels: 1,
result: {
$filter: {
input: "$labels",
as: "label",
cond: {
$or: [
{ $eq: ["$$label.description", "Blue"] },
{ $eq: ["$$label.description", "Red"] },
{ $eq: ["$$label.description", "Black-and-white"] },
{ $eq: ["$$label.description", "Purple"] },
{ $eq: ["$$label.description", "Orange"] },
{ $eq: ["$$label.description", "Yellow"] },
{ $eq: ["$$label.description", "Green"] },
{ $eq: ["$$label.description", "Teal"] }
]
}
}
}
}
},
{
$unwind: "$labels"
},
{
"$group": {
_id: "$labels.description",
x: {
$push: "$result.description"
},
total: { "$sum": 1 }
}
},
{
$project: {
x: {
$reduce: {
input: '$x',
initialValue: [],
in: {$concatArrays: ['$$value', '$$this']}
}
},
total: 1
}
},
{
$project: {
x: 1,
y: { $setUnion: "$x" },
total: 1
}
},
{
$project: {
_id: 0,
description: "$_id",
"colors": {
$map: {
input: "$y",
as: "item",
in: {
_id: "$$item",
count: {
$size: {
$filter: {
input: "$x",
as: "itemx",
cond: {
$eq: ["$$item", "$$itemx"]
}
}
}
}
}
}
},
total: 1
}
},
{
$out: "backgrounds_meta"
}
])
db.test2.aggregate([
{
$project: {
labels:1,
colours: {
$filter: {
input: "$labels",
as: "label",
cond: {
$or: [
{$eq:["Yellow","$$label.description"]},
{$eq:["Red", "$$label.description"]}
]
}
}
}
}
},
{$unwind:"$labels"},
{$group:{
_id: "$labels.description",
total: {$sum:1},
colours: {$addToSet:"$colours.description"}
}},
{
$project:{
_id:0,
description:"$_id",
total:1,
colours: {
$reduce:{
input: "$colours",
initialValue: [],
in: {$concatArrays: ["$$value", "$$this"]}
}
}
}
},
{
$unwind: {
path:"$colours",preserveNullAndEmptyArrays: true
}
},
{
$group:{
_id:{
description:"$description",
total:"$total",
colour:"$colours"
},
count: {
$sum: {$cond:[{$ifNull:["$colours",false]},1,0]}
}
}
},
{
$group:{
_id:{
description:"$_id.description",
total:"$_id.total"
},
colours: {
$push: {
$cond: [{$gt:["$count",0]},
{
"_id":"$_id.colour",
total:"$count"
},
"$$REMOVE"
]
}
}
}
},
{
$project: {
_id:0,
description: "$_id.description",
total: "$_id.total",
colours: 1
}
}
]);
**Edit In your answer, you are missing the Yellows for Red and Dog because you are taking the first item from $result with $arrayElemAt: ["$result.description", 0].
If description is a colour, do you also want to include the counts for itself in colours?
Never mind, you've updated the answer

How to find prev/next document after sort in MongoDB

I want to find prev/next blog documents whose publish date is closest to the input document.
Below is the document structure.
Collection Examples (blog)
{
blogCode: "B0001",
publishDate: "2020-09-21"
},
{
blogCode: "B0002",
publishDate: "2020-09-22"
},
{
blogCode: "B0003",
publishDate: "2020-09-13"
},
{
blogCode: "B0004",
publishDate: "2020-09-24"
},
{
blogCode: "B0005",
publishDate: "2020-09-05"
}
If the input is blogCode = B0003
Expected output
{
blogCode: "B0005",
publishDate: "2020-09-05"
},
{
blogCode: "B0001",
publishDate: "2020-09-21"
}
How could I get the output result? In sql, it seems using ROW_NUMBER can solve my problem, however I can't find a solution to achieve the feature in MongoDB. The alternate solution may be reference to this answer (But, it seems inefficient). Maybe using mapReduce is another better solutions? I'm confused at the moment, please give me some help.
You can go like following.
We need to compare existing date with given date. So I used $facet to categorize both dates
The original data should be one Eg : B0003. So that I just get the first element of the origin[] array to compare with rest[] array
used $unwind to flat the rest[]
Substract to get the different between both dates
Again used $facet to find previous and next dates.
Then combined both to get your expected result
NOTE : The final array may have 0<elements<=2. The expected result given by you will not find out whether its a prev or next date if there is a one element. So my suggestion is add another field to say which date it is as the mongo playground shows
[{
$facet: {
origin: [{
$match: { blogCode: 'B0001' }
}],
rest: [{
$match: {
$expr: {
$ne: ['$blogCode','B0001']
}
}
}]
}
}, {
$project: {
origin: {
$arrayElemAt: ['$origin',0]
},
rest: 1
}
}, {
$unwind: {path: '$rest'}
}, {
$project: {
diff: {
$subtract: [{ $toDate: '$rest.publishDate' },{ $toDate: '$origin.publishDate'}]
},
rest: 1,
origin: 1
}
}, {
$facet: {
prev: [{
$sort: {diff: -1}
},
{
$match: {
diff: {$lt: 0 }
}
},
{
$limit: 1
},
{
$addFields:{"rest.type":"PREV"}
}
],
next: [{
$sort: { diff: 1 }
},
{
$match: {
diff: { $gt: 0 }
}
},
{
$limit: 1
},
{
$addFields:{"rest.type":"NEXT"}
}
]
}
}, {
$project: {
combined: {
$concatArrays: ["$prev", "$next"]
}
}
}, {
$unwind: {
path: "$combined"
}
}, {
$replaceRoot: {
newRoot: "$combined.rest"
}
}]
Working Mongo playground
Inspire for the solution of varman proposed. I also find another way to solve my problem by using includeArrayIndex.
[
{
$sort: {
"publishDate": 1
},
},
{
$group: {
_id: 1,
root: {
$push: "$$ROOT"
}
},
},
{
$unwind: {
path: "$root",
includeArrayIndex: "rownum"
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{
rownum: "$rownum"
}
]
}
}
},
{
$facet: {
currRow: [
{
$match: {
blogCode: "B0004"
},
},
{
$project: {
rownum: 1
}
}
],
root: [
{
$match: {
blogCode: {
$exists: true
}
}
},
]
}
},
{
$project: {
currRow: {
$arrayElemAt: [
"$currRow",
0
]
},
root: 1
}
},
{
$project: {
rownum: {
prev: {
$add: [
"$currRow.rownum",
-1
]
},
next: {
$add: [
"$currRow.rownum",
1
]
}
},
root: 1
}
},
{
$unwind: "$root"
},
{
$facet: {
prev: [
{
$match: {
$expr: {
$eq: [
"$root.rownum",
"$rownum.prev"
]
}
}
},
{
$replaceRoot: {
newRoot: "$root"
}
}
],
next: [
{
$match: {
$expr: {
$eq: [
"$root.rownum",
"$rownum.next"
]
}
}
},
{
$replaceRoot: {
newRoot: "$root"
}
}
],
}
},
{
$project: {
prev: {
$arrayElemAt: [
"$prev",
0
]
},
next: {
$arrayElemAt: [
"$next",
0
]
},
}
},
]
Working Mongo playground

if condtion MongoDB aggregation

I need to display the currentLocation of which the process is completed,which are presented in four different collections. It's a chain process, in the order of labtest, inwards, goods, production. Each of them is different collections. I want the result in which, if labtest status is completed it goes to inwards, if inwards status also completed, then it goes and checks on goods. If goods status is In-Progress, it should return Inwards as currentLocation.
But, I am getting a response as Production....it seems like me if the condition is not working. Please help me to solve this issue
async function getGrnDetails(userParam) {
var user = User.findOne({ grnNo: userParam.grnNo })
var userss = User.aggregate([
{
$match:
{
grnNo: userParam.grnNo
}
}, {
$lookup: {
from: "goods",
let: { grnNo: "$grnNo" },
pipeline: [
{
$match: {
$expr:
{
$eq: ["$grnNo", '$$grnNo']
}
}
},
],
as: "Goods",
},
},
{
$lookup: {
from: "inwards",
let: { grnNo: "$grnNo" },
pipeline: [
{
$match: {
$expr:
{
$eq: ["$grnNo", '$$grnNo']
}
}
}
],
as: "Inwards",
}
},
{
$lookup: {
from: "productions",
let: { grnNo: "$grnNo" },
pipeline: [
{
$match: {
$expr:
{
$eq: ["$grnNo", '$$grnNo']
}
}
},
],
as: "Productions",
}
},
{
$unwind: {
"path": "$Productions"
}
},{
$unwind: {
"path": "$status"
}
},{
$unwind: {
"path": "$Goods"
}
},
{
$unwind: {
"path": "$Inwards"
}
},
{
$group: {
"_id": {
beforeHeadSettingArray: '$beforeHeadSettingArray',
beforeWashingArray: '$beforeWashingArray', id: '$id',
status: '$status', defaultAccountId: '$defaultAccountId',
beforeStenterArray: '$beforeStenterArray',
beforeCompactingArray: '$beforeCompactingArray',
afterCompactingArray: '$afterCompactingArray',
status: "$status",
Goods: '$Goods.processArray', Inwards: '$Inwards.fabricArray', Production: '$Productions.operationDisplay', currentLocation: {
$cond: {
if: {
$strcasecmp: ["$status", "Completed"]
}, then: {
$cond: {
if: {
$strcasecmp: ["$Inwards.status", "Completed"]
},
then: {
$cond: {
if: {
$strcasecmp: ["$Goods.status", "Completed"]
},
then:
{
$cond: {
if: {
$strcasecmp: ["$Productions.status", "Completed"]
},
then: "Productions",
else: "Goods"
}
},
else: "Inwards"
}
},
else: "LabTest"
}
},
else: "Yet To be Started"
}
}
},
"ProgressArray": {
$addToSet: {
"Inwards": "$Inwards.status",
"Goods": "$Goods.status",
"Productions": "$Productions.status",
"LabTest": "$status"
},
}
}
} ,{ $sort: { _id: 1 } },
{ $limit: 1 }
]);
return await userss
}
The Problem is I used strcasecmp, when I changed to eq it worked perfectly
currentLocation: {
$cond: {
if: {
$eq: ["$status", "Completed"]
}, then: {
$cond: {
if: {
$eq: ["$Inwards.status", "Completed"]
},
then: {
$cond: {
if: {
$eq: ["$Goods.status", "Completed"]
},
then:
{
$cond: {
if: {
$eq: ["$Productions.status", "Completed"]
},
then: "Finished",
else: "Productions"
}
},
else: "Goods"
}
},
else: "Inwards"
}
},
else: "LabTest"
}
}
,