I am trying to count the number of children in my DB table that meet a certain condition. If the isSeen column equals false then I want to count that, if it equals true I don't want to count it.
Currently, it's not working but if I change the query from Query query = usersRef.orderByChild("isSeen").equalTo(true); to Query query = usersRef.orderByChild("isSeen"); I get a number but it's not the correct way. Can someone please help me?
Rules:
{
"rules": {
".read": true,
".write": true,
"Messages": {
".indexOn": "isSeen"
}
}
}
"Messages": {
"b3vYlKZFrje0e3wHyBlWIK4ooK93": {
"DIt5bGqw2WS4eGHNqQJKxZSn3B72": {
"-N8NCgnwX6V7ghfGlcWS": {
"dateAdded": 1659337356887,
"date_time": "Aug-01-2022 3:02:36 AM",
"from": "DIt5bGqw2WS4eGHNqQJKxZSn3B72",
"isSeen": true,
"message": "Yoo",
"to": "b3vYlKZFrje0e3wHyBlWIK4ooK93",
"type": "text"
},
"-N99iQjlMfeyOM_VCAEB": {
"dateAdded": 1660184797462,
"date_time": "Aug-10-2022 10:26:37 PM",
"from": "DIt5bGqw2WS4eGHNqQJKxZSn3B72",
"isSeen": true,
"message": "Wassup",
"to": "b3vYlKZFrje0e3wHyBlWIK4ooK93",
"type": "text"
}
}
}
},
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("Messages");
Query query = usersRef.child(firebaseUser.getUid()).orderByChild("isSeen").equalTo(true);
query.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
long count = task.getResult().getChildrenCount();
Log.d("TAG1", "count: " + count);
} else {
Log.d("TAG2", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
Error message:
D/TAG2: Index not defined, add ".indexOn": "isSeen", for path "/Messages/b3vYlKZFrje0e3wHyBlWIK4ooK93", to the rules
Database schema
When you're using the following query:
Query query = usersRef.orderByChild("isSeen").equalTo(true);
Firebase will always return the exact data you are querying, meaning that you'll get all elements that have the isSeen field set to true. Please note that there is no way you can query by a negation. So something like this is not possible:
Query query = usersRef.orderByChild("isSeen").notEqualTo(true);
// 👆
According to your comment in which you say that you don't have any elements where the isSeen field is set to true, then your query will yield no results, and that's the expected behavior.
While #TimothyPham's answer will work, using getChildrenCount() might be the best solution. Why? Because if you have a lot of messages this operation requires you to read all of them in order to provide a number. The best solution I can think of would be to increment/decrement a counter as explained in my answer from the following post:
How to save users score in firebase and retrieve it in real-time in Android studio
Edit:
Query query = usersRef.orderByChild("isSeen").equalTo(true);
query.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
long count = task.getResult().getChildrenCount();
Log.d("TAG", "count: " + count);
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
But this code will only work if you have elements in the database that have he isSeen field is set to true.
I think you should try this, it working for me. It read your messages for the first time
usersRef.orderByChild("isSeen").equalTo(true).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
Log.d("TAG", "Count:" + String.valueOf(dataSnapshot.getChildrenCount()));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Try this:
Log.e(dataSnapshot.getKey(),dataSnapshot.getChildrenCount() + "");
Related
I have an app where I am using a custom authentication method. First, on the user login into the app, I generate the JWT Token on the server and send it back to the app.
function generateJWT($con,$userID,$cretedTime) {
$secret_Key = "-----BEGIN PRIVATE KEY-----\HCPW\nAtY9K1/19yScEhdmhw8Ozek=\n-----END PRIVATE KEY-----\n";
$date = time();
//$expire_at = $date->modify('+3 minutes')->getTimestamp(); // Add 60 seconds
$domainName = "firebase-adminsdk-XXXXXXXX.iam.gserviceaccount.com";
$request_data = [
'iss' => $domainName,
'sub' => $domainName,
'aud' => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
'iat' => $date, // Issued at: time when the token was generated
// Issuer
//'exp' => $date+(60*60), // Maximum expiration time six month in seconds //15778476
'uid' => $userID, // User name
'created' => $cretedTime, // User name
];
$newToken = JWT::encode($request_data,$secret_Key,'RS256');
return $newToken;
}
Then In the app send on receiving this token I am start the login process.my app using custom firebase auth
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isComplete()){
if(getActivity()!=null){
//User Logged In Successfully
}else{
// Failed
}
}
}
});
So after days of googling, I got the Firebase rules for the structure of my database to look like this
{
"Chat": {
"206-4": "",
"311-158": "",
"215-112": "",
"734-115": "",
"734-55": "",
"734-468": "",
"34-32": "",
"534-179": "",
"734-345": {
"-NI7hqW3YTFKpnSZU422": {
"Message": "Test",
"Message_From": "Support ",
"Message_From_ID": "4",
"Message_To": "Demo",
},
"-NMVOwlAqmyIA52QU9F-": {
"Message": "Hi",
"Message_From": "Support ",
"Message_From_ID": "4",
"Message_To": "Demo",
}
},
"347-234": {
"-NI7hXybU02Mg6vYqdKp": {
"Message": "Ohio",
"Message_From": "Elaxer Support ",
"Message_From_ID": "4",
"Message_To": "Demo 2",
}
},
"281-69": "",
"317-34": ""
},
"Users": {
"4": {
"Online": false,
"lastSeen": "1675785660782"
},
"284": {
"Online": false,
"lastSeen": "1673611185873"
}
},
"UsersLocations": {
"4-210": {
"-1": {
"Latitude": "22.605",
"Longitude": "88.375"
}
},
"25-21": {
"-1": {
"Latitude": "22.605",
"Longitude": "88.375"
}
}
}
}
Firebase Rules
{
"rules": {
"Chat": {
"$room_id": {
".read": "auth.uid === $room_id && $room_id.beginsWith(auth.uid + '-') || auth.uid === $room_id && $room_id.endsWith('-' + auth.uid)",
".write": "auth.uid === $room_id && $room_id.beginsWith(auth.uid + '-') || auth.uid === $room_id && $room_id.endsWith('-' + auth.uid)"
}
},
"Users": {
"$uid": {
".write": "$uid === auth.uid"
}
},
"UsersLocations": {
"$user_location_id": {
".read": "auth.uid === $user_location_id && $user_location_id.endsWith('-location')",
".write": "auth.uid === $user_location_id && $user_location_id.endsWith('-location')"
}
}
}
}
So when ever i tried to create or get the Chat node (Chatroom).
DatabaseReference db = FirebaseDatabase.getInstance().getReference().child("Chat");
db.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
It gives me error
Listen at /Chat failed: DatabaseError: Permission denied
I am not able to understand why i am getting this error when i am only checking the user id exist in room name and my jwt token on generation time having user id of one user. Please help me out, what's wrong i am doing with my rules
As Alex also answered: you're not granting anyone read access to /Chat, so any code trying to read from there will be rejected. I would not recommend his answer though, as the rule on /Chat makes the more strict rules on /Chat/$room_id meaningless.
I recommend reading the documentation on rules don't filter data (which explains why your current code don't work and on the fact that permissions cascade (which explains why the rules in Alex' answer make the ones you already have meaningless).
The data structure you have look like what I described in my answer to: Best way to manage Chat channels in Firebase. In my answer there I also showed how to model security rules to allow read access and how to get a list of the chat room for the user, so I recommend checking that out.
As I tried explaining in the comments, the way you handle the sign-in result is wrong:
// ❌ THIS IS WRONG ❌
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isComplete()){
if(getActivity()!=null){
//User Logged In Successfully
}else{
// Failed
}
}
}
});
Instead, when a Task completes you should check whether it succeeded or failed, as shown in the documentation on handling tas results. In your case that'd be:
// ✅ Handling success and failure correctly 👍
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()!=null) {
// User signed in
// TODO: access database
} else {
// Sign in failed
throw task.getException();
}
}
});
I am writing data to the database using another class with a constructor, but I still need to read data from the database. In the documentation, when using other classes, you need to create HashMap lists for each element, but I have 2 classes (since I need to write more than 255 entries to the database) and in each class, I will have to write a HashMap. How can I load the name of a DB variable that is identical to the name in the file itself? For example int b = 0; and in the database - b: 0 and how can you get the value of each variable from the database?
I send data like this:
if (user != null) {
if(database.child(getEmail)==null) {
User newUser = new User(getEmail, coins, ....);
User1 newUser1 = new User1(a256, a257, ....);
database.child(getEmail).push().setValue(newUser1);
database.child(getEmail).push().setValue(newUser);
}
I read data like this:
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for(DataSnapshot ds : snapshot.getChildren()){
User user = ds.getValue(User.class);
//saving information witch will read from db in SharedPreferences
PreferenceConfig.GetEmail(getApplicationContext(), getEmail);
PreferenceConfig.GetCoin(getApplicationContext(), getEmail);
PreferenceConfig.GetA256(getApplicationContext(), a256);
...
}
}
database.addValueEventListener(valueEventListener);
But i can`t understend how can i get data from db without hashmap
JSON file:
{
"User": {
"mail#gmail:com": {
"-NHTVinbEVUAqJwK8Umt": {
"getEmail": "mail#gmail.com",
"coins": 100,
.....
},
"-NHTVinpCPOJ4UPZvgpN": {
"a256":0,
"a257":0
...............
}
}
}
}
You won't be able to read the data under fields that are dynamically created:
{
"User": {
"mail#gmail:com": {
"-NHTVinbEVUAqJwK8Umt": {
"getEmail": "mail#gmail.com",
"coins": 100,
},
"-NHTVinpCPOJ4UPZvgpN": {
"a256":0, //👈
"a257":0 //👈
}
}
}
}
You'll be able to read all the data if your second child will have the same fields as the first one:
{
"User": {
"mail#gmail:com": {
"-NHTVinbEVUAqJwK8Umt": {
"getEmail": "mail#gmail.com",
"coins": 100,
},
"-NHTVinpCPOJ4UPZvgpN": {
"getEmail": "other#gmail.com", //👈
"coins": 200, //👈
}
}
}
}
Now, to read the data from such a structure, you have to create a reference that points to mail#gmail:com node and make a get() call as you can see in the following lines of code:
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
DatabaseReference emailRef = db.child("User").child("mail#gmail:com");
emailRef.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
for (DataSnapshot ds : task.getResult().getChildren()) {
String email = ds.child("getEmail").getValue(String.class);
Log.d("TAG", email);
}
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
The result in the logcat will be:
mail#gmail.com
other#gmail.com
Or you can map each DataSnapshot object into an object of type User:
User user = ds.getValue(User.class);
This operation will work, only if your User class contains two fields called getEmail and coins. The first being a string and the second one a number.
I am having two tables Customers and Transaction nodes. Transaction node is related to data of Customer node by Vehicle Number a data in Customer node. I want to subtract the amount paid from the Customer node Due Amount
Customer Table:-
"Customers": {
"-NFFTcWFGLXi063jCTcT": {
"branch": "Rajeev Nagar",
"currentDownPayment": "80000",
"currentdueAmount": "20000.0",
"customerAadharNumber": "123456",
"customerAddress": "ANYWHERE",
"customerName": "VIKASH",
"customerPhoneNumber": "7481900892",
"customerTenure": "12",
"dateOfdelevry": "28/10/2022",
"downPayment": "100000",
"duePayment": "100000.0",
"emiPerMonth": "8333",
"emiStartDate": "26/10/2022",
"granterName": "RATHI",
"granterPhoneNumber": "7481900896",
"localitymodal": "NEAR ME",
"motorNumber": "QWERT",
"outStandingAmount": "120000.0",
"vehicleAmount": "200000",
"vehicleBodyType": "Steel",
"vehicleColour": "Gray",
"vehicleNumber": "1234567890QWERTYU"
},
Transection table:-
Transaction": {
"1234567890QWERTYU": {
"-NFFZLr7-9sTrZ1OlbjF": {
"dateOfPayment": "27/10/2022",
"discountGiven": "00",
"discountRemarks": "8000",
"lateFine": "no discount ",
"paymentReceived": "500"
}
The key of the Transaction Table is the same as the Customer Table Vehicle Number see the last data of the Customer Table
databasePaymentUpdate = FirebaseDatabase.getInstance().getReference("Customers");
databasePaymentUpdate.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.exists()) {
for (DataSnapshot ds : snapshot.getChildren()) {
String VehicleNumber = ds.child("vehicleNumber").getValue(String.class);
String vehNo = PaymentUpdateVehicleNumber.getText().toString().trim();
if (VehicleNumber.equals(vehNo)) {
Toast.makeText(PaymentUpdate.this, "Welcome to Jyoti Motors", Toast.LENGTH_SHORT).show();
}
To solve this problem, you have to use nested listener, as you can see in the following lines of code:
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
DatabaseReference customersRef = db.child("Customers");
customersRef.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> customersTask) {
if (customersTask.isSuccessful()) {
for (DataSnapshot customerSnapshot : customersTask.getResult().getChildren()) {
String vehicleNumber = customerSnapshot.child("vehicleNumber").getValue(String.class);
DatabaseReference vehicleNumberRef = db.child("Transaction").child(vehicleNumber);
vehicleNumberRef.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> vehicleNumberTask) {
if (vehicleNumberTask.isSuccessful()) {
long sum = 0;
for (DataSnapshot paymentSnapshot : vehicleNumberTask.getResult().getChildren()) {
String paymentReceived = paymentSnapshot.child("paymentReceived").getValue(String.class);
sum += Integer.valueOf(paymentReceived)
}
Log.d("TAG", "sum: " + sum);
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
}
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
And the result in the logcat will be:
sum: 500
Please also note, that is recommended to store the prices as numbers and not as strings, case in which the above addition should look like this:
long paymentReceived = paymentSnapshot.child("paymentReceived").getValue(Long.class);
sum += paymentReceived;
I am calling values from my database to use in an android MP like chart, it was originally working but for some reason no longer is may be because the line chart needs to use float values so changed the data in the database to float values.
Here is my code for calling the data:
DatabaseReference databaseref = FirebaseDatabase.getInstance().getReference().child("Carbonfootprint").child(userid).child(String.valueOf(total));
databaseref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final LineChartresult linechats = dataSnapshot.getValue(LineChartresult.class);
score = linechats.getCarbonfootprint();
dataVals.add(new Entry(total, score));
total++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
have checked by database here is the Json format
{
"Carbonfootprint" : {
"yV3msXDJxDXFo5gchxgbk7OjLHi2" : [ null, {
"carbonfootprint" : 13.300000190734863
}, {
"carbonfootprint" : 12.699999809265137
}, {
"carbonfootprint" : 12.100000381469727
}, {
"carbonfootprint" : 9.100000381469727
} ]
},
Unsure as to why it says null ? as on my database it looks like this: screenshot attached..
screenshot
If total is a floating point number, then String.valueOf(total) will certainly contain a ".". The error message is telling you that "." is not a valid character to use in a path.
It seems also that you're not approaching the query correctly from the perspective of your data. It seems like you would want to query the entire node called "Carbonfootprint/yV3msXDJxDXFo5gchxgbk7OjLHi2", and look through the array for the data you need.
I am trying to get some list data and put in a list. But I couldn't make it. I tried so many things but none of them is worked.
This is my database structure.
So there is a main root which is user then username in this case it is "mm". This node includes some data and some other nodes. Expenses is one of them. In expenses there are some unique keys. Every unique key indicates a different receipt. And each unique key stores products of this receipt. In this case there are three ( 0-1-2). There is no limitation to three. It can be 3 or 1 or 15. Each of this nodes store a value in "Expenses" class type.
I want to take this datas into a list. So this list should store data like that.
{
{
Receipt1: {
amount: 1,
marketName: "aa",
price: 2,
productName: "bb"
},
{
amount: 2,
marketName: "cc",
price: 3,
productName: "dd"
}
}, {
Receipt2: {
{
amount: 3,
marketName: "ee",
price: 5,
productName: "ff"
},
{
amount: 1,
marketName: "gg",
price: 7,
productName: "jj"
},
{
amount: 9,
marketName: "nn",
price: 5,
productName: "vv"
}
}
}
So I should able to access Receipts seperately. If I search for Receipt1 then I should get 3 nodes like example.
I try to get this datas like that. But It doesn't work.
private void receiptGetter(String userName,SimpleCallback<Boolean> finishedCallback) {
DatabaseReference rootRef = databaseManager.getReference();
DatabaseReference expensesRef = rootRef.child("user").child(userName).child("expenses");
Query queryReceiptFinder = expensesRef.orderByKey();
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
expenses = ds.getValue(Expenses.class);
expensesList.add(expenses);
}
finishedCallback.run(true);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d("error", databaseError.getMessage());
finishedCallback.run(false);
}
};
queryReceiptFinder.addListenerForSingleValueEvent(valueEventListener);
}
Then I try to get this data like that.
searchButton.setOnClickListener(v -> {
receiptGetter("userName", (success) -> {
if (success) {
String receipt = expensesList.toString();
receiptText.setText(receipt);
} else
Log.d("error", getString(R.string.ErrorOccured));
});
I think Firestore is more suitable for your purpose than realtime database. If you can use it in the documentation you can see how to access every document.
This is a link for the official documentation:
https://firebase.google.com/docs/firestore/query-data/get-data
In general you can access a document with:
DocumentReference docRef = db.collection("cities").document("SF");
// asynchronously retrieve the document
ApiFuture<DocumentSnapshot> future = docRef.get();
// ...
// future.get() blocks on response
DocumentSnapshot document = future.get();
if (document.exists()) {
System.out.println("Document data: " + document.getData());
} else {
System.out.println("No such document!");
}
if you have nested document:
db.collection("cities").document("SF").collection("nameCollection").document("documentName");