How to compare two columns whether they are equal in mongoose? - node.js

I need to compare two columns and return the count value by grouping.
I've tried the below aggregation:
Activity.aggregate([
{
$group: {
_id: '$phasename',
data: {
'$push': {
Complete:
{
$cond: [
{$eq: ["$plannedEndDate", "$actualEndDate"]},
0,
1
]
}
}
},
}
},
{
$project: {
Complete1: "$data.Complete"
}
}
]);
But its not taking the column values, if I hardcode then this works fine.
please suggest how to fix this.thanks
my sample collection is ,
{
"_id": "5e0dd8628003b63cf48eb2b9",
"text": "act1",
"incumbentUser": "Sam",
"vendorUser": "Sam",
"plannedEndDate": "2020-01-01T00:00:00.000+00:00",
"phasename": "Knowledge Transfer",
"actualEndDate": "2010-01-01T00:00:00.000+00:00"
},
{
"_id": "5e0dd8628003b63cf48eb2b8",
"text": "act1",
"incumbentUser": "Sam",
"vendorUser": "Sam",
"plannedEndDate": "2020-01-01T00:00:00.000+00:00",
"phasename": "Analysis",
"actualEndDate": "2010-01-03T00:00:00.000+00:00"
}

Related

select single document with minimum value $Min mongodb

I have made several efforts to select a single specific document that contains the minimum value from the database.
let Lowestdate = await BTCMongo.aggregate(
[
// { "$sort": { "name": 1,
{
$match : { "createdAt" : { $gte: new Date(last),$lte: new Date(NEW) } } },
{
$group:
{
_id:null,
minFee_doc:{$min: "$$ROOT"},
minFee: { $min:{$toDouble:"$one"}},
firstFee: { $first: "$one" },
lastFee: { $last: "$one" },
maxFee: { $max: {$toDouble:"$one"}},
}
},
]
).then(result => {}):
with minFee_doc:{$min: "$$ROOT"}, I have been trying to return the document containing the minimum $min but it keeps returning document containing $first
How do i select the document with minimum value?
Note : i will like to return the whole document including the "CreatedAt" "UpdatedAt", and _id. of the document containing the minimum value
Expected Result should look like:
{
"minFee_doc": {
"_id": "61e84c9f622642463640e05c",
"createdAt": "2022-01-19T17:38:39.034Z",
"updatedAt": "2022-04-24T14:48:38.100Z",
"__v": 0,
"one": 2
},
"minFee": 2,
"firstFee": 3,
"lastFee": 5,
"maxFee": 6
}
Edit: also to provide a single document not multiple
$push all docs in $group then $set the array with $filter
db.collection.aggregate([
{
$match: {}
},
{
$group: {
_id: null,
minFee_doc: { $push: "$$ROOT" },
minFee: { $min: { $toDouble: "$one" } },
firstFee: { $first: "$one" },
lastFee: { $last: "$one" },
maxFee: { $max: { $toDouble: "$one" } }
}
},
{
$set: {
minFee_doc: {
$filter: {
input: "$minFee_doc",
as: "m",
cond: { "$eq": [ "$$m.one", "$minFee" ] }
}
}
}
}
])
mongoplayground

Mongoose: Updation of parent-child data attribute with sum of nested object array through findOneAndUpdate query not working

I'm trying to update one of the nested array elements in a mongo collection using the Node mongoose lib. Here is how my mongo schema looks like:
{
"_id": ObjectId("5f8af2fc5f23667adf3bbaf2"),
"score": 2.5,
"questions": [{
"_id": ObjectId("5f8af30d5f23667adf3bbaf5"),
"score": 1.5,
},
{
"_id": ObjectId("5f8af3115f23667adf3bbaf8"),
"score": 1,
"options": [{
"_id": ObjectId("5f8af3115f23667adf3bbaf9"),
"score": 1,
"desc": "description 1"
},
{
"_id": ObjectId("5f8af3115f23667adf3bbafa"),
"score": 0,
"desc": "description 2"
}
]
}
]
}
I'm updating the score inside the question array, the score attribute at the root array to be updated which is the sum of the array score i.e.
root score => question array1.score + array2.score
question score => option array1.score + array2.score
I used below mongoose function as mentioned in this answer by #turivishal which is working fine to update the root score based onn question array:
https://stackoverflow.com/a/64401747/3159714
Point to note here that, some other attributes like desc in the option array also needs to be updated in the same call.
Note: that, $setoninsert is not an option as in this case, upsert is always false.
Is this at all possible to perform both of this updates using a single query?
Add one more $map for options array check condition if option _id match then update option object and merge with current object using $mergeObjects
let id = mongoose.Types.ObjectId("5f8a94ccc8452643f1498419");
let oid = mongoose.Types.ObjectId("5f8af3115f23667adf3bbafa");
let option = {
desc: "updated desc"
};
let qid = mongoose.Types.ObjectId("5f8a94e8c8452643f149841c");
let question = {
score: 1,
order: 1,
category: "TEXT",
options: {
$map: {
input: "$$this.options",
in: {
$mergeObjects: [
"$$this",
{ $cond: [{ $eq: ["$$this._id", oid] }, option, {}] }
]
}
}
}
};
Model.findOneAndUpdate(
{ _id: id },
[{
$set: {
questions: {
$map: {
input: "$questions",
in: {
$mergeObjects: [
"$$this",
{ $cond: [{ $eq: ["$$this._id", qid] }, question, {}] }
]
}
}
}
}
},
{
$set: {
score: {
$reduce: {
input: "$questions",
initialValue: 0,
in: { $add: ["$$value", "$$this.score"] }
}
}
}
}]
])
Playground

How to remove _id from aggregate $match & $group query

I am trying to display unique records grouped by the particular slug passed in.
My output in postman looks like this though:
"subcats": [
{
"_id": {
"subcategory": {
"_id": "5d2b42c47b454712f4db7c37",
"name": "shirts"
}
}
}
]
when my desired output is:
"subcats": [
{
"_id": "5d2b42c47b454712f4db7c37",
"name": "shirts"
}
]
An example of a product in the database:
"_id": "5d39eff7a48e6e30ace831dc",
"name": "A colourful shirt",
"description": "A nice colourful t-shirt",
"category": {
"_id": "5d35faa67b19e32ab3dc91ec",
"name": "clothing",
"catSlug": "clothing"
},
"subcategory": {
"_id": "5d2b42c47b454712f4db7c37",
"name": "shirts",
"catSlug": "shirts"
},
"price": 19
}
I don't want that top level _id there with everything nested inside of it.
I tried using $project but then I just end up with an empty array.
const products = await Product.find({ "category.catSlug": catslug }).select({
name: 1,
description: 1,
price: 1,
category: 1
});
const subcats = await Product.aggregate([
{ $match: { "category.catSlug": catslug } },
{ $group: { _id: { subcategory: "$subcategory" } } }
{ $project: { _id: 0, name: 1 } }
]);
Promise.all([products, subcats]);
res.status(200).json({
products,
subcats
});
What I have understood from your question is you need product by category and all sub categories as a list.
Below is the aggregation:
db.getCollection('test').aggregate([
{ $match: { "category.catSlug": "clothing" } },
{ $group: { _id: '$subcategory.name', subCategory: { $first : "$subcategory" }}},
{ $group: { _id: null, subcats: { $push: {_id: "$subCategory._id", name: "$subCategory.name" }}}},
{ $project: {_id: 0, subcats: 1}}
]);
Note: You can directly push "$subcategory" but it will have catSlug inside object
Output:
{
"_id" : null,
"subcats" : [{
"_id" : "5d2b42c47b454712f4db7c37",
"name" : "shirts"
},
{
"_id" : "5d2b42c47b454712f4db7c37",
"name" : "pents"
}]
}
Hope this help!
Hi you need to assign the required data in other fields while projection then define the _id to 0 as given below
{$project:{subcat_data:'$_id.subcategory'},_id:0}

Node.js calling MongoDB with aggregate find

I have used SQL Server for a long time and just really learning MongoDB. I am trying to figure out how to do the aggregate finds to get just the data I want. Here is a sample from the database:
{
"_id": "1",
"user_id": "123-88",
"department_id": "1",
"type": "start",
"time": "2017-04-20T19:40:15.329Z"
}
{
"_id": "2",
"user_id": "123-88",
"department_id": "1",
"type": "stop",
"time": "2017-04-20T19:47:15.329Z"
}
What I want to do is find each unique user_id of department 1, only take the record with the latest time and tell me if they are oncall or not. So in the example above user 123-88 is not oncall now. How would you make this query? I know you will need something like this:
TimeCard.aggregate([
{ $match: {department_id: req.query.department_id}},
{ $sort: { user_id: 1, time: 1 }},
{ $group: { _id: "$user_id", current_type: "$type", lastTime: { $last: "$time" }}}
], function(err, docs){
if(err){console.log(err);}
else {res.json(docs);}
});
But I keep erroring so I know I am not correct in my logic. I know if I just have the match it works and if I add the sort it matches and sorts but the final group is not working. Also what would I add to then only show the people that are oncall. Thanks again for your help.
You can count how many "types" per user_id you have, this can be done by $sum, if it's odd, the user is oncall, because there is a start without a stop. This approach is correct only if you always have a stop for a start.
TimeCard.aggregate([
{ $match: { department_id: req.query.department_id } },
{ $sort: { user_id: 1, time: 1 } },
{ $group: { _id: "$user_id", count_types: { $sum: 1 }, lastTime: { $last: "$time" }}},
{ $match: { count_types: { $mod: [ 2, 1 ] } } },
], function(err, docs) {
if(err) { console.log(err); }
else { res.json(docs); }
});

Mongoose SUM + $cond + array field

"payments": [
{
"_id": "57bea755acfbfc4e37c3dfdf",
"user": "57b1c3d2d591a46848c25f45",
"transferred_amount": 10,
"transaction_type": "refund",
"reason": "#1968 shop box refunded",
"__v": 0
},
{
"_id": "57beb883acfbfc4e37c3dfe0",
"user": "57b1c3d2d591a46848c25f45",
"transferred_amount": 10,
"transaction_type": "payout",
"reason": "#1968 shop box refunded",
"__v": 0
}
]
this is my db data.
Model.aggragate().project({
paid_out_amount: {
$sum: {
$cond: [{
$eq: ['$payments.transaction_type', 'payout']
}, 0, '$payments.transferred_amount']
}
}
})
This is my node code to fetch those data. I'm trying sum payout amount alone and store it into a field. Here $cond always returns zero. can anyone help me out.
You can try using $unwind operator.
Like:
Model.aggregate([
{ $unwind: "$payments" },
{
$group:
{
_id: null,
paid_out_amount: { $sum: {$cond: [ { $eq: [ "$payments.transaction_type", 'payout' ] }, '$payments.transferred_amount', 0 ] } }
}
}
]);
I assume that you want to add all transferred_amount of payout type and return total sum that's why use _id:null. if need you can add fieldName for group by

Resources