I'm currently have the exact same issue as outlined in this article:
https://medium.com/#danbroadbent/firebase-multi-path-updates-updating-denormalized-data-in-multiple-locations-b433565fd8a5
Currently the Firebase single node .update() function will not overwrite other same level child nodes.
However when using multi-location ref.update() all other child nodes in the same level are overwriten, essentially acting as a .set() function.
Is there someway I can actually run multi-location .update() without overwriting all same level child nodes?
Based on this github issue: https://github.com/EddyVerbruggen/nativescript-plugin-firebase/issues/313
One possible workaround would be defining each field you want to update as separate update, and update it as a part of atomic update, that way you would actually update each individual field needed to be updated?
var companiesPath = 'companies/company_name';
var usersPath = 'users/user_nickname';
var data = {};
data[companiesPath] = 'Best Company Name';
data[usersPath] = 'John';
firebase.update(data);
So you can't update the complete object, you need to target the individual fields of that object. I just tested it and this works...
Related
I have a firebase realtime database trigger on a create node. my need is to update a property based on some condition in the create trigger for the same object. The way i am doing currently is below:
exports.on_order_received_validate_doodle_cash_order = functions.database.ref("/orders/{id}")
.onCreate((change, context) => {
console.log("start of on_order_received_deduct_doodle_cash")
const orderId = context.params.id
const order = change.val();
var db = admin.database();
const orderRef = db.ref('orders/')
return orderRef.child(orderId).update({"_verifiedOrder": true})
})
As you can see i am getting order id from context and then querying object again and updating it. My question is do i need to do this circus or can i just update it without querying again?
Generally it looks good. Just some small feedback to make you feel more confident about being on the right track.
Call the parameter snapshot instead of change because the parameter name change only make sense for the onUpdate event trigger.
You do not need to log that you're entering the function. Because entering and leaving the function is automatically logged by Firebase also.
You can remove the order variable that is unused.
You are actually not "querying" the object again. Making a reference to a node in the database doesn't make any network call itself. Not until you subscribe to receiving data. So doing orderRef.child(orderId) is not a query, it's just a database reference.
You can use the snapshot's own reference attribute to shorten your code a bit... effectively throwing away almost all code :-)
So your code code look like this instead. It is doing the exact same thing, just shorter. It was also correct from the beginning.
exports.on_order_received_validate_doodle_cash_order = functions
.database
.ref("/orders/{id}")
.onCreate((snapshot) => {
return snapshot.ref.child("_verifiedOrder").set(true);
});
But as mentioned in my comment above, you are effectively just setting a flag that is confirming that data was saved (or rather: confirming that the function was triggered). You might want to add some logic in there to check whether the order can be placed or not and then set the verified flag to true or false depending on that. Because with the logic of the implementation, all orders will have the value _verifiedOrder set to true, which is a waste of storage in your database.
I had created the firebase database with some values and to avoid the duplication, I had maintained the other child in the database. This child only gets created if the record gets added if more 1 or more than one record is available. Now my question is how I can create the child even if there is no value in it as I want to write firebase rule for the validation of the data.
Please help.
I'm assuming you're using javascript. If so, you can push an object with empty strings as their values. For example:
function pushEmptyChildren() {
var ref = firebase.database().ref('yourDatabase');
var yourDatabase = {
childOne: "",
childTwo:"",
childThree:""
}
ref.push(yourDatabase);
}
Hope this answers your question.
I have a database with the following structure:
I need to update the order's amount child value and then add a new child updatedAt which will have the timestamp of this update.
I use an object of which contains the paths that I want to update and then call ref.update(dataToBeUpdated).
The problem is that the children that I didn't update as the timestamp is removed when I call update() .. it acts like I rewrite the order node and not editing some of its children.. any help?
UPDATE :
Here is how the code looks like :
var dataToUpdate = { [orderPath] : { 'amount': newAmount, 'updatedAt': firebase.database.ServerValue.TIMESTAMP } } return ref.update(dataToUpdate)
It's over writing your data, most likely because you aren't providing the full-path. For example if you are only providing ref.child("postID").update(update) you're overwriting everything under that "postID". I can't really provide an exact solution without seeing your data and code. If you update your post I can provide additional details, but for now try this: ref.child("amount").update(newAmount) and ref.child("updatedAt").update(newTimestamp)
The first one should overwrite the old amount and the second one should add the child updatedAt with your timestamp.
Another way would be to download all the existing data for that order update it locally and then re-upload the whole thing.
Take a look at the update documentation
I have a node that can potentially have tens of thousands of Children, I need to be able to just fetch the number of children it has without downloading the whole data.
From what I understand using the on('value') function and using DataSnapShot.numChildren(), will cause the whole data of that node to be downloaded first before counted.
Using on('value') would indeed download the entire node. But you can use the shallow feature of their REST API to download only the keys, which you can then count.
curl 'https://samplechat.firebaseio-demo.com/.json?shallow=true&auth=CREDENTIAL'
I do not think that Firebase currently has an operation to do that. You could always keep a property under the parent object that reflects the number of children and update it atomically upon the addition of a new child.
For example (in Javascript):
var ref = new Firebase({base_url}/{parent_id}/children_count);
ref.transaction(function(currentVal){
return (currentVal || 0) + 1;
});
On my meteor project users can post events and they have to choose (via an autocomplete) in which city it will take place. I have a full list of french cities and it will never be updated.
I want to use a collection and publish-subscribes based on the input of the autocomplete because I don't want the client to download the full database (5MB). Is there a way, for performance, to tell meteor that this collection is "static"? Or does it make no difference?
Could anyone suggest a different approach?
When you "want to tell the server that a collection is static", I am aware of two potential optimizations:
Don't observe the database using a live query because the data will never change
Don't store the results of this query in the merge box because it doesn't need to be tracked and compared with other data (saving memory and CPU)
(1) is something you can do rather easily by constructing your own publish cursor. However, if any client is observing the same query, I believe Meteor will (at least in the future) optimize for that so it's still just one live query for any number of clients. As for (2), I am not aware of any straightforward way to do this because it could potentially mess up the data merging over multiple publications and subscriptions.
To avoid using a live query, you can manually add data to the publish function instead of returning a cursor, which causes the .observe() function to be called to hook up data to the subscription. Here's a simple example:
Meteor.publish(function() {
var sub = this;
var args = {}; // what you're find()ing
Foo.find(args).forEach(function(document) {
sub.added("client_collection_name", document._id, document);
});
sub.ready();
});
This will cause the data to be added to client_collection_name on the client side, which could have the same name as the collection referenced by Foo, or something different. Be aware that you can do many other things with publications (also, see the link above.)
UPDATE: To resolve issues from (2), which can be potentially very problematic depending on the size of the collection, it's necessary to bypass Meteor altogether. See https://stackoverflow.com/a/21835534/586086 for one way to do it. Another way is to just return the collection fetch()ed as a method call, although this doesn't have the benefits of compression.
From Meteor doc :
"Any change to the collection that changes the documents in a cursor will trigger a recomputation. To disable this behavior, pass {reactive: false} as an option to find."
I think this simple option is the best answer
You don't need to publish your whole collection.
1.Show autocomplete options only after user has inputted first 3 letters - this will narrow your search significantly.
2.Provide no more than 5-10 cities as options - this will keep your recordset really small - thus no need to push 5mb of data to each user.
Your publication should look like this:
Meteor.publish('pub-name', function(userInput){
var firstLetters = new RegExp('^' + userInput);
return Cities.find({name:firstLetters},{limit:10,sort:{name:1}});
});