Mongoose update inner field - javascript

I have this kind of documents in my mongoDB :
{
id: my_id
inner_foo : [
{
inner_id : id_A
inner_field : field_A
},
{
inner_id : id_B
inner_field : field_B
}
]
}
When receiving a request, i got two ids, the id one (here my_id), and the inner_id one (here for example id_A).
How can I, using JS and mongoose, update an inner field ? such as the inner_field field of one of the array contained objet using its inner_id
I think I cannot use findByIdAndUpdate since I can't make a direct reference to the correct inner_foo entry using that method, so how should I do it ?
Many thanks !
Kev.

Demo - https://mongoplayground.net/p/2pW4UrcVFYr
Read - https://docs.mongodb.com/manual/reference/operator/update/positional/
db.collection.update(
{ id: "my_id", "inner_foo.inner_id": "id_A" }, // find the matching document
{ $set: { "inner_foo.$.inner_field": "id_A_Update" }} // set the value on the matched array index
)

Related

copying values inside a record from one key to other in mongodb

I am working on MongoDB. I am trying to append some new key-value pairs to my existing collection. I got more than 50 documents in my collection, in all of which I want to update a new key value pair. In the new key value pair I am adding, one of the values of the key should be from one of the existing key value pair of the same collection.
For example:
{ name : "algebra", quantity : 25 }
Now I want to update each of the document with
{ category : "maths" , available_quantity : 25 }
Without entering distinctly for every document its available quantity. I want it to be picked from it's respective quantity's value, and be inserted in the new key available_quantity.
I am using the code to update the document :
db.books.update( {} , { $set : { category : "maths" , available_quantity : `__ ? __` }, false, true }
What best can I put in __ ? __ block?
You can do it with Aggregation framework:
db.books.update({},
[
{
"$set": {
"available_quantity": "$quantity"
}
}
],
{
multi: true
})
Working example

How to Query a nested child from firebase angular?

// my db structure now
rcv : {
visible: 'all',
ids: [
[0] : userId,
[1] : user2Id ]
}
this is how i query to get the data it works.
//service.ts
getAlbumByUserId(userId) {
return this.afs.collection('albums', ref => ref.where('rcv.visible', '==', 'all').where('rcv.ids', 'array-contains', userId)).valueChanges();
}
//component.ts
this.service.getAlbumByUserId(this.userId);
but i want to set the structure like this but i don't know how to query nested objects in firebase
// database structure
rcv : {
visible: 'all',
ids: {
userId: {
id: userId
}
user2Id: {
id: user2Id
}
}
}
You're looking for the array-contains operator, which can check if a field that is an array contains a certain value.
You're already using the correct array-contains operator, but not with the correct syntax. The array-contains operator checks whether any element of your array is exactly the same as the value you pass in. So you need to pass in the complete value that exists in the array:
ref.where('rcv.visible', '==', 'all').where('rcv.ids', 'array-contains', { id: userId })
As you add more data to the array, it may become unfeasible to reproduce the entire array element for the query. In that case, the common approach is to add an additional field where you keep just the IDs.
So you'd end up with one field (say rcv.users) where you keep all details about the receiving users, and one field (say rcv.ids) where you just keep their IDs, and that you use for querying.

Mongoose: Add more items to existing object

Using Mongoose, How can I add more items to an object without replacing existing ones?
User.findOneAndUpdate(
{ userId: 0 },
{ userObjects: { newItem: value } }
);
The problem with above code is that it clears whatever was there before and replaces it with newItem when I wanted it just to add another item to userObjects(Like push function for javascript arrays).
Use dot notation to specify the field to update/add particular fields in an embedded document.
User.findOneAndUpdate(
{ userId: 0 },
{ "userObjects.newerItem": newervalue } }
);
or
User.findOneAndUpdate(
{ userId: 0 },
{ "$set":{"userObjects.newerItem": newervalue } }
);
or Use $mergeObjects aggregation operator to update the existing obj by passing new objects
User.findOneAndUpdate(
{"userId":0},
[{"$set":{
"userObjects":{
"$mergeObjects":[
"$userObjects",
{"newerItem":"newervalue","newestItem":"newestvalue"}
]
}
}}]
)
According to your question, i am guessing userObjects is an array.
You can try $push to insert items into the array.
User.findOneAndUpdate(
{ userId: 0 },
{ $push : {"userObjects": { newItem: value } }},
{safe :true , upsert : true},function(err,model)
{
...
});
For more info, read MongoDB $push reference.
Hope it helps you. If you had provided the schema, i could have helped better.
Just create new collection called UserObjects and do something like this.
UserObject.Insert({ userId: 0, newItem: value }, function(err,newObject){
});
Whenever you want to get these user objects from a user then you can do it using monogoose's query population to populate parent objects with related data in other collections. If not, then your best bet is to just make the userObjects an array.

MongoDB - $set to update or push Array element

In products collection, i have an Array of recentviews which has 2 fields viewedBy & viewedDate.
In a scenario if i already have a record with viewedby, then i need to update it. For e.g if i have array like this :-
"recentviews" : [
{
"viewedby" : "abc",
"vieweddate" : ISODate("2014-05-08T04:12:47.907Z")
}
]
And user is abc, so i need to update the above & if there is no record for abc i have to $push.
I have tried $set as follows :-
db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },
{ $set:
{ "recentviews":
{
viewedby: 'abc',
vieweddate: ISODate("2014-05-09T04:12:47.907Z")
}
}
}
)
The above query erases all my other elements in Array.
Actually doing what it seems like you say you are doing is not a singular operation, but I'll walk through the parts required in order to do this or otherwise cover other possible situations.
What you are looking for is in part the positional $ operator. You need part of your query to also "find" the element of the array you want.
db.products.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
"$set": {
"recentviews.$.vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
)
So the $ stands for the matched position in the array so the update portion knows which item in the array to update. You can access individual fields of the document in the array or just specify the whole document to update at that position.
db.products.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
"$set": {
"recentviews.$": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
)
If the fields do not in fact change and you just want to insert a new array element if the exact same one does not exist, then you can use $addToSet
db.products.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
$addToSet:{
"recentviews": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
)
However if you are just looking for for "pushing" to an array by a singular key value if that does not exist then you need to do some more manual handling, by first seeing if the element in the array exists and then making the $push statement where it does not.
You get some help from the mongoose methods in doing this by tracking the number of documents affected by the update:
Product.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095"),
"recentviews.viewedby": "abc"
},
{
"$set": {
"recentviews.$": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
},
function(err,numAffected) {
if (numAffected == 0) {
// Document not updated so you can push onto the array
Product.update(
{
"_id": ObjectId("536c55bf9c8fb24c21000095")
},
{
"$push": {
"recentviews": {
"viewedby": "abc",
"vieweddate": ISODate("2014-05-09T04:12:47.907Z")
}
}
},
function(err,numAffected) {
}
);
}
}
);
The only word of caution here is that there is a bit of an implementation change in the writeConcern messages from MongoDB 2.6 to earlier versions. Being unsure right now as to how the mongoose API actually implements the return of the numAffected argument in the callback the difference could mean something.
In prior versions, even if the data you sent in the initial update exactly matched an existing element and there was no real change required then the "modified" amount would be returned as 1 even though nothing was actually updated.
From MongoDB 2.6 the write concern response contains two parts. One part shows the modified document and the other shows the match. So while the match would be returned by the query portion matching an existing element, the actual modified document count would return as 0 if in fact there was no change required.
So depending on how the return number is actually implemented in mongoose, it might actually be safer to use the $addToSet operator on that inner update to make sure that if the reason for the zero affected documents was not just that the exact element already existed.

execMatch only returning first result

I am trying to find a particular record (with an id of 1) and then to return only its history field. This history field is an array of objects with a timestamp property. When I return this history field, I want it to contain only objects with timestamp properties greater than 0.
Sample collection
[{
"_id" : 1,
"history" : [
{
"content" : "hello",
"timestamp" : 1394639953878,
"_id" : ObjectId("53208451767743b748ddbd7d")
},
{
"content" : "world",
"timestamp" : 1394639953879,
"_id" : ObjectId("33208451767743b748ddbd7e")
}
]
}]
Thus, according to Mongo and Mongoose documentation, I do something like:
model.find({_id: 1})
.select({ history: { $elemMatch: { timestamp: { $gt: 0 } } } });
The issue: I understand that there will only be one result (since I select the id 1) but I am using $elemMatch to populate its history field with all objects that have a timestamp greater than 0. The issue is that only one object is ever in history.
The $elemMatch projection returns only the first matching element satisfying the condition. Checkout the documentation.
Using aggregate would be the right approach to achieve what you want I think. Please see below:
db.bar.aggregate([
{$match:{_id:1}},
{$unwind:"$history"},
{$match:{"history.timestamp":{$gt:0}}},
{$group:{_id:"$_id", history:{$push:"$history"}}}
])

Categories

Resources