Update & Remove child document (subdocument) using nestjs and mongoose - mongodb

I have recently started using nestjs with mongoose. I have an existing app in nodejs and mongoose. I am not able to figure out how to update and remove a subdocument based on the document _id and subdocument _id. I am trying to update & remove productSubCategory array elements
Below is the sample document
{
"_id": "62d389321d26d175d140425e",
"productCategory": "snacks",
"active": true,
"productSubCategory": [
{
"productSubCategory": "biscuits",
"active": true,
"productSecondSubCategory": [],
"created_at": "2022-07-17T16:46:13.000Z",
"updated_at": "2022-07-17T16:46:13.000Z",
"_id": "62d43cd5ff45a56ae27de6d0"
}
],
"created_at": "2022-07-17T03:59:46.416Z",
"updated_at": "2022-07-17T03:59:46.416Z",
"__v": 1
},

You could use the findByIdAndUpdate function:
Document.findByIdAndUpdate(document_id, { $pull: { 'productSubCategory._id': subdocument_id } })

Related

how to sort mongodb document to appear document with certain key value to appear first,

// i want to appear document with isFeatured:"true" at first
{ "_id": "625c13ea5c5d3f49f152783b",
"name": "helmet 1",
"description": "gold",
"category": [
"helmet"
],
"price": "25000",
"stock": 25,
"user": "",
"isFeatured": true // with this property to come first and "isFeatured:false" later
}
You can invoke sort on the cursor to sort the documents in myCollection on the isFeatured field in descending order.
db.myCollection.find().sort({ isFeatured: -1 })
Or on aggregation pipeline query
db.myCollection.aggregate([{ $sort: { isFeatured: -1 } }])

modifiedCount is 1 even though the document didn't change

I am using a simple updateOne query where I pull an ObjectId out of an array.
await this.portfolioModel.updateOne(
{
_id: portfolioId,
'watchlist.custom._id': watchlistId
},
{
$pull: {
'watchlist.custom.$.assets': assetId
}
}
)
But I want to detect if the document actually changed or if the assetIds wasn't in the array to begin with. The problem is that even though the document hasn't changed MongoDB still returns modifiedCount with 1.
{
acknowledged: true,
modifiedCount: 1,
upsertedId: null,
upsertedCount: 0,
matchedCount: 1
}
I checked the document and the __v doen't increment with the updates so I don't know why MongoDB thinks the document has changed.
This is how the document looks (before and after update):
{
"_id": {
"$oid": "600188dab0b1c70050084e3e"
},
"watchlist": {
"all": [],
"custom": [
{
"assets": [],
"_id": {
"$oid": "624d7a18c89b3937212b3a55"
}
}
]
},
"__v": 18,
"createdAt": {
"$date": "2021-05-11T22:19:05.450Z"
},
"updatedAt": {
"$date": "2022-04-06T11:45:37.415Z"
}
}
I encountered this same issue using mongoose 6.3.5 and MongoDB serverless version 5.3.1.
The fix for me was removing {timestamps: true} from my schema. This causes the document to be 'modified' even if no change was made to its values. I believe it is because the 'updatedAt' value would change whenever updateOne() would access the document.

MongoDB delete embedded documents through array of Ids

I am working on a Node.js application that is using a MongoDB database with Mongoose. I've been stuck in this thing and didn't come up with the right query.
Problem:
There is a collection named chats which contain embedded documents (rooms) as an array of objects. I want to delete these embedded documents (rooms) through Ids which are in the array.
{
"_id": "ObjectId(6138e2b55c175846ec1e38c5)",
"type": "bot",
"rooms": [
{
"_id": "ObjectId(6138e2b55c145846ec1e38c5)",
"genre": "action"
},
{
"_id": "ObjectId(6138e2b545c145846ec1e38c5)",
"genre": "adventure"
}
]
},
{
"_id": "ObjectId(6138e2b55c1765846ec1e38c5)",
"type": "person",
"rooms": [
{
"_id": "ObjectId(6138e2565c145846ec1e38c5)",
"genre": "food"
},
{
"_id": "ObjectId(6138e2b5645c145846ec1e38c5)",
"genre": "sport"
}
]
},
{
"_id": "ObjectId(6138e2b55c1765846ec1e38c5)",
"type": "duo",
"rooms": [
{
"_id": "ObjectId(6138e21c145846ec1e38c5)",
"genre": "travel"
},
{
"_id": "ObjectId(6138e35645c145846ec1e38c5)",
"genre": "news"
}
]
}
I am converting my array of ids into MongoDB ObjectId so I can use these ids as match criteria.
const idsRoom = [
'6138e21c145846ec1e38c5',
'6138e2565c145846ec1e38c5',
'6138e2b545c145846ec1e38c5',
];
const objectIdArray = idsRoom.map((s) => mongoose.Types.ObjectId(s));
and using this query for the chat collection. But it is deleting the whole document and I only want to delete the rooms embedded document because the ids array is only for the embedded documents.
Chat.deleteMany({ 'rooms._id': objectIdArray }, function (err) {
console.log('Delete successfully')
})
I really appreciate your help on this issue.
You have to use $pull operator in a update query like this:
This query look for documents where exists the _id into rooms array and use $pull to remove the object from the array.
yourModel.updateMany({
"rooms._id": {
"$in": [
"6138e21c145846ec1e38c5",
"6138e2565c145846ec1e38c5",
"6138e2b545c145846ec1e38c5"
]
}
},
{
"$pull": {
"rooms": {
"_id": {
"$in": [
"6138e21c145846ec1e38c5",
"6138e2565c145846ec1e38c5",
"6138e2b545c145846ec1e38c5"
]
}
}
}
})
Example here.
Also you can run your query without the query parameter (in update queries the first object is the query) like this and result is the same. But is better to indicate mongo the documents using this first object.

sum key and group by key with Mongodb and Laravel

Having this collection -
{
"_id": "5b508587de796c0006207fa7",
"id": "1",
"status": "pending",
"updated_at": "2018-07-19 13:02:40",
"created_at": "2018-07-19 12:35:19"
},
{
"_id": "5b508587de796c0006207fa5",
"id": "2",
"status": "completed",
"updated_at": "2018-07-19 13:02:40",
"created_at": "2018-07-19 12:35:19"
},
I want to have a query that will sum the status key by the id key.
For example -
{
"id":"1",
"pending":"1"
}
I am using Laravel 5.5 with MongoDB
Here is a working MongoPlayground. Check out Mongo's reference for Aggregations, as well as the $group operator.
db.collection.aggregate([
{
$group: {
_id: "$status",
sumOfStatus: {
$sum: 1
}
}
}
])
EDIT: After proof-reading, I'm not really sure that was what you were looking for. This example will return your a list of statuses for each id such as:
[
{
"_id": "5",
"completed": 3,
"pending": 3
}
]
To do so, I'm leveraging theĀ $cond operator in order to conditionally $sum documents depending on their status value. One drawback is that you have to repeat this for each value. Not sure of a way around that.
Regarding the Laravel implementation, I'm definitely not a Laravel expert, but check out this answer which shows an example on how to access the aggregate() method.

MongoDB moving array of sub-documents to it's own collection

I'm looking to move an array of subdocuments into a collection of it's own keyed by the owner id. Currently, my collection is formed like this:
"_id": ObjectId("123"),
"username": "Bob Dole",
"logins": [{
"_id": ObjectId("abc123"),
"date": ISODate("2016")
}, {
"_id": ObjectId("def456"),
"date": ISODate("2016")
}]
I'm looking for the best way to write a script that would loop over each user, and move each item in the logins array to it's own "logins" collection, as follows:
{
"_id": ObjectId("abc123"),
"_ownerId": ObjectId("123"),
"date": ISODate("2016")
}
{
"_id": ObjectId("def567"),
"_ownerId": ObjectId("123"),
"date": ISODate("2016")
}
When the script ends, I'd like the login array to be removed entirely from all users.
this query will create new collection using aggregation framework
to see how it looks - just remove $out pipeline phase
db.thinking.aggregate([
{
$unwind:"$logins"
},{
$project:{
_id:"$logins._id",
_ownerId:"$_id",
date:"$logins.date"
}
},
{
$out: "newCollection"
}
])
to delete array records - as suggested in comment:
db.thinking.update({},{ "$unset": { "logins": "" } },{ "multi": true })