5

I need help with Firestore queries. I have a all_users data collection, user id-documents with each user information. firestore database image i want to check if username already exists. i get how to get() documents and compare as demonstrated on their webpage but what about data query?, this is my code

updating the widgets - (if mUser text field and current username is not same)

 private void saveProfileSettings(){
    final String username = mUsername.getText().toString();
    //Case 1: user did not change their username
    if (!mUsers.getUsername().equals(username)){

        checkingIfusernameExist(username);

    }else {

    }
}

checkingIfusernameExist method

    private void checkingIfusernameExist(final String username){
    Log.d(TAG, "checkingIfusernameExist: Checking if " + username + " Exists");

    Query mQuery = mFirebaseFirestore.collection("all_users")
            .orderBy(getString(R.string.fields_username))
            .whereEqualTo("username", username);

    mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

            if (documentSnapshots != null){
                Log.d(TAG, "onEvent: username does not exists");
                Toast.makeText(getActivity(), "Username is available", Toast.LENGTH_SHORT).show();
            }
            for (DocumentSnapshot ds: documentSnapshots){
                if (ds.exists()){
                    Log.d(TAG, "checkingIfusernameExist: FOUND A MATCH: " + ds.toObject(Users.class).getUsername());
                    Toast.makeText(getActivity(), "That username already exists.", Toast.LENGTH_SHORT).show();
                }
            }
        }
    });
 }

I don't get any errors neither do results. I have searched everywhere and I haven't seen issues as mine. Plus there aren't much queries around i can work with. any correction would be appreciated, thanks in advance.

UPDATE : after days of searching i actually came up with a solution with the help of answers i got below. so, since firestore does not not have an operational logic, and you want to update if username does not exists with the .whereEqualTo, use the task to find contains any payload.

code that worked for me

checkingIfUsernameExists method

private void checkingIfusernameExist(final String usernameToCompare){

    //----------------------------------------------------------------
    final Query mQuery = mFirebaseFirestore.collection("all_users").whereEqualTo("username", usernameToCompare);
    mQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
        @Override
        public void onComplete(@NonNull Task<QuerySnapshot> task) {
            Log.d(TAG, "checkingIfusernameExist: checking if username exists");

            if (task.isSuccessful()){
                for (DocumentSnapshot ds: task.getResult()){
                    String userNames = ds.getString("username");
                        if (userNames.equals(usernameToCompare)) {
                            Log.d(TAG, "checkingIfusernameExist: FOUND A MATCH -username already exists");
                            Toast.makeText(getActivity(), "username already exists", Toast.LENGTH_SHORT).show();
                        }
                }
            }
            //checking if task contains any payload. if no, then update
            if (task.getResult().size() == 0){
                try{

                Log.d(TAG, "onComplete: MATCH NOT FOUND - username is available");
                Toast.makeText(getActivity(), "username changed", Toast.LENGTH_SHORT).show();
                //Updating new username............


                }catch (NullPointerException e){
                    Log.e(TAG, "NullPointerException: " + e.getMessage() );
                }
            }
        }
    });
Seraware
  • 53
  • 2
  • 9

3 Answers3

2

The following query returns all users with provided usernameToCheck. If username is unique then youll get only one documentSnapShot.

    Query mQuery = mFirebaseFirestore.collection("all_users")
                .whereEqualTo("username", "usernameToCheck");

      mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

                for (DocumentSnapshot ds: documentSnapshots){
                    if (ds!=null){
               String userName = document.getString("username");
                        Log.d(TAG, "checkingIfusernameExist: FOUND A MATCH: " +userName );
                        Toast.makeText(getActivity(), "That username already exists.", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });
Mohammed Farhan
  • 1,120
  • 8
  • 14
  • How do you handle case when it does not find a duplicate username? – Seraware Feb 02 '18 at 10:40
  • Are you providing login authentication like Google auth in your app? – Mohammed Farhan Feb 02 '18 at 10:53
  • there is authentication but it is with email and password. this method only checks if username exists. it dosen't include "else" statement. or do you have a method using your code? – Seraware Feb 02 '18 at 15:08
  • 1
    `if (ds!=null)` means that username is present, you can use `else` block which tells that `ds is null` which means there is no username present like of `userNameToCheck`. Above method is direct and efficient since it query only for `userNameToCheck` in all documents. It will return ds set if matches the username else `ds` will be null which means no such username is present. Simple As That!! – Mohammed Farhan Feb 03 '18 at 04:21
1

To solve this, please use the following lines of code:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference allUsersRef = rootRef.collection("all_users");
Query userNameQuery = allUsersRef.whereEqualTo("username", "userNameToCompare");
userNameQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            for (DocumentSnapshot document : task.getResult()) {
                if (document.exists()) {
                    String userName = document.getString("username");
                    Log.d(TAG, "username already exists");
                } else {
                    Log.d(TAG, "username does not exists");
                }
            }
        } else {
            Log.d("TAG", "Error getting documents: ", task.getException());
        }
    }
});

In which userNameToCompare is of type String and is the user name of the user with which you want to make the comparison.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • what would "userNameToCompare'" look like?. i understood your code. it's just a logic to iterate through all username if existing username shows. seems comparing documents is easy not datas in the documents. can you help with a solution? btw, i remembered your username from my previous question. i got it fixed with your solution. – Seraware Feb 02 '18 at 08:51
  • How would `userNameToCompare` look like? It's a String. Is the name that the user types and you want to verify if exists. It's `userNameToCompare = mUsername.getText().trim().toString();`. I don't understand. Isn't this what you want? To check if a user name exists? – Alex Mamo Feb 02 '18 at 08:56
  • Reading all user documents to find one with a specific name is quite wasteful, both in bandwidth and charged reads ($). A much better approach is to use a query, like Mohammed shows in his [answer](https://stackoverflow.com/a/48579868). – Frank van Puffelen Dec 23 '19 at 21:28
  • @FrankvanPuffelen Oh, an old answer. Indeed a query is more likely to be used. Thanks again puf! – Alex Mamo Dec 24 '19 at 10:13
0

Since I am unable to comment, I am typing this as an answer instead:

I want to know how does your logs look like when the snapshotListener run.

As in, which of the above logs under your conditions are being printed on the console when the spanshotListener runs.

  • Log.d(TAG, "onEvent: username does not exists");
  • Log.d(TAG, "checkingIfusernameExist: FOUND A MATCH: " + ds.toObject(Users.class).getUsername());

Also, you wrote you are getting no result so is it safe to assume that you are expecting to see the Toasts but are not able to?

Also, after skimming through your code, I have put comments based on this documentation :

mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
    @Override
    public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

        if (documentSnapshots != null){ 
        // you are checking here whether your querySnapshot is not null
        // instead of checking whether it is null or empty.
        // taking a wild guess here but could try to modify 
        // your if condition as follows -->
        // if (documentSnapshots.isEmpty()) {
            Log.d(TAG, "onEvent: username does not exists");
            Toast.makeText(getActivity(), "Username is available", Toast.LENGTH_SHORT).show();
        }
        // below as well documentSnapshots is assumed to be not null, 
        // hence all the more reason I am taking the above wild guess
        for (DocumentSnapshot ds: documentSnapshots){
            if (ds.exists()){
                Log.d(TAG, "checkingIfusernameExist: FOUND A MATCH: " + ds.toObject(Users.class).getUsername());
                Toast.makeText(getActivity(), "That username already exists.", Toast.LENGTH_SHORT).show();
            }
        }
    }
});

I am not able to debug any further on the data you have provided. But this link might help you more.

Dhara Bhavsar
  • 345
  • 4
  • 11
  • for some reason, (document.isEmpty()) dosen't seem to be available for firestore as it was in realtime database,. that would have made a bit more sense. i thought it would do the same. in general, i am still through the documentation and might have to go through a rougher part. the link you gave might help. – Seraware Feb 01 '18 at 21:53
  • Yes in your case for FireStore, you need to check the **QuerySnapshot variable**, that is, `documentSnapshots.isEmpty()` instead of `document.isEmpty()` which works in Realtime Database. – Dhara Bhavsar Feb 01 '18 at 22:34