Farrukh Okhunov
Farrukh Okhunov

Reputation: 441

Firebase Data Desc Sorting in Android

I am storing data in Firebase storage.

Object Comment with attribute timestamp. When I push data from device to Firebase I'm populating timestamp with currentTime and store in long data type.

When I do retrieving the data with firebaseRef.orderByChild("timestamp").limitToLast(15) result is not sorting how I expected.

I even played around with rules and no result:

{
    "rules": {
        ".read": true,
        ".write": true,
        ".indexOn": "streetrate",
        "streetrate": {
          ".indexOn": ".value"
        }
    }
}

I tried store timestamp in String data type, same issue.

Upvotes: 40

Views: 46091

Answers (19)

Alan Bosco
Alan Bosco

Reputation: 837

The Current version of firebase will allow you the fetch the data by sorting it in descending order.

All you have to do is pass an optional bool flag to let the firebase to know to fetch the data in descending order.

Syntax

Query<Object?> orderBy(Object field, {bool descending = false})

Example

QuerySnapshot eventsQuery =
        await ref.orderBy('DateTime', descending: true).get();

Thanks

Upvotes: 0

Ravi Bhushan Kumar
Ravi Bhushan Kumar

Reputation: 49

public static List<'YourModel> desireList= new ArrayList<>();

//Your Realtime Database Query on "ORDERBY","Name" or Key or etc.

@Override
public void onDataChange(DataSnapshot dataSnapshot) 
{
 desireList.clear();
 List<YourModel>tempList = new ArrayList<>();//Create temp list Object.

 for (DataSnapshot dataSnapshotList : dataSnapshot.getChildren()) 
   {
    tempList.add(new YourModel(
       dataSnapshotList.child("Name").getValue() + "",
       dataSnapshotList.getKey() + "",
       dataSnapshotList.child("ORDERBY").getValue() + ""));
   }
       //Save object list in reverse order start.
       for (int i =tempList.size(); i>0; i--)
        {
          desireList.add(tempList.get(i-1));
        }
       //Save object list in reverse order end.

        tempList.clear(); //To clear memory only(not required.)
        //Then update your adapter
        yoursAdapter.notifyDataSetChanged();
 }

Upvotes: 1

Adam Rhoades
Adam Rhoades

Reputation: 37

If you are passing an arraylist to the recyclerView you can do something like this:

FirebaseDatabase.getInstance().getReference().child("parent_id")
.child("id").orderByChild("count");

    tpDBListener = tpDatabaseRef.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            tpUploads.clear();
            for(DataSnapshot postSnapshot : snapshot.getChildren()){
                newUpload pupload = postSnapshot.getValue(newUpload.class);
                pupload.setKey(postSnapshot.getKey());
                
                    tpUploads.add(pupload);//add new object on end
                    //tpUploads.add(0,pupload);//add new object at beginning
                
            }
            tpAdapter.notifyDataSetChanged();
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {
            Toast.makeText(home.this, error.getMessage(),Toast.LENGTH_SHORT).show();
        }
    });

to flip the order so first is last and last is first I recommend when putting the value in the database putting it as 9999999999 and then -1 for what ever ordering you want then use the: if you are not ordeing them by a value or a child.

 tpUploads.add(0, pupload);//add new object at beginning

if you want to order by a value this is the better approach and initialize it in db as 9999999999 and then -1 for its value

.orderBy("child_ID") 

firebase will try to read each digit for example 1 is before 6 but 10 will also come before 6 because of the first digit. That is why I start off with 9999999999 as the starting value and decrease acordingly

Upvotes: 1

Mohammed Sadiq
Mohammed Sadiq

Reputation: 11

This is a client side solution , it will sort the data according to a specific child in firebase database after filling the list

Collections.sort(list, new Comparator<ModuleClass>() {
                    @Override
                    public int compare(ModuleClass module, ModuleClass t1) {
                        return Integer.compare(module.getViews(), t1.getViews());
                    }
                });

Upvotes: 1

Dyno Cris
Dyno Cris

Reputation: 2414

As I understand there is no descending method, for default it is ascending only, but you can reverse it manually. For more information read an official documantation about sorting and filtering data of Realtime Database https://firebase.google.com/docs/database/android/lists-of-data#sorting_and_filtering_data

Upvotes: 0

Mr. Disability
Mr. Disability

Reputation: 838

Based on Himanshus answer but Im using kotlin and instead of the timestamp in the question Im ordering by score which should be similar. I'm not sure if firebase has an ascending or descending function but this is what ended working for me in my current project. I hope it will help someone else in the near future.

FirebaseFirestore.getInstance().collection("leaderboard")
            .orderBy("score")
            .addSnapshotListener { snapshot, exception ->

                if (exception != null) {
                    Log.e("Exception:", "Could not retrieve scores ${exception.localizedMessage}")
                }

                if (snapshot != null) {
                    scores.clear()

                    for (document in snapshot.documents) {
                        val data = document.data

                        val name = data?.get("name") as? String
                        val score = data?.get("score") as? Number

                        val documentId = document.id

                        val newScore = Score(name, score, documentId)
                        scores.add(newScore)
                    }

                    scores.reverse()
                    scoresAdapter.notifyDataSetChanged()
                }
            }

This is what worked for me. Happy coding!

Upvotes: 1

Mridul Das
Mridul Das

Reputation: 101

You can simply just reverse the list (ListView or RecyclerView) that you are using.

Upvotes: 0

Kumar Rohit
Kumar Rohit

Reputation: 65

I found the current firebase version supports descending order sorting:

citiesRef.orderBy("name", "desc").limit(3)

https://firebase.google.com/docs/firestore/query-data/order-limit-data

Hope it helps

Upvotes: -1

PPL
PPL

Reputation: 3

It is very simple,Use -1,-2,-3, etc to store data in the firebase database.Now the data will be displayed in the reverse order in recyclerView.

-3
-2
-1

Upvotes: 0

Durgesh Kumar
Durgesh Kumar

Reputation: 1001

add a column namely timestamp with values (-1*System.currentTimeMillis()) and while fetching data use orderByChild("timestamp").limitToFirst(15) or orderByChild("timestamp").limitToLast(15) according to your requirement. it is smartway to get data in sorted manner, as firebase has no rule for descending order.

Upvotes: 2

Saurabh
Saurabh

Reputation: 11

If you are doing it on web then you can push values in an array and then print the stored values in reverse order.

var a=[]; //declaring an array a.
var num=snapshot.numChildren(); //storing number of children in var num.
a.push(snapshot.val()); // pushing data in the array.
if (a.length==num){ //checking if length of array is same as number of children.
a.reverse(); //reversing the array elements.
for(i=0; i<num; i++){
a[i].name;//this will sort the data in descending order.
}
}

Upvotes: 1

Mirza Asad
Mirza Asad

Reputation: 606

Sorting child items by TIMESTAMP can be done using android.support.v7.util.SortedList

  class post{
private Object time; 

public Object getTime() {
        return time;
    }

public void setTime(Object time) {
    this.time = time;
}
                        ...//rest code}

SortedList<post> data;

 data = new SortedList<post>(post.class, new SortedList.Callback<post>() {
        @Override
        public int compare(post o1, post o2) {
            Long o1l = Long.parseLong(o1.getTime().toString());
            Long o2l = Long.parseLong(o2.getTime().toString());

            return o2l.compareTo(o1l);
        }......//rest code


ref.addChildEventListener(new ChildEventListener() {
              @Override
              public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                 mSwipeRefreshLayout.setRefreshing(true);
                  post p=dataSnapshot.getValue(post.class);


                  data.add(p);



              }...// rest code

android.support.v7.util.SortedList can also be used with RecyclerView

Upvotes: 1

Crashalot
Crashalot

Reputation: 34513

Swift 3:

    let query = firebase.child(YourQueryPath).queryOrdered(byChild: YourQueryChild).queryLimited(toLast: YourLimit)

    query.observeSingleEvent(of: .value, with: { (snapshot) in
         // Reverse order here to get top **YourLimit** results in descending order
    })

Upvotes: -1

Himanshu Rawat
Himanshu Rawat

Reputation: 677

I don't see any option to reverse the data. But One Brute way is to get the data.

List<ModelClass> mList=new ArrayList();
public void onDataChange(DataSnapshot dataSnapshot) 
{
     mList.clear();
     for(DataSnapshot children: dataSnapshot.getChildren()){
        ModelClass modelClass=children.getValue(ModelClass.class);
        mList.add(modelClass);
     }
     Collections.reverse(mList);
     Adapter.notifyDataSetChanged();
}

Upvotes: 23

RBK
RBK

Reputation: 2417

one good solution I find if you are using recycler view to render that data...

mLayoutManager = new LinearLayoutManager(getActivity());
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);

// And set it to RecyclerView
mRecyclerView.setLayoutManager(mLayoutManager);

it will reverse the data rendering...

private static class ChatMessageViewHolder extends RecyclerView.ViewHolder {
         TextView messageText;
         TextView nameText;

         public ChatMessageViewHolder(View itemView) {
             super(itemView);
             nameText = (TextView)itemView.findViewById(android.R.id.text1);
             messageText = (TextView) itemView.findViewById(android.R.id.text2);
         }
     }

     FirebaseRecyclerViewAdapter<ChatMessage, ChatMessageViewHolder> adapter;
     ref = new Firebase("https://<yourapp>.firebaseio.com");

     RecyclerView recycler = (RecyclerView) 

     findViewById(R.id.messages_recycler);
         recycler.setHasFixedSize(true);

         //////////////////////////////////////////////////////////
         mLayoutManager = new LinearLayoutManager(getActivity());
         mLayoutManager.setReverseLayout(true);
         mLayoutManager.setStackFromEnd(true);
         recycler.setLayoutManager(mLayoutManager);
         /////////////////////////////////////////////////////////

         adapter = new FirebaseRecyclerViewAdapter<ChatMessage, ChatMessageViewHolder>(ChatMessage.class, android.R.layout.two_line_list_item, ChatMessageViewHolder.class, mRef) {
         public void populateViewHolder(ChatMessageViewHolder chatMessageViewHolder, ChatMessage chatMessage) {
             chatMessageViewHolder.nameText.setText(chatMessage.getName());
             chatMessageViewHolder.messageText.setText(chatMessage.getMessage());
         }
     };
     recycler.setAdapter(mAdapter);

Upvotes: 19

lee
lee

Reputation: 161

Sort at client side is simple and not require more system resources. Each data snapshot has previousChildkey field. If you want to desc sorting, imagine previousChildkey is nextChildKey. Here are my sample:

class LessonFirebaseArray<ObjectModel>{

  private ArrayList<ObjectModel> mItems;
  ...
  public LessonFirebaseArray() {
    mItems = new ArrayList<>();
  }

  public int addItem(ObjectModel item, boolean isReverse){
    int index;
    if (item.getPreviousChildKey() != null) {
      index = getIndexForKey(item.getPreviousChildKey());
      if (index < 0) {
        index = mItems.size();
      }else if(index>0 && !isReverse) {
        index = index + 1;      
      }
    }else{
      index = mItems.size();
    }
    mItems.add(index, item);
    notifyInsertedListeners(index);
    return index;
  }

  private int getIndexForKey(String key) {
    int index = 0;
    for (ObjectModel snapshot : mItems) {
      if (snapshot.getKey().equals(key)) {
         return index;
      } else {
         index++;
      }
    }
    return -1;
  }

  private void notifyInsertedListeners(int index) {
    if (mListener != null) {
      mListener.onInserted(index);
    }
  }
}

Upvotes: 0

Ricardo Carmo
Ricardo Carmo

Reputation: 376

The @Monet_z_Polski approach, based on

@Override
public T getItem(int position) {
  return super.getItem(getCount() - (position + 1));
}

does have a weird effect on not update FirebaseRecyclerView automatically (PopulateViewHolder is not triggered in realtime changes). So, the best option is use a negative key to index the data.

Upvotes: 5

Monet_z_Polski
Monet_z_Polski

Reputation: 366

I have solved problem by extending FirebaseListAdapter and overriding getItem method:

public abstract class ReverseFirebaseListAdapter<T> extends FirebaseListAdapter<T>  {

public ReverseFirebaseListAdapter(Activity activity, Class<T> modelClass, int modelLayout, Query ref) {
    super(activity, modelClass, modelLayout, ref);
}

public ReverseFirebaseListAdapter(Activity activity, Class<T> modelClass, int modelLayout, DatabaseReference ref) {
    super(activity, modelClass, modelLayout, ref);
}

@Override
public T getItem(int position) {
  return super.getItem(getCount() - (position + 1));
}

}

Upvotes: 10

Frank van Puffelen
Frank van Puffelen

Reputation: 598775

Firebase can order the items in ascending order by a given property and then returns either the first N items (limitToFirst()) or the last N items (limitToLast()). There is no way to indicate that you want the items in descending order.

There are two options to get the behavior you want:

  1. Use a Firebase query to get the correct data, then re-order it client-side

  2. Add a field that has a descending value to the data

For the latter approach, it is common to have a inverted timestamp.

-1 * new Date().getTime();

Upvotes: 71

Related Questions