以降序显示帖子

Posted

技术标签:

【中文标题】以降序显示帖子【英文标题】:Display posts in descending posted order 【发布时间】:2014-09-01 18:37:56 【问题描述】:

我正在尝试测试 Firebase 以允许用户使用 push 发布 cmets。我想用以下内容显示我检索到的数据;

fbl.child('sell').limit(20).on("value", function(fbdata)  
  // handle data display here

问题是数据按从旧到新的顺序返回 - 我希望它以相反的顺序。 Firebase 可以做到这一点吗?

【问题讨论】:

【参考方案1】:

自从编写此答案以来,Firebase 添加了一项功能,该功能允许按任何子项或按值进行排序。所以现在有四种方式来对数据进行排序:按键、按值、按优先级或按任何命名子项的值。看到这个blog post that introduces the new ordering capabilities。

基本方法保持不变:

1.添加具有倒置时间戳的子属性,然后对其进行排序。

2.以升序读取孩子,然后在客户端上反转它们。

Firebase 支持以两种方式检索集合的子节点:

按名称 按优先级

你现在得到的是名字,它恰好是按时间顺序排列的。顺便说一句,这并非巧合:当您将push 一个项目放入集合时,会生成名称以确保以这种方式对子项进行排序。引用Firebase documentation for push

push() 生成的唯一名称以客户端生成的时间戳为前缀,以便生成的列表按时间顺序排序。

Firebase guide on ordered data 对这个话题有这样的看法:

数据是如何排序的

默认情况下,Firebase 节点上的子节点按名称按字典顺序排序。使用push() 可以生成按时间顺序自然排序的子名称,但许多应用程序要求它们的数据以其他方式排序。 Firebase 允许开发人员通过为每个项目指定自定义优先级来指定列表中项目的顺序。

获得所需行为的最简单方法是在添加项目时还指定一个始终递减的优先级:

var ref = new Firebase('https://your.firebaseio.com/sell');
var item = ref.push();
item.setWithPriority(yourObject, 0 - Date.now());

更新

您还必须以不同的方式检索孩子:

fbl.child('sell').startAt().limitToLast(20).on('child_added', function(fbdata) 
  console.log(fbdata.exportVal());
)

在我使用on('child_added' 的测试中,确保最后添加的几个孩子按时间倒序返回。另一方面,使用 on('value' 按名称顺序返回它们。

请务必阅读section "Reading ordered data",它解释了使用child_* 事件来检索(有序)子级。

一个 bin 来证明这一点:http://jsbin.com/nonawe/3/watch?js,console

【讨论】:

setWithPriority 方法对我来说效果很好。请务必先清除所有现有数据,否则可能会由于与现有优先级值的冲突而看起来像是损坏了。【参考方案2】:

从 firebase 2.0.x 开始,您可以使用 limitLast() 来实现:

fbl.child('sell').orderByValue().limitLast(20).on("value", function(fbdataSnapshot)  
  // fbdataSnapshot is returned in the ascending order
  // you will still need to order these 20 items in
  // in a descending order

这是公告的链接:More querying capabilities in Firebase

【讨论】:

您可以使用prepend 以相反的顺序添加项目,而不是append 除了limitLast(n),我们还需要对数据进行排序。在角度的情况下,这可以使用 orderBy 过滤器根据日期字段按降序完成,因为 ng-repeat="item in ctrl.items | orderBy:'-timestamp'" -timestamp 反转顺序 Mygod 感谢 prepend 正是我想要的。省去额外的排序 + 追加。【参考方案3】:

为了增加弗兰克的回答,也可以通过简单地使用 endAt().limit(x) 像这样 demo

var fb = new Firebase(URL);

// listen for all changes and update
fb.endAt().limit(100).on('value', update);

// print the output of our array
function update(snap) 
   var list = [];
    snap.forEach(function(ss) 
       var data = ss.val();
       data['.priority'] = ss.getPriority();
       data['.name'] = ss.name();
       list.unshift(data); 
    );
   // print/process the results...

请注意,即使多达 1000 条记录(假设有效负载很小),这也是相当高效的。对于更强大的用法,Frank 的答案具有权威性且更具可扩展性。

还可以通过监视child_added/child_removed/child_moved 事件来代替value 并使用debounce 来应用,来优化这种蛮力以处理更大的数据或更多记录DOM 批量更新,而不是单独更新。

DOM 更新自然而然,不管采用哪种方法,一旦您进入数百个元素,那么 debounce 方法(或 React.js 解决方案,本质上是一个 uber debounce)是一个很好的工具.

【讨论】:

【参考方案4】:

确实没有办法,但似乎我们有 recyclerview 我们可以拥有这个

 query=mCommentsReference.orderByChild("date_added");
        query.keepSynced(true);

        // Initialize Views
        mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
        mManager = new LinearLayoutManager(getContext());
//        mManager.setReverseLayout(false);
        mManager.setReverseLayout(true);
        mManager.setStackFromEnd(true);

        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(mManager);

【讨论】:

【参考方案5】:

我有一个日期变量(长),并希望将最新项目保留在列表顶部。所以我所做的是:

添加新的长字段“dateInverse” 添加一个名为“getDateInverse”的新方法,它只返回:Long.MAX_VALUE - 日期; 使用以下命令创建我的查询:.orderByChild("dateInverse") 快! :p

【讨论】:

请注意,这不适用于退出项目,除非您再次设置它们的值。 你也可以在日期前加一个减号。【参考方案6】:

您正在搜索 limitTolast(Int x) 。这将为您提供数据库中最后“x”个较高的元素(它们按升序排列),但它们是“x”个较高的元素

如果您进入数据库 10,300,150,240,2,24,220

这个方法:

myFirebaseRef.orderByChild("highScore").limitToLast(4)

将检索您:150,220,240,300

【讨论】:

【参考方案7】:

android 中,有一种方法可以通过适配器实际反转对象 Arraylist 中的数据。在我的情况下,我无法使用 LayoutManager 以降序反转结果,因为我使用水平 Recyclerview 来显示数据。将以下参数设置为 recyclerview 会破坏我的 UI 体验:

llManager.setReverseLayout(true);
llManager.setStackFromEnd(true);

我发现的唯一工作方法是通过 RecyclerView 适配器的 BindViewHolder 方法:

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) 
    final SuperPost superPost = superList.get(getItemCount() - position - 1);

希望这个答案能帮助所有在 Firebase 中遇到此问题的开发人员。

【讨论】:

什么是superPost?【参考方案8】:

Firebase:如何以相反的顺序显示一个项目线程,每个请求都有一个限制和一个“加载更多”按钮的指示器。

这将获得列表的最后 10 项

FBRef.child("childName") .limitToLast(loadMoreLimit) // 例如 loadMoreLimit = 10

这将获得最后 10 个项目。获取列表中最后一条记录的 id 并保存以加载更多功能。接下来,将对象集合转换为数组并执行 list.reverse()。

LOAD MORE 功能:下一个调用将做两件事,它将根据第一个请求的参考 ID 获取下一个列表项序列,并在您需要显示“加载更多“ 按钮。

this.FBRef .child("childName") .endAt(null, lastThreadId) // 从上一步得到这个 .limitToLast(loadMoreLimit+2)

您需要删除此对象集合的第一项和最后一项。第一项是获取此列表的参考。最后一项是显示更多按钮的指示器。

我还有很多其他的逻辑可以让一切保持干净。您只需要为加载更多功能添加此代码。

  list = snapObjectAsArray;  // The list is an array from snapObject
  lastItemId = key; // get the first key of the list

  if (list.length < loadMoreLimit+1) 
    lastItemId = false; 
  
  if (list.length > loadMoreLimit+1) 
    list.pop();
  
  if (list.length > loadMoreLimit) 
    list.shift();
  
  // Return the list.reverse() and lastItemId 
  // If lastItemId is an ID, it will be used for the next reference and a flag to show the "load more" button.

【讨论】:

【参考方案9】:

我使用 ReactFire 来轻松集成 Firebase。

基本上,它可以帮助我将数据以array 的形式存储到组件状态中。然后,我只需要使用reverse() 函数(read more)

我是这样实现的:

import React,  Component, PropTypes  from 'react';
import ReactMixin from 'react-mixin';
import ReactFireMixin from 'reactfire';

import Firebase from '../../../utils/firebaseUtils'; // Firebase.initializeApp(config);

@ReactMixin.decorate(ReactFireMixin)
export default class Add extends Component 
    constructor(args) 
        super(args);

        this.state = 
            articles: []
        ;
    

    componentWillMount() 
        let ref = Firebase.database().ref('articles').orderByChild('insertDate').limitToLast(10);
        this.bindAsArray(ref, 'articles'); // bind retrieved data to this.state.articles
    

    render() 
        return (
            <div>
                
                    this.state.articles.reverse().map(function(article) 
                        return <div>article.title</div>
                    )
                
            </div>
        );
    

【讨论】:

【参考方案10】:

有更好的方法。您应该按负服务器时间戳排序。即使离线也如何获得负服务器时间戳?有一个隐藏的字段可以提供帮助。文档中的相关sn-p:

var offsetRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/serverTimeOffset");
  offsetRef.on("value", function(snap) 
  var offset = snap.val();
  var estimatedServerTimeMs = new Date().getTime() + offset;
);

【讨论】:

【参考方案11】:

为了补充 Dave Vávra 的答案,我使用负时间戳作为我的 sort_key,就像这样

设置

const timestamp = new Date().getTime();
const data = 
  name: 'John Doe',
  city: 'New York',
  sort_key: timestamp * -1 // Gets the negative value of the timestamp

获取

const ref = firebase.database().ref('business-images').child(id);
const query = ref.orderByChild('sort_key');
return $firebaseArray(query); // AngularFire function

这会从最新到最旧的顺序获取所有对象。你也可以$indexOnsortKey让它运行得更快

【讨论】:

为什么这是负面的:timestamp * 1?那是javascript特定的吗? @JemshitIskenderov 我的错误,我的意思是写timestamp * -1。那将得到负值【参考方案12】:

我也遇到了这个问题,我找到了一个非常简单的解决方案,无论如何都不涉及操纵数据。如果您要将结果呈现给 DOM,则在某种列表中。您可以使用 flexbox 并设置一个类来反转其容器中的元素。

.reverse 
  display: flex;
  flex-direction: column-reverse;

【讨论】:

【参考方案13】:

myarray.reverse();或 this.myitems = items.map(item => item).reverse();

【讨论】:

【参考方案14】:

我是通过前置来做到这一点的。

query.orderByChild('sell').limitToLast(4).on("value", function(snapshot)
    snapshot.forEach(function (childSnapshot) 
        // PREPEND
    );
);

【讨论】:

【参考方案15】:

有人指出有两种方法可以做到这一点:

    在客户端操作数据 进行对数据进行排序的查询

我发现最简单的方法是使用选项 1,但通过 LinkedList。我只是将每个对象附加到堆栈的前面。它足够灵活,仍然允许在ListViewRecyclerView 中使用列表。这样,即使它们按从旧到新的顺序排列,您仍然可以查看或检索最新到旧的。

【讨论】:

【参考方案16】:

您可以添加一个名为 orderColumn 的列来节省时间

Long refrenceTime = "large future time"; Long currentTime = "currentTime"; Long order = refrenceTime - currentTime;

现在在名为 orderColumn 的列中以及检索数据时保存长订单 作为 orderBy(orderColumn) 你会得到你需要的。

【讨论】:

【参考方案17】:

只需在数组上使用reverse(),假设您将值存储到数组items[] 然后执行this.items.reverse()

 ref.subscribe(snapshots => 
       this.loading.dismiss();

      this.items = [];
      snapshots.forEach(snapshot => 

        this.items.push(snapshot);

      );
      **this.items.reverse();**
    ,

【讨论】:

【参考方案18】:

对我来说,是limitToLast 起作用了。我还发现limitLast 不是函数:)

const query = messagesRef.orderBy('createdAt', 'asc').limitToLast(25);

以上对我有用。

【讨论】:

【参考方案19】:

以相反的顺序打印

让我们跳出框框思考...如果您的信息将直接打印到用户的屏幕上(没有任何需要按连续顺序修改的内容,例如总和什么的),只需从下到上打印即可。

因此,不要将每个新内容块插入到打印空间的末尾 (A += B),而是将该块添加到开头 (A = B+A)。

如果您将元素作为连续的有序列表包含在内,那么如果您将每个元素作为列表项 (&lt;li&gt;) 插入有序列表 (&lt;ol&gt;) 中,则 DOM 可以为您放置数字。

这样可以节省数据库空间,避免不必要的反向数据。

【讨论】:

以上是关于以降序显示帖子的主要内容,如果未能解决你的问题,请参考以下文章

按降序过滤时向 Firebase 观察者添加新帖子

Rails:在嵌套视图中订购商品

WP-API 获取帖子并按降序排列

查询显示 2010 年每个月的总利润降序排列?

以降序遍历 collections.Counter() 实例的 Pythonic 方式?

树集以降序排列元素