Updating a subfield in a mongoDB document using findOne and save - javascript

I'm trying to update a particular subfield in a mongoDB document and have decided to first find the object in question and then save an updated one. For some reason, the save option seems to ignore my changes.
I have one object in my collection, and it meets the following schema:
var tschema= mongoose.Schema({
a: Object
})
var t = db.model('tongoose',tschema);
t.findOne({},function(err,obj){
console.log(obj.a); //yields ['banana',3]
obj.a[1]=1; //to make ['banana',1]
console.log(obj); //yields ['banana',1]
obj.save(function(err,real){
console.log(real); //yields ['banana',1]
});
});
But when I go back to the mongoDB and look up the saved object, it never shows any changes. Can you spot what I'm doing wrong?
Much appreciated.

Because your schema defines a as a generic object, that field is treated as the Mixed type by Mongoose and you need to mark it as changed by calling markModified or save will ignore the change.
obj.markModified('a');
obj.save(function(err,real){ ...
See the discussion of Mixed types on this page of the docs.

Related

How to change data in a mongoose document with the _id?

I know this may seem like a generic question but I have done research and it didn't seem to work. I'm making a discord bot and I'm new to this kind of thing. I keep getting this error UnhandledPromiseRejectionWarning: MongoError: E11000 duplicate key error collection: Data.users index
This is the code I used:
user.findOneAndUpdate({_id: msg.author, gold: 5});
What is the reason of this problem and how to fix it?
The documentation descibes very clearly what the parameters are:
first parameter: filter
second parameter: update
third parameter: settings object
In our case, you pass a single object, which is interpreted as the filter. You need to pass two objects:
user.findOneAndUpdate({_id: msg.author}, {gold: 5});
This specifies that you want to update gold to 5 for the first record whose _id equals msg.author.
See more examples here: https://mongoosejs.com/docs/tutorials/findoneandupdate.html
Note, that this answer assumes that you have an _id field. If it has a different name, then use that instead.
mongoose .findOneAndUpdate() expects 2 parameters:
the _id of the document you'd update
the new data of the document
So, your code should be this way instead:
user.findOneAndUpdate({ _id: msg.author}, {gold: 5});

How to increment a map value in a Firestore array

I have a firestore firebase database , in which I have a collection users
there is an array in the collection and in the array there is a map
in map there is a field qty.. I want to increment that qty value..
using increment doesnt help as the qty is inside a array index
db.collection("users").doc(checkId).update({
myCart: firebase.firestore.FieldValue.arrayUnion({
qty: firebase.firestore.FieldValue.increment(1),
}),
this is the error Output =>
Uncaught (in promise) FirebaseError: Function FieldValue.arrayUnion() called with invalid data. FieldValue.increment() can only be used with update() and set()
My answer below won't work, given that the qty is in an array. The only way to update an item in an array is to read the entire document, update the item in the array, and then write the entire array with the updated item back to the document.
An alternative would be to use a map instead of an array, and then update the qty using the approach outlined in my (old, and non-working) answer below 👇
You need to specify the full path to the field you're trying to update. So I think in your case, that'll be:
db.collection("users").doc(checkId).update({
"myCart.0.qty": firebase.firestore.FieldValue.increment(1)
}),
The field you want to update is embedded in an array. In this case, you can't use FieldValue.increment(), since it's not possible to call out an array element as a named field value.
What you'll have to do instead is read the entire document, modify the field in memory to contain what you want, and update the field back into the document. Also consider using a transaction for this if you need to update to be atomic.
(If the field wasn't part of an array, you could use FieldValue.increment().)
As of today (29-04-2020)... this is tested by me.
Suppose my data structure is like this:
collection: Users
Any document: say jdfhjksdhfw
It has a map like below
map name: UserPageVisits
map fields: field1,field2,field3 etc
Now we can increment the number field in the map like below:
mapname.field1 etc...
That is use the dot operator to access the fields inside the map just like you would do to an object of javascript.
JAVA Code (Android), update the field using transactions so they can complete atomically.
transaction.update(<documentreference object>,"UserPageVisits.field1",FieldValue.increment(1));
I have just pushed a version of my app which uses this concept and it's working.
Kudos !!
My Best Regards
Previous answers helped me as well, but dont forget about the "merge" property!!! Otherwise it will overwrite your entire array, losing other fields.
var myIndex = 0;
const userRef = db.collection('users').doc(checkId);
return userRef.update({
'myCart.${myIndex}.qty': admin.firestore.FieldValue.increment(1)
}, {
merge: true
});

Firestore data model is inconsistent

I'm trying to save some data in firestore, the data consists of events, each event has a date and each some attendees.
What I'm trying to do now is model it like this events/${eventDate}/${userEmail} and then I would set this with the user's data. However when I try to set this data I get an error saying that the segment number should be even.
When I added another segment in the path (which I didn't want to do):
events/${eventDate}/attendees/${userEmail} I was able to set the data but I wasn't able to retrieve it (trying to retrieve all attendees of a given event date.
// insertion - this worked after some tweaking
this.db.collection('pickups').doc(pickupDate).set({ [email]: userData})
// deletion (this doesn't work - expects even number of segments)
this.db.collection('pickups').doc(`${pickupDate}/${email}`).delete()
// retrieval (works)
this.db.collection('pickups').doc(pickupDate).valueChanges()
Current delete:
this.db.collection('pickups').doc(pickupDate).update({
[email]: firestore.FieldValue.delete()
})
What am I missing here? Isn't this supposed to be like regular JSON?
The path you're currently trying events/${eventDate}/${userEmail} is interpreted as a collection (events) then a document (eventDate) then another document (userEmail).
What you actually have is a collection, document within that collection, field within that document.
It looks like you're adding the email correctly (I would remove the brackets around the word email though), but trying to delete incorrectly. You delete fields like this:
var removeCapital = cityRef.update({
capital: firebase.firestore.FieldValue.delete()
});
You can see the documentation here: https://firebase.google.com/docs/firestore/manage-data/delete-data#fields
The delete may look like this:
this.db.collection('pickups').doc(pickupDate).update({
email: firebase.firestore.FieldValue.delete()
})
It sounds like what you're trying to do is delete a field out of a document. However, this code you have:
this.db.collection('pickups').doc(`${pickupDate}/${email}`).delete()
is trying to build a reference to a collection, then delete it. It's not correct to use collection() and doc() to reference fields in a document. They are just used to build references to documents and collections.
If you want to delete a field in a document, first build a reference to the document that contains the field:
const ref = this.db.collection('pickups').doc(pickupDate)
Then update the document to indicate that you want the field removed:
ref.update({ [email]: firebase.firestore.FieldValue.delete() }}
The way you reference delete() out of FieldValue is going to change based on how you have the SDK imported into your code.
See the documentation on deleting fields for more information.

Append to an arary field in Firestore

I'm using Firebase and Vuejs to create an database element, which has object array inside.
That's how the field looks, and I want to add tasks through the form into the 'moreTasks' as an array.
I tried using this, but it just creates new entity in the database.
db.collection('Tasks').add({
tasker: this.tasker.taskerName
})
I also tried checking API but I couldnt understand the refs, because I was using different methods to achieve that goal.
creatTask() {
db.collection('Tasks').add({
task_id: this.task_id,
name: this.name,
What would be correct way to approach this problem?
You can append an item to an array using FieldValue.arrayUnion() as described in the documentation. For example:
// Atomically add a new region to the "regions" array field.
washingtonRef.update({
regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")
});
The accepted answer used to be correct but is now wrong. Now there is an atomic append operation using the arrayUnion method:
https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
This is true as long as you are using firestore and not real time db (which I assume is the case from the tags)

How can I get the key as well as the value when using db.js to query IndexedDB?

I have an IndexedDB of changes. I add an item like this, and then log the result to check the key has been created successfully:
_this._idb.add('steps', step).done(function (items) {
var item = items[0];
_logger.log("ADDED STEP", { id: item.__id__, step: item }, "CT");
});
The output from this is as expected:
...as you can see, the id has been added to the object when it is stored.
However, when I query the db to getback a list of objects, using this code:
this._idb.steps.query('timestamp').bound(start, end).execute().done(function (results) {
_logger.log("Results", results, "CT");
}
I don't get the id as part of the object that is returned:
... and the lack of id makes updating and deleting impossible.
How can I get the id of the item when I query indexed db using db.js - or am I approaching this in the wrong way, and is there something else I should be doing?
(Note: I'm using TypeScript to compile the JS, but I don't think that's especially relevant to this question)
This is expected behaviour, you're only going to get the __id__ property if you don't define a keyPath in your db schema.
Because there's no keyPath defined the value is not associated with it in indexeddb, it's only added to the resulting object after it has been added, because at that point in time we know the auto-incremented value that IndexedDB has assigned to it.
Since the value isn't really part of the object I don't have any way to assign it to the object when it comes out during a query, maybe I could use the position in the array but that's more likely to be wrong than right.
If you want the ID to be persisted against the object then you need to define a keyPath as part of the object store schema and the property will be added to the resulting object and available and it will be on the object returned from a query.
Disclaimer - I wrote db.js
Looking at the source, __id__ is only defined when your keyPath is null in the add() method. From what I'm seeing, you'll never see this in a query() response.
In IDB null keyPaths are allowed only when using auto-incrementing ("out-of-line") keys. So if you're getting the object back, it should have an auto-incrementing key on it or some other keyPath.
The __ prefix in JavaScript usually means the developer intended it to be a "private" property. I'm guessing this is for internal use and you shouldn't be counting on this in your application code.
Consider using explicit, so-called "in-line" keys on your object store.
The goal of db.js is easy and simple to use. Your is advanced use case.

Categories

Resources