Every time the user retrieves the Firebase Realtime Database messages, he would like only the last 50 messages from the messaging node to be retrieved (read) through the Realtime Database rules. How to do this?
Message node structure:
+ chats
+ regionChat (ex: eua)
+ idChat (ex: 534854923)
+ messages
+ idMessage1
+ idMessage2
+ idMessage3
+ idMessage4
+ idMessage5
...
I saw this in the firebase documentation, but I can't adapt my data structure:
messages: {
".read": "query.orderByKey &&
query.limitToFirst <= 50"
}
At the moment my rules are like this:
{
"rules": {
".read": true,
".write": true
}
}
you need to enhance your "query"
this should work
{
"rules": {
".read": "query.limitToFirst <= 50",
".write": true
}
}
Or, instead of using the rules, you can do it via Query.
DatabaseReference myReference = FirebaseDatabase.getInstance().getReference();
myReference = myReference().child("chats")...child("messages");
Query query = myReference.orderByID().limitToFirst(20).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the data
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Related
I have a simple database structure that i want to add firebase security rules to but the rules is blocking all the permission even when i set the value of .read and .write to true under the node structure, Here is the sample of the rule
{
"rules": {
"Lines": {
".read": true,
".write": false
},
"Links": {
".read": true,
".write": false
}
}
}
Here is a Sample of My Database Structure
{
"Lines" : {
"Line 1" : "Lines 1238443",
"Line 2" : "Lines 4657673"
},
"Links" : {
"Links 1" : "Link 3282873",
"Links 2" : "Link 3493934"
}
}
I am trying to allow only read operations but my application keeps saying permission is denied i have check the documentation it looks straight forward but i can't tell what exactly is happening because even when i tried
{
"rules": {
"Lines": {
".read": true,
".write": true
},
"Links": {
".read": true,
".write": true
}
}
}
I still get permission denied error
Here is the code to read from the database
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String lin = snapshot.child("Links").child("Links 1").getValue(String.class);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(getApplicationContext(), "Error "+error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
You're trying to read from the root of the database. And since your rules don't grant anyone read access to the root of the database, the read is rejected.
If you only want to read the Links child from the database, you should specify that child name before attaching a listener, so:
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
databaseReference.child("Links").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String lin = snapshot.child("Links 1").getValue(String.class);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(getApplicationContext(), "Error "+error.getMessage(), Toast.LENGTH_SHORT).show();
}
})
Now we're attaching a listener to /Links, where the security rules do allow reading the data.
I am trying to get all emails of users using firebase Query like below
private void searchUserByEmail(final String searchText) {
DatabaseReference mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference();
Query query = mFirebaseDatabaseReference.child(Constants.FB_TABLE_USERS).orderByChild(Constants.FB_EMAIL).equalTo(searchText);
query.addListenerForSingleValueEvent(new ValueEventListener() {
}}
And While Searching Any email Which is not in list I am getting a warning message in my console Like
W/PersistentConnection: pc_0 - Using an unspecified index. Consider adding '".indexOn": "email"' at table_users to your security and Firebase Database rules for better performance
And My Rules in FireBase is
{
"rules": {
".read" : "auth != null",
".write" : "auth != null",
"table_users": {
".indexOn": ["email"]
}
}
}
This is my Users Table screenshot
you should remove the brackets from ".indexOn" value
"table_users": {
".indexOn": "email"
}
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String email = snapshot.child("email").getValue();
System.out.print(email);
}
}
});
Parse will shut down at the end of the year, so I decided to start using Firebase. I need to implement a register process with 3 fields : email, username, password (Email & username must be unique for my app).
Since, Firebase is not providing an easy way to manage username like Parse, I decided to use only the email/password registration and save some additional data like username. Here is my users data structure :
app : {
users: {
"some-user-uid": {
email: "test#test.com"
username: "myname"
}
}
}
But, what I want to do is to make the username unique and to check it before creating an account.
These are my rules :
{
"rules": {
".read": true,
".write": true,
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {".validate": "!root.child('users').child(newData.child('username').val()).exists()"}
}
}
}
}
Thank you very much for your help
Part of the answer is to store an index of usernames, that you check against in your security rules:
app : {
users: {
"some-user-uid": {
email: "test#test.com"
username: "myname"
}
},
usernames: {
"myname": "some-user-uid"
}
}
So the usernames node maps a username to a uid. It essentially reads as "username 'myname' is owned by 'some-user-uid'".
With this data structure, your security rules can check if there is already an entry for a given username:
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
}
This validates that the username isn't claimed by anyone yet OR it is claimed by the current user.
Save usernames as suggested by Frank but when you save usernames, use runTransaction function in Firebase to make sure that the username is not taken. This function is guaranteed by Firebase to be an atomic operation so you can be rest assured of no collision
firebaseRef.child("usernames").child(username).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.getValue() == null) {
mutableData.setValue(authData.getUid());
return Transaction.success(mutableData);
}
return Transaction.abort();
}
#Override
public void onComplete(FirebaseError firebaseError, boolean commited, DataSnapshot dataSnapshot) {
if (commited) {
// username saved
} else {
// username exists
}
}
});
make a new branch for username and when new user login get list of all username and check wether it is present in db or not if it is present show them toast otherwise put its username in the username branch ..
I dont know much about firebase security yet, but I may have solved the problem using Java. I have posted it below.
my data structure is
myapp
{
users: {
<unique generated-id>
{ username: "example.username" }
}
}
public boolean isUsernameExists(final String enteredUsername) {
final Boolean[] isExist = {false};
FBref.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
String existingUsername = (String) userSnapshot.child("userName").getValue();
if (existingUsername.equals(enteredUsername)) {
isExist[0] = true;
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
//some error thrown here
}
});
return isExist[0];
}
I have this rule in my firebase database
"Item":{
"$userId": {
".write": "auth != null && $userId === auth.uid"
}
}
I have this data
Item
nXQw4a2jlMg34567811glaTD0bE2
-KNJLSxfR_S8AeLj0v7d
height:20
id:-KNJLSxfR_S8AeLj0v7d
When i write the below query, everything is ok.
mDataBase = FirebaseDatabase.getInstance().getReference(FirebaseConstants.ITEM);
query = mDataBase.orderByChild("userId/height").limitToLast(2);
But the below query returns zero
public void getItems(String key){
//key is -KNJLSxfR_S8AeLj0v7d
DatabaseReference reference = FirebaseDatabase.getInstance().getReference(FirebaseConstants.ITEM);
reference.orderByChild("userId/id").equalTo(key).addValueEventListener(new MyEventListener());
}
My listener
private class MyEventListener implements ValueEventListener{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
Log.d("size is "+dataSnapshot.getChildrenCount()) // returns 0
for(DataSnapShot entry: dataSnapshot){
Item item = entry.getValue(Item.class);
Log.d("height is "+ item.getHeight())
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
}
Why is it returning zero and already i have data in my firebase Db?
There are a few small problems with your code:
There is no child userId, that you are trying to order/filter on: orderByChild("userId/height"). So that means the result will be empty.
You cannot loop over a snapshot (I'm surprised that even compiles). Instead you should loop over its children: for(DataSnapShot entry: dataSnapshot.getChildren()) {...
You're using a query, but your code would be more efficient and a lot more scalable if you'd simply look up the item by their key: reference.child(key).addValueEventListener(...
My Firebase storage looks like this -
{
notes: {
note_1: {
$uid: 0 | 1,
title: "",
description: "",
priority: "",
state: "",
time: 0
},
note_2: {
...
}
}
}
where $uid is the user id.
I have configured my rules like this -
{
"rules": {
"notes": {
"$note_id": {
".read": "data.child(auth.uid).exists()",
".write": "(auth != null && !data.exists()) || data.child(auth.uid).val() === 1"
}
}
}
}
Now, I want to query all the notes for a particular user. I tried with the below code -
mDatabase.child("notes").orderByChild(getLoggedInUserId())
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(getClass().getCanonicalName(), Long.toString(dataSnapshot.getChildrenCount()));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
However, this does not work, as I don't have read permission on "notes".
Can anyone advise what should be done to retrieve data like this?
Change orderByChild for child
mDatabase.child("notes").child(getLoggedInUserId()