How to implement ondelete cascade in Firebase (Android) - android

I am new with Firebase. I want to implement ondelete cascade in Firebase.
Here is the problem for which I want solution
I have two table "users" and "groups".
{ "users":{
"user1":{
"username":"john",
"full_name":"John Vincent",
"created_at":"9th Feb 2015",
"groups":{
"group1":true,
"group3":true
}
"last_logins":...
},
"user2": ...,
"user3": ...
}"groups": {
"group1"{
"group_name":"Administrators",
"group_description":"Users who can do anything!",
"no_of_users":2,
"members":{
"user1":true,
"user3":true
}
},
"group2"{
"group_name":"Moderators",
"group_description":"Users who can only moderate!",
"no_of_users":1,
"members":{
"user2":true
}
}
}
}
Please pardon me for above code indentation.
Now if I removed user1 from users table then how it should be automatically removed from groups table using Firebase.
This can easily done using SQL but I don't know how to do this in Firebase. One way to do this in Firebase is to remove user1 from users and then makes group1 and group3 to null and then in groups table make user1 to null under group1/member but this need 2-3 calls. So is there any another best way to do this.
Please help me I am stuck here.

The Firebase Database has no knowledge of relations between values in its JSON tree. In SQL/relations terms: it doesn't have the concept of foreign keys. This means that it also doesn't have an option to delete related objects with a cascading delete. You will need to delete each value separately.
But you can combine all those deletes into a single call by using multi-location updates. If you write null to each of the locations for the user, you can delete all of them with one call to updateChildren():
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
Map<String,Object updates = new HashMap<String,Object>();
updates.put("users/user1", null);
updates.put("groups/group1/members/user", null);
// Do a deep-path update
ref.updateChildren(updatedUserData, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Error updating data: " + firebaseError.getMessage());
}
}
})
With this in place, you could then write security rules that validate that members of a group must also exist under the /users node:
{
"rules": {
"groups":
"$groupid": {
"users": {
"$uid": {
".validate": "newData.parent().parent().parent().parent().child('users').hasChild($uid)"
}
}
}
}
}
}
The validation rule is a bit easier to understand if you read the multiple .parent() calls as newRoot (which unfortunately doesn't exist). So in pseudo-code it is:
newRoot.child('users').hasChild($uid)
In words: a UID can only be a member of a group if it also exists under /users.

Related

how to check the current Firebase data under uid?

I am trying to check uid from firebase Realtime database, but its only returns the current uid and when I try to check uid with previous uid then also it only returns the current uid. I had tried many ways and searched but I can't get the exact solution, please if any one could help.
here what I am using to check the uid
FirebaseAuth.getInstance().getCurrentUser().getUid();
here is my code how I am trying to check
String userEnteredUserName = binding.textLoginUserName.getEditText().getText().toString().trim();
String userEnteredPassword = binding.textPassword.getEditText().getText().toString().trim();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users");
Query checkUser = reference.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).orderByChild("userName").equalTo(userEnteredUserName);
Log.d("uids", "uid: " + uid);
checkUser.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
binding.textLoginUserName.setError(null);
binding.textLoginUserName.setErrorEnabled(false);
Log.d("users", "Username: " + snapshot.toString());
if (snapshot.exists()) {
String passwordFromDB = snapshot.child(userEnteredUserName).child("password").getValue(String.class);
if (passwordFromDB.equals(userEnteredPassword)) {
//next activity
}else{
Toast.makeText(Login.this, "Error", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(Login.this, "Error", Toast.LENGTH_SHORT).show();
}
});
In log what I get,
2022-02-06 13:13:06.173 18093-18093/abc C/uids: uid: OFtpR6bfISP3Odd9K1oGWCQmeEf2
Here is my firebase data, "aisha12" is working which is under the current uid but when I try to check "vgbb" it returns only the current uid
If I understand correctly, you are trying to allow a user to register a username and ensuring that the username is unique. Your current data structure doesn't allow you to do that query though.
Firebase Realtime Database can only order/filter on a value that is at a fixed path under each child node of the location you query. So in your current code:
reference.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).orderByChild("userName").equalTo(userEnteredUserName);
You are querying the direct child nodes of /Users/$uid looking for the username under there. Since you're specifying the UID in that path, you're only searching under that specific user.
There is no way with your current data structure to search across all /Users, since the property you are looking for is under /Users/$uid/$username/userName, so with two dynamic keys in there, and the database can only handle one dynamic key.
To allow the query, you will need to change the data structure and remove the $userName level from it, so that you get:
Users: {
"3l6Rm....": {
"userName": "vgbb",
...
},
"OftpR...": {
"userName": "aisha12",
...
}
}
Now you can search for the username with:
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users");
Query checkUser = reference.orderByChild("userName").equalTo(userEnteredUserName);
I also recommend checking out these previous questions on allowing a user to register a unique username:
How do you prevent duplicate user properties in Firebase?
Firebase security rules to check unique value of a child #AskFirebase
How to check if usernames are unique in Firebase
unique property in Firebase
How to give unique usernames from a list of usernames to the users in the firebase
Check value already exists or not in Firebase?

Filter Firebase Realtime Database data based on authentication

I'm using Firebase Realtime Database as the datastore for an Android app, and right now I'm trying to implement some basic filtering based on user permissions.
Here's my database:
{
"admins" : {
`user1hash` : true
},
"clients" : {
"client1hash" : {
"owner": "user1hash",
"name" : "Client 1"
},
"client2hash" : {
"owner": "user1hash",
"name" : "Client 2"
},
"client3hash" : {
"owner": "user2hash",
"name" : "Client 3"
}
}
}
I followed the examples in the Query-based Rules section here https://firebase.google.com/docs/database/security/securing-data and defined my rules like this:
{
"rules": {
"clients": {
".indexOn": "owner",
".read": "auth.uid != null && (root.child('admins/' + auth.uid).val() == true || query.orderByChild == 'owner' && query.equalTo == auth.uid)",
".write": "auth.uid != null",
".validate": "newData.hasChildren(['owner']) && newData.child('owner').val() == auth.uid"
}
}
}
And this is my client code (Android):
FirebaseDatabase database = FirebaseDatabase.getInstance();
String authUser = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference clientsDatabase = database.getReference("clients").orderByChild("owner").equalTo(authUser).getRef();
clientsDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// handle success
}
#Override
public void onCancelled(DatabaseError databaseError) {
// handle error
}
});
So basically I just want to be able to fetch all clients, but filter the results according to the access rules of the current user. Access rules are pretty simple, an user can only see the clients where he is the owner, except for admin users who can see all clients. For example if this was run by user1 which is an admin he should see all three clients, but if this is run by user2 which is a regular user he should only see client 3.
This implementation is working for admins, but I get a permissions error for regular users.
I tried the rules simulator in Firebase console, but it doesn't provide any documentation on how to use queries. Anyway I tried adding the query as a regular URL query string like /clients?orderByChild=owner&equalTo=user2hash, but this returns an empty error on the simulator with no description of the cause. The error I'm getting on the Android side doesn't have any description either, just a generic permissions error.
The problem is in this statement:
DatabaseReference clientsDatabase =
database.getReference("clients")
.orderByChild("owner")
.equalTo(authUser)
.getRef();
Specifically that last line getRef(), which throws everything away that you've done to build the query. The above statement leaves clientsDatabase exactly the same as:
DatabaseReference clientsDatabase = database.getReference("clients");
Which explains quite well why the statement fails.
You need to keep the Query that you get back and attach your listener on that:
DatabaseReference clientsDatabase = database.getReference("clients");
Query query = clientsDatabase.orderByChild("owner").equalTo(authUser);
query.addListenerForSingleValueEvent(new ValueEventListener() { ...

Rule entries duplicates firebase not working

I have an application in android that registers sellers, which have a unique email, I am storing them in firebase. Create a rule to not allow duplicates to be added but it does not seem to work. What am I doing wrong?
{
"rules": {
".read": true,
".write": true,
"sellers": {
"$seller": {
"email": {
".write": "!data.exists()"
}
}
}
}
}
my method to add
public void addSeller(Seller seller){
HashMap<String,Seller> map= new HashMap<>() ;
String email = seller.getEmail().replace(".",",");
map.put(email,seler);
database.child("sellers").setValue(map);
}
You're calling push(), which generates a new child that is statistically guaranteed to be unique.
If you want to ensure unique email addresses, you will have to keep a collection where the (encoded) email addresses are the keys:
emails
pete#somedomain,com
puf#somedomain,com
With this structure, the following rule will work to ensure an email address can only be written once:
{
"rules": {
".read": true,
"emails": {
"$email": {
".write": "!data.exists()"
}
}
}
}
The topic of unique values comes up regularly, so I recommend you also check out these:
Firebase android : make username unique
How do you prevent duplicate user properties in Firebase?
Enforcing unique usernames with Firebase simplelogin
unique property in Firebase
Firebase Unique Value
What Firebase rule will prevent duplicates in a collection based on other fields?

How to add array value in Firebase

I want to add some certain data to a Firebase as arrays. Example:
groups : ['a','b','c']
How can I add and read data in Firebase from Android?
When you have a structure like that, you actually shouldn't be using an array to model it. It seems much more like a set in my eyes.
In the Firebase Database sets are best modeled as keys, since that automatically guarantees that items are unique. So your structure then becomes:
groups: {
"a": true,
"b": true,
"c": true
}
The true values are just markers, since Firebase won't allow you to store keys without a value.
Now to add a group to this, you'd use Firebase's setValue() function:
DatabaseReference root = FirebaseDatabase.getInstance().reference();
DatabaseReference groupsRef = root.child("groups");
groupsRef.child("d").setValue(true);
From the documentation:
setValue() - Record or change exists values
If you want to only append datas, you can to use updateChildren().
In Java, if we know that the data is array-like, it can be cast as a List:
Firebase julieRef = new Firebase("https://SampleChat.firebaseIO-demo.com/users/julie/");
julieRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
GenericTypeIndicator<List<String>> t = new GenericTypeIndicator?<List<String>>() {};
List messages = snapshot.getValue(t);
if( messages === null ) {
System.out.println('No messages');
}
else {
System.out.println("The first message is: " + messages.get(0) );
}
}
// onCancelled...
});
Check this best practices post from the Firebase Blog.

Does firebase realtime data create a new instance for each user or does everything just get stored in a large tree

I am new to firebase and Nosql databases. I have gone through the documentations already, but I cannot seem to get my head around a concept.
I have gone through almost every question on here about it, but everyone just seem to conveniently skip the little detail i am looking for.
Suppose I have successfully registered my users using firebaseauth, and can log them in and out, I have my database rules as follows
{
"rules": {
"users":{
"$userid":{
".read": "auth !== null && auth.uid == $userid",
".write": "auth !== null && auth.uid == $userid"
}
}
}
}
Great!, now thats the basic database for a multiuser application. My question is that the users data doesnt get pushed to database from auth automatically, so i have to do a
mRef = FirebaseDatabase.getInstance().getReference();
mUserRef = mRef.child("users");
mSendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String uid = mAuth.getCurrentUser().getUid();
String name = "User " + uid.substring(0, 6);
Userid userid = new Userid(uid);
mUserRef.push().setValue(userid, newDatabaseReference.CompletionListener() {
and so on, on the registeractivity so any userid can have its own node under users where i can post any user specific data.
I have not implemented this yet, but i forsee that for any read or write data performed by a user will have to search every node to find its own userid, which would take a lot of time when you scale up to like a lot of users and im sure firebase is better than that. So is this how firebase expect us to handle user stuff or does every user just have his own database instance
The push() method creates a new, random ID. This is useful for things like messages in a chat application, but is likely not what you are looking for.
I think you mean to do this:
// Get current UID
String uid = mAuth.getCurrentUser().getUid();
// Get reference to /users/<uid>
DatabaseReference ref = mUserRef.child(uid);
// Set the value of /users/<uid> to the UserId
Userid userid = new Userid(uid);
ref.setValue(userid);

Categories

Resources