【问题标题】:Firebase Data Desc Sorting in AndroidAndroid中的Firebase数据描述排序
【发布时间】:2016-03-13 10:31:27
【问题描述】:

我将数据存储在 Firebase 存储中。

对象Comment 具有timestamp 属性。当我将数据从设备推送到 Firebase 时,我使用 currentTime 填充 timestamp 并存储在 long 数据类型中。

当我使用firebaseRef.orderByChild("timestamp").limitToLast(15) 检索数据时,结果没有按照我的预期排序。

我什至玩弄规则却没有结果:

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

我尝试将timestamp 存储在String 数据类型中,同样的问题。

【问题讨论】:

  • 我建议按您想要的值排序,然后向后翻页。 (按 asc 排序,然后从末尾减去 x 个条目进行翻页。)

标签: android firebase sorting firebase-realtime-database


【解决方案1】:

Firebase 可以按给定属性对项目进行升序排序,然后返回前 N 项 (limitToFirst()) 或最后 N 项 (limitToLast())。无法表明您希望项目按降序排列。

有两个选项可以得到你想要的行为:

  1. 使用 Firebase 查询获取正确的数据,然后在客户端重新排序

  2. 向数据中添加一个具有降序值的字段

对于后一种方法,通常使用倒置时间戳。

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

【讨论】:

  • 嗨弗兰克,第二种方法更好。要获得最后 15 行添加的行,我正在使用查询 - firebaseRef.orderByChild("timestamp").limitToFirst(15),同时存储 -1*timestamp。
  • 您更喜欢第二种方法,但这绝不是更好的方法。它与第一个有很多权衡,特别是在规模上。一种是存储——第一种方法不需要存储额外的数据,因为 push() ids 自然会按时间顺序排列数据,并且通常您一次在客户端拉取的数据量很容易反转,对性能的影响可以忽略不计。这两种方法都不应该被认为是劣等的。
  • @Frank van Puffelen 我认为这种方法非常好。但是,当时间戳值使用 ServerValue.TIMESTAMP 时,您能给我们一些 desc 排序的技巧吗?
  • 如果ordering field下两层而不是直接子级,这能实现吗?
【解决方案2】:

我没有看到任何反转数据的选项。但是一种蛮力方法是获取数据。

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();
}

【讨论】:

  • 我认为这是最好的通用解决方案
  • 这是我为这个问题找到的最通用、最简单的解决方案!应该是公认的答案!
  • 男人..!它帮助我将 Recycler View 中的新项目置于顶部。谢谢。
  • 谢谢!那拯救了我的一天!这应该被接受为正确答案!
  • 您的回答使我朝着正确的方向前进。谢谢兄弟!
【解决方案3】:

如果您使用回收器视图来呈现该数据,我会找到一个很好的解决方案...

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

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

它将反转数据呈现...

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);

【讨论】:

  • -1。您似乎将服务器问题纳入客户端问题。这意味着每个客户都必须处理这个问题。如果我们有 iOS、Android 和网络,那么所有人都需要这样做。更好地使用具有负值的函数。
  • 请注意,GridLayoutManager 不支持 stackFromEnd
【解决方案4】:

我通过扩展 FirebaseListAdapter 和覆盖 getItem 方法解决了问题:

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));
}

}

【讨论】:

  • 不错的解决方案。谢谢。
【解决方案5】:

@Monet_z_Polski 方法,基于

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

对于不自动更新 FirebaseRecyclerView 确实有奇怪的影响(PopulateViewHolder 不会在实时更改中触发)。因此,最好的选择是使用否定键来索引数据。

【讨论】:

    【解决方案6】:

    添加一列即时间戳,其值为(-1*System.currentTimeMillis()) 在获取数据时使用orderByChild("timestamp").limitToFirst(15) 或orderByChild("timestamp").limitToLast(15) 根据您的要求。 以排序方式获取数据是一种明智的方式,因为 firebase 没有降序规则。

    【讨论】:

    • 不知道为什么有些人投反对票?至少他们会给出理由。 :(
    【解决方案7】:

    可以使用android.support.v7.util.SortedList对子项按TIMESTAMP排序

      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也可以和RecyclerView一起使用

    【讨论】:

      【解决方案8】:

      如果您在网络上执行此操作,则可以将值推送到数组中,然后以相反的顺序打印存储的值。

      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.
      }
      }
      

      【讨论】:

        【解决方案9】:

        基于 Himanshus 的回答,但我使用的是 kotlin,而不是问题中的时间戳,我按分数排序,应该相似。我不确定 firebase 是否具有升序或降序功能,但这就是在我当前的项目中为我工作的结果。我希望它会在不久的将来对其他人有所帮助。

        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()
                        }
                    }
        

        这对我有用。 编码愉快!

        【讨论】:

          【解决方案10】:

          这是一个客户端解决方案,它会在填充列表后根据firebase数据库中的特定子项对数据进行排序

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

          【讨论】:

            【解决方案11】:

            如果您将 arraylist 传递给 recyclerView,您可以执行以下操作:

            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();
                    }
                });
            

            翻转顺序,所以第一个是最后一个,最后一个是第一个我建议在将值放入数据库时​​将其设置为 9999999999 然后 -1 用于您想要的任何顺序,然后使用: 如果您没有按价值或孩子对它们进行排序。

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

            如果您想按值排序,这是更好的方法,并在 db 中将其初始化为 9999999999,然后将其值设为 -1

            .orderBy("child_ID") 
            

            firebase 将尝试读取每个数字,例如 1 在 6 之前,但由于第一个数字,10 也会在 6 之前。这就是为什么我以 9999999999 为起始值并相应减少的原因

            【讨论】:

              【解决方案12】:

              public static ListdesireList= new ArrayList();

              //您对“ORDERBY”、“名称”或键等的实时数据库查询

              @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();
               }
              

              【讨论】:

                【解决方案13】:

                在客户端排序很简单,不需要更多的系统资源。 每个数据快照都有 previousChildkey 字段。如果你想 desc 排序,想象 previousChildkey 是 nextChildKey。 这是我的示例:

                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);
                    }
                  }
                }
                

                【讨论】:

                  【解决方案14】:

                  很简单,使用-1,-2,-3等方式将数据存储在firebase数据库中。现在recyclerView中会以相反的顺序显示数据。

                  -3
                  -2
                  -1
                  

                  【讨论】:

                    【解决方案15】:

                    您可以简单地反转您正在使用的列表(ListView 或 RecyclerView)。

                    【讨论】:

                      【解决方案16】:

                      据我了解没有降序方法,默认情况下只有升序,但您可以手动反转它。如需更多信息,请阅读有关实时数据库的排序和过滤数据的官方文档https://firebase.google.com/docs/database/android/lists-of-data#sorting_and_filtering_data

                      【讨论】:

                        【解决方案17】:

                        斯威夫特 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
                            })
                        

                        【讨论】:

                          【解决方案18】:

                          我发现当前的firebase版本支持降序排序:

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

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

                          希望对你有帮助

                          【讨论】:

                          • 这是 Cloud Firestore,而不是实时数据库。
                          猜你喜欢
                          • 2019-10-11
                          • 1970-01-01
                          • 2018-09-10
                          • 2019-09-23
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-06-07
                          相关资源
                          最近更新 更多