I have this model, and I want to calulate the sum of the amounts with selecting status and eventId.
I use $aggregate, $match and $group
Model
var moneySchema = new mongoose.Schema({
Id:Number,
Amount: {type: Float},
Status: Number,
EventId: Number
}, {
timestamps: true
}, {
collection: 'e_money'
});
moneySchema.plugin(autoIncrement.plugin, 'e_money');
module.exports = mongoose.model('e_money', moneySchema);
Function
getTotalAmount: function (id,callback) {
var model = require("./Schema/e_money");
model
.aggregate([{
$match: {
Status: 1,EventId:id
}
}, {
$group:{
_id: null,
sum: {
$sum: "$Amount"
}
}
}])
.exec(function (err, doc) {
if (err) {
callback(err);
} else {
callback(doc);
}
})
};
But my function returns an empty array. Is there any solution ?
Related
I want to search and return all 'plays' based on if a relationship exists in the 'payees' array using mongoose and Node.js.
Here is the schema:
const playSchema = new Schema({
streamer: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
song: { type: mongoose.Schema.Types.ObjectId, ref: 'Song' },
revenue: { type: Number },
createdAt: { type: Date },
payees: [ { type: mongoose.Schema.Types.ObjectId, ref: 'User' } ]
});
And here is what I am trying to do (just an example):
Play.aggregate([{ $match: { payees: { req.user.id } } }]);
You can use the $elemMatch operator.
The $elemMatch operator matches documents that contain an array field
with at least one element that matches all the specified query
criteria.
Playground
Sample route:
router.get("/plays", async (req, res) => {
const userId = req.user.id;
try {
let docs = await Play.find({
payees: {
$elemMatch: {
$eq: userId
}
}
});
res.send(docs);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
app.post("/admin/editAssignedTask/:id", (req, res) => {
Task.findById(req.params.id, (err, task) => {
task.title = req.body.title;
task.priority = req.body.priority;
task.date = new Date(req.body.date);
task.description = req.body.description;
if (req.body.assignTo) {
console.log(req.body.assignTo);
if (typeof req.body.assignTo == "string") {
task.assignedTo = [...task.assignedTo, req.body.assignTo];
Staff.find({ _id: req.body.assignTo }, (err, staff) => {
console.log(staff);
});
Staff.updateOne(
{ _id: req.body.assignTo },
{ $set: { $push: { todo: req.params.id } } },
(err, up) => {
console.log(up);
}
);
in the above code staff.find() returns
[ { todo: [ 5cc44b02abde080691893e41, 5cc46186db0195071f117808 ],
completed: [],
_id: 5cc34724ab9d2d231642f925,
name: 'gokul',
mailId: 'gokul',
__v: 12 } ]
but staff.updateOne() returns
{ ok: 0, n: 0, nModified: 0 }
here n is 0 which means no obects are matched.!
Why does this happen?
below is the Staff schema
var userSchema = new mongoose.Schema({
name: String,
mailId: String,
todo: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Task"
}
],
completed: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Task"
}
]
});
change
Staff.updateOne(
{ _id: req.body.assignTo },
{ $set: { $push: { todo: req.params.id } } },
(err, up) => {
console.log(up);
}
);
to
Staff.updateMany(
{ _id: { $in: req.body.assignTo } },
{ $push: { todo: mongoose.Types.ObjectId(req.params.id) } },
(err, up) => {
console.log(up);
}
);
1) don't use $set for pushing or pulling array elements.
2) since in the schema, attribute 'todo' is an array of objectIDs and not strings, we need to explicitly convert the string to objectIDs using mongoose.Types.ObjectId()
Suppose I have the two following schemas:
var SchemaOne = new mongoose.Schema({
created_at: { type: Date },
schemaTwo: { type: mongoose.Schema.Types.ObjectId, ref: 'SchemaTwo' },
ancestor: { type: mongoose.Schema.Types.ObjectId, ref: 'SchemaOne' }
});
var SchemaTwo = new mongoose.Schema({
headline: { type: String, required: true }
});
What I would like to do is: for every SchemaOne document with the same ancestor as one provided, return the SchemaTwo's headline to which they are associated (if any), taking into account that the results from the query should not return any duplicates and should be limited to 15 results sorted by descending order of SchemaOne's created_at field.
I started doing the following:
SchemaOne
.find({ 'ancestor': ancestor })
.sort({ 'created_at': -1 })
.populate({ path: 'schemaTwo', select: 'headline', options: { limit: 15 } })
.exec(function(err, docs) {
// do something with the results
});
But by doing so, I will still get duplicated results, i.e., I will have multiple SchemaOne documents associated with the same SchemaTwo document.
Can you give me a helping hand on how to solve this?
Using mongoose aggregate method and the async library, I managed to get it to work the way I want and need:
SchemaOne.aggregate([
{ $match: { $and: [{'ancestor': ancestor }, { 'schemaTwo': { $exists: true } }] } },
{ $group: { _id: '$schemaTwo' } },
{ $limit: 15 },
{ $sort : { 'created_at': -1 } }
],
function(err, results) {
// do something with err
async.map(results, function(doc, callback) {
SchemaTwo.findById(doc, function(err, result) {
if(err) { return callback(err, null); }
return callback(null, result);
});
}, function(err, docs) {
// do something with err
// here I have my results the way I want
});
});
User Schema
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false},
favouriteid:[{eventid:String}]
});
Event Schema
var EventSchema=new Schema({
name:String,
location:{ type:String },
description:{type:String },
price: String,
rating: {
value: String,
count: {type: String, default: 10},
userrating: [{
uservalue: String,
userid: String
}]
},
imageurl:[String],
userimageurl:[String],
reviews:[{ userid:String,
username: String,
comment:String}]
});
POST METHOD to push the value of userid and uservalue in Event Schema.
api.post('/rateevent', function (req, res) {
var userid = req.body.userid;
var uservalue = req.body.uservalue;
var eventid = req.body.eventid;
Event.findById({_id: eventid},
{$push: {rating: {userrating: {uservalue: uservalue, userid: userid}}}},
{upsert: true},
function (err, events) {
if (err) {
res.send(err);
return;
}
else {
calculaterating(events);
}
});
function calculaterating(event) {
event.rating.count++;
event.rating.value = (event.rating.value * (event.rating.count - 1) + uservalue) / event.rating.count;
res.json("rating updated");
}
});
It is showing the following error:
{
"name": "MongoError",
"message": "Can't canonicalize query: BadValue Unsupported projection option: $push: { rating: { userrating: { uservalue: \"5\", userid: \"56593f3657e27af8245735d7\" } } }",
"$err": "Can't canonicalize query: BadValue Unsupported projection option: $push: { rating: { userrating: { uservalue: \"5\", userid: \"56593f3657e27af8245735d7\" } } }",
"code": 17287
}
Is the post method not correct? I have seen other mongodb documents but not able to find this type of thing. I am new to node js. Help me.
It should be Event.update instead of Event.findById, Also your push operation looks wrong. It should be like this:
Event.findOneAndUpdate(
{_id: eventid},
{$push: {'rating.userrating': {uservalue: uservalue, userid: userid}}},
{new: true},
function (err, events) {
if (err) {
res.send(err);
return;
}
else {
if(events.length > 0){
calculaterating(events);
}
else {
res.json({msg: "Nothing to update"});
}
}
});
function calculaterating(event) {
event = event[0]; //get the object from array
event.rating.count++;
event.rating.value = (event.rating.value * (event.rating.count - 1) + uservalue) / event.rating.count;
Event.update(
{_id: eventid},
{$set: {
'rating.count': event.rating.count,
'rating.value': event.rating.value
}},
function(err, response){
if (err) {
res.send(err);
return;
}
else {
res.json({msg: "rating updated"});
}
});
}
In events variable you will get the document that was updated in the new state. If you had passed {new: false} you will get the document as it was before the update.
in MY case
i was using the wrong method like below i was updating the record by
findOne , that can`t be possible , in my case , thats why issues
occurs
Solution: if you want to update the record , use .update() method,
and if you want to find records , then you can use .find() , .findOne() , don`t mismatch
domain.Cart.findOne({
UserId: req.body.UserId,
shopId: req.body.shopId,
},
{ $addToSet: { "productDetails": _productDetails } }
).exec(function (err, results) {
console.log(err, results)
callback(null, {
result: results,
msg: "productCount has been updated"
})
})
I have this kind of 'comment' model:
{ _id: <comment-id>,
user: {
id: { type: Schema.ObjectId, ref: 'User', required: true },
name: String
},
sharedToUsers: [{ type: Schema.ObjectId, ref: 'User' }],
repliedToUsers: [{ type: Schema.ObjectId, ref: 'User' }],
}
And I want to query for all comments which pass the following conditions:
sharedToUsers array is empty
repliedToUsers array is empty
But also, I want the result to contain only 1 comment (the latest comment) per user by the user id.
I've tried to create this aggregate (Node.js, mongoose):
Comment.aggregate(
{ $match: { "sharedToUsers": [], "repliedToUsers": [] } },
{
$group: {
_id: "$user.id",
user: { $first: "$user" },
}
},
function (err, result) {
console.log(result);
if (!err) {
res.send(result);
} else {
res.status(500).send({err: err});
}
});
It is actually working, but the serious problem is that the results comments _id field is been overwritten by the nested user _id.
How can I keep the aggregate working but not to overwrite the original comment _id field?
Thanks
Ok, I have a solution.
All I wanted is to group by _id but to return the result documents with their _id field (which is been overwritten when using $group operator).
What I did is like wdberkley said, I've added comment_id : { "$first" : "$_id" } but then I wanted not to return the comment_id field (because it doesn't fit my model) so I've created a $project which put the comment_id in the regular _id field.
This is basically how it looks:
Comment.aggregate(
{
$match: {
"sharedToUsers": [], "repliedToUsers": []
}
},
{
$group: {
comment_id: { $last: "$_id" },
_id: "$user.id",
content: { $last: "$content" },
urlId: { $last: "$urlId" },
user: { $last: "$user" }
}
},
{
$project: {
_id: "$comment_id",
content: "$content",
urlId: "$urlId",
user: "$user"
}
},
{ $skip: parsedFromIndex },
{ $limit: (parsedNumOfComments - parsedFromIndex) },
function (err, result) {
console.log(result);
if (!err) {
Comment.populate(result, { path: "urlId"}, function(err, comments) {
if (!err) {
res.send(comments);
} else {
res.status(500).send({err: err});
}
});
} else {
res.status(500).send({err: err});
}
});
thanks wdbkerkley!