MongoDB aggregate lookup not working with multiple parameters - javascript

I am pulling data from comments table and it works. I want to do a join equivalent with performance in mind on the users collection to get details about the user who commented.
Here is my code where I use Next js. I added the aggregate/ lookup and now I dont get anything back.
const from = req.query.from ? new Date(req.query.from) : new Date();
const postId = req.query.by;
const comments = await req.db
.collection('commentsPosts')
.find({
commentedAt: {
$lte: from,
},
...(postId && { postId }),
})
.sort({ commentedAt: -1 })
.limit(parseInt(req.query.limit, 10) || 10)
//This part is not working and breaking the whole query
.aggregate({
$lookup:{
from:"users",
localField:"createdBy",
foreignField:"_id",
as:"commenterDetails"
}
})
/////////////
.toArray();
res.send({ comments });
Edit
Updated as per the answer below but still no results
.collection('commentsPosts')
.aggregate(
[
{$match:{
commentedAt: {
$lte: from,
},
...(postId && { postId }),
}
.sort({ commentedAt: -1 })
.limit(parseInt(req.query.limit, 10) || 10)
},
{$lookup:
{
from:"users",
localField:"createdBy",
foreignField:"_id",
as:"commenterDetails"
}
}
]
).toArray();

you do not need to do a find() when you are planning to use aggregate()
Remove the find() and introduce a $match in aggregate pipeline.
db.comments.aggregate(
[
{$match:{commentedAt:{$gt:QUERY_CLAUSE_HERE}},
{$lookup:{...}}
]
)

Related

Compare if ObjecticID already exist or not in array

In mongo, I made history column array for each user to get the Id of specific card, so I would like to made a condition or not for knowing that :
To understand my code :
find the ID user, find his history :
if card ID already present => don't duplicate ID on this array
but, if is not present :
add the ID on his own history array.
My req.body._id is the Id value who the user submit
const forThisUser = { _id: req.user._id }
const condition = await User.find(forThisUser, {"history" : { $in : req.body._id}})
async function alreadyPresentOrNot(res, req){
if(condition>0){
console.log("Already present !")
res.sendStatus(401);
}
else{
console.log("Card not finding, add on array now !")
await User.findOneAndUpdate(forThisUser, { $addToSet: { history: req.body._id } }, {upsert: true})
res.sendStatus(201);
}
}
I got this error :
ERROR Expression $in takes exactly 2 arguments. 1 were passed in.
Thanks
Just try using it in an array.
const ids = [req.body._id];
const condition = await User.find(forThisUser, {"history" : { $in : [ids]}})
Here is the official link that says you have to use $in : [array]
https://mongoosejs.com/docs/api/aggregate.html?
Please refer to Monngoose's documentation:
Your query should be:
const forThisUser = { _id: req.user._id }
const condition = await User.find({...forThisUser, "history" : { $in : req.body._id}})
Find takes the filters argument as an object, which would be:
{
_id: req.user._id,
history: { $in: req.body._id }
}
Unrelated to your question, but you may want to have a look at your HTTP response codes as well.
For example, for a duplicated entry you would return a 409 Conflict, or a 400 Bad Request not a 401 Unauthorized.
And if the card is added you can return 200 Success or 204 No Conntent, unless you are creating the card resource in that same request your return 201 Created

How to do aggregation using match and lookup operation?

How to include aggregation in if condition ,do I need to use project or condition method in if condition above as well catalogue populate. I need to get the data from the mongo dB in the same order as video Ids array but it's coming in a different order so I decided to use aggregation to get the video in a proper order as in the video Ids array. Please help me to resolve this issue.
let filter = {$match:{
customerId: customerId,
_id: {
$in: _.map(videoIds, id => mongoose.Types.ObjectId(id)) || []
},
_isDeleted: false,
isActive: true
},
$lookup:{
from:'catalogues',
localField:'_isDeleted',
foreignField:'_id',
as:false
}
}
if (!req.isLocationWhitelisted) {
if (req._countryCode) {
$or=
filter.$match['languages.country'] = {
$in: req._countryCode
}
filter.$lookup['languages.country'] = {
$in: req._countryCode
}
,
filter.$match['languages.country.141'] = { $exists: true }
filter.$lookup['languages.country.141'] = { $exists: true }
}
}
let videoList = await Video.aggregate(filter);

Get the _id of the created element instead of the whole data in mongodb

I have a problem accessing the _id of the last created element inserted in to mongodbe.
is there any solution to just get the id, instead of getting all elements? especially if the data list is so long and nested so its really hard to pin the created element and gain access to his id
I am using mongoose driver on this one.
let updateDeptArr = await Budget.findOneAndUpdate(
// Dynamic
{
'_id': `${propertyValues[0]}`, // user ID
[`${keys[2]}._id`]: `${propertyValues[1]}`
},
{
'$push': {
[`${keys[2]}.$.${keys[3]}`]: propertyValues[3]
}
}, { _id: true, new: true }
).then(function (data) {
// we need to get and send The id of the last created element!!!
console.log(data[keys[2]]);
// let order = data[keys[1]].length - 1
// let id = data[keys[1]][`${order}`]._id
// res.json({ _id: id })
})
}
You can use select after query.
In the upcoming listing, you have a mongoose schema being used to query MongoDB, and just two fields are selected, as you want.
Loc
.findById(req.params.locationid)
.select('name reviews')//select chained
.exec();
Try to chain select to your call. It will just give back the name and reviews.
Try this:
let updateDeptArr = await Budget.findOneAndUpdate(
// Dynamic
{
'_id': `${propertyValues[0]}`, // user ID
[`${keys[2]}._id`]: `${propertyValues[1]}`
},
{
'$push': {
[`${keys[2]}.$.${keys[3]}`]: propertyValues[3]
}
}, { _id: true, new: true }
).select("_id")// not sure if Mongoose will chain this way
.then(function (data) {
// we need to get and send The id of the last created element!!!
console.log(data[keys[2]]);
// let order = data[keys[1]].length - 1
// let id = data[keys[1]][`${order}`]._id
// res.json({ _id: id })
})
}

$addToSet / $set does not push the ObjectId

Currently using Strapi v3.5.3ce. I have a post model with has relation with category model.
A post already exist, but I want to update the post with multiple categories, so I thought of using $addToSet / $set in order to eliminate duplicates added.
But it doesn't seem to push.
ways I have tried are
// with $set
const up = {
$set: { categories: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] },
...update
}
// with $addToSet
const up = {
$addToSet: { categories: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] },
...update
}
// with $addToSet and $each
const up = {
$addToSet: { categories: { $each: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] } },
...update
}
const post = await strapi.query('post').model
.findOneAndUpdate({_id: 'blahblahblah'}, up, { new: true })
.populate({
path: 'categories',
select: 'name'
});
the above uses findOneAndUpdate but I have also tried using update / updateOne
None of the above worked, anyone has any idea what I have gone wrong?
Thanks in advance for any suggestions.

is there a method of sorting a mongoose model by the sum of 2 fields?

i have those models
[
{"trisWin":18,"trisLost":2,"pongWin":4,"_id":"6068c0c237326d13706569aa","username":"Tommaso","password":"$2a$10$2TiaY12yCcRKWleDkXCeGe1ujJf8Liv5WBzsxFjGesrZR.KtakQtW","__v":0},
{"trisWin":13,"trisLost":0,"pongWin":10,"_id":"606b838a81811a3734a9c717","username":"Test1234","password":"$2a$10$0KoOhIRzjQlsMho0rmFsLeoorTn4bjL3eGofRQuy.cc//3zIttWsa","__v":0},
{"trisWin":9,"trisLost":0,"pongWin":2,"_id":"6068ca2ad9587c2b2c8df467","username":"Giacomo","password":"$2a$10$h24RPkGssur29K1WM5aCuOVO6Uw7cY9DD.s9nOU1iEp4qb6TxSuvu","__v":0}
]
i want to sort them by the sum of "pongWin" and "trisWin"
i have tryied this const result = await User.find().sort({ trisWin: -1 })
it works, but only with the trisWin variable
You only have to add the field to the object that you pass to the sort method.
User.find().sort({ trisWin: -1, pongWin: -1 })
You cannot sort on a sum of two fields without using aggregation.
However, aggregation is quite slow and it doesn't persist the output.
What you can do is create a new field on each document as trisTotal and then sort based on that field.
If the trisWin and trisLoss``keep updating, you can also update the trisTotalin the same update query that you use for updatingtrisWinandtrisLoss```.
User.update_many(filter={}, update= {$set: {trisTotal: { $trisWin + $trisLost}}})
const result = await User.find().sort({ trisTotal: -1 })
You can use the aggregate pipeline for this
const result = await User.aggregate([
{
$addFields: { // new field to hold the total
total: {
$sum: ["$pongWin", "$trisWin"],
},
},
},
{
$sort: { // Sorting based on total
total: -1,
},
},
{
$unset: ["total"], // Removing the newly added total field after sorting
},
]);

Categories

Resources