Get a single element from array based on condition MongoDB - node.js

Schema
// Doc
{
_id:"sr_1",
posts:[
{
_id:"1",
title:"Post 1",
tags:["rahul","kumar","thakur"]
},
{
_id:"2",
title:"Post 2",
tags:["shani","kumar","sharma"]
},
....
]
}
What do i want?
I want to get title attribute of a post.
Where
post id 2
post is inside posts array
posts array is inside document with _id sr_1.
My Solution
Blog.find({
_id: "sr_1",
"posts._id": "2"
},
{ "posts.$": 1 }
)
.then(docs => {
docs.forEach(post => console.log(post))
})
.catch(err => {
console.log(err);
})
This solution is working but when i replace Blog.find by Blog.findById or Blog.findOne then i am getting error:
MongoError: positional operator '.$' couldn't find a matching element in the array
Can anyone tell me why i am getting error?

You can use elemMatch and try this:
Blog.find({_id: "sr_1"
},
{ posts: { $elemMatch: { _id: '2', title: 'Post 2' } } },
{ "posts.$": 1}
);

Related

How can I find specific document and update a value of specific key inside array?

I have a structure like this:
{
_id: new ObjectId("634aa49f98e3a05346dd2327"),
filmName: 'Film number 1',
episodes: [
{
episodeName: 'Testing 1',
slugEpisode: 'testing-1',
_id: new ObjectId("6351395c17f08335f1dabfc9")
},
{
episodeName: 'Testing 2',
slugEpisode: 'testing-2',
_id: new ObjectId("6351399d9a2533b9be1cbab0")
},
],
},
{
_id: new ObjectId("634aa4cc98e3a05346dd232a"),
filmName: 'Film number 2',
episodes: [
{
episodeName: 'Something 1',
slugEpisode: 'something-1',
_id: new ObjectId("6367cce66d6b85442f850b3a")
},
{
episodeName: 'Something 2',
slugEpisode: 'something-2',
_id: new ObjectId("6367cd0e6d6b85442f850b3e")
},
],
}
I received 3 fields:
_id: Film _id
episodeId: Episode _id
episodeName: The content I wish to update
I tried to find a specific Film ID to get a specific film, and from then on, I pass an Episode ID to find the exact episode in the episodes array. Then, update the episodeName of that specific episode.
Here's my code in NodeJS:
editEpisode: async (req, res) => {
const { _id } = req.params
const { episodeId, episodeName } = req.body
try {
const specificResult = await Films.findOneAndUpdate(
{ _id, 'episodes._id': episodeId },
{ episodeName }
)
console.log(specificResult)
res.json({ msg: "Success update episode name" })
} catch (err) {
return res.status(500).json({ msg: err.message })
}
},
But what console.log display to me is a whole document, and when I check in MongoDB, there was no update at all, does my way of using findOneAndUpdate incorrect?
I'm reading this document: MongooseJS - Find One and Update, they said this one gives me the option to filter and update.
The MongoDB server needs to know which array element to update. If there is just one array element to update, here's one way you could do it. (I picked a specific element. You would use your req.params and req.body.)
db.films.update({
"_id": ObjectId("634aa4cc98e3a05346dd232a"),
"episodes._id": ObjectId("6367cd0e6d6b85442f850b3e")
},
{
"$set": {
"episodes.$.episodeName": "Something Two"
}
})
Try it on mongoplayground.net.
You can use the filtered positional operator $[<identifier>] which essentially finds the element or object (in your case) with a filter condition and updates that.
Query:
const { _id } = req.params
const { episodeId, episodeName } = req.body
await Films.update({
"_id": _id
},
{
$set: {
"episodes.$[elem].episodeName": episodeName
}
},
{
arrayFilters: [
{
"elem._id": episodeId
}
]
})
Check it out here for example purpose I've put ids as numbers and episode name to update as "UpdatedValue"

Update multiple objects in nested array

Question: Is it possible to update multiple objects in a nested array based on another field in the objects, using a single Mongoose method?
More specifically, I'm trying to update subscribed in each object of the Contact.groups array where the object's name value is included in groupNames. Solution 1 works, but it seems messy and inefficient to use both findOne() and save(). Solution 2 is close to working with just findOneAndUpdate(), but only the first eligible object in Contact.groups is updated. Am I able to update all the eligible objects using just findOneAndUpdate()?
Contact schema (trimmed down to relevant info):
{
phone: { type: String, unique: true },
groups: [
{
name: { type: String },
subscribed: { type: Boolean }
}
]
}
Variables I have at this point:
const phoneToUpdate = '1234567890' // Contact.phone to find
const groupNames = [ 'A', 'B', 'C' ] // Contacts.groups <obj>.name must be one of these
const subStatus = false // Contacts.groups <obj>.subscribed new value
Solution 1 (seems inefficient and messy):
Contact
.findOne({ phone: phoneToUpdate })
.then(contact => {
contact.groups
.filter(g => groupNames.includes(g.name))
.forEach(g => g.subscribed = subStatus)
contact
.save()
.then(c => console.log(c))
.catch(e => console.log(e))
})
.catch(e => console.log(e))
Solution 2 (only updates the first matching object):
Contact
.findOneAndUpdate(
{ phone: phoneToUpdate, 'groups.name': { $in: groupNames } },
{ $set: { 'groups.$.subscribed': subStatus } },
{ new: true }
)
.then(c => console.log(c))
.catch(error => console.log(error))
// Example Contact after the findOneAndUpdate
{
phone: '1234567890',
groups: [
{ name: 'A', subscribed: false },
{ name: 'B', subscribed: true } // Should also be false
]
}
You can not use $ operator since he will act as a placeholder only for the first match.
The positional $ operator acts as a placeholder for the first match of
the update query document.
What you can use is arrayFilters operator. You can modify your query like this:
Contact.findOneAndUpdate({
"phone": phoneToUpdate
},
{
"$set": {
"groups.$[elem].subscribed": subStatus
}
},
{
"arrayFilters": [
{
"elem.name": {
"$in": groupNames
}
}
]
})
Here is a working example: https://mongoplayground.net/p/sBT-aC4zW93

Mongodb findone document in array by object id

Can I get only 1 photo by objectid? I only need to get 1 Image detail from 1 post by photo but what i get is all photo of post.
this is my db structure
and this is my code looks like:
Post.findOne({
$and: [
{ photo: { $elemMatch: { _id: id } } } ]
}).exec((err, post) => {
if (err) {
return res.status(400).json({ error: err });
}
req.post = post;
console.log(req.post);
next();
});
what i get in req.post is only [].
Thanks in advance
The $elemMatch projection operator provides a way to alter the returned documents, in here coupled with find utilizing second parameter which is projection will achieve that.
Post.find(
{},
{
_id: 0,
photo: { $elemMatch: { _id: id } }
}
);
This will provide leaned documents with the promise: .lean().exec():
Post.find(
{},
{
_id: 0,
photo: { $elemMatch: { _id: id } }
}
)
.lean()
.exec();
NOTE: $elemMatch behaviour is to return the first document where photo's id matches.
You can try with aggregate instead of findOne:
https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
Post.aggregate([
{ $match: { 'photo._id': id } },
{ $unwind: "$photo" },
{ $match: { 'photo._id': id } },
]);
Maybe not the best, but single photo data is achievable.

in which case to use objectId over string validation in mongodb?

getting validation failed error while insertion in comment collection.
I am having 3 collections(Rule, Page and Comment).
Rule collection:-
{validator: { $or: [ { r_name: { $type: "string"} } ] }}
Page collection:-
{validator: { $or: [ { url: { $type: "string"} } ] }}
Comment collection:-
{ validator: { $or: [ { rule_id: { $type: 'objectId' } }, { page_id: { $type: 'objectId' } } ] } }
insertion in Comment collection:-
process
.myDb
.comment
.insertOne({
rule_id: "5a035eb6eea8b4ba363e6f8d",
page_id: "5a035effeea8b4ba363e6f8e"
})
.then(resp => {
console.log('Success');
})
.catch(() => {
// HERE i am getting "Document failed validation"
console.log('Error');
});
Doubts:-
I am not sure what kind of validation to be used in this case for comment collection.
what is the difference between validation $type objectId & string.
as far as I found objectId is binary but the string would be character array implementation.
when to use objectId?
in RDBMS we would create a foreign key how to achieve the same in MongoDB.
this can be solved by mongoose plugin but I cant use that because of some other reasons.
You're passing in strings where your validator expects ObjectIds. You can however compute the corresponding ObjectId from a given string by using ObjectId(stringValue).
In your code:
process
.myDb
.comment
.insertOne({
rule_id: ObjectId("5a035eb6eea8b4ba363e6f8d"),
page_id: ObjectId("5a035effeea8b4ba363e6f8e")
})
.then(resp => {
console.log('Success');
})
.catch(() => {
// HERE i am getting "Document failed validation"
console.log('Error');
});
You might need to prefix ObjectId() with a module name depending on how you import mongoDB etc., but you get the idea.
See here for reference.

$pulling an object from an array based on _id in Mongoose [duplicate]

Doc:
{
_id: 5150a1199fac0e6910000002,
name: 'some name',
items: [{
id: 23,
name: 'item name 23'
},{
id: 24,
name: 'item name 24'
}]
}
Is there a way to pull a specific object from an array? I.E. how do I pull the entire item object with id 23 from the items array.
I have tried:
db.mycollection.update({'_id': ObjectId("5150a1199fac0e6910000002")}, {$pull: {id: 23}});
However I am pretty sure that I am not using 'pull' correctly. From what I understand pull will pull a field from an array but not an object.
Any ideas how to pull the entire object out of the array.
As a bonus I am trying to do this in mongoose/nodejs, as well not sure if this type of thing is in the mongoose API but I could not find it.
try..
db.mycollection.update(
{ '_id': ObjectId("5150a1199fac0e6910000002") },
{ $pull: { items: { id: 23 } } },
false, // Upsert
true, // Multi
);
I have a document like
I have to delete address from address array
After searching lots on internet I found the solution
Customer.findOneAndUpdate(query, { $pull: {address: addressId} }, (err, data) => {
if (err) {
return res.status(500).json({ error: 'error in deleting address' });
}
res.json(data);
});
my database:
{
"_id" : ObjectId("5806056dce046557874d3ab18"),
"data" : [
{ "id" : 1 },
{ "id" : 2 },
{ "id" : 3 }
]
}
my query:
db.getCollection('play_table').update({},{$pull:{"data":{"id":3}}},{multi:true}
output:
{
"_id" : ObjectId("5806056dce046557874d3ab18"),
"data" : [
{ "id" : 1 },
{ "id" : 2 }
]
}
You can try it also:
db.getCollection('docs').update({ },{'$pull':{ 'items':{'id': 3 }}},{multi:true})
For a single record in array:
db.getCollection('documents').update(
{ },
{'$pull':{ 'items':{'mobile': 1234567890 }}},
{new:true}
);
For a multiple records with same mobile number in array:
db.getCollection('documents').update(
{ },
{
$pull: {
items: { mobile: 1234567890 }
}
},
{ new:true, multi:true }
)
Use $pull to remove the data
return this.mobiledashboardModel
.update({"_id": args.dashboardId}, { $pull: {"viewData": { "_id": widgetId}}})
.exec()
.then(dashboardDoc => {
return {
result: dashboardDoc
}
});
Kishore Diyyana:
If you want to remove all elements including the key of the element attributes list.
Here is the example of mongoDB unset operator:
db.UM_PREAUTH_CASE.update(
{ 'Id' : 123}, { $unset: { dataElements: ""} } )
JSON Look like this:
{ "Id":123,"dataElements" : [ { "createdBy" : "Kishore Babu Diyyana", "createdByUserId" : 2020 }, { "createdBy" : "Diyyana Kishore", "createdByUserId" : 2021 } ] }

Resources