Divyansh Tripathi
Divyansh Tripathi

Reputation: 37

Not able while search through Firebase recycler view

I was trying to search through the Firebase database from my material search dialog. I was successful in doing that but the problem is that I can search only through titles(my database contains title and descriptions too), although I tried quite a many things nothing worked for me.

At some point, I succeeded by making another search function for searching descriptions but then it made the searched results for titles to misbehave.

I am attaching the code below can, please have a look at it and let me know if there is anything that I can do to search through both titles and descriptions.

Declarations and code(I have skipped the unrequired parts from the activity code)

    materialSearchBar = homeView.findViewById(R.id.searchBar);
        materialSearchBar.setHint("Search Ad");
        loadSuggest();
        materialSearchBar.setLastSuggestions(suggestList);
        materialSearchBar.setCardViewElevation(10);
        materialSearchBar.addTextChangeListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                List<String> sugest = new ArrayList<String>();
                for (String search : suggestList) {
                    if (search.toLowerCase().contains(materialSearchBar.getText().toLowerCase()))
                        sugest.add(search);
                }
                materialSearchBar.setLastSuggestions(sugest);

            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
        materialSearchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() {
            @Override
            public void onSearchStateChanged(boolean enabled) {
                if (!enabled)
                    mallUsers.setAdapter(firebaseRecyclerAdapter);
            }

            @Override
            public void onSearchConfirmed(CharSequence text) {

                startSearch(text);

            }

            @Override
            public void onButtonClicked(int buttonCode) {

            }
        });
// start search and load suggestions methods
    private void startSearch(CharSequence text) {
        String searchText = text.toString();
        Query query1 = mDatabase1.orderByChild("title").startAt(searchText).endAt(searchText + "\uf8ff");
        Query query2 = mDatabase1.orderByChild("description").startAt(searchText).endAt(searchText + "\uf8ff");
        FirebaseRecyclerOptions<Ad> firebaseRecyclerOptions2 = new FirebaseRecyclerOptions
                .Builder<Ad>()
                .setQuery(query1, Ad.class)
                .build();

        searchAdapter = new FirebaseRecyclerAdapter<Ad, UsersViewHolder1>(firebaseRecyclerOptions2) {
            @Override
            protected void onBindViewHolder(@NonNull UsersViewHolder1 holder, int position, @NonNull Ad ad1) {
                holder.setTitle(ad1.getTitle());
                holder.setPrice(ad1.getPrice());
                holder.setCategory(ad1.getCategory());
                holder.setImage(ad1.getImage(), getContext());
                holder.setTime(ad1.getTime());


                String user_id = getRef(position).getKey();
                final String kk = user_id.toString();

                holder.mView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent mew = new Intent(getActivity(), ExpandActivity.class);
                        mew.putExtra("user_id", kk);
                        startActivity(mew);

                    }
                });

            }

            @NonNull
            @Override
            public UsersViewHolder1 onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                View view1 = LayoutInflater.from(parent.getContext()).inflate(R.layout
                                .user_ad_layout, parent,
                        false);

                return new UsersViewHolder1(view1);
            }
        };
        searchAdapter.startListening();
        mallUsers.setAdapter(searchAdapter);


    }


    private void loadSuggest() {
        mDatabase1.orderByKey().addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                for (DataSnapshot postdataSnapshot : dataSnapshot.getChildren()) {
                    Ad item = postdataSnapshot.getValue(Ad.class);
                    if (item != null) {
                        suggestList.add(item.getTitle());
                        suggestList.add(item.getDescription());
                    }
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }

Upvotes: 2

Views: 276

Answers (2)

areyouSEARious
areyouSEARious

Reputation: 117

You have already experienced the lowest feature of Firebase, querying. However you can query multiple values if you for example upload on firebase both title and description like this:

user:
post:
    title: "cake"
    description: "yummy"
    titledescription: "cake_yummy"

something like this. Another thing you can do is

private boolean hasValue = false;
private boolean hasValue1 = false;

set value is true inside onBindViewHolder and then using handler delay query values one at a time:

Handler myHandler1 = new Handler();
Runnable myRunnable1 = new Runnable() {
   @Override
   public void run() {
       // query1 here
   }
};

Handler myHandler2 = new Handler();
Runnable myRunnable2 = new Runnable() {
   @Override
   public void run() {
       // query2 here
   }
};

then you check them one at a time until it finds a value

   if(!hasValue){
     myHandler1.postDelayed(myRunnable1, 1000);
        if(!hasValue1){
           myHandler2.postDelayed(myRunnable2, 1500); 
        } else {
           myHandler1.removeCallbacks(myRunnable1);
           hasValue1 = false;
     } else {
     myHandler2.removeCallbacks(myRunnable2);
     hasValue2 = false;
     }
 }

Hope it helps.

Upvotes: 0

Alex Mamo
Alex Mamo

Reputation: 138824

According to this post, there is no way in which you can pass two queries to a single adapter. So you can pass either query1 or query2.

Unfortunately, Firebase Realtime database does not support queries on multiple properties (some people say "multiple where clauses" in SQL terms), supports only queries on a single child property. So you'll need to create an extra field to keep both fields. So to achieve this, you need to create a new field which in your database should look like this:

Firebase-root
   |
   --- itemId
          |
          --- title: "valueOfTitle"
          |
          --- description: "valueOfDescription"
          |
          --- title_description: "valueOfTitle_valueOfDescription"

So as you see, the title_description property combines the values that you want to filter on. But Firebase real-time database doesn't support native indexing or search for text fields in objects. Additionally, downloading an entire mode to search for fields client-side isn't practical. However, you can do it for small data sets but as I said before is not practical for large data sets. To enable full text search of your Firebase real-tme database, I recommend you to use a third-party search service like Algolia.

Unlike Firebase Realtime database, Cloud Firestore allows compound queries. You should take a look at this. So a query as the one below is allowed in Cloud Firestore without creating a combined property.

itemIdRef.whereEqualTo("title", "valueOfTitle").whereEqualTo("description", "valueOfDescription");

If you want to use Algolia in Cloud Firestore, I recommend you see my answer from this post. For more information, I also recommend you see this video.

Upvotes: 1

Related Questions