Offline sort of FirebaseRecyclerAdapter - android

I'm currently using FirebaseRecyclerAdapter to retrieve data from a Firebase database, and I've pretty much worked out how to retrieve the data in the order I desire.
To avoid duplication and ease database maintenance, I'd like to add a key to a database entry that allows me to return queries based on that key. I was originally storing data twice. Once for all to see, and once if a user had joined a certain group (under groupName).
To return a query based on group, my original search was as follows:
databaseReference.child(groupName).child("exerciseId"+mExerciseId).orderByChild(sortOrder).limitToFirst(100);
but I believe duplication can be avoided by adding the key "group" to my post. (it also make maintenance much easier as users switch groups).
The "group" database query has now become:
databaseReference.child("exerciseId"+mExerciseId).orderByChild("group").equalTo(groupName);
All is good, except that the data is no longer sorted as per "sortOrder". As firebase does not allow multiple sort criteria, I believe my solution lies in offline sorting.
So, how does one sort the adapter offline?
My adapter is pretty standard:
mAdapter = new FirebaseRecyclerAdapter<Post, PostViewHolder>(Post.class, R.layout.item_post, PostViewHolder.class, dataQuery)
{
#Override
protected void populateViewHolder(final PostViewHolder viewHolder, final Post model, final int position)
{
final DatabaseReference postRef = getRef(position);
// Bind Post to ViewHolder, setting OnClickListener for the star button
viewHolder.bindToPost(model, position, postRef.getKey(), null);
}
mRecycler.setAdapter(mAdapter);
}
I've implemented Comparable in Post, the problem is where is the data stored so that I can pass it to Collections.sort() in this sort of way:
private List<Post> mPosts = new ArrayList<>();
mPosts.add(model);
Collections.sort(mPosts, Post.Comparators.ALLTIME);

The adapters in the FirebaseUI library currently always display data in the order that it is returned by the underlying FirebaseDatabase reference or Query.
You could file a feature request on the Github repo for it (since I'm not sure it is covered in this one yet). Alternatively you could fork the library and roll your own implementation of this functionality.

To feedback on the solution I came up with. I ended up populating my own List, which I can then sort or add to (if using multiple queries) depending on what I'm trying to achieve. This is then displayed using a standard RecyclerView adapter. It works great.
private final List<Post> postList = new ArrayList<>();
dataQuery.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
postList.clear(); // in this example it's a newQuery, so clear postList
for (DataSnapshot child : dataSnapshot.getChildren())
{
Post post = child.getValue(Post.class);
postList.add(post);
int myPosition = sortData(); // uses Collections.sort() to sort data
mAdapter.addPosts(postList);
mAdapter.notifyDataSetChanged();
mRecycler.scrollToPosition(myPosition);
}
}
});

Related

Retrieve Data using Multiple keys Firebase Realtime Database

I have a list of Keys in a Array List . I want to retrieve data only from those keys but at a same time.
DatabaseReference mDBRef;
List<String> keys = new ArrayList<>();
I tried this with loop but the result coming in Model class is repeated 2 times.
for (int i= 0;i<keys.size();i++)
{
String id = keys.get(i);
Log.d("Keys",id);
mDBRef = FirebaseDatabase.getInstance().getReference().child("Gyms").child(id);
mDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
dataSnapshot.getKey();
gyms = dataSnapshot.getValue(Gyms.class);
if (gyms != null)
{
Log.d("Names",gyms.getName());
Toast.makeText(NearMeActivity.this, ""+ gyms.getName(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
The Firebase Realtime database doesn't support loading items based on an array of values. You'll have to load each item with a separate call.
While this may convolute your code, it is not nearly as slow as most developers think. This is because Firebase pipelines the requests. For a longer explanation of that, see my answer here: http://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786
firebase real time database doesn't support multiple matches. You can see Firestore which is NoSQL and provide some flexibility.
See Firestore: https://firebase.google.com/docs/firestore/
You have to use custom search solution like Elastic Search.

Recyclerview set list size

I am using firebase recyclerview to populate my data and retrieving those data from firebase realtime database. I don't want to retrieve all of my data but with a condition that is if "Available == yes " then show me the data in recyclerview. My condition is working but the problem is I have 4 data set on firebase realtime database. From them in 3 set value of available is yes .So, I supposed to get 3 list of data but I am getting all 4 set. 3 set data has actual value and 1 set taking the dummy one. How can I stop that?
FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder>(
BloodModelSchema.class,
R.layout.search_card,
UserViewHolder.class,
databaseReference
) {
#Override
protected void populateViewHolder(UserViewHolder viewHolder, BloodModelSchema model, int position) {
if(model.getAvailable().equals("Yes"))
{
viewHolder.setDetails(model.getBlood_Group(),model.getName(),
model.getArea(),model.getMobile(),
model.getEmail());
}
}
};
recyclerView.setAdapter(firebaseRecyclerAdapter);
firebaseRecyclerAdapter.notifyDataSetChanged();
If you have one databaseReference just add .limitToFirst(3)
FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder>(
BloodModelSchema.class,
R.layout.search_card,
UserViewHolder.class,
databaseReference.limitToFirst(3))
) { .....
But you can create second databaseReference2 and limit it to 4.
For different cases use two different adapters with different limitations. Good luck!

Firebase Reference Query on Root

I am working on a student-teacher private messenger application using Firebase and I can insert data inside the root like this:
The usernames are retrived from a MySQL database, so I just only need to make a query. I would like to get only the data inside a ListView where chatID equals to my generated ID. I've searched in a lot of places like here but can't make the DatabaseReference working. Here is my code:
private void displayStudentChatMessages() {
listofMessages = (ListView) findViewById(R.id.list_msg);
adapter = new FirebaseListAdapter<BubbleMessageStudent>(this, BubbleMessageStudent.class, R.layout.item_chat_right, FirebaseDatabase.getInstance().getReference()) {
#Override
protected void populateView(View v, BubbleMessageStudent model, int position) {
TextView studentUsername = (TextView) v.findViewById(R.id.message_user_sender);
TextView studentMessage = (TextView) v.findViewById(R.id.message_user);
TextView studentSentDate = (TextView) v.findViewById(R.id.message_usertime);
studentUsername.setText(model.getStudentUsername());
studentMessage.setText(model.getStudentMessage());
studentSentDate.setText(android.text.format.DateFormat.format("yyyy-MM-dd%n(HH:mm:ss)", model.getStudentMessageTime()));
}
};
listofMessages.setAdapter(adapter);
listofMessages.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
}
As #cricket_007 commented, you'll need a query to get the chat messages with the correct ID:
rootRef = FirebaseDatabase.getInstance().getReference();
chatQuery = rootRef.orderByChild("chatID").equalTo("teststudent-testteacher...");
adapter = new FirebaseListAdapter<BubbleMessageStudent>(
this, BubbleMessageStudent.class, R.layout.item_chat_right, chatQuery)...
With this code your adapter will only show the messages with the correct chatID.
But in general your data model seems suboptimal. You've essentially mapped a table from your MySQL database into a node in Firebase. It probably works for now, but there are better ways to do this mapping.
In Firebase (and many other NoSQL databases) you should model your data for what you show on the screen. So if your app shows a chat room, which is a list of messages with the same chatID, then you should model precisely that in the database: a list of messages with the same chat ID:
chatId1
chat1Message1: ...
chat1Message2: ...
chat1Message3: ...
chatId2
chat2Message1: ...
chat2Message2: ...
chat2Message3: ...
With this structure you don't need a query to read the messages for a specific chat room. Instead you can directly access the node where they're under:
chatRoom = rootRef.child("teststudent-testteacher...");
adapter = new FirebaseListAdapter<BubbleMessageStudent>(
this, BubbleMessageStudent.class, R.layout.item_chat_right, chatRoom)...
This results in much better scalability.
I recommend reading NoSQL data modeling and watching Firebase for SQL developers.

Why Firebase DB is jumping one level deeper?

the situation is following, I'm using Firebase DB for a small application, the structure of DB is the following.
I'm aware this kind of structuring is not the best for Firebase, probably the problem is here.
My question is, when I'm trying to get childrens list from a parent, Firebase provides me with childrens list from one level deeper entity. For ex. related to this DB sample - if I try to get childrens list from the "University" entity, I get the output "Faculty", but should get the output "UNN". Am I right?
Here is the code sample I'm using for retreiving data:
mDBRef = FirebaseDatabase.getInstance().getReference().child("University");
final ListView listView = (ListView) findViewById(R.id.lv_main);
mGruppeChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
List<String> lst = new ArrayList<String>(); // Result will be holded Here
for (DataSnapshot dsp : dataSnapshot.getChildren()) {
lst.add(String.valueOf(dsp.getKey())); //add result into array list
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, lst);
listView.setAdapter(adapter);
}
Well it is because of firebase's onChildAdded method come with parameter of children of the parent you are reading from. If you added child event listener on 'University', you will get value like 'UNN'-->Faculty. Again if you perform getChildren method on this, you will get 'Faculty'-->RGF. So here if you apply getKey, off course it will return Faculty.

Filter results in FirebaseRecyclerAdapter

I am new to Android and just started to understand some concepts like the RecyclerView. I am using Firebase as a database so I implemented the Firebase solution for that.
My Adapter:
FirebaseRecyclerAdapter<Offer,OfferViewHolder> adapter = new FirebaseRecyclerAdapter<Offer, OfferViewHolder>(
Offer.class,
R.layout.card_item,
OfferViewHolder.class,
mDatabaseReference.child("offer").getRef()
) {
#Override
protected void populateViewHolder(OfferViewHolder viewHolder, Offer model, int position) {
if(tvNoMovies.getVisibility()== View.VISIBLE){
tvNoMovies.setVisibility(View.GONE);
}
viewHolder.tvHeading.setText(model.getHeader());
viewHolder.tvStoreName.setText(model.getStoreName());
}
};
Now I have two questions:
Is it possible to filter results inside the adapter or do i have to use ChildEventListeners for that?
When referencing to a key that contains an Array or an object itself how to retrieve child values?
You can initialize the FirebaseRecyclerAdapter with either a DatabaseReference or a Query. As its name implies, the latter allows you to get a subset of the children at a specific location in the database.
For example say if you want to only show offers from a specific store:
DatabaseReference offers = mDatabaseReference.child("offer").getRef();
Query storeOffers = offers.orderByChild("storeName").equalTo("A. lazzi's store");
FirebaseRecyclerAdapter<Offer,OfferViewHolder> adapter = new FirebaseRecyclerAdapter<Offer, OfferViewHolder>(
Offer.class,
R.layout.card_item,
OfferViewHolder.class,
storeOffers
) {

Categories

Resources