I'm using the Cloud Firestore of Firebase to store users and the android Searchview to provide search fonctionnality. When a user search for "jonathan" for example, if he begin typing "j" i want to bring in all users with the name starting par "j". How can i achieve this ?
Here is what i have tried:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem search = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) search.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
//Toast.makeText(MainActivity.this, "SEARCH " + query, Toast.LENGTH_LONG).show();
searchUsers(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
//Toast.makeText(MainActivity.this, "SEARCH " + newText, Toast.LENGTH_LONG).show();
searchUsers(newText);
return false;
}
});
return true;
}
Getting the user from Firebase:
private void searchUsers(String recherche) {
if(recherche.length() > 0)
recherche = recherche.substring(0,1).toUpperCase() + recherche.substring(1).toLowerCase();
listUsers = new ArrayList<>();
db.collection("users").whereGreaterThanOrEqualTo("name", recherche)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(@Nullable QuerySnapshot snapshots,
@Nullable FirebaseFirestoreException e) {
if (e != null) {
System.err.println("Listen failed:" + e);
return;
}
listUsers = new ArrayList<User>();
for (DocumentSnapshot doc : snapshots) {
User user = doc.toObject(User.class);
listUsers.add(user);
}
updateListUsers(listUsers);
}
});
}
This works only for the first letter "j" as soon as i add "ja" for example i still have all the "jonathan" displayed
Inside that click on Firebase. After clicking on Firebase, you can get to see the right column mentioned below in the screenshot. Inside that column Navigate to Firebase Cloud Firestore. Click on that option and you will get to see two options on Connect app to Firebase and Add Cloud Firestore to your app.
js admin clients have listCollections() on Firestore to get that list. Or, if you're looking for subcollections nested under a document, use DocumentReference. listCollections(). If you want to get a list on any platform, you should maintain that list yourself in a known collection inside a known document id.
To read a single document, we can use DocumentReference's get() method that returns a Task<DocumentSnapshot>, while reading multiple documents from a collection or Query, we can use Firestore Query's get() method that returns an object of type Task<QuerySnapshot>. Both methods read the data only once.
Thanks to the suggestion of @Oby if found the solution. In fact i dont really need to query the database every time the search is triggered since i have the list of users. I just have to make the search on the list like this: We get the list first:
private void getUsers() {
db.collection("users").whereEqualTo("etat", 1)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(@Nullable QuerySnapshot snapshots,
@Nullable FirebaseFirestoreException e) {
if (e != null) {
System.err.println("Listen failed:" + e);
return;
}
listUsers = new ArrayList<User>();
for (DocumentSnapshot doc : snapshots) {
User user = doc.toObject(User.class);
listUsers.add(user);
}
updateListUsers(listUsers);
}
});
}
Here is the search function:
private void searchUsers(String recherche) {
if (recherche.length() > 0)
recherche = recherche.substring(0, 1).toUpperCase() + recherche.substring(1).toLowerCase();
ArrayList<User> results = new ArrayList<>();
for(User user : listUsers){
if(user.getName() != null && user.getName().contains(recherche)){
results.add(user);
}
}
updateListUsers(results);
}
Here i notify the the Adapter of the RecyclerView that the data changed:
private void updateListUsers(ArrayList<User> listUsers) {
// Sort the list by date
Collections.sort(listUsers, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
int res = -1;
if (o1.getDate() > (o2.getDate())) {
res = 1;
}
return res;
}
});
userRecyclerAdapter = new UserRecyclerAdapter(listUsers, InvitationActivity.this, this);
rvUsers.setNestedScrollingEnabled(false);
rvUsers.setAdapter(userRecyclerAdapter);
layoutManagerUser = new LinearLayoutManager(getApplicationContext());
rvUsers.setLayoutManager(layoutManagerUser);
userRecyclerAdapter.notifyDataSetChanged();
}
And of course the SearchView:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem search = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) search.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
//Toast.makeText(MainActivity.this, "SEARCH " + query, Toast.LENGTH_LONG).show();
searchUsers(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
//Toast.makeText(MainActivity.this, "SEARCH " + newText, Toast.LENGTH_LONG).show();
searchUsers(newText);
return false;
}
});
return true;
}
My way of searching is rather inefficient as I am using two recyclerviews, one is hidden and the other with the data visible. All data in the document is fetched to an array and I query the array in real-time as you want. The data remains synced always so it seems real-time. Not a good practice but it gets the job done for me. If you need further help I can create a gist for you in that.
The proper way would be to use this official link to search for exactly what you need Full-text search
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With