I want to update the data with in existing document and not create a other json document in the database. But its not modifing log is showing { n: 0, nModified: 0, ok: 1 }..please help me thanks in advance
this is my code:
const userUpdatedPassword = await UserPreferred.updateOne({_id: userPreferredExists._id},
{
$push: {
languages: req.body.languages,
storeType: req.body.storeType,
intrestedIn: req.body.intrestedIn
}})
}
i'm passing this data in postman:
{
"languages":["korean,"Hindi"],
"storeType":["gas station"],
"intrestedIn":["sarees"]
}
I've a json document in my db.
{
"_id" : ObjectId("60e968581871a42ab43a2345"),
"languages" : [
"English"
],
"intrestedIn" : [
"clothing",
"foot wear",
"watches",
],
"storeType" : [
"department stores",
"Medical stores"
]
}
I want in this fromat:-
{
"_id" : ObjectId("60e968581871a42ab43a2345"),
"languages" : [
"English",
"korean,
"Hindi"
],
"intrestedIn" : [
"clothing",
"foot wear",
"watches",
"sarees"
],
"storeType" : [
"department stores",
"Medical stores",
"gas station"
]
}
You can use following query as your requirment, i have tried and tested at my local , One thing you have forgot to use closing quote language array for korean
const userUpdatedPassword = await UserPreferred.updateOne({_id: userPreferredExists._id},
{
$push: {
languages: {
$each: req.body.languages
},
storeType: {
$each: req.body.storeType
},
intrestedIn: {
$each: req.body.intrestedIn
}
}
})
But this code will write duplicates in the array, so if you don't want to push duplicates in the array you can use $addToSet in place of $push, like following
const userUpdatedPassword = await UserPreferred.updateOne({_id: userPreferredExists._id},
{
$addToSet: {
languages: {
$each: req.body.languages
},
storeType: {
$each: req.body.storeType
},
intrestedIn: {
$each: req.body.intrestedIn
}
}
})
Related
How can i update values in array inside an object in an array.
{
"_id" : ObjectId("63c7ca9584535c160ee4aaed"),
"status" : "REJECTED",
"steps" : [
{
"_id" : ObjectId("63c7ca9884535c160ee4ab03"),
"status" : "REJECTED",
"stepUsers" : [
{
"_id" : ObjectId("63c7ca9884535c160ee4ab04"),
"status" : "REJECTED",
}
]
},
]
}
I tried to update using arrayFilters but that didn't work. Mongo throw an error MongoServerError: Found multiple array filters with the same top-level field name steps
Collection.updateOne({
_id: id
}, {
$set: {
"steps.$[steps].stepUsers.$[stepUsers].status": 'PENDING',
}
}, {
arrayFilters: [
{ "steps._id": step._id },
{ "steps.stepUsers._id": stepUser._id }
]
})
I need to update steps.stepUsers.status in the collection.
Try to change the arrayFilters: "steps.stepUsers._id" -> "stepUsers._id"
since arrayFilters is referencing the string inside the [], not the path to it.
Collection.updateOne({
_id: id
},
{
$set: {
"steps.$[steps].stepUsers.$[stepUsers].status": "PENDING",
}
},
{
arrayFilters: [
{
"steps._id": step._id
},
{
"stepUsers._id": stepUser._id
}
]
})
See how it works on the playground example
I would like to perform autocompletion on the name but filtered on a specific city with mongoose and nodejs.
I have a mongodb collection like this :
{
"_id" : ObjectId("6007fd9d984e2507ad452cf3"),
"name" : "John",
"city" : "A",
},
{
"_id" : ObjectId("6007ff6844d9e517e1ec0976"),
"name" : "Jack",
"city" : "B",
}
What i have done so far :
I have setup MongoDB Atlas with a Search Index (with the help of search doc)
And set up the autocomplete like that :
router.get('/search', async (request, response) => {
try {
let result = await Client.aggregate([
{
"$search": {
"autocomplete": {
"query": `${request.query.term}`,
"path": "name",
"fuzzy": {
"maxEdits": 2,
"prefixLength": 3,
},
},
},
},
{
$limit: 3
},
{
$project: {
"_id": 0,
}
}
]);
response.send(result);
} catch (e) {
response.status(500).send({message: e.message});
}
});
In front-end, with autocompleteJs :
const autoCompleteJS = new autoComplete({
data: {
src: async () => {
const query = document.querySelector("#autoComplete").value;
const source = await fetch(`${window.location.origin}/search?term=${query}`);
const data = await source.json();
return data;
},
key: ["name"],
},
trigger: {
event: ["input", "focus"],
},
searchEngine: "strict",
highlight: true,
});
So far it is working well. But I don't know how to make the autocomplete result filtered based on city. It seems that the documentation does not mention this. Do you have any leads.
Use the $where pipeline stage from the aggregation pipeline after performing your search to filter out unwanted documents. So for example,
Client.aggregate([
{
"$search": {
"autocomplete": {
"query": `${request.query.term}`,
"path": "name",
"fuzzy": {
"maxEdits": 2,
"prefixLength": 3,
},
},
},
},
{
$match: { city: 'city-name' }
},
{
$limit: 3
},
{
$project: {
"_id": 0,
}
}
]);
Use a compound operator like so which lets you have more control over your results in a performant fashion:
"$search": {
"compound" : {
"filter" : [{
"text" : { path: "city", query: "New York" }
}],
"must": [{
"autocomplete": {
"query": `${request.query.term}`,
"path": "name",
"fuzzy": {
"maxEdits": 2,
"prefixLength": 3,
},
},}
}] }
using filter will filter your results, without impacting the score. must will require that the name field will also match. Also take a look at should and mustNot in the compound docs for more options.
This is my data structure:
{
studentName: 'zzz',
teachers: [
{
teacherName: 'xxx',
feedbacks: []
}, {
teacherName: 'yyy',
feedbacks: []
}
]
}
Now I am trying to code a query to add an 'feedback' object to the 'feedbacks' array that belongs to the teacher named 'yyy'.
await collection.updateOne({
studentName: 'zzz',
teachers: {
$elemMatch: {
teacherName: {
$eq: 'yyy'
}
}
}
}, {
$push: {
'teachers.$.feedbacks': {}
}
})
The query part is faulty somehow. If I change '$' to 0 or 1, then the code works finally. Otherwise, the object cannot be pushed.
This update works fine, adds the string element "Nice work" to the teachers.feedbacks nested array.
db.test.updateOne(
{
studentName: "zzz",
"teachers.teacherName": "yyy"
},
{ $push: { "teachers.$.feedbacks" : "Nice work" } }
)
The $elemMatch syntax is not required, as the query condition for the array elements is for a single field.
The updated document:
{
"studentName" : "zzz",
"teachers" : [
{
"teacherName" : "xxx",
"feedbacks" : [ ]
},
{
"teacherName" : "yyy",
"feedbacks" : [
"Nice work"
]
}
]
}
I am trying to update an embedded document in MongoDB using mongoose in nodejs. The document is simplified and shown below (The names in friendList is assumed to be unique):
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList" : [
{
"name" : "Alex",
"flag" : false,
},
{
"name" : "Bob",
"flag" : false,
},
{
"name" : "Caleb",
"flag" : true,
},
{
"name" : "Debbie",
"flag" : false,
}
]
}
I would like to update this collection by:
accepting a Patch API with a request body containing a subset of friendList and
update the nested field flag.
For example, if I were to do a patch call from postman with the request body:
{
"friendList":[
{
"name":"Alex",
"flag":true
},
{
"name":"Caleb",
"flag":false
},
{
"name":"Debbie",
"flag":false
}
]
}
then I should expect my document in MongoDB to look like this:
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList":[
{
"name":"Alex",
"flag":true
},
{
"name":"Bob",
"flag":false
},
{
"name":"Caleb",
"flag":false
},
{
"name":"Debbie",
"flag":false
}
]
}
What I have tried on nodejs is updating the entire request body:
function updateUser(req){
User.findOneAndUpdate({'_id':req.params._id},req.body,{new:true});
}
which replaces the entire friendList array:
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList":[
{
"name":"Alex",
"flag":true
},
{
"name":"Caleb",
"flag":false
},
{
"name":"Debbie",
"flag":false
}
]
}
I have also tried using array operators like $:
function updateUser(req){
User.findOneAndUpdate(
{'_id':req.params._id},
{$addToSet:{
"friendList":{
$each:req.body.friendList}
}
},
{new:true}
);
}
which gave me the output:
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList" : [
{
"name" : "Alex",
"flag" : false,
},
{
"name" : "Bob",
"flag" : false,
},
{
"name" : "Caleb",
"flag" : true,
},
{
"name" : "Debbie",
"flag" : false,
},
{
"name" : "Alex",
"flag" : true,
},
{
"name" : "Caleb",
"flag" : false,
},
]
}
which $addToSet considers both name and flag when making a comparison to check if the values exist in the array. It might work if I am able to intercept at this comparison phase such that only the name field is checked.
I have been exploring concepts like $[<identifier>] and arrayFilter but can't seem to make it work.
Simple $addToSet does not work, because your array is not ["Alex","Caleb","Debbie"]. Your array is
[
{name: "Alex", flag: true},
{name: "Caleb", flag: false},
{name: "Debbie", flag: false}
]
Element {name:"Alex", flag: true} is different to element {name: "Alex", flag: false}, that's the reason why your approach failed. I think you have to use aggregation pipeline, e.g. this one:
db.collection.aggregate([
{ $addFields: { newFriends: friendList } },
{
$set: {
friendList: {
$map: {
input: "$friendList",
in: {
name: "$$this.name",
flag: {
$cond: [
{ $eq: [{ $indexOfArray: ["$newFriends.name", "$$this.name"] }, - 1] },
"$$this.flag",
{ $arrayElemAt: [ "$newFriends.flag", { $indexOfArray: ["$newFriends.name", "$$this.name"] } ] }
]
}
}
}
}
}
},
{ $unset: "newFriends" }
])
Or if you like to work with index variable:
db.collection.aggregate([
{ $addFields: { newFriends: friendList } },
{
$set: {
friendList: {
$map: {
input: "$friendList",
in: {
$let: {
vars: { idx: { $indexOfArray: ["$newFriends.name", "$$this.name"] } },
in: {
name: "$$this.name",
flag: {
$cond: [
{ $eq: ["$$idx", - 1] },
"$$this.flag",
{ $arrayElemAt: ["$newFriends.flag", "$$idx"] }
]
}
}
}
}
}
}
}
},
{ $unset: "newFriends" }
])
Note, this will update only existing names. New names are not added to the array, your question is not clear in this regard. If you like to add also new elements then insert
{
$set: {
friendList: { $setUnion: ["$friendList", "$newFriends"] }
}
},
just before { $unset: "newFriends" }
The aggregation pipeline can be used in an update:
User.findOneAndUpdate(
{'_id':req.params._id},
[
{ $addFields: { newFriends: req.body.friendList } },
{
$set: { ...
}
]
);
I have a sample Post collection
[{
"_id" : ObjectId("5da832caeb173112348e509b"),
"body" : "Lorem Ipsu.",
"comments" : [
{
"comment" : "my sample comment",
"_id" : ObjectId("5db06e11d0987d0aa2cd5593"),
"replies" : [
{
"likes" : [
"5d999578aeb073247de4bd6e",
"5da85558886aee13e4e7f044"
],
"_id" : ObjectId("5db6a88f7c6cfb0d0c2b689b"),
"reply" : "my reply to this comment",
"user" : "5da85558886aee13e4e7f044"
}
],
"likes" : [
"5da85558886aee13e4e7f044",
]
},
],
}]
To add or remove element into comments.likes I can do like this,$push or $pull, it works with this below operation.
Post.updateOne(
{_id: req.body.id_post, "comments._id": req.body.id_comment},
{ $push: {"comments.$.likes": req.user._id}});
But if I want to push into replies.likes, could anyone please help to guide me?
I have tried something like this, but replies.likes is not updated.
db.posts.updateOne(
{ "comments.replies._id": Object("5db6a88f7c6cfb0d0c2b689b") },
{ "$push": { "comments.0.replies.$.likes": Object("5db6a88f7c6cfb0d0c2b689a") } },
function(err,numAffected) {
// something with the result in here
}
);
you can use array filter :
db.getCollection("test").updateOne(
{
"comments.replies": {
"$elemMatch": {
"_id": Object("5db6a88f7c6cfb0d0c2b689b")
}
}
},
{ "$set": { "comments.$[outer].reply.$[inner].likes": Object("5db6a88f7c6cfb0d0c2b689a") } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5cd26a886458720f7a66a413") },
{ "inner._id": ObjectId("5cd26a886458720f7a66a415") }
]
}
)
don't mind ids