Mongodb update object in multi nested array - node.js

I'm trying to update an object that is inside a multi nested array in Mongodb. I know that I cannot use the positional $ operator for multi nested arrays. So I'm trying to use arrayFilters instead.
Here is my query structure:
var objTest = {
name: 'blah',
color: 'blablah'
}
SomeModel.updateOne(
{
an_id: anId,
"an_array.another_array.and_another_array.some_id": id,
},
{
$set: {
"an_array.$[element].another_array.$[another_element].and_another_array": objTest,
},
},
{
arrayFilters: [
{ "element.name": 'test' },
{ "another_element.id": 'test2' },
],
}
)
.then((result) => {
console.log("result ", result);
resolve(result);
})
.catch((err) => {
console.log("err is ", err);
reject(err);
});
So as per the example, I'm trying to update the object that matches some_id in the and_another_array array. When I try this, I'm receiving the error The top-level field name must be an alphanumeric string beginning with a lowercase letter, found 'another_element'
I don't understand what I'm doing wrong here.

I was able to figure out what the issue was. For some reason, mongodb didn't like the in another_element. renaming it to anotherelement worked.

Related

Can't acces specific values in my node.js mongoose Model (only the Object)

I have been working at this for the past 4 hours. I would therefore like some help. I want to access the specific values in my database, eg. as response.data.values.imglink although when adding imglink in console.log() I get undefined. I can get the general object but not the specifik values.
I have defined my Song Schema as:
const songSchema = new Schema({
values: [{
imglink: {
type: String
},
id: {
type: String
},
spotify: {
type: String,
},
soundCloud: {
type: String,
},
youtube: {
type: String,
},
appleMusic: {
type: String,
}}
],
}, {
timestamps: true,
})
As you can see values is an array of objects. People with a similiar problem on here hadn't included the correct values in their Schema, so maybe that's my problem? Although to me it looks correct. I then GET the values in my database. The JSON object usually looks something like this:
[
{
"_id": "5ffbba4dc47e847a79c9c68f",
"values": [
{
"_id": "5ffbba4dc47e847a79c9c690",
"imglink": "imagelink",
"id": "id",
"soundCloud": "soundcloudvalue",
"youtube": "youtubevalue",
"appleMusic": "applemusicvalue",
"spotify": "spotifyvalue"
}
]
}
]
I call it by this function, which is supposed to print out the individual values:
const getAllSongs = () => {
axios.get('http://localhost:5000/songs/'+id)
.then(function (response) {
console.log(response); // returns an object
console.log(response.data.values.imglink); // returns an object
})
.catch(function (error) {
// handle error
console.log(error);
})
}
I have an Express route object that allows me to access a song by it's id as GET http://localhost:5000/songs/id in the VS-code HTTP client (similiar to postman):
router.get(`/:id`, function(req, res) {
return Song.find(
{"values.id": req.params.id}
).then(function(song) {
// return orders when resolved
res.send(song);
console.log(id);
res.json('works yesss');
})
.catch(function (err) {
// handle error
res.status(400).json('Error: '+err)
})
});
Here are some popular solutions I have tried:
Wrapping response in JSON.stringify() doesn't work.
toObject() and toJSON() don't work either as they aren't defined when I use them.
the _doc hack doesn't work either.
I have tried looking at the Schema which is where I think the problem is. The POST-request adds the right data, the GET-request goes through I just can't acces the specific values.
I hope you have the time to help, thanks. I will be extremely grateful. And of course let me know if you have any questions.
the result of find() is a Array so to access the desired key, if length of result Array is one, to access the desired key is response.data[0].values[0].imglink.
note: the values key is a array of obejct
If the array size is more than one, you want to see the result, you can use map()
if it's not worked, using lean() like this
router.get(`/:id`, function(req, res) {
return Song.find(
{"values.id": req.params.id}
).lean().then(function(song) {
// return orders when resolved
res.send(song);
console.log(song[0].values[0].imglink); // type of song is array of object [] and values is array
res.json('works yesss');
})
.catch(function (err) {
// handle error
res.status(400).json('Error: '+err)
})
});

Mongoose $inc updateMany cannot increment with non-numeric argument

Node API, where we have Mongo collection of profiles, and every profile have subscription_plan which represent rest of the days they have paid for using app.
Now, i works on part of the backend which should decrease subscription_plan of all profiles by 1.
The problem is, subscription_plan is declared as String, so I can't just decrease it by 1
I tried with this after getting some tips here:
router.put('/reduceSubscription', async (req, res) => {
try {
const updatedProfiles = await Profile.updateMany({ is_active: true }, [
{
$set: {
subscription_plan: {
$toString: {
$subtract: [{ $toInt: '$subscription_plan' }, 1]
}
}
}
}
]).exec();
console.log(updatedProfiles);
} catch (err) {
res.status(500).send({ msg: err.message });
}
});
After testing in postman, i got message:
"msg": "Failed to parse number 'NaN' in $convert with no onError value: Bad digit \"N\" while parsing NaN"
I would appreciate any help or code snippet that will help me solve this problem.
Thanks :)
What you are trying to do (as the error states) is not possible with the current schema simply because your Profile model schema defines subscription_plan as a String and $inc only works on numeric fields.
Either change the schema type for that field or use the aggregate pipeline to update the value by creating a pipeline that has a set of aggregation pipeline operations i.e. 1) get the current value, 2) convert it to a numeric value using $toInt, 3) decrement the value with $subtract and 4) set the new value back to string using $toString:
router.put('/reduceSubscription', async (req, res) => {
try {
await Profile.updateMany(
{ is_active: true },
[
{ '$set': {
'subscription_plan': {
'$toString': {
'$subtract': [
{ '$toInt': '$subscription_plan' },
1
]
}
}
} }
]
).exec();
res.json('Profiles updated');
} catch (err) {
res.status(500).send({ msg: err.message });
}
});

How to delete an element from an array using mongoose

I'm a little bit stuck. I'm trying to delete an element from an array using mongoose.
I used :
my_collection.update({
user: req.query.user
}, {
$pullAll: { //or $pull
my_array: array[index] //= "elem1"
}
});
Unfortunately it really doesn't work...
Here is my document, if it could help :
{
"_id":"5a997cde9872f41085391f51",
"my_array":
["elem1",
"elem2",
"elem3",
"elem4"],
"user":"rodolphe",
"__v":0
}
Thank you for your help!
See $pullAll, it requires an array argument, you passed a string.
This is the error I get when I run your code:
MongoError: $pullAll requires an array argument but was given a string
Make sure you console.log your errors with .catch()
// mock data
const req = { query: { user: "rodolphe" } }
const array = ["elem1"];
const index = 0;
// update record
Collection.update({
user: req.query.user
}, {
$pullAll: { //or $pull
my_array: [array[index]] // WRAP WITH AN ARRAY
}
})
.then(res => console.log(res))
.catch(err => console.log(err));

Making a double query ( Find inside a Find) in Mongoose

I want to query a document. here's its schema
{
_id,
notes: [{_id: 243234,
text: "hey"},
_id, 421123,
text: "hi"}
]
}
I want to first find the document by _id and then find the value of 'text' on notes[1].
Using this, I can find the actual document but how can I find the object inside notes array? I have to find and update the 'text' inside note.
socket.on("individualnote edit", function(data) {
rooms.find({ _id: data.roomId}, function( err, doc) {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});
You can use positional $ operator to find and update an element within a sub document array.
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
rooms.findOneAndUpdate({
_id: _id,
'notes.text': 'hey'
}, {
'$set': {
'notes.$.text': 'new text'
}
}).then(() => {
console.log('Success');
}).catch((err) => {
console.log('err', err.stack);
});

Updating an array item using NodeJS, MongoDB & Monk

I have a data set like this:
{
name : 'Doc Name',
photos: [
{
name: 'photo1',
url: 'http://.....'
},
{
name: 'photo2',
url: 'http://......'
}
],
etc ...
Using Monk https://github.com/LearnBoost/monk how do I update photo2? I can use an index as I am iterating over the fields at the moment.
My current attempt below gives me an error, and I can't use a variable for the JSON selector (as in the index).
collection.update({_id: data._id}, {photos[i].data: filename}, function(err, updatedata) {
});
Updating items at a position in an array can be done using the positional $ operator
collection.update(
{ _id: data.id, "photos.name": "photo2" },
{ $set: { "photos.$.data": "yourdata" } }
)
So I found a solution to my problem but there may be some better options and I will leave it unanswered. But for anyone else with the same issue this is what I did:
I extracted the MongoDB document as an object in Node.js, manipulated the document, and then replaced the entire array in a single update statement. For example here is some pseudo code:
collection.find({id: 1}, function(err, doc){
for(i=0; i< doc.array.length; i++) {
//do what you gotta do
doc.array[i].property = 'new value';
}
collection.update({id: 1}, {$set : {"doc.array": doc.array}}, function(err,doc){
console.log(err);
}
})

Resources