Mongo's aggregation help - why hardcoding works and not req.body? [duplicate] - mongodb

This question already has answers here:
req.params.number is string in expressjs?
(1 answer)
Confused about how to get req.body
(1 answer)
Closed 3 years ago.
When I set customer_id = 10001, it returns the response I want.
Using the same number (eg 10001), if I comment/remove the above code and set customer_id = req.body.customerNo, it returns '[]'.
Any idea why the first one works and not the second?
using MongoDB v4.0.5
router.get('/customer/invoice-balance/', function (req, res) {
var customer_id = 10001; // ************ works
var customer_id = req.body.customerNo; // ******** doesn't work
console.log(customer_id) // output ---> 10001
var balanceTotal = function (customer_id, callback) {
Transactions.aggregate([{
$match: {
customerNo: customer_id,
status: "Completed"
}
},
{
$group: {
_id: {
date: {
$dateToString: {
format: "%m-%d-%Y",
date: "$shippedDate"
}
},
customer: "$customerNo",
total: {
$sum: "$extAmount"
}
}
}
},
{
$sort: {
"_id.date": 1
}
}
],
function (err, results) {
console.log("this is the result: ", results);
callback(err, results);
});
};
balanceTotal(customer_id, function (err, results) {
if (err) {
res.send(err);
}
res.json(results);
});
});
updated fix:
router.get('/customer/invoice-balance/:customerNo', function (req, res) {
var customer_id = parseInt(req.params.customerNo);

Related

Update field using previous value (mongodb) [duplicate]

Is it possible, using mongoose middleware, to increment two fields one with a condition and the other without? In this case i want to increment "stats.ratings" by one, if the user inserts an input greater than 0, else increment zero.
"stats.answered" always increments one
See code below
module.exports.updateStats = function (req, res) {
var rating = parseInt(req.body.rating, 10);
var wasRated;
if (rating > 0) {
wasRated = true;
} else wasRated = false
Collection.findOneAndUpdate({
_id: req.body._id
}, {
$cond: {
if: wasRated,
then: {
$inc: {
"stats.answered": 1,
"stats.ratings": 1
}
},
else: {
$inc: {
"stats.answered": 1,
"stats.ratings": 0
}
}
}
},
function (err, doc) {
if (err)
throw err;
res.status(200);
})
}
What you can do is this:
// define the default case
var update = {
$inc: {
"stats.answered": 1
}
};
if(parseInt(req.body.rating, 10) > 0) {
// override default in some cases
update = {
$inc: {
"stats.answered": 1,
"stats.ratings": 1
}
}
}
and then
Collection.findOneAndUpdate({
_id: req.body._id
}, update,
function (err, doc) {
if (err)
throw err;
res.status(200);
})
}

MongoDB Aggregate Query Returning Empty Array

I'm new to Mongo and am trying to run an aggregate command on my model in my Node.js application (using Express). I'm trying to run the query for finding all users registered within the last month.
When I run User.find(), it returns all 2 users in the DB, so I know the users are definitely there.
However, when I run this, data is just an empty array. Is there a solution I'm missing here?
router.get("/stats", verifyTokenAndAdmin, async (req, res) => {
const date = new Date();
const lastYear = new Date(date.setFullYear(date.getFullYear() - 1));
try {
const data = await User.aggregate([
{ $match: { createdAt: { $gte: lastYear } } },
{
$project: {
month: { $month: "$createdAt" },
},
},
{
$group: {
_id: "$month",
total: { $sum: 1 },
},
},
]);
res.status(200).json(data)
} catch (err) {
res.status(500).json(err);
}
});
Also, the response is a 200 so no errors there.

MongoDB + Mongoose Aggregate w/ Asnyc

I've got the following route in my express file, which takes parameters passed in from a middleware function and queries my backend MongoDB database. But for some reason, it only ever returns an empty array.
I'd like to convert the Mongoose model that allows me to use aggregate functions into async/await to conform with the rest of my code. It's online here.
module.exports = {
search: asyncWrapper(async(req, res, next) => { // Retrieve and return documents from the database.
const {
filterTarget,
filter,
source,
minDate,
maxDate,
skip,
limit,
sortBy,
sortOrder
} = req.search;
try {
const mongoData = await Model.aggregate([
{
$match: {
date: {
$gt: minDate, // Filter out by time frame...
$lt: maxDate
}
}
},
{
$match: {
[filterTarget]: filter // Match search query....
}
},
{
$set: {
[filterTarget]: { $toLower: `$${filterTarget}` } // Necessary to ensure that sort works properly...
}
},
{
$sort: {
[sortBy]: sortOrder // Sort by date...
}
},
{
$group: {
_id: null,
data: { $push: "$$ROOT" }, // Push each document into the data array.
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
count: 1,
data: {
$slice: ["$data", skip, limit]
},
}
}
])
return res.status(200).json({ data: mongoData.data || [], count: mongoData.count || 0 });
} catch (err) {
next(err);
}
})
};
For some reason, the route is only returning an empty array every time. I've double and triple checked my variables, they are not the problem.
How can I use the Mongoose.aggregate() function in an async await route?

How to update a field using its previous value in MongoDB/Mongoose [duplicate]

This question already has answers here:
Update MongoDB field using value of another field
(12 answers)
Closed 5 years ago.
I know I can do an update using $set:
Contact.update({
_id: request.id
}, {
$set: { name: newNameValue }
}, {
upsert: false
}, function(err) { ... });
But in this case, instead of passing newNameValue, I'd like to use the previous name value to compute the new one. Let's say I want to capitalize the old name, something like:
Contact.update({
_id: request.id
}, {
$set: { name: $old.name.toUpperCase() }
}, {
upsert: false
}, function(err) { ... });
I think this has already been answered here: How to add new data to current string in MongoDB?, so please, check that for a more detailed answer, but anyway, in short, you can't do that with a single query.
The way to do it using Mongoose, as shown in this official Mongoose example:
Contact.findById(request.id, (err, contract) => {
if (err) return handleError(err);
contract.name = contract.name.toUpperCase();
contract.save((err, contractContract) => {
if (err) return handleError(err);
...
});
});
as far as I know it's not possible. You would need to find and then update
Contact
.find({
_id: request.id
})
.exec(function(err, data) {
if (err) {
return ...;
}
Contact.findByIdAndUpdate(request.id, {
$set: {
name: data.name.toUpperCase()
}
}, {
new: true
}, function(err, doc) {
if (err) return ...;
console.log(doc)
});
}

How to update embedded document in mongoose?

I've looked through the mongoose API, and many questions on SO and on the google group, and still can't figure out updating embedded documents.
I'm trying to update this particular userListings object with the contents of args.
for (var i = 0; i < req.user.userListings.length; i++) {
if (req.user.userListings[i].listingId == req.params.listingId) {
User.update({
_id: req.user._id,
'userListings._id': req.user.userListings[i]._id
}, {
'userListings.isRead': args.isRead,
'userListings.isFavorite': args.isFavorite,
'userListings.isArchived': args.isArchived
}, function(err, user) {
res.send(user);
});
}
}
Here are the schemas:
var userListingSchema = new mongoose.Schema({
listingId: ObjectId,
isRead: {
type: Boolean,
default: true
},
isFavorite: {
type: Boolean,
default: false
},
isArchived: {
type: Boolean,
default: false
}
});
var userSchema = new mongoose.Schema({
userListings: [userListingSchema]
});
This find also doesn't work, which is probably the first issue:
User.find({
'_id': req.user._id,
'userListings._id': req.user.userListings[i]._id
}, function(err, user) {
console.log(err ? err : user);
});
which returns:
{ stack: [Getter/Setter],
arguments: [ 'path', undefined ],
type: 'non_object_property_call',
message: [Getter/Setter] }
That should be the equivalent of this mongo client call:
db.users.find({'userListings._id': ObjectId("4e44850101fde3a3f3000002"), _id: ObjectId("4e4483912bb87f8ef2000212")})
Running:
mongoose v1.8.1
mongoose-auth v0.0.11
node v0.4.10
when you already have the user, you can just do something like this:
var listing = req.user.userListings.id(req.params.listingId);
listing.isRead = args.isRead;
listing.isFavorite = args.isFavorite;
listing.isArchived = args.isArchived;
req.user.save(function (err) {
// ...
});
as found here: http://mongoosejs.com/docs/subdocs.html
Finding a sub-document
Each document has an _id. DocumentArrays have a special id method for looking up a document by its _id.
var doc = parent.children.id(id);
* * warning * *
as #zach pointed out, you have to declare the sub-document's schema before the actual document 's schema to be able to use the id() method.
Is this just a mismatch on variables names?
You have user.userListings[i].listingId in the for loop but user.userListings[i]._id in the find.
Are you looking for listingId or _id?
You have to save the parent object, and markModified the nested document.
That´s the way we do it
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Profile.findById(req.params.id, function (err, profile) {
if (err) { return handleError(res, err); }
if(!profile) { return res.send(404); }
var updated = _.merge(profile, req.body);
updated.markModified('NestedObj');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, profile);
});
});
};