Remove particular object from array in mongoose document (Mongodb) - javascript

I have to remove one element from array in document 'dsrNote'
which has "_id" : ObjectId("58a5594bb77d2f1dd49e2986")
{
"_id": ObjectId("58a558efb77d2f1dd49e2983"),
"userId": ObjectId("586356b199248d1517a6758b"),
"dsrNote": [
{
"activity": "ajay kumar dogra",
"_id": ObjectId("58a5594bb77d2f1dd49e2986")
},
{
"activity": "ajay kumar",
"_id": ObjectId("58a55969b77d2f1dd49e2987")
}
],
}
In above document i need to remove first object from dsrNote array
i have tried this, but it is not working
collectionName.update({ "userId": "586356b199248d1517a6758b" }, { $pull: { "dsrNote": { "_id": "58a5594bb77d2f1dd49e2986" } } }).exec(function (err, data) {});

You can try this it is working on my case
DSR.update({"userId":req.ID,"dsrNote._id":req.params.id}, { $pull: {"dsrNote": { "_id":req.params.id}}})
.exec(function(err,data){
});

Related

Get a single sub-object from a nested object array with MongoDB

I have a node.js server using express and mongoose and I also have a Json structure of :
[
{
"_id": "63dbaf5daabee478202ae59f",
"experimentId": "85a91abe-ef2f-416f-aa13-ec1bdf5d9766",
"experimentData": [
{
"scanId": 1652890241,
"scanData": [
{
"areaName": "A1",
"areaData": [],
"_id": "63dbaf94aabee478202ae5a5"
},
...
],
"_id": "63dbaf6caabee478202ae5a3"
},
...
],
"__v": 2
},
...
]
How can I create a query to return a single object from the scanData array like
{
"areaName": "A1",
"areaData": [],
"_id": "63dbb006e322869df811eea4"
}
The best I was able to do was:
// * Get all shapes in a well/area
// ! uses the experiment id, scan id and areaName
router.get("/:id/scan/:scanId/area/:areaName", async (req, res) => {
try {
const experiment = await Experiment.findOne({
experimentId: req.params.id,
experimentData: {
$elemMatch: {
scanId: req.params.scanId,
scanData: {
$elemMatch: {
areaName: req.params.areaName
}
}
}
}
}, {'experimentData.scanData.$': 1})
console.log(experiment)
if (!experiment || experiment.length === 0) res.status(404).json({})
else {
res.send(experiment.experimentData[0])
}
} catch (err) {
res.status(500).json({ message: err.message })
}
})
But that just returned the scanData array it would be great if I could go one level deeper and just get the object with the areaName.
I also tried some solutions with $aggregate but was not able to get any data displayed it kept returning an empty array
You can $match by your criteria layer-by-layer and $unwind to get the final scanData object in an aggregation pipeline. Use $replaceRoot to get only the scanData object.
db.collection.aggregate([
{
"$match": {
"experimentId": "85a91abe-ef2f-416f-aa13-ec1bdf5d9766"
}
},
{
"$unwind": "$experimentData"
},
{
"$match": {
"experimentData.scanId": 1652890241
}
},
{
"$unwind": "$experimentData.scanData"
},
{
"$match": {
"experimentData.scanData.areaName": "A1"
}
},
{
"$replaceRoot": {
"newRoot": "$experimentData.scanData"
}
}
])
Mongo Playground

MongoDB refuses to insert data

I have been trying to insert a piece of data in my db collection, mys chema looks like this
{
"_id": {
"$oid": "62d30157607575f6be6ce948"
},
"semester": "sem-5",
"subjectData": {
"subjectName": "eco",
"questionBank": [{
"question": "who are",
"answer": {
"introduction": "Hello my name is Izaan",
"features": ["tall", "handsome"],
"kinds": ["very", "kind"],
"conclusion": "Mighty man",
"_id": {
"$oid": "62d30157607575f6be6ce94b"
}
},
"_id": {
"$oid": "62d30157607575f6be6ce94a"
}
}],
"_id": {
"$oid": "62d30157607575f6be6ce949"
}
},
"__v": {
"$numberInt": "0"
}
}
And I would like to insert some questions and answers depending on semester and subject, first I want to query for the semester and then subject and then insert data without deleting prev data in the questionBank array
Here is how I am doing this and which is giving all kinds of error and still I have not managed to get the desired result
// IF DATA EXISTS UPDATE QUESTIONSBANK WITHOUT DELETING ANY ITEMS
try {
const result = await semesterData.update({
semester: {
$eq: semesterRecieved
},
subjectName: {
$eq: subject
},
}, {
$push: {
question: questionRecieved,
answer: answerRecieved2,
},
}, );
res.send(result);
} catch (error) {
console.log(error);
res.status(404).send(error.message);
}
}

MongoDB conditional update array elements

I'm using Mongoose in a Node.js backend and I need to update a subset of elements of an array within a document based on a condition. I used to perform the operations using save(), like this:
const channel = await Channel.findById(id);
channel.messages.forEach((i) =>
i._id.toString() === messageId && i.views < channel.counter
? i.views++
: null
);
await channel.save();
I'd like to change this code by using findByIdAndUpdate since it is only an increment and for my use case, there isn't the need of retrieving the document. Any suggestion on how I can perform the operation?
Of course, channel.messages is the array under discussion. views and counter are both of type Number.
EDIT - Example document:
{
"_id": {
"$oid": "61546b9c86a9fc19ac643924"
},
"counter": 0,
"name": "#TEST",
"messages": [{
"views": 0,
"_id": {
"$oid": "61546bc386a9fc19ac64392e"
},
"body": "test",
"sentDate": {
"$date": "2021-09-29T13:36:03.092Z"
}
}, {
"views": 0,
"_id": {
"$oid": "61546dc086a9fc19ac643934"
},
"body": "test",
"sentDate": {
"$date": "2021-09-29T13:44:32.382Z"
}
}],
"date": {
"$date": "2021-09-29T13:35:33.011Z"
},
"__v": 2
}
You can try updateOne method if you don't want to retrieve document in result,
match both fields id and messageId conditions
check expression condition, $filter to iterate loop of messages array and check if messageId and views is less than counter then it will return result and $ne condition will check the result should not empty
$inc to increment the views by 1 if query matches using $ positional operator
messageId = mongoose.Types.ObjectId(messageId);
await Channel.updateOne(
{
_id: id,
"messages._id": messageId,
$expr: {
$ne: [
{
$filter: {
input: "$messages",
cond: {
$and: [
{ $eq: ["$$this._id", messageId] },
{ $lt: ["$$this.views", "$counter"] }
]
}
}
},
[]
]
}
},
{ $inc: { "messages.$.views": 1 } }
)
Playground

Update subarray of objects in mongodb

I have this document in my database:
{
"_id": "ObjectId(...)",
"chapters": [
{
"_id": "ObjectId(...)",
"link": "128371.html",
"content": ""
}
]
}
The chapters array can have up to 3k items, and I have to populate each content attribute with some info. I want to be able to save the info I want inside the right object. Until now I was able to change the content attribute generally (in all items), but I am having trouble filtering it. This is what I managed to code using what I found in other questions:
let content = "Testing";
await models.ListNovel.updateOne(
{ link: novel_link },
{ $set: { "chapters.$[].content": content } }
);
I saw that { arrayFilters: [{ link: { $eq: chapter_link } }], multi: false } may work in some cases, but I don't use the link identifier in the update.
Thank you!
UPDATE
Similar to Suleyman's solution, I ended up with the following working code, I hope it may be useful for you.
await models.ListNovel.updateOne(
{ link: novel.link },
{ $set: { "chapters.$[elem].content": content } },
{
multi: true,
arrayFilters: [{ "elem.link": { $eq: chapter.link } }]
}
);
The condition in updateOne must match parent object, but you are using { link: novel_link } which belongs to the inner array object field, so it cannot find the document, and update doesn't happen.
To illustrate this, let's say your schema is like this:
const mongoose = require("mongoose");
const schema = new mongoose.Schema({
name: String,
chapters: [
new mongoose.Schema({
link: String,
content: String
})
]
});
module.exports = mongoose.model("ListNovel", schema);
Let's have this existing document in this collection:
{
"_id": "5e498a1fe21eea0e10690e39",
"name": "Novel1",
"chapters": [
{
"_id": "5e498a1fe21eea0e10690e3b",
"link": "128371.html",
"content": ""
},
{
"_id": "5e498a1fe21eea0e10690e3a",
"link": "222222.html",
"content": ""
}
],
"__v": 0
}
If we want to update this document's chapter with "link": "128371.html", first we need to find it with name or _id field, and update it using the filtered positional operator $.
router.put("/novels/:name", async (req, res) => {
const novel_link = "128371.html";
const content = "Testing";
const result = await ListNovel.findOneAndUpdate(
{ name: req.params.name },
{
$set: { "chapters.$[chapter].content": content }
},
{
arrayFilters: [{ "chapter.link": novel_link }],
new: true
}
);
res.send(result);
});
Here I used findOneAndUpdate to immediately retrieve the updated document, but you can also use the updateOne instead of findOneAndUpdate.
The result will be like this:
{
"_id": "5e498a1fe21eea0e10690e39",
"name": "Novel1",
"chapters": [
{
"_id": "5e498a1fe21eea0e10690e3b",
"link": "128371.html",
"content": "Testing" // => UPDATED
},
{
"_id": "5e498a1fe21eea0e10690e3a",
"link": "222222.html",
"content": ""
}
],
"__v": 0
}

Aggregate match pipeline not equal to in MongoDB

I am working on an aggregate pipeline for MongoDB, and I am trying to retrieve items where the user is not equal to a variable.
For some reason, I couldn't make it work. I tried to use $not, $ne and $nin in different possible way but can't make it to work.
This is how it looks like:
Data sample:
[{
"_id": { "$oid": "565674e2e4b030fba33d8fdc" },
"user": { "$oid": "565674832b85ce78732b7529" }
}, {
"_id": { "$oid": "565674e2e4b030fba33d8fdc" },
"user": { "$oid": "565674832b85ce78732b7529" }
}, {
"_id": { "$oid": "565674e2e4b030fba33d8fdc" },
"user": { "$oid": "56f9dfc5cc03ec883f7675d0" }
}]
Pipeline sample (simplified for this question):
Where req.query.user.id = "565674832b85ce78732b7529"
collection.aggregate([
{
$match: {
user: {
$nin: [ req.query.user.id ],
}
}
}
]
This should return only the last item.
Do you have any idea how to retrieve the data that doesn't match the user?
Thanks
Edit:
The following doesn't work either:
collection.aggregate([
{
$match: {
'user.$oid': {
$nin: [ req.query.user.id ],
}
}
}
]);
I also tried with ObjectID() and mongodb complains: [MongoError: Argument must be a string]
var ObjectID = require('mongodb').ObjectID;
// Waterline syntax here
MyCollection.native(function (err, collection) {
collection.aggregate([
{
$match: {
'user': {
$nin: [ ObjectID(req.query.user.id) ],
}
}
}
], function (err, result) {
console.log(err, result);
});
});
But this line works in the shell:
db.collection.aggregate([{$match:{"user":{"$nin":[ObjectId("565674832b85ce78732b7529")]}}}])
Based on the answer here, you can change
var ObjectId = require('mongodb'). ObjectID;
to
var ObjectId = require('sails-mongo/node_modules/mongodb').ObjectID;

Categories

Resources