Mongodb document object array filter - node.js

How can I filter if all num of qty greater then 50 then this is return otherwise not:
this is examples documents;
{
_id: ObjectId("5234cc89687ea597eabee675"),
code: "xyz",
tags: [ "school", "book", "bag", "headphone", "appliance" ],
qty: [
{ size: "S", num: 10, color: "blue" },
{ size: "M", num: 45, color: "blue" },
{ size: "L", num: 100, color: "green" }
]
}
{
_id: ObjectId("5234cc8a687ea597eabee676"),
code: "abc",
tags: [ "appliance", "school", "book" ],
qty: [
{ size: "6", num: 100, color: "green" },
{ size: "6", num: 70, color: "blue" },
{ size: "8", num: 100, color: "brown" }
]
}
{
_id: ObjectId("5234ccb7687ea597eabee677"),
code: "efg",
tags: [ "school", "book" ],
qty: [
{ size: "S", num: 10, color: "blue" },
{ size: "M", num: 100, color: "blue" },
{ size: "L", num: 100, color: "green" }
]
}
Is it possiblt with mongodb query? how?
I have tried many ways but didnt work. please help
I expected:
{
_id: ObjectId("5234cc8a687ea597eabee676"),
code: "abc",
tags: [ "appliance", "school", "book"],
qty: [
{ size: "6", num: 100, color: "green" },
{ size: "6", num: 70, color: "blue" },
{ size: "8", num: 100, color: "brown" }
]
}
{
_id: ObjectId("52350353b2eff1353b349de9"),
code: "ijk",
tags: [ "electronics", "school" ],
qty: [
{ size: "M", num: 100, color: "green" }
]
}

If I understood your request correctly you would like to filter on the qty.num field right? If so, use this aggregation pipeline:
var options = {
allowDiskUse: false
};
var pipeline = [
{
"$unwind": {
"path": "$qty"
}
},
{
"$match": {
"qty.num": {
"$gte": 50.0
}
}
},
{
"$group": {
"_id": "$_id",
"code": {
"$first": "$code"
},
"tags": {
"$first": "$tags"
},
"qty": {
"$push": "$qty"
}
}
}
];
var cursor = collection.aggregate(pipeline, options);
I hope this helps. It works for me.

It is confusing as the record with _id: ObjectId("52350353b2eff1353b349de9") does not appear in your sample dataset. Nevertheless, from your description, you may be looking for $allElementsTrue. You can use $map to apply your > 50 criteria to the qty array and feed the result to $allElementsTrue.
db.collection.find({
$expr: {
$allElementsTrue: {
$map: {
input: "$qty",
as: "q",
in: {
$gt: [
"$$q.num",
50
]
}
}
}
}
})
Mongo Playground

Related

distinct strings value and concatenating the results

i'm trying to distinct different string values of "comment" and "dates" and concatenate them in two different strings one for comments and one for reclamation dates I used $addToSet to distinct values but every time the concatenating is giving empty result
and this is m code :
db.collection.aggregate([
{
$group: {
_id: {
b: "$type"
},
total: {
$sum: 1
},
root: {
$push: "$$ROOT"
}
}
},
{
"$unwind": "$root"
},
{
$group: {
_id: {
r: "$root.situation",
b: "$root.type"
},
cnt: {
$sum: 1
},
total: {
"$first": "$total"
},
}
},
{
$project: {
a: [
{
k: "$_id.r",
v: "$cnt"
}
],
type: "$_id.b",
total: "$total",
_id: 0,
}
},
{
$project: {
d: {
$arrayToObject: "$a"
},
type: 1,
total: 1
}
},
{
$group: {
_id: "$type",
situation: {
$push: "$d"
},
sum: {
"$first": "$total"
},
//add to set of commets and dates
comment: {
$addToSet: "$comment"
},
daterec: {
$addToSet: "$daterec"
},
}
},
{
$project: {
_id: 0,
type: "$_id",
sum: 1,
"options": {
$mergeObjects: "$situation"
},
//concatenating results
comment: {
$reduce: {
input: "$comment",
initialValue: "",
in: {
$cond: [
{
"$eq": [
"$$value",
""
]
},
"$$this",
{
$concat: [
"$$value",
" ",
"$$this"
]
}
]
}
}
},
daterec: {
$reduce: {
input: "$daterec",
initialValue: "",
in: {
$cond: [
{
"$eq": [
"$$value",
""
]
},
"$$this",
{
$concat: [
"$$value",
" ",
"$$this"
]
}
]
}
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
"$options"
]
}
}
},
{
$project: {
options: 0,
}
},
])
and this is the output :
{
"after sales management": 4,
"comment": "",
"daterec": "",
"instock": 1,
"sum": 5,
"type": "pc"
}
its always giving "comment" and"daterec": empty while i want it to show the concatenating of distinct strings it should looks something like this :
{
"after sales management": 4,
"comment": "waiting for pieces waiting for approval nothing",
"daterec": "1",//this is just an example beacause all dates are set to "1"
"instock": 1,
"sum": 5,
"type": "pc"
}
this is an example of my work with a collection example:
https://mongoplayground.net/p/dB3xBFsIfun
PS : i think the problem is in the of using the $addToSet insite $group

Include array to mongoDB query with group()

I need to add the paramater sector as an array in the group () statement.
I have the following code:
await Escaneado.aggregate([
{
$match: {
$and: [
{ "gestion": id },
{ "disponible": true }
]
},
},
{
$group: {
_id: {
code:"$codigo",
quantityTarget:"$cantidadObjetivo",
},
quantityTotalScanded : { $sum: "$cantidad" }
}
},
{
$addFields:{
difference:{ $subtract: ["$quantityTotalScanded", "$_id.quantityTarget"]}
}
},
])
output:
{
"ok": true,
"escaneadosDB": [
{
"_id": {
"code": "0V3011123A00",
"quantityTarget": 36
},
"quantityTotalScanded": 36,
"difference": 0
},
{
"_id": {
"code": "0V3011123B00",
"quantityTarget": 36
},
"quantityTotalScanded": 4,
"difference": -32
},
{
"_id": {
"code": "0V3012121D00",
"quantityTarget": 56
},
"quantityTotalScanded": 56,
"difference": 0
}
]}
output expected:
{
"ok": true,
"escaneadosDB": [
{
"_id": {
"code": "0V3011123A00",
"quantityTarget": 36,
"sector": ["A", "B", "C"]
},
"quantityTotalScanded": 36,
"difference": 0
},
{
"_id": {
"code": "0V3011123B00",
"quantityTarget": 36,
"sector": ["A"]
},
"quantityTotalScanded": 4,
"difference": -32
},
{
"_id": {
"code": "0V3012121D00",
"quantityTarget": 56,
"sector": ["A", "B"]
},
"quantityTotalScanded": 56,
"difference": 0
}
]}
I think I can add it as an array, but i do not know how implement! .
The sectors are different parameters, therefore I cannot use it as "_id". I need total quantity and the sectors in the query.
How could I do this with mongo?
this worked for me:
add this in group() sentence:
sectors: { $push: { sector: "$sector" } }
all code:
await Escaneado.aggregate([
{
$match: {
$and: [
{ "gestion": id },
{ "disponible": true }
]
},
},
{
$group: {
_id: {
code:"$codigo",
quantityTarget:"$cantidadObjetivo",
},
quantityTotalScanded : { $sum: "$cantidad" },
sectors: { $push: { sector: "$sector" } }
}
},
{
$addFields:{
difference:{ $subtract: ["$quantityTotalScanded", "$_id.quantityTarget"]}
}
},
{$sort: {"cantidadTotal": -1}},

Percentage of amount in a subdocument grouped per type in Mongoose/NodeJS

I have the following MongoDB schema:
const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Email is required.']
},
transactions: [
{
categoryName: {
type: String,
required: [true, 'Category name in transaction is required.']
},
categoryType: {
type: String,
required: [true, 'Category type in transaction is required.']
},
amount: {
type: Number,
required: [true, 'Transaction amount is required.']
}
}
]})
transactions.categoryType can only be Income or Expense. Now per queried _id, I want to return the ratio/percentage of transactions.CategoryName per Income and Expense. Meaning if I have the following data:
{
"_id": 000001,
"email": "asdasd#email.com"
"transactions": [
{
"categoryName": "Food",
"categoryType": "Expense",
"amount": 200
},
{
"categoryName": "Rent",
"categoryType": "Expense",
"amount": 1000
},
{
"categoryName": "Salary",
"categoryType": "Income",
"amount": 15000
}
]
}
the result that I would want is:
{ "email": "asdasd#email.com",
"Income": [["Salary", 100]],
"Expense": [["Food", 16.67],["Rent",83.33]],
}
Now, I have the following query:
return User.aggregate([
{ $match: { _id : ObjectId(request.params.id) } },
{ $unwind : "$transactions"},
{ $group : { _id : { type: "$transactions.categoryType" },
        total: {$sum : "$transactions.amount"},
transactionsArray: { $push: "$transactions"}
        }
},
{ $project: {
_id: 0,
transactionsArray:1,
    type: "$_id.type",
total:1
}
}
])
which returns a data like this:
[
{
"total": 1200,
"transactions": [
{
"categoryName": "Food",
"categoryType": "Expense",
"amount": 200,
},
{
"categoryName": "Rent",
"categoryType": "Expense",
"amount": 1000,
}
],
"type": "Expense"
},
{
"total": 15000,
"transactions": [
{
"categoryName": "Salary",
"categoryType": "Income",
"amount": 15000,
}
],
"type": "Income"
}
]
Now, I do not know how am I going to further process the result set to divide the transactions.amount by the total to get the result that I want.
You may go with multiple steps in aggregations
$unwind to deconstruct the array
$group- first group to group by _id and $categoryType. So we can get the total amount and an amount for particular transaction. This helps to calculate the ratio.
$map helps to loop over the array and calculate the ratio
$reduce- You need comma separated string array of objects. So loop it and get the structure.
$group to group by _id only so we can get the key value pair of category type and Income/Expense when we push
$replaceRoot to make the $grp object as root which should be merged with already existing fields ($mergeObjects)
$project for remove unwanted fields
Here is the code
db.collection.aggregate([
{ "$unwind": "$transactions" },
{
"$group": {
"_id": { id: "$_id", catType: "$transactions.categoryType" },
"email": { "$first": "$email" },
"amount": { "$sum": "$transactions.amount" },
"category": {
$push: { k: "$transactions.categoryName", v: "$transactions.amount" }
}
}
},
{
$addFields: {
category: {
$map: {
input: "$category",
in: {
k: "$$this.k",
v: {
"$multiply": [
{ "$divide": [ "$$this.v","$amount" ]},
100
]
}
}
}
}
}
},
{
"$addFields": {
category: {
"$reduce": {
"input": "$category",
"initialValue": [],
"in": {
"$concatArrays": [
[
[ "$$this.k", { $toString: "$$this.v" } ]
],
"$$value"
]
}
}
}
}
},
{
"$group": {
"_id": "$_id.id",
"email": { "$first": "$email" },
"grp": { "$push": { k: "$_id.catType", v: "$category" } }
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [ { "$arrayToObject": "$grp" }, "$$ROOT" ]
}
}
},
{ "$project": { grp: 0 } }
])
Working Mongo playground

Compare and add two array inside nested arrays

I have below array of array inside my document
{
items: [
["Tax", 10, 20, 30],
["FX Adjustments", 10, 20, 30],
["Tax", 10, 20, 30],
["FX Adjustments", 10, 20, 30]
]
}
I need to combine Tax with Tax and FX Adjustments with FX Adjustments.
Output I need
{
items: [
["Tax", 20, 40, 60],
["FX Adjustments", 20, 40, 60]
]
}
I tried but with no luck
db.collection.aggregate([
{
$project: {
items: {
$reduce: {
input: "$items",
initialValue: [],
in: {
$cond: [
]
}
}
}
}
}
])
Kindly help with this
Thank you!!!
You need to start with $unwind and then $group by first array element ($arrayElemAt). Then you can run $reduce like you tried but since your array is multidimensional, you need to wrap it with $map to represent the index. To get back the original data format you can group by null and use $concatArrays to put grouping _id as first array element:
db.collection.aggregate([
{
$unwind: "$items"
},
{
$group: {
_id: { $arrayElemAt: [ "$items", 0 ] },
values: { $push: { $slice: [ "$items", 1, { $size: "$items" } ] } }
}
},
{
$project: {
_id: 1,
values: {
$map: {
input: { $range: [ 0, { $size: { $arrayElemAt: [ "$values", 0 ] } } ] },
as: "index",
in: {
$reduce: {
input: "$values",
initialValue: 0,
in: { $add: [ "$$value", { $arrayElemAt: [ "$$this", "$$index" ] } ] }
}
}
}
}
}
},
{
$group: {
_id: null,
items: { $push: { $concatArrays: [ [ "$_id" ], "$values" ] } }
}
}
])
Mongo Playground
Try below aggregate pipeline
collectionName.aggregate([
{
$unwind: "$items"
},
{
$group: {
_id: {
$arrayElemAt: [
"$items",
0
]
},
sum1: {
$sum: {
$arrayElemAt: [
"$items",
1
]
}
},
sum2: {
$sum: {
$arrayElemAt: [
"$items",
2
]
}
},
sum3: {
$sum: {
$arrayElemAt: [
"$items",
3
]
}
}
}
},
{
$project: {
_id: null,
items: {
$map: {
input: {
$objectToArray: "$$ROOT"
},
as: "item",
in: "$$item.v"
}
}
}
},
{
$group: {
_id: null,
items: {
$push: "$items"
}
}
}
])
unwind the items array
group according to Tax(first position element of item using $arrayElemAt)
In project stage use map on objectToArray to get value of keys pushed into array
push the array using $group giving desired output
Output:
[
{
"_id": null,
"items": [
[
"Tax",
20,
40,
60
],
[
"FX Adjustments",
20,
40,
60
]
]
}
]

How to aggregate the marks of all the subjects in mongoDB

I have the collection with following data
{
"_id": "SG01",
"name": "Pawan",
"marks": [
{
"English": 93,
"Maths": 90,
"Hindi": 89,
"Sci": 98
}
],
"__v": 0
}
{
"_id": "SG02",
"name": "Dravid",
"marks": [
{
"English": 40,
"Maths": 67,
"Hindi": 56,
"Sci": 45
}
],
"__v": 0
}
{
"_id": "SG03",
"name": "Kartik",
"marks": [
{
"English": 65,
"Maths": 77,
"Hindi": 80,
"Sci": 79
}
],
"__v": 0
}
I would like to perform the operation in which marks should be displayed as total_marks of a particular student.
As I'm newbie with mongo and know how to perform basic aggregation with sum but wasn't able to understand with arrays.. However I tried but failed to get the result.
You can use below aggregation:
db.col.aggregate([
{
$unwind: "$marks"
},
{
$project: {
_id: 1,
name: 1,
marks: {
$objectToArray: "$marks"
}
}
},
{
$project: {
_id :1,
name: 1,
total_marks: {
$reduce: {
input: "$marks",
initialValue: 0,
in: { $add : ["$$value", "$$this.v"] }
}
}
}
},
{
$group: {
_id: "$_id",
name: { $first: "$name" },
total_marks: { $sum: "$total_marks" }
}
}
])
Since your marks are stored as an object you should use $objectToArray to get an array of subjects. Then you can use $reduce to sum all subjects for one student.

Resources