0

I'm using Firebase Realtime Database with Firebase UI, however i'm not being able to search with multiple clauses.

What i need is to take the Users that do not have an specific id. As i'm already filtering them by City i need to filter these ones with specific id. I've been trying to solve it in many ways, however none of them has come to success.

This is my Database

enter image description here

I cant take and render the card with the user with that username.

public class SearchFragment extends android.support.v4.app.Fragment {


    private EditText mSearchField;
    private ImageButton mSearchBtn;
    private RecyclerView mResultList;
    private DatabaseReference mUserDatabase;

    public static SearchFragment newInstance() { return new SearchFragment();  }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_search, container, false);
        mSearchField = view.findViewById(R.id.search_field);
        mSearchBtn = view.findViewById(R.id.search_btn);

        mResultList = view.findViewById(R.id.result_list);
        mResultList.setHasFixedSize(true);
        mResultList.setLayoutManager(new LinearLayoutManager(mResultList.getContext()));

        mSearchBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String searchText = mSearchField.getText().toString();
                firebaseUserSearch(searchText);
                mSearchField.setText("");
            }
        });

        mUserDatabase = FirebaseDatabase.getInstance().getReference("usuarios");
        return view;
    }

    public void firebaseUserSearch(String searchText) {
        Toast.makeText(getContext(), "Buscando usuários", Toast.LENGTH_LONG).show();

        String searchTextLower = searchText.toLowerCase();

        final Query firebaseSearchQuery = mUserDatabase.orderByChild("city").startAt(searchTextLower).endAt(searchTextLower + "\uf8ff");

        firebaseSearchQuery.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                GenericTypeIndicator<Map<String, String>> genericTypeIndicator = new GenericTypeIndicator<Map<String, String>>() {};
                Map<String, String> map = dataSnapshot.getValue(genericTypeIndicator );
                String username = map.get("username").toString();
                Log.d("oi", username);
                Log.d("olar", UserDetails.username);

                if(username != UserDetails.username) {
                    final FirebaseRecyclerOptions<User> options =
                            new FirebaseRecyclerOptions.Builder<User>()
                                    .setQuery(firebaseSearchQuery, User.class)
                                    .build();
                    bindAndBuildFirebaseUi(options);
                }
            }



    public void bindAndBuildFirebaseUi(FirebaseRecyclerOptions options) {
        final FirebaseRecyclerAdapter<User, UsersViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<User, UsersViewHolder>(options) {
            @Override
            public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout, parent, false);
                return new UsersViewHolder(v);
            }

            @Override
            protected void onBindViewHolder(final UsersViewHolder holder, int position, final User model) {
                holder.bind(model);
                Log.d("SearchFragment", "Binded the model");
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        UserDetails.chatWithId = model.getUsername();
                        UserDetails.chatWith = model.getName();
                        startActivity(new Intent(getContext(), Chat.class));
                    }
                });
            }
        };
        firebaseRecyclerAdapter.startListening();
        mResultList.setAdapter(firebaseRecyclerAdapter);
    }
}

As you can see i even tried to filter them throught the database again, howeever, Firebase ui is taking the filter from options which is the first query filtering the users by city.

Does anyone have any idea how to take avoid rendering the users with an specific id???

And i also know that Firebase does not allow to use more than one orderBy.

  • you mean those that don't have a ID? –  May 20 '18 at 19:36
  • Basically i need to take the one that dont have the id that iam filtering. Thats why there is the if(username != UserDetails.username) . However this approach does not work because FirebaseUi is taking the data coming from Query firebaseSearchQuery. –  May 20 '18 at 19:38
  • this is a big problem for Firebase,it doesn't support not equal queries, however you can use java smth like this:: `for(Iterator> it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = it.next(); if(entry.getKey().equals("test")) { it.remove(); }` –  May 20 '18 at 19:57
  • The problem with that approach is that FirebaseUI is taking the data from firebaseSearchQuery, it ignores what i do next –  May 20 '18 at 19:59
  • calvin Rather than `username != UserDetails.username` simply remove current user from the map,and leave everything else as it is –  May 20 '18 at 20:30
  • It doesnt work because of what i said above. This line:: `final Query firebaseSearchQuery = mUserDatabase.orderByChild("city").startAt(searchTextLower).endAt(searchTextLower + "\uf8ff");` fucks eveything up because FirebaseUI is taking the data that is filtered there and ignores the rest. –  May 20 '18 at 21:44
  • `final FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder() .setQuery(firebaseSearchQuery, User.class) .build();` this line is the trouble –  May 20 '18 at 21:46

1 Answers1

0

The Firebase RTDB doesn't support multiple where clauses unfortunately. However, if you denormalize your data properly, you can use one query to link to another ref in the database and effectively filter data in that way. Take a look at the docs: https://github.com/firebase/FirebaseUI-Android/blob/master/database/README.md#using-firebaseui-with-indexed-data.

As a side note, you really shouldn't be using the RTDB to store actual data as it won't scale. It's designed for low latency real-time updates for multiplayer stuff or a ticketing app etc. Instead, I'd recommend looking into Firestore, especially if this is a new app.

Here's the hack I mentioned in my comment below:

public class MyAdapter extends FirebaseRecyclerAdapter {
    private static final int TYPE_NORMAL = 0;
    private static final int TYPE_HIDDEN = 1;

    private final View mEmpty = new View(getContext());

    public MyAdapter(@NonNull FirestoreRecyclerOptions options) {
        super(options);
        mEmpty.setVisibility(View.GONE);
    }

    @Override
    public int getItemViewType(int position) {
        if (getItem(position).username.equals(UserDetails.username)) {
            return TYPE_HIDDEN;
        } else {
            return TYPE_NORMAL;
        }
    }

    @Override
    public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HIDDEN) {
            return new UsersViewHolder(mEmpty);
        }

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout, parent, false);
        return new UsersViewHolder(v);
    }

    @Override
    protected void onBindViewHolder(final UsersViewHolder holder, int position, final User model) {
        if (getItemViewType(position) == TYPE_HIDDEN) return;

        holder.bind(model);
        Log.d("SearchFragment", "Binded the model");
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                UserDetails.chatWithId = model.getUsername();
                UserDetails.chatWith = model.getName();
                startActivity(new Intent(getContext(), Chat.class));
            }
        });
    }
}
SUPERCILEX
  • 3,929
  • 4
  • 32
  • 61
  • Yes, i realise that however, i need to come up with a solution quickly, i cant redo all my database. –  May 21 '18 at 00:34
  • Is this in production? – SUPERCILEX May 21 '18 at 00:44
  • yes, it is.. thats why i am asking lots of questions –  May 21 '18 at 00:45
  • Ah, well that's kinda painful. I personally think it would be better to take your time with this and use a Cloud Function to perform live migrations, but I did come up with a quick (and ugly) hack. See my update answer above. – SUPERCILEX May 21 '18 at 01:01
  • The problem of your solution is the it ignores my FirebaseSearch. ` final Query firebaseSearchQuery = mUserDatabase.orderByChild("city").startAt(searchTextLower).endAt(searchTextLower + "\uf8ff");` –  May 21 '18 at 01:17
  • Yeah, sorry for not being clearer. Just get rid of your child event listener and pass the search directly into FirebaseUI. Then do the client side filtering I showed above. – SUPERCILEX May 21 '18 at 04:59