I've been going through the docs, but I don't see a way to update a value at a specific key (while keeping the rest). Is the only way to achieve this is to fetch the existing data and overwrite it using set()?
I have something like this:
firebase.database()
.ref(`/users/${currentUser.uid}/products/${productKey}`)
.set({category: name});
where products have many other key-value pairs, and using set() overwrites the entire object, which is not what I want in this case.
I appreciate any help!
From the section Update specific fields:
To simultaneously write to specific children of a node without overwriting other child nodes, use the update() method.
In your snippet that would be:
firebase.database()
.ref(`/users/${currentUser.uid}/products/${productKey}`)
.update({category: name});
Related
So I'm working on a personal project to learn react-native and Firestore.
I have a DB like this:
And I want my code to add a new battery in the array batteries.
The elements in the array are just a map{string, string}
The problem is that when I update the array with a new brand that's work but if I want to update it with the same brand again have,
so having by the end
batteries[0]: {'brand': 'cestmoi'}
batteries[1]: {'brand': 'cestmoi'}
The DB doesn't update, doesn't have any error or so.
I don't understand why and I followed their tutorial. Here is my code:
async function addData(collection, doc, value) {
console.log(`Add data ${value.brand}`)
try {
const result = await firestore()
.collection(collection)
.doc(doc)
.set({
batteries: firestore.FieldValue.arrayUnion(value)
})
console.log(result);
return result;
} catch (error) {
return error;
}
}
I use try-catch by habit but I don't know if the then...catch is better or not.
As already #windowsill mentioned in his answer, there is no way you can add duplicate elements in an array using client-side code. If your application requires that, then you have to read the entire array, add the duplicates and then write the document back to Firestore.
However, if you want to update an existing element in an array of objects (maps) then you have to use arrayUnion with the entire object. If you want to understand the mechanism better, you can read the following article which is called:
How to update an array of objects in Firestore?
arrayUnion says that it "adds elements to an array but only elements not already present". Maybe it does a stringify or something to check equality and therefore doesn't add the new element. I think you'll have to 1. get the current list, 2. add your element, 3. set the batteries field to the updated list.
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
});
At the moment I am storing a few objects in Firebase. After successfully retrieving the items from Firebase and storing them in a firebaseArray, I want to further thin out the unwanted elements by deleting the elements in the firebaseArray that do not have the desired property. Consider my code at the moment, that does not do as wanted, however there are no errors in the console:
var querylatestPosts = firebase.database().ref("Topics");
$scope.latestPosts = $firebaseArray(querylatestPosts);
console.log($scope.latestPosts) ;
$scope.latestPosts.forEach(function(el) {
if ($scope.checkWorldview(el) == false) {
delete $scope.latestPosts.el ;
}
});
(Note I am unable to log 'el' in the console, nor does the forEach seem to execute, as I can log nothing in the function in the console)
The 'checkWorldview' function behaves as expected when elements are fed in different instances and returns false if the required property is not present in the element under consideration. Thus if the function returns false, I want to delete the specific element in $scope.latestPosts that does not contain the wanted property.
I hope this is clear, thank you in advance for any help you can offer!
The way you are using the $firebaseArray isn't recommended by the docs (see here), which state that $firebaseArray is read only and should not be manipulated.
So you have a few options:
Instead of filtering the array on the client-side, you should modify the query you're using to retrieve data from Firebase to only get elements that have the desired property (ex: use 'equalTo' in the query)
OR
Don't use a $firebaseArray because you're not using it in the way it was intended. Use a regular, good ol' fashion JavaScript array instead.
** Also, just a general comment: don't delete elements from an array as you loop through it as this is generally bad practice (we don't expect arrays to have elements added/removed while we loop through them). Instead, use Array.filter.
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)
I’m making a collection of React Elements and displaying them; what follows is a trivial example to frame the problem of how-would-one-modify-an-preexisting-instantiated-element only.
var c = [
<div>A</div>,
<div>B</div>,
// ...
<div>Z</div>
];
var ListComponents = React.createClass({
render: function() {
return <div>{c}</div>;
}
});
ReactDOM.render(<ListComponents/>, document.getElementById('root'));
While the code above “works,” it renders a console message I’d rather not ignore:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `ListComponents`.
See https://fb.me/react-warning-keys for more information.
Superficially, I could just add a unique key="…" string to each element in c and be done with it.
However, that seems a quite verbose, especially since I have the data in an indexed array and a functional language that in theory can assign each key its matching index value without manually having to enter it as a source literal.
I’d love to be able to just do this...
c.forEach( (e,i) => e.key = i ); // ...or call some setter
What’s the *right* React-way to do this -and- keep the code clean?
ADDENDUM:
...for the curious or those that want to just say add a key field...
The collection I'm using is actually an array of tuples containing meta-data and a corresponding React Element, a custom Component, or some huge JSX block. The example above overly trivializes what the actual data looks like as well as its irregularities.
As the source data itself is quite long, updated often, and not maintained by a developer; it is highly error prone to missed key fields or duplicates values from manual entry. Hence the desire to do it entirely programmatically. I can not count on the data owners to do it properly. They can't read code, so ideally I'd rather not mess up the data structures with a lot of "programming goop."
The collection is manipulated a few times, putting various runs of certain elements into other dynamically created wrappers, so that the final collection is actually generated by a few transformations, filters, and maps before it is ultimately displayed.
A major shout out to Wes Bos, who came up with a clever solution that works!
The code is a simple one liner and does exactly what I was looking for:
c = c.map( (el,key) => React.cloneElement(el, {key} ));
We're building a new collection using the .cloneElement() method, which I was unaware of. That was what I needed, it turns out.
In the .map() operation, the lambda function is passed both the element and the index. It's return value is a cloned element, but with the key property set.
By cleverly naming the index element key, it allows the short notation for the expression { "key" : key }. This object augments the cloned object.
In the end, I end up with a new collection of identical objects, each with a key property set to the index.