Pull element from an array of arrays MongoDb - node.js

I am struggling to find the format for a query to remove an element (with an _id) from an array of arrays in Mongo.
When looking at the docs I couldn't find anything that was similar to what I have
https://www.mongodb.com/docs/manual/reference/operator/update/pull/#up._S_pull
I know: the _id of the document in MySchema and the _id of the array element in innerArray.
I don't know the outerArray _id
Could someone help point out where I went wrong Thank you!
This is an example of the data (imagine the _id in ObjectId)
{
outerArray:[
{
_id: 1
innerArray: [{_id: 23, name: '123'}, {_id: 13, name: 'asdac'} ]
},
{
_id: 2,
innerArray: [{_id: 16,name:'asf' }, {_id: 18,name:'asf' } ]
},
{
_id: 3,
innerArray: [{_id: 136,name:'asf' }, {_id: 128,name:'asf' } ]
}
]
}
innerIds is an array of mongoose.Types.ObjectId
return MySchema.updateOne(
{
_id: documentId,
},
{ $pull: { outerArray: { innerArray: { _id: { $in: innerIds } } } } },
)
.session(session)
.exec()

db.collection.update({},
{
$pull: {
"outerArray.$[].innerArray": {//$[] does the trick
_id: {
$in: [
16
]
}
}
}
})
playground

Related

mongoDB aggregation- unwind, sort and group

Considering I have a users collection contains those documents:
{
_id: 1,
hobbies: ['eat', 'read', 'swim']
},
{
_id: 2,
hobbies: ['eat', 'sleep', 'swim']
},
{
_id: 3,
hobbies: ['code', 'read', 'eat']
}
I want to do an aggregation on this collection so the result will be a distinct list of all those hobbies sorted in alphabetic order, for example:
{
result: [code, eat, read, sleep, swim]
}
I've tried this solution, but it didn't work for me:
{
$unwind: {path: "$hobbies"},
$group: {_id: null, result: {$addToSet: "$hobbies"}}
}
My problem is to sort the result field...
Your approach is very close already. Just remember $unwind and $group are separate pipeline stages. You need to wrap them with individual curly brackets. And for the sorting, you can do a $sortArray at the end of the pipeline.
db.collection.aggregate([
{
$unwind: {
path: "$hobbies"
}
},
{
$group: {
_id: null,
result: {
$addToSet: "$hobbies"
}
}
},
{
$set: {
result: {
$sortArray: {
input: "$result",
sortBy: 1
}
}
}
}
])
Mongo Playground

Node.js- Add new field in array of Mongodb object

I have a collection of users that has an array of multiple plates :
users : [
{
_id: '61234XXX'
plates: [
{
_id: '123'
'color': 'orange'
},
{
_id: '124'
'color': 'blue'
}
]
},
{
_id: '63456XXX'
plates: [
{
_id: '321'
'color': 'orange'
},
{
_id: '432'
'color': 'green'
}
]
}
]
I'm trying to figure out a way to add a new field to the all current plate objects for every user:
I've got to this:
await User.findOneAndUpdate(
{ _id: '63456XXX' },
{
$set : {
[`plates.0.plateStyle`]: "testValue"
}
}
)
Tho this works it's not fit for purpose. Is there a better way I can iterate over this?
You can try this query:
Here you are using array filters to tell mongo: "Where you find an object with _id: "61234XXX" and exists the plates then, there, get the document (elem) which match that plates is not empty (to get all) and add plateStyle".
await User.updateMany({
_id: "61234XXX",
plates: {
$exists: true
}
},
{
$set: {
"plates.$[elem].plateStyle": "testValue"
}
},
{
arrayFilters: [
{
elem: { $ne: [ "$plates", [] ] }
}
]
})
Example here
Also if you are finding by _id you won't need updateMany since _id is unique and only will be 1 or 0 results.

Use Mongoose aggregate to retrieve data of array inside of a schema

I have a schema in MongoDB like this.
{
productID:1,
reviews:
[
{
_id:1
likes:[{userID:1},{userID:2}],
dislikes:[{userID:3},{userID:4}],
comment:"first comment"
},
{
_id:2
likes:[{userID:1},{userID:2}],
dislikes:[{userID:3},{userID:4}],
comment:"first comment"
}
]
}
I want to fetch the likes count of a userID of a particular review for example like count of userID 2 of review id 2. I tried to get it with the help of aggregate but got stuck.
this is the code that I tried.
ProductReview.aggregate([
{ $match: { productID: productID } },
{ $match: {reviews._id:_id}}
])
but it looks like I am messing with the mongoose syntax.
To get likes by user on particular reviews then use this query
You will have to pass productID, reviewsID and userID
db.collection.aggregate([
{
$match: {
"productID": 1
}
},
{
$unwind: "$reviews"
},
{
$match: {
"reviews._id": 2
}
},
{
$unwind: "$reviews.likes"
},
{
$match: {
"reviews.likes.userID": 2
}
},
{
$group: {
_id: "$reviews.likes",
count: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
userID: "$_id.userID",
count: 1
}
}
])
Mongo Playground: https://mongoplayground.net/p/wUC5tbnLC47
OLD
This returns for all reviews
Mongo Playground: https://mongoplayground.net/p/Ob5BLCAHrw1
if you want both likes and dislikes of users with one query you can use $facet
Mongo Playground: https://mongoplayground.net/p/LELfQfKjw_h

Mongoose Aggregation not working, tried different ways still not able to solve the problem, even dont know what is problem

I am new to node and mongoose. Tried many ways though none worked for me.
Aggregate query on mongo shell is working, here is query:-
db.collection.aggregate([
{
$match: {
p_id: ObjectId("5d8b4f24d86d9f2400d7ff46"),
m_id: { $in: [ObjectId("5dde14b3f34ac02500a2b0aa")] }
}
},
{ $sort: { updated_at: 1 } },
{ $group: { _id: "$m_id", evaluation: { $last: "$evaluation" } } }
]);
Aggregate query in mongoose:-
Model.aggregate([
{ $match: { p_id: pId, m_id: { $in: mIds } } },
{ $sort: { updated_at: 1 } },
{
$group: {
_id: "$m_id",
evaluation: { $last: "$evaluation" }
}
}
]);
Error:- throwing cursor option is required
Aggregate query after adding cursor optionr:-
Query:-
Model.aggregate(
[
{ $match: { p_id: pId, m_id: { $in: mIds } } },
{ $sort: { updated_at: 1 } },
{
$group: {
_id: "$m_id",
evaluation: { $last: "$evaluation" }
}
}
],
{ cursor: {} }
);
Error: - error_stack=Error: Arguments must be aggregate pipeline operators
Mongoose aggregate api fluent returning aggregationcursor but on doing aggregationcursor.next() a promise is returned in pending state. After adding then() on aggregationcursor.next() a null object is returned:-
Model.aggregate()
.match({ p_id: pId, m_id: { $in: mIds } })
.sort({ $updated_at: 1 })
.group({
_id: "$m_id",
evaluation: { $last: "$evaluation" }
})
.cursor({ batchSize: 1000 });
Schema:
const PSchema = new Schema({
m_id: { type: Schema.Types.ObjectId },
p_id: { type: Schema.Types.ObjectId },
evaluation: { type: String, enum: _.values(EvaluationType) },
created_at: { type: Date },
updated_at: { type: Date }
});
Mongo/Mongoose version:-
"#types/mongoose": "^3.8.36",
"mongodb": "^3.3.5",
"mongoose": "~4.5.9",
I had to try different things, finally following query worked for me.
let cursor = Model.aggregate([
{
$match: {
'p_id': mongoose.Types.ObjectId(providerId) ,'m_id': { $in:objectIds }
}
},
{
$sort: {
'updated_at':1
}
},
{
$group: {
_id: '$m_id',
evaluation: { $last: '$evaluation' }
}
}
]).cursor({async:true});
Three things which i had to do:
I have to pass objectIds only in $match, as in normal find strings work, mongoose is able to convert strings into objectIds internally. For aggregate have to pass objectIds in $match.
Have to pass cursor option. As per [https://docs.mongodb.com/manual/reference/command/aggregate/#dbcmd.aggregate][1] cursor is mandatory after mongodb 3.6 version in aggregate. My mongodb version id 4.0.5 .
Had to pass cursor in async mode cursor({async:true}). Got results from cursor as cursor.toArray().

Get only the last element of array mongoose

I have array in a document, and I try to receive the last element of this array.
My code is:
Post.find({_id:postId},{'comments':{'$slice':-1}});
this gives me all the object but the comments array contains only the last element.
on the other hand,
Post.find({_id:postId},{'comments':1});
give me only the comments.
I dont find how to combine the two commands together. How it can be done?
{
"users":[],
"comments":["string1","string2","string3"],
"lastValue":"Wow"
"name":"jow"
"_id": {
"$oid": "5747d6bdecfae9d0560077cc"
},
}
Thanks
You might want to use mongodb (version 3.2) aggregation $slice like that:
Post.aggregate([
{
$match: {
'_id.$oid': postId
}
},
{
$project: {
comments: {
$slice: [ "$comments", -1 ]
}
}
}
]);
In earlier versions of mongodb:
Post.aggregate([
{
$match: {
'_id.$oid': postId
}
},
{
$unwind: "$comments"
},
{
$group : {
_id: "$_id.$oid",
comment: { $last: "$comments" }
}
}
]);
I hope this helps.
db.Post.find(
{ _id: postId },
{ comments: { $slice: -1 }, _id: 0, users: 0, lastValue: 0, name: 0 },
);
In case of Mongoose, slice can work this way also,
model.find({
// condition
})
.select('fields')
.slice('array', -1) // <------ Here
.then((data) => {
// handle
})
.catch();
Just wrote pseudo code, as it might help someone.
db.collection_name.find({'name':'how'},{'comments': {$slice: -1}})

Resources