I have 2 issues with mongoose aggregation and index method - node.js

I have 2 issues
FIRST ONE:
I am trying to make review schema that a user should add 1 review per bootcamp
Code:
ReviewSchema.index({ bootcamp: 1, user: 1 }, { unique: true });
It doesnt work .. and the user still can add more than one review
SECOND ISSUE:
I am trying to calculate the averagerating of reviews but it doesn`t get added to the db when am fetching the bootcamps
Code:
// Static Method to get the avg rating of reviews and save
ReviewSchema.statics.getAverageRating = async function (bootcampId) {
const obj = await this.aggregate([
{
$match: { bootcamp: bootcampId },
},
{
$group: {
_id: '$bootcamp',
averageRating: { $avg: '$rating' },
},
},
]);
try {
await this.model('Bootcamp').findByIdAndUpdate(bootcampId, {
averageRating: obj[0].averageRating,
});
} catch (err) {
console.log(err);
}
//Call averageRating after save
ReviewSchema.post('save', async function () {
await this.constructor.getAverageRating(this.bootcamp);
});
//Call averageRating before remove
ReviewSchema.pre('remove', async function () {
await this.constructor.getAverageRating(this.bootcamp);
});
** It doesnt work and the averagerating never gets added to the database (as a bootcamp`s field)**

I Did the same as the tutorial and it didn`t work at the first but then i figured out that missing a semi-colon.

Related

GET request with $function in Mongodb

Summary:
I am trying to Combine the result of Mongodb aggregation with a third party API and I can't find anything relevant to it.
Explanation:
The below Express route finds all Games that comes after the provided Date and have not been cancelled. The next step is to get some data of that single game from the Third party API and attach it to the object and continue further in the pipeline
Issue:
It seems that you can't have a XHR request inside the $function (I didn't find anything in the official documentation so I'm not sure on that)
const today = moment();
today.year(2021);
today.month(5);
let response = await Game.aggregate([
{
$match: {
$expr: {
$and: [
{ $gte: ["$date", moment(today).startOf('day').toDate()] },
{ $eq: ["$canceled", false] },
]
}
}
},
{ $sort: { date: 1 } },
{
$addFields: {
boxScore: {
$function:
{
body: async function (season, week, home_team) {
const result = await axios.get(
`SINGLEGAMEURL/${season}/${week}/${home_team}`,
{
headers: {
'Subscription-Key': 'SOMEKEY',
},
}
);
return result.data;
},
args: ["$season", '$week', 'home_team'],
lang: "js"
}
}
}
}
]);
I would really appreciate any help/direction on this, Cheers!
I doubt that you can use asynchronous functions in $function, because they return a promise that resolves to result.data, rather than the data themselves. Instead, consider performing the asynchronous operation in your express middleware, after the MongoDB operation. Something like this:
app.use("/path", async function(req, res) {
const today = moment();
today.year(2021);
today.month(5);
let response = await Game.aggregate([
{$match: ...},
{$sort: {date: 1}}
]).toArray();
await Promise.all(response.map(row => axios.get(
`SINGLEGAMEURL/${row.season}/${row.week}/${row.home_team}`,
{headers: {'Subscription-Key': 'SOMEKEY'}}
).then(function(result) {
row.boxScore = result.data;
})));
res.json(response);
});
(Probably the Promise.all can be avoided, but I'm not experienced enough with async/await to know how.)

How to fix 'Updated field become undefined' issue in MongoDb query

I'm currently writing a mongoDB query to update set of queries in the db, The requirement is to fetch the db entries that has mediaOrigin = 'Kaltura' and the mediaType= 'YOUTUBE', then update the entries as mediaOrigin= 'AWS' and the mediaUrl.dataUrl and mediaUrl.downloadUrl with the value of mediaUrl.originalUrl. So I have completed a script to update the relevent queries but the value that the mediaUrl.dataUrl and mediaUrl.downloadUrl taking is undefined. So how can I solve that, I need to fill that two fields with the value of mediaUrl.originalUrl.
Here is the query I have written,
try {
db.getCollection('media').find({mediaOrigin: { $eq: 'KALTURA' }, mediaType: {$eq: 'YOUTUBE' }, delete: false
})
.forEach(function(media) {
var youtubeUrl = media.mediaUrl.originalUrl;
var Url = youtubeUrl;
db.getCollection('media').update(
{
_id: media._id
},
{
$set: {
'mediaUrl.downloadUrl': Url,
'mediaUrl.dataUrl': Url,
mediaOrigin: 'AWS'
}
}
);
});} catch (e) {
print(e);}
So how can I solve that.
Here I have attached the example entry in the db that I need to update.
You are attaching .forEach end of the .find() method get results from your collection.
You have to wait to get results before sending the result into foreach.
So use it like this:
const medias = await db.getCollection('media').find({
mediaOrigin: { $eq: 'KALTURA' },
mediaType: {$eq: 'YOUTUBE' },
delete: false
}).toArray();
medias.forEach(async(media) => {
var youtubeUrl = media.mediaUrl.originalUrl;
var Url = youtubeUrl;
await db.getCollection('media').update(
{
_id: media._id
},
{
$set: {
'mediaUrl.downloadUrl': Url,
'mediaUrl.dataUrl': Url,
mediaOrigin: 'AWS'
}
}
);
});

MongoDb $inc auto increase by twice

In my code, I have to push some value in the reported array of collections of activity doc. so i use this code
return new Promise(async (resolve, reject) => {
const logData = {
'reportedBy': body.reportedBy,
'reportedAt': new Date().toISOString(),
'reportReason': body.reportReason
}
await model.findOneAndUpdate({ _id: body.id },
{
$addToSet: { 'reported.log': [logData] },
$inc: {
'reported.count': 1
}
},
{ new: true }, (err, activity) => {
createError(err, reject);
resolve(activity);
});
});
}
After successfully submit it show me count increase by 1 but in MongoDB, it shows a count 2 can any please guide me about what I miss in the above code.
You are using async & callback styles which makes the operation execute twice. Pick one style.
See also UpdateMany in MongoDB running twice with $inc

Mongoose $inc updateMany cannot increment with non-numeric argument

Node API, where we have Mongo collection of profiles, and every profile have subscription_plan which represent rest of the days they have paid for using app.
Now, i works on part of the backend which should decrease subscription_plan of all profiles by 1.
The problem is, subscription_plan is declared as String, so I can't just decrease it by 1
I tried with this after getting some tips here:
router.put('/reduceSubscription', async (req, res) => {
try {
const updatedProfiles = await Profile.updateMany({ is_active: true }, [
{
$set: {
subscription_plan: {
$toString: {
$subtract: [{ $toInt: '$subscription_plan' }, 1]
}
}
}
}
]).exec();
console.log(updatedProfiles);
} catch (err) {
res.status(500).send({ msg: err.message });
}
});
After testing in postman, i got message:
"msg": "Failed to parse number 'NaN' in $convert with no onError value: Bad digit \"N\" while parsing NaN"
I would appreciate any help or code snippet that will help me solve this problem.
Thanks :)
What you are trying to do (as the error states) is not possible with the current schema simply because your Profile model schema defines subscription_plan as a String and $inc only works on numeric fields.
Either change the schema type for that field or use the aggregate pipeline to update the value by creating a pipeline that has a set of aggregation pipeline operations i.e. 1) get the current value, 2) convert it to a numeric value using $toInt, 3) decrement the value with $subtract and 4) set the new value back to string using $toString:
router.put('/reduceSubscription', async (req, res) => {
try {
await Profile.updateMany(
{ is_active: true },
[
{ '$set': {
'subscription_plan': {
'$toString': {
'$subtract': [
{ '$toInt': '$subscription_plan' },
1
]
}
}
} }
]
).exec();
res.json('Profiles updated');
} catch (err) {
res.status(500).send({ msg: err.message });
}
});

getting sequence number from mongodb always undefined

I am trying to get by code the next sequence number but it always says "undefined".
I did this in my mongoDB before:
db.PresentationCollection.insert(
{
_id: "editorID",
seq: 0
}
)
my code (name is editorID):
function getNextSequence(name, db) {
var collection = db.get('PresentationCollection');
var ret = collection.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
You're missing the callback. Callback-based asynchronous functions generally do not return anything meaningful. See the documentation for findAndModify in the node binding's readme.
I had the same problem from following this link and it is indeed the callback not being specified and your code not waiting for the returned result - mongo db documents create auto increment
Here is what I did to solve it. Keep in mind I am using Q for promise helping but you could use straight up javascript promises.
function _getNextSequence(name) {
var deferred = Q.defer();
db.counters.findAndModify(
{ _id: name }, //query
[], //sort
{ $inc: { seq: 1 } }, //update
{ new:true }, //options
function(err, doc) { //callback
if (err) deferred.reject(err.name + ': ' + err.message);
if (doc){
deferred.resolve(doc.value.seq);
}
});
return deferred.promise;
}

Resources