1

I have the following Firebase database. enter image description here

I would like to query the database "Ratings" sucht that every entry with a certain orderID should be returned. For this I tried to combine the asynchronous handling of the Firebase API (explained in this video https://www.youtube.com/watch?v=OvDZVV5CbQg&ab_channel=AlexMamo) with the query approach (explained here Checking if a particular value exists in the Firebase database).

Here you can see the code of my Java Fragment (the full name of the database URL is not given due to privacy reasons):

public class FR_Rating extends Fragment implements View.OnClickListener {
    private int orderId =-1;
    private FragmenRateBinding binding;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        binding = FragmenRateBinding.inflate(inflater, container, false);
        orderId = FR_RatingArgs.fromBundle(getArguments()).getOrderId();

        // Set onclick Listeners to the buttoms
        binding.rateButton.setOnClickListener(this);

        return binding.getRoot();
    }

    @Override
    public void onClick(View view) {
            float numberOfStars = binding.ratingBar.getRating();

            /*
            Read data from the Firebase DB
             */
            long currentTimeMillis = System.currentTimeMillis();
            SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yy");
            sdf1.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
            SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm (dd.MM.yy)");
            sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

            String entryIdPrefix = "table_" + MainActivity.tableNumber + "_rating_" + MainActivity.ratingNumberOfTheSession;

            Item_FirebaseDB_Rating currentRating = new Item_FirebaseDB_Rating(MainActivity.ratingNumberOfTheSession, numberOfStars, orderId, MainActivity.tableNumber,currentTimeMillis, sdf2.format(new Date()));

            DatabaseReference rootRef;
            DatabaseReference ratingRef;
            ArrayList<String> ratingList;

            rootRef = FirebaseDatabase.getInstance("https://...").getReference();
            Query query = rootRef.child("Ratings").orderByChild("orderID").equalTo(Integer.toString(orderId));
            
            ratingList = new ArrayList<>();

            readDataFirebase(new FirebaseCallback() {
                @Override
                public void onCallBack(List<String> list) {
                    Log.e("LogTag", "list.toString(): " + list.toString());
                    if (ratingList.size()==0) {
                        Log.e("LogTag", "Item will be rated");
                        writeDataIntoFirebaseDB(currentRating, entryIdPrefix);
                    }
                    if (ratingList.size()>0) {
                        Log.e("LogTag", "Item has already been rated");
                    }
                }
            }, ratingList, query, Integer.toString(orderId));

            Navigation.findNavController(getView()).navigate(FR_RatingDirections.actionFRRatingToFRMyMenu());
    }//End method onClick

    private void readDataFirebase (FirebaseCallback firebaseCallback, ArrayList ratingList,Query query, String currentOrderID ) {
        ValueEventListener valueEventListener = new ValueEventListener() {
            int numberOfChildren =0;
            @Override
            public void onDataChange( DataSnapshot dataSnapshot) {
                for(DataSnapshot ds: dataSnapshot.getChildren()) {
                    numberOfChildren++;

                    String itemName = ds.child("orderID").getValue(Long.class).toString();
                    if (itemName.equals(currentOrderID)) {
                        ratingList.add(itemName);
                    }
                }
                Log.e("LogTag", "Method readDataFirebase - numberOfChildren: " + numberOfChildren);
                firebaseCallback.onCallBack (ratingList);
            }

            @Override
            public void onCancelled( DatabaseError error) {
            }
        };

        query.addListenerForSingleValueEvent(valueEventListener);

    }

    private interface FirebaseCallback  {
        void onCallBack(List<String> list);
    }
    
    public void writeDataIntoFirebaseDB (Item_FirebaseDB_Rating currentRating, String entryIdPrefix) {

        MainActivity.ratingNumberOfTheSession++;
        
        //Call of a static method in the class HelpFunctions that just writes the data into the Firebase Database
        boolean internetConnectionWasAvailableWhileWriting = HelpFunctions.writeDataIntoFirebaseDB(currentRating, DataBaseEntries.FIREBASE_TABLE_RATINGS, entryIdPrefix);
    }
}//End class Fragment

Unfortunately I always get an empty result of the query, even if the particual entry exists. For example I would like to return the entry whose attribute ´orderID´ is 76. But I get an empty result in the method readDataFirebase.

Can some tell me, what to do this properly?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
VanessaF
  • 515
  • 11
  • 36
  • So you say that this line `Log.e("LogTag", "list.toString(): " + list.toString());` prints `0`? Besides that, if you use `.equalTo(Integer.toString(76));` are you getting the same result? – Alex Mamo Dec 12 '21 at 10:08
  • @AlexMamo: Thanks for your answer Alex. Frank has already answered my question. But I have another question to the approach you explained in your video (https://www.youtube.com/watch?v=OvDZVV5CbQg&ab_channel=AlexMamo). In the video you put all the code for the firebase database operations inside the MainActivity and I put all the code for the firebase database operations inside my fragment. Is it also possible to just have one static database class for firebase that I can use for mutiple fragments? Otherwise I would have to implement your approach in every fragment (mosty copy and paste)? – VanessaF Dec 17 '21 at 17:12
  • I think you might be interested in reading this article, [How to read data from Firebase Realtime Database using get()?](https://medium.com/firebase-tips-tricks/how-to-read-data-from-firebase-realtime-database-using-get-269ef3e179c5). – Alex Mamo Dec 18 '21 at 08:01
  • @AlexMamo: Thanks for the answer. I already knew this article. It is however not helpful in my case as I don't use Kotlin (and everything in this article is explained with Kotlin). Further, I would like to use the approach you explained in the video (https://www.youtube.com/watch?v=OvDZVV5CbQg&ab_channel=AlexMamo) just with a query as I am doing now. I don't want to change from that to the get approach. So my question is whether I can use the approach explained in your video with a query inside a single Java class for all Firebase queries that has static methods for all Firebase operations? – VanessaF Dec 18 '21 at 08:27
  • If I knew the queries that you want to perform, I could answer that. If you want, you can add a new question, here on Stackoverflow. – Alex Mamo Dec 18 '21 at 08:42
  • Thanks Alex Mamo for your answer and effort. Actually I was just generally asking whether and how to use your approach explained in the your video (https://www.youtube.com/watch?v=OvDZVV5CbQg&ab_channel=AlexMamo) in one class with (most probably) static methods as you use the Firebase operations for reading data in the video in the MainActivity. One example is the Fragment and the query I posted above (with the tiny corrections from Frank regarding the String in the query). In the code the Firebase read operations are in the Fragment and I'm wondering how to put them in a separate Java class. – VanessaF Dec 18 '21 at 09:01
  • I think I get it. In that case, I recommend you use [Android Architecture Components](https://developer.android.com/topic/architecture) as seen in this [repo](https://github.com/alexmamo/FirebaseApp-Clean-Architecture-MVVM). And that's Java code. – Alex Mamo Dec 18 '21 at 09:08
  • 1
    Thanks for your answer Alex. I will have a look at it. But think I will later ask a separate question about my code and the approach (it will be exactly the same code as in this question but the question will be a different one). – VanessaF Dec 18 '21 at 09:19

1 Answers1

1

Your orderID field is stored as a number, yet you're trying to compare it to a string here:

Query query = rootRef.child("Ratings").orderByChild("orderID").equalTo(Integer.toString(orderId));

Comparisons in Firebase are type-sensitive, so a string "76" is not the same as the number 76, which is why you get no results.

The solution is to pass the value to the database as a number:

Query query = rootRef.child("Ratings").orderByChild("orderID").equalTo(orderId);
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks for your answer Frank. Actually now I returns what it should, altough I have to admit that I do not understand why my previous solution was not working. I know that in Firebase the `orderID` field is stored as a real number. However, I convert it to a string in the method `readDataFirebase` by using `String itemName = ds.child("orderID").getValue(Long.class).toString();` – VanessaF Dec 13 '21 at 18:22
  • " I do not understand why my previous solution was not working" You were comparing a string with a number, and the string `"76"` is not equal to the number `76` in the database. Since it's the database performing the comparison, it doesn't really matter what your code does when reading the data - only when it writes the data, and when it executes the query (which is what I included in my answer). – Frank van Puffelen Dec 13 '21 at 18:26
  • Thanks for your comment Frank. I convert the database entry to a string by using `String itemName = ds.child("orderID").getValue(Long.class).toString();`. Now I have a String `itemName ` that I compare to another String `currentOrderID` that is an argument of the `readDataFirebase` method by using the code `if (itemName.equals(currentOrderID))`. So I compare a String `"76"` to a String `"76"`. This is why I don't understand why my previous answer was not working. Is it because of this `.getValue(Long.class)`? – VanessaF Dec 13 '21 at 18:51
  • No, the problem is that **inside** your database, the value is stored as a number. Since `orderByChild("orderID").equalTo(...)` is asking the database to perform the comparison, you need to pass a number to the database, and not a string (as you were explicitly doing with `Integer.toString(orderId)`). I hope that explains why the code I provided works, but even if it doesn't make sense now, just using it should allow you to move forward. – Frank van Puffelen Dec 13 '21 at 18:59
  • Ah okay. Now I understand. Thanks a lot for your effort Frank. I accepted and upvoted your answer. – VanessaF Dec 13 '21 at 19:04