How can I paginate a document array in mongoose using aggregate? - database

I want to paginate an array of documents called "Reviews" but I can't find a way to do it, since the code I execute paginate the documents for me and not the array of documents "Reviews".
Code you tried to run
return await this.animeModel
.aggregate()
.match({ _id: Types.ObjectId(reviewPaginateData.animeId) })
.unwind('$reviews')
.group({
_id: '$_id',
reviews: { $push: '$reviews' },
})
.sort(reviewPaginateData.sort)
.limit(reviewPaginateData.limit)
.skip(reviewPaginateData.limit * reviewPaginateData.skip)
.exec();
Document structure
Any way to solve this problem? Thank you very much in advance.

I don't have much experience with mongoose, but I can write a shell query to perform the pagination on an array. So, that you can integrate it in mongoose.
The reason you are not able to paginate is that you are applying the skip and limit after group which is giving you an array as output. So, to apply pagination on the array itself you need to use $slice for more read here
db.collection.aggregate([
{
$match:{
"_id":ObjectId("xyz")
}
},
{
$unwind:"$reviews"
},
{
$group:{
"_id":"$_id",
"reviews":{
$push:"$reviews"
}
}
},
{
$sort:{
sort_value:1
}
},
{
$project:{
"reviews":{
$slice:[
"$reviews",
skip_value,
limit_value
]
}
}
}
]).pretty()
Hope this will help :)

Related

Use nestjs to add unique ObjectID to each document that doesn't have one

Just looking for a bit of guidance. I am connected to my local mongoDB instance and I am trying to build a query that will add a unique objectID to every object in an array that doesn't have an _id already. I am trying to do this in nestjs so I can utilize javascript and get a unique ObjectID. Here is what I have so far.
const temp = await this.vehiclesRepository.updateMany(
{
$stipulations: [],
},
{
$set: {
$stipulations: {
$function: {
body: "function (ar) {return ar.map(x => { if(x.hasOwnProperty('_id')) return x; else {x['_id']=new ObjectId(); return x;}})}",
args: ['$stipulations'],
lang: 'js',
},
},
},
},
);
public async updateMany(filter: FilterQuery<T>, doc: UpdateQuery<T>) {
this.model.updateMany(filter, doc, { upsert: true, multi: true });
}
Can anyone tell me where I am going wrong with this? Based on my research this seems like the proper method to accomplish this task.
The $function operator is an aggregation operator. You should use it within an aggregation pipeline. There is also a $set operator specifically meant for aggregations.
You should combine the $function and $set (aggregation) operators within an aggregation pipeline.
I was able to answer my question in the MongoDB shell, with no javascript like so
db.vehicles.find().forEach(function(doc){
if(doc.stipulations != null) {
if(doc.stipulations.length > 0) {
doc.stipulations.forEach(function(stip) {
if(stip._id == null) {
stip._id = new ObjectId();
}
});
}
}
db.vehicles.updateOne({_id: doc._id}, {$set: doc});
});
For optimization it is possible to filter purely in find(), but for this one time query I am not posting that.

Delete many items from array of documents in mongodb that match this value

I am trying to delete all documents from an array of documents that match this value but cannot figure it out,
This is my delete query that is not working, courses is the array I need to be in and code is the document I need to check to see its value for deletetion. So if courses.code == 123, then delete and keep going sort of thing.
result = await mongoClient.db(DB_NAME).collection("technologies").deleteMany({ 'courses': {$in: 'courses.code'} });
That is what it looks like in my collection.
"name": "Sass",
"description": "Sass (Syntactically awesome style sheets) is a preprocessor scripting language that is interpreted or compiled into Cascading Style Sheets (CSS).",
"difficulty":2,
"courses":[
{"code":"PROG2700","name":"Client Side Programming"},
{"code":"PROG3017","name":"Full Stack Programming"}
]
},
Any help would be great, thanks!
You can filter documents that contain the field you want to update
Do tests before updates on the original collection.
After some searches, I've found this method:
const filter = { }
db.collection.update(filter,
{
$pull: {
"courses": {
code: {
$in: [
"PROG3017",
"PROG2700"
]
}
}
}
},
{
multi: true
})
This pulls out any element in the array where the value of code is $in the array.

Mongo find position by id in an Array of Objects

I have a range of documents
{
_id: ObjectId("5e388da4df54cb8efb47e61b"),
userId:'test_user'
productId:'product_6_id'
recommendations:{
_id:123
rankedList:[
0:{id:ObjectId('product_5_id'),Name:'Product_5'},
1:{id:ObjectId('product_6_id'),Name:'Product_6'},
2:{id:ObjectId('product_3_id'),Name:'Product_3'}],
Date:'2020-02-25T05:03:55.439+00:00'
}
},
{
_id: ObjectId("5e388da4df54cb8efb47e62b"),
userId:'test_user1'
productId:'product_3_id'
recommendations:{
_id:123
rankedList:[
0:{id:ObjectId('product_1_id'),Name:'Product_1'},
1:{id:ObjectId('product_5_id'),Name:'Product_5'},
2:{id:ObjectId('product_3_id'),Name:'Product_3'}],
Date:'2020-02-25T05:03:55.439+00:00'
}
}
and I need to find each time the position of productId within the Array of objects rankedList.
Thus here the answer would be positionIndex=1 for first doc and positionIndex=2 for second document.
I am quite confused with $indexOfArray and how I should use it here with aggregate.
Yes, you need $indexOfArray. The tricky part is that recommendations.rankedList is an array of objects however MongoDB allows you to use following expression:
$recommendations.rankedList.id
which evaluates to a list of strings, ['product_5_id', 'product_6_id', 'product_3_id'] in this case so your code can look like this:
db.collection.aggregate([
{
$project: {
index: {
$indexOfArray: [ "$recommendations.rankedList.id", "$productId" ]
}
}
}
])
Mongo Playground

Mongodb update an object in multi nested array

I have a document in mongodb with 2 level deep nested array of objects that I need to update, something like this.
{
"id":12362,
"onGoing":[
{
"id":14597,
"offers":[
{
"id":56897,
"status":"validated"
},
{
"id":86127,
"status":"validated"
}
]
},
{
"id":89451,
"offers":[
{
"id":12235,
"status":"validated"
},
{
"id":56457,
"status":"validated"
}
]
}
]
}
I would like to update all offers matching with their id.
I have tried to update like
db.repairJobs.update({
"onGoing.offers":{
$elemMatch:{
_id:{
$in:[
'56897', '56457'
]
}
}
}
},
{
$set:{
"ongoing.offers.$.status":"ACCEPTED"
}
});
But getting the error: cannot use the part (ongoing of ongoing.offers.0.status) to traverse the element ({ongoing: [ { _id: null, ...
Are there any ways to update, the solution need to be compatible with spring Data.
As far as I know there is no way to update documents two level deep in MongoDB. I stumbled upon this JIRA item. I don't think there's a way to use multiple $ operators in update operations.
https://jira.mongodb.org/browse/SERVER-831
I am not aware of any workarounds based on your current schema but I would recommend you to let's say, split your each of your onGoing arrays into different documents.
You can use array filters to do this
Update Nested Arrays in Conjunction with $[]
as for your question, my solution is
db.repairJobs.update(
{},
{
$set: {
"onGoing.$[].offers.$[elem].status": "ACCEPTED"
}
},
{
arrayFilters: [{ "elem.id": {$in: [56897, 56457]} }],
multi: true
});

How do I create a query from an array with mongoose?

So I have this array with 3 IDs: ["56ba576f66b9add00e4d3082","56b9252266b9add00e4d3080","56b908f166b9add00e4d307f"]
I want to build a query that says something like that:
userModel.find({ $or:
[
{_id:"56ba576f66b9add00e4d3082"},
{_id:"56ba576f66b9add00e4d3082"},
{_id:"56ba576f66b9add00e4d3082"}
]},
{email:true});
Is there some sort of a method to use in order to produce such query with the array?
Should I be using mapReduce or the $where method or anything else? (I never used those before so I'm not sure if this is the case or not)
Perhaps I'm asking the wrong question? Any recommendations are very much welcome.
If you just have one element then just use $in:
var array = [
"56ba576f66b9add00e4d3082",
"56b9252266b9add00e4d3080",
"56b908f166b9add00e4d307f"
];
userModel.find({ "_id": { "$in": array } }, { "email": true });
An $in condition is basically an $or applied to the same field.
Try using the map function:
array = ["56ba576f66b9add00e4d3082","56b9252266b9add00e4d3080","56b908f166b9add00e4d307f"]
userModel.find({ $or: array.map(function(id) {
return {_id: id};
})}, {email:true});

Resources