Dynamically update key in array of objects + mongoose - javascript

I wouldnt be surprised if this has been asked before but I have not found a working example.
Basically I have a set of Boolean data inside of an array of objects and I would like to reuse my API routes/logic to update array of objects dynamically
Data Example:
{
"_id": 1,
"posts": [
{ "_id": d323d32, "published": true, "homepage": false, (...moreBooleanData) },
{ "_id": ffwfwfwc, "published": true, "homepage": false, (...moreBooleanData) },
{ "_id": fdscsdad, "published": true, "homepage": false, (...moreBooleanData) }
]
}
Mongoose Query
await Project.findOneAndUpdate(
{ _id: 1 },
{ $set: { "posts.$[el].published": isChecked } },
{
arrayFilters: [{ "el._id": postid }],
new: true
}
)
The problem is in this line "posts.$[el].published": isChecked. Here I have the key published hard coded but I would like to have this key dynamic so I can grab it from the body of my post request
const { DYNAMIC_KEY , isChecked } = req.body
"posts.$[el].$[DYNAMIC_KEY]": isChecked`
I have tried formatting the $set string with backticks, building it outside the query and passing it as 1 variable all without success. Any ideas?

You can achieve this using bracket notation:
router.post("/project/:id/:postid", async (req, res) => {
const { isChecked, dynamicKey } = req.body;
let set = `posts.$[el].${dynamicKey}`;
console.log(set);
const result = await Project.findOneAndUpdate(
{ _id: req.params.id },
{ $set: { [set]: isChecked } },
{
arrayFilters: [{ "el._id": req.params.postid }],
new: true
}
);
res.send(result);
});
I have a project document with these 3 posts:
{
"_id" : ObjectId("5def81070066dc23e05b816e"),
"posts" : [
{
"_id" : ObjectId("5def81070066dc23e05b8171"),
"published" : true,
"homepage" : false
},
{
"_id" : ObjectId("5def81070066dc23e05b8170"),
"published" : true,
"homepage" : false
},
{
"_id" : ObjectId("5def81070066dc23e05b816f"),
"published" : true,
"homepage" : false
}
],
"__v" : 0
}
I send a post request to my router ../project/5def81070066dc23e05b816e/5def81070066dc23e05b8170 with this body:
{
"isChecked": false,
"dynamicKey": "published"
}
The result is like this: ( post's (with id 5def81070066dc23e05b8170) published value is updated to false)
{
"_id": "5def81070066dc23e05b816e",
"posts": [
{
"_id": "5def81070066dc23e05b8171",
"published": true,
"homepage": false
},
{
"_id": "5def81070066dc23e05b8170",
"published": false,
"homepage": false
},
{
"_id": "5def81070066dc23e05b816f",
"published": true,
"homepage": false
}
],
"__v": 0
}

Related

how to convert 10 digit number field to string field in mongodb

i want to update mobile field to string in mongodb.
{
"_id": "1373b7723",
"firstname": "name1",
"mobile":1000000099
},
{
"_id": "137be30723",
"firstname": "name2",
"mobile":1000000088
}
i need an output like this.
{
"_id": "1373b7723",
"firstname": "name1",
"mobile":"1000000099"
},
{
"_id": "137be30723",
"firstname": "name2",
"mobile":"1000000088"
}
db.users.updateMany(
{}, //To match all documents
[{ $set: {mobile: { $concat: [ "", "$mobile" ] } } }],
{
new: true,
runValidators : true
});
i tried with the above code but its not getting the desired output.
I did something similar earlier, but it was not conversion to string but to Date. See this
You can also use $toString as mentioned here. See this example similar to your use case
Try below if it works for you.
db.users.find({}).forEach(function (doc) {
db.users.updateOne(
{
_id: doc._id,
},
{
$set: {
mobile: String(doc.mobile)
},
}
)
})
or modified version of your code
db.users.updateMany(
{}, //To match all documents
[{ $set: {mobile: { $toString: {$toLong:"$mobile"} } } }],
{
new: true,
runValidators : true
});

Having trouble updating a Mongodb collection

I have this collection setup in mongo:
[{
"_id": {
"$oid": "5f55fdede873f132ec09207e"
},
"edition_id": 35889464,
"rounds": [
{
"round": 1,
"image": "https://i.imgur.com/cT7aHQ9.gif",
"type": "normal",
"questions": [
{
"songs": [
"2vfvGlqCB7oertO5VLE0sz"
],
"open": false,
"setValue": 100,
"varValue": 50,
"type": "normal"
}
]
}
]
}]
I'm trying to update a field open:
const questionToggle = await db.collection('editions').updateOne(
{ edition_id: editionNumber },
{
$set: {
[`questionToggle.$.rounds.${roundIndex}.questions.${questionIndex}.open`]: targetValue,
},
},
);
res.json(questionToggle);
I keep getting MongoError: The positional operator did not find the match needed from the query. any ideas on what I'm doing wrong? my variables are:
edition number 35889464
round 0
question 0
editionNumber, roundIndex, and questionIndex, respectively. I've checked and they are all of type number.
I got it working:
const edition = await db.collection('editions').updateOne(
{ edition_id: editionNumber },
{
$set: {
[`rounds.${roundIndex}.questions.${questionIndex}.open`]: targetValue,
},
},
);

Adding property to mulitple documents in mongoDB

I have this data structure:
{
"_id": "5ebd08794bcc8d2fd893f4a7",
"username": "johan#gmail.com",
"password": "123",
"decks": [{
"cards": [{
"_id": "5ebd08794bcc8d2fd893f4a9",
"planeetnaam": "Venus",
"kleur": "Grijs"
},
{
"_id": "5ebd08794bcc8d2fd893f4aa",
"planeetnaam": "Neptunus",
"kleur": "Paars"
}
],
"_id": "5ebd08794bcc8d2fd893f4a8",
"name": "Planeten"
},
{
"cards": [{
"_id": "5ebd08794bcc8d2fd893f4ac",
"diernaam": "Hond",
"poten": "4"
},
{
"_id": "5ebd08794bcc8d2fd893f4ad",
"diernaam": "Kangoeroe",
"poten": "2"
}
],
"_id": "5ebd08794bcc8d2fd893f4ab",
"name": "Dieren"
}
],
"__v": 0
}
Now i want to add a new property to all the cards in deck with deckname: "Planeten". How do i do this with a mongoose query?
The cards array of deck "Planeten" should look like this after the query
"cards": [{
"_id": "5ebd08794bcc8d2fd893f4a9",
"planeetnaam": "Venus",
"kleur": "Grijs",
"newProp": null
},
{
"_id": "5ebd08794bcc8d2fd893f4aa",
"planeetnaam": "Neptunus",
"kleur": "Paars",
"newProp": null
}
],
EDIT:
This works in Robo3T:
db.getCollection('users').findOneAndUpdate(
{ '_id': ObjectId("5eba9ee0abfaf237f81fb104") },
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck._id': ObjectId("5eba9ee0abfaf237f81fb108") } ] }
)
But the server query doesnt edit any data:
User.findOneAndUpdate(
{ '_id': req.session.userid },
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck._id': req.params.deckid } ] }, function(err, user){
res.send('test');
})
Thanks in advance
you can use array update operators
the query may look something like that
db.collection.updateOne(
{ _id: <ObjectId> }, // the filter part
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck.name': 'Planeten' }] }
)
$[deck] refers to each element in the decks array
$[] is used to update all the elements in the cards array
your function may look something like that
User.updateOne(
{ '_id': req.session.userid },
{ $set: { 'decks.$[deck].cards.$[].newProp': null } },
{ arrayFilters: [{ 'deck.name': 'Planeten' }] })
.then(function (user) {
if (!user) {
res.status(404).send('Er ging helaas iets fout')
} else {
res.status(201).send("Card is toegevoegd");
}
})
hope it helps

Can elasticsearch with node, update only a certain field without rewrite?

My document has a field that looks like this:
_source:
...
"produto_tags" : [
{
"id_produto_cor" : "1623153806",
"tags" : []
},
{
"id_produto_cor" : "875623985732",
"tags": []
},
{...}
...
I need to insert on this field another Object without rewrite the entire produto_tags
data to be inserted:
{
"id_produto_cor" : "312411",
"tags": []
},
and my final document will looks like this:
_source:
...
"produto_tags" : [
{
"id_produto_cor" : "1623153806",
"tags" : []
},
{
"id_produto_cor" : "875623985732",
"tags": []
},
{
"id_produto_cor" : "312411",
"tags": []
}
]
Im using NODE js, I`m using this but it rewrites what I put :
const query = {
doc: {
nome_relatorio: nome_relatorio,
produto_tags: produto_tags,
referencia_tags: referencia_tags,
}
};
return await esClient.update({
index: indexName,
body: query,
id: id,
method: 'post',
type: typeDoc,
})
1. You can use update_by_query api
POST <index_name>/_update_by_query
{
"script": {
"source": "ctx._source.produto_tags.add(params.product)",
"params": {
"product": {
"product_id": "1004600287",
"tags": []
}
}
},
"query": {
"match_all": {} --> select the document
}
}
2. Update api
POST <index_name>/_update/<doc_id>
{
"script": {
"source": "ctx._source.produto_tags.add(params.product)",
"lang": "painless",
"params": {
"product": {
"product_id": "11",
"tags": []
}
}
}
}

How to return an array of subdocuments that match a given criteria in Mongodb?

Given a collection like:
{
"_id": "XXXX",
"JobId": [
100
],
"PersonalDetails": [
{
"Level": 1,
"Zone": [
{
"Id": 1,
"Code": "XXXXXXXX",
"IsAvailable": true
},
{
"Id": 45,
"Code": "ZZZZZZZZZ",
"IsAvailable": false
}
]
}
],
"Timestamp": ISODate("2015-11-01T00:00:00.000Z")
}
I need to get all Zone ids and codes that have the IsAvailable flag set to true.
I have tried the following:
var details = db.test.find(
{
JobId: {$in: [100]},
'PersonalDetails': {$elemMatch: {Zone : {$elemMatch: {IsAvailable: true}}}}
},
{
'PersonalDetails.Zone.Id': 1,
'PersonalDetails.Zone.Code': 1,
'PersonalDetails.Zone.IsAvailable': 1
});
details.forEach(function(doc){
var myDetails = doc.PersonalDetails;
myDetails.forEach(function(doc2){
var myZones = doc2.Zone;
print(myZones);
This gives me
{
"0" : {
"Id": 1,
"Code": "XXXXXXXX",
"IsAvailable": true
},
"1" : {
"Id": 45,
"Code": "ZZZZZZZZZ",
"IsAvailable": false
}
}
But I just want only where the IsAvailable flag is set to true returned.
Am I going about this the wrong way?? I tried using aggregate but ran into the same problem - returning all and not filtering the IsAvailable flag.
You need to use the .aggregate() method.
First of all you need to reduce the size of the documents to process using the $match operator. From there you will need to denormalize your "PersonalDetails" array using the $unwind operator.
You can then use the $project operator to return only sub-documents that match your criteria.
The $map operator in the project stage is used to return array of sub-documents.
db.collection.aggregate([
{ "$match": {
"JobId": 100,
"PersonalDetails.Zone.IsAvailable": true
}},
{ "$unwind": "$PersonalDetails" },
{ "$project": {
"zone": {
"$setDifference": [
{ "$map": {
"input": "$PersonalDetails.Zone",
"as": "z",
"in": { "$cond": [ "$$z.IsAvailable", "$$z", false ] }
}},
[false]
]
}
}}
])
Which returns:
{
"_id" : "XXXX",
"zone" : [
{
"Id" : 1,
"Code" : "XXXXXXXX",
"IsAvailable" : true
}
]
}
Starting from MongoDB 3.2 we can use the $filter operator to do this efficiently
db.collection.aggregate([
{ "$match": {
"JobId": 100,
"PersonalDetails.Zone.IsAvailable": true
}},
{ "$unwind": "$PersonalDetails" },
{ "$project": {
"zone": {
"$filter": {
"input": "$PersonalDetails.Zone",
"as": "z", "cond": "$$z.IsAvailable"
}
}
}}
])

Categories