Firebase security rules fails during multi path update - android

In firebase I set security rules in order to secure some nodes.
Problem
When I use multi path update to update multiple paths at the same time, If one security rule fails for any of the multiple paths, then the whole update fails.
example of my problem
Lets say I have 3 nodes (users, people , tasks) in my real time database, in android the way to update the 3 paths together is by doing something like this:
Map multi-update =new HashMap();
multi-update.put("users/user1/name","any_name");
multi-update.put("people/user2/status", "any_status");
multi-update.put("tasks/task1/details", "any_details");
DatabaseReference root=FirebaseDatabase.getInstance().getReference();
//update
root.updateChildren(multi-update);
lets say my rules are like this:
"users":{
".write":"true"
}
,"people":{
".write":"true"
}
,"tasks":{
".write":"false"
}
since task doesn't allow writes to it, then the multi path update is never updating until all paths allow the write.
can someone explain why that happens?
Thanks.

According to the firebase docs:
Simultaneous updates made this way are atomic: either all updates
succeed or all updates fail.
So the problem you are describing isn't actually a problem but the intended behaviour of root.updateChildren(multi-update);
If this behaviour is a problem in your case you could change your firebase rules to give permission to all parts of your multi-update or split up your multi-update in parts to make sure that the parts that can succeed will succeed.
Another option in your case would be to check for errors like this:
root.updateChildren(multi-update, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError firebaseError, DatabaseReference firebase) {
if (firebaseError != null)
{
//Update the minimum required fields
}
}
});
The downside here is you don't know what part of your update failed.

Related

Retrieving data from firebase takes too much time

I think this is a crazy question but I'm new to firebase...Here is my problem...
I want to retrieve firebase data to textview, imageview and listview! this is my example database structure..it has many other nodes like this 😁
https://i.stack.imgur.com/JURr3.png
So, I add username to a textview, profile photo to imageview, birthdate to a textview and there are many other nodes like messages and other usernames(in an activity).It's ok but the problem is "it takes to much time to retrieve data to each view and then it shows all the data at the same time"..I mean it don't show each data simultaneously after loading but show all requested data to all views after loading. What I want is a view after a view loading and showing...😢😢
I don't know how to explain my problem and how to call it as I have bad Eng skill...😭😭
Here is UI of that activity after fetching data.(Birthdate and some data haven't added yet). https://i.stack.imgur.com/GRX0O.png This is the photo while retrieving with default image and text. https://i.stack.imgur.com/iEhv9.png
Step 1 :
Use Sqlite as a local database to store your data as well along wid maybe if u r using Firebase Realtime Database. Everytime a data is loaded for the first time , it will also be stored in the local database. So the next time when u open the app, u would see the results that u r expecting !
Step 2:
If u r using Firebase Realtime Database as a backend, make use of the Offline Data Handling feature which firebase provides. This too can solve your problem !
Not sure how you are feting data, ideally this should be the set of steps that you should be doing.
1) Fetch all the data(all children data as well) from that node and put it in a list.
2) Pass the data to a RecyclerView and populate it this was there wont be any lag.
This is an example code to fetch all the child nodes
myQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
// TODO: Add to list to send it to recyclerview
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
Hopefully, this will help you.
My personal advice is not to reach firebase data directly from client because of possible authentication and performance problems due to unintented implementation and usage of firestore api.
Instead, use Firebase cloud functions and write a service for what you need. To call those services from the app, you can use Firebase calls. You may suffer from cold boot but you can solve it with a warm up process ex. call those functions every minute with a cron job that can be set via cron-job.org
You can enable offline data in Firebase so every time you don't need to reload from the server first. First, you populate the data from the cache source and then silently load the data from the server source.
Enable Offline Data in Firebase

Firebase on android how to return only the node without children

This might be an odd question. However, I am running out of the free quota of my firebase database and before start paying for it, I want to optimize some of my firebase queries.
I was observing the firebase console where we can manage the database and I noticed that it only shows us the nodes on each level. To see the children we must click on the + button (next to the node name) or at the node itself.
I believe this behaviour is designed to avoid requesting the whole database at once what would lead to a huge traffic and resource consumption. I also believe that firebase console was built on firebase public API, so it should be possible for us to implement the same behaviour.
My question is considering this following database:
{
"root":{
"node1":{
"childA":"a",
"childB":"b",
"childC":"c"
},
"node2":{
"childA":"2a",
"childB":"2b",
"childC":"2c"
},
"node3":{
"childA":"3a",
"childB":"3b",
"childC":"3c"
},
"node4":{
"childA":"4a",
"childB":"4b",
"childC":"4c"
},
"lululu":{
"childA":"1a",
"childB":"2b",
"childC":"3c"
},
"node1214":{
"childA":"1a",
"childB":"1b",
"childC":"1c"
},
"node10":{
"childA":"a",
"childB":"b",
"childC":"c"
}
}
}
How can I get the list of nodes under root without their content (as a list for example like ["node1", "node2", "node3"...] or as a proper Map but without the child data (to reduce bandwidth usage)?
This is not possible with any of the mobile SDKs. It's only possible with the REST API using the shallow parameter.
In my opinion, the data structure should be as flat as possible to avoid nested data. You might consider restructuring your data as follows. You might consider taking a look at the best practices of storing and organizing your data in Firebase.
{
"root": {
"node1":"1",
"node2":"2",
"node3":"3"
},
"nodes" : {
"1": {
"childA":"a",
"childB":"b",
"childC":"c"
},
"2":{
"childA":"2a",
"childB":"2b",
"childC":"2c"
},
"3":{
"childA":"3a",
"childB":"3b",
"childC":"3c"
}
}
}
When you get the item, you get the children under the item as well. So avoid nesting your data. You might consider checking the answer here as well.

Query.equalTo(value) or Query.startAt(value).endAt(value) causes delay in sync

My queries with .equalTo() return out-of-date data when used with addListenerForSingleValueEvent, while removing .equalTo() causes the listener to return updated data. Any idea why?
.
I'm using the following query to fetch user's posts from Realtime Database with persistance enabled on Android:
mDatabase.child("posts").orderByChild("uid").equalTo(id)
where id is the id of the current user and each post stores its author's id as a field.
When .equalTo(id) is present, the new posts for the particular user are not returned in that query for the first few minutes. Even more, it seems to affect other queries for the same root ("posts") that contain .orderByChild. Eg, following would also fail to recognise the new post:
mDatabase.child("posts").orderByChild("archived")
Once I remove the .equalTo(id) the behaviour goes back to normal. I'm using addListenerForSingleValueEvent. Tried it also withaddValueEventListener which fires two events, one without the new post, one with it. Without .equalTo(id) both single and non-single listeners return the new post in the first callback. Restarting the app doesn't seem to help straight away - the first event stays out-of-date for the next few minutes. The new post is successfully fetched by different queries in other parts of the application (eg mDatabase.child("posts").child(id))
Any idea why .equalTo() causes such behaviour and how to avoid it (other than using non-single listener and ignoring first event)?
Note 1: same thing happens for .startAt(id).endAt(id)
Note 2: other parts of the Realtime Database are functioning normally, device is connected to the internet and new posts are containing the valid uid field matching the current user.
Update 26/10/2016
Calling mDatabase.child("posts").startAt(key).limitToFirst(4) also produces similar behaviour when trying to query a segment of the database (in our case to implement infinite scroll). It seems that explicitly adding .orderByKey() fixes that particular problem: mDatabase.child("posts").orderByKey().startAt(key).limitToFirst(4).
Though the issue outlined in the original question remains.
I've ran into the exact same problem as you, and after experimenting with almost everything, I've managed to solve it on my end.
I'm scanning barcodes and fetching foods that have the scanned barcode:
Query query = refFoods.orderByChild("barcode").equalTo(barcode);
query.addListenerForSingleValueEvent(new Value ... })};
On my rules i had
".indexOn": "['barcode']"
and after i changed it and took the "[]" out as in:
".indexOn": "barcode"
it started working delay free, where before it would take something like 5 minutes to udpate.

better explain couchbase lite map function rules and common mistakes

I am new to couchbase and I am trying to implement couchbase lite in one of my Android applications. What i am struggling with in particular is the concept of views and the rules for the map function as stated in the docs.
In the database the app stores documents with various doc types. In one query i need to get the entire document by document type ("payments")
and by value of an attribute of the document (doc["approved"] = true)
Hence I would create a view like so:
com.couchbase.lite.View view = database.getView("payments");
if (view.getMap() == null) {
Mapper map = new Mapper() {
#Override
public void map(Map<String, Object> doc, Emitter emitter) {
if (doc.get("type").equals("payments") && doc.get("approved") == true) {
emitter.emit(doc.get("name"), doc);
}
}
};
view.setMap(map, "1");
}
Note that the doc["approved"] value can be updated over time. In one of the rules about map functions in the docs it says:
It must be a "pure" function: ... That means any time it's called with the
same input, it must produce exactly the same output.
Would the implementation of the map function as show above violate that rule?
In the docs it further says :
In particular, avoid these common mistakes: ... Don't make any assumptions
about when the map function is called. That's an implementation detail
of the indexer. (For example, it's not called every time a document
changes.).
Does that mean when the approved status of one of the documents is updated from false to true that the following query not nessesarily contains the updated document? If so what would I need to do to achieve this? I am quite uncertain about what rule that exacly means? Could anyone try to open my eyes please?
What "pure" means is that you cannot use outside state in your map function. All your determinations must be based solely on the parameters that were passed into it. Your map function does not violate this.
I think the missing piece in your understanding is the difference between storage and indexing. You can store revisions of a document to the database, right? That in and of itself will not cause the view's index to be updated. That's what the documentation means by "not called every time a document changes." The index will be updated by default when the next query is run, so the newest state of the document will be output. It could realistically have been changed many times since the last query was run.

Firebase transaction doesn't abort remotely if previously committed locally

I'm building an Android app in which several users can access, modify and delete the same item and I'm using Firebase to sync all the devices.
In order to keep track of the updates the item has a timestamp.
I wrote a transaction so that when I try to delete the item it checks if the timestamp of my copy is older than the remote copy: in that case the transaction aborts and the item is not deleted.
Here my problem:
My device goes offline
I successfully delete the item
Another user modifies the item on the remote database
My device goes online and propagates his deletion
I thought it would have aborted remotely as the remote timestamp is newer.
I really can't see the point of the abort function if I can only abort basing the decision on my local data...
How should I handle these kinds of conflicts in Firebase?
-- UPDATE
This is the code I use to remove an item. It should abort if another user has changed the item remotely after the deletion has happened locally.
private void removeItem(final ListItem item, final Firebase itemRef) {
itemRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
if(mutableData == null) return Transaction.abort();
else if((long)mutableData.child("lastUpdate").getValue() > item.getLastUpdate()) return Transaction.abort();
else{
itemRef.removeValue();
return Transaction.success(mutableData);
}
}
});
Please note I use itemRef.removeValue() instead of mutableData.setValue(null) because the second one doesn't seem to work.
Firebase initially applies transactions client-side, to improve concurrency. If that decision does not meet your use-case, you can pass an additional argument to the transaction method that tells it to by-pass the local apply.
See https://www.firebase.com/docs/web/api/firebase/transaction.html
applyLocally Boolean Optional
By default, events are raised each time the transaction update function runs. So if it is run multiple times, you may see intermediate states. You can set this to false to suppress these intermediate states and instead wait until the transaction has completed before events are raised.
I solved my problem in this way:
I used mutableData.setValue(null), otherwise Firebase can't make the transaction work properly
I set the applyLocallyboolean explicitly to true as I need to see local events too
I don't understand why mutableData.setValue(null) wasn't working before, I may be missing some previous mistake, but that was the problem.

Categories

Resources