如何使用 Firebase 将初始数据加载与增量子项分开?

Posted

技术标签:

【中文标题】如何使用 Firebase 将初始数据加载与增量子项分开?【英文标题】:How to separate initial data load from incremental children with Firebase? 【发布时间】:2015-03-14 17:03:26 【问题描述】:

我有一个应用程序,每隔 5 秒左右就会将新的孩子添加到 Firebase。我有成千上万的孩子。

在应用程序加载时,我希望以不同的方式处理最初的数千个与每 5 秒滴流的后续子级。

您可能会建议我使用 value,处理所有内容,然后使用 children_added。但我相信如果处理时间太长,我可能会错过任何一点。

有没有办法在 Firebase 中做到这一点,保证我不会错过任何一点?

【问题讨论】:

如果您使用Firebase.ServerValue.TIMESTAMP 为孩子添加时间戳,使用startAt 这将是微不足道的。你已经尝试过什么了吗?如果是这样,您能否分享代码以及它在哪里(您担心)失败? how to discard initial data in a Firebase DB 的可能重复项 另见:How to retrieve only new data @FrankvanPuffelen 您如何管理 Firebase 服务器的时间戳与请求数据的任何事物之间的时间戳差异? 查看加藤发布的第一个链接。 【参考方案1】:

在数据库中添加了 createdAt 时间戳,并将 child_add 限制为当前时间,这对我来说似乎工作正常。

const onChildAdded = database()
  .ref(refURL)
  .limitToLast(constValues.LAST_MESSAGE_ADDED)
  .orderByChild('createdAt')
  .startAt(date.getTime())
  .on('child_added', (snapshot) => 
    parseMessage(snapshot);
  );

database()
  .ref(refURL)
  .limitToLast(constValues.LAST_MESSAGES_LIMIT)
  .once('value', (snapshot) => 
    parseInitialMessages(snapshot);
    setLoading(false);
  );

【讨论】:

【参考方案2】:

改进了@jacobawenger 的答案,不使用全局状态变量

var ref = new Firebase('https://<your-Firebase>.firebaseio.com');

ref.once('value', function(snapshot) 
  // do something here with initial value

  ref.on('child_added', function(snapshot) 
    // do something here with added childs
  );

);

【讨论】:

这不会引入竞争条件吗?如果在执行的过程中添加了一个孩子怎么办: // 在这里用初始值做一些事情 这看起来像是我正在尝试的东西,但我得到了重复的数据。我认为原因是 child_added 添加了所有数据,甚至从一开始 - 这就是它似乎工作的方式。所以我们需要像***.com/a/27995609/129202 这样的检查,这样我们就不会添加重复的数据。【参考方案3】:

由于初始的 child_added 事件,预加载数据将在 value 事件在父级上触发之前触发,您可以将 value 事件用作一种“初始数据加载”通知。这是我从another similar *** question 稍微修改的一些代码。

var initialDataLoaded = false;
var ref = new Firebase('https://<your-Firebase>.firebaseio.com');

ref.on('child_added', function(snapshot) 
  if (initialDataLoaded) 
    var msg = snapshot.val().msg;
    // do something here
   else 
    // we are ignoring this child since it is pre-existing data
  
);

ref.once('value', function(snapshot) 
  initialDataLoaded = true;
);

值得庆幸的是,Firebase 会巧妙地缓存这些数据,这意味着创建 child_addedvalue 侦听器只会下载一次数据。为已经通过网络的数据添加新的 Firebase 侦听器非常便宜,您应该对定期执行此类操作感到自在。

如果您担心在实际不需要时下载所有初始数据,我会按照 @FrankvanPuffelen 在 cmets 中的建议使用时间戳查询。这非常有效,并且使用 Firebase 查询进行了优化。

【讨论】:

这是一个很好的答案。我不知道智能缓存,也没有在其他任何地方看到过。谢谢雅各布。我确实有两个问题:如果值的回调没有立即发生,或者回调本身需要很长时间才能执行(由于数据处理或其他原因),会发生什么?是不是有可能在那个时候加个孩子? Firebase 应该保证 value 事件会在任何 new child_added 事件触发之前触发,所以你应该没问题。至于回调的数据处理,只要您立即检查initialDataLoaded 变量,这无关紧要。如果您稍后在一堆阻塞代码之后在回调中检查它,它可能已经切换到true,这就是我认为您得到的。您应该在此处遵循通常的 javascript 最佳实践,因为这并不是 Firebase 特定的问题。 @jacobawenger 我一直在读到,Firebase 没有内置方法来获取初始数据,然后只获取更改的内容,这是“设计使然”吗?那是因为按照设计 3 方式数据绑定应该已经管理实时同步吗?我只是担心当 Firebase 被构建为声明式处理时,我会尝试手动同步。 祝福雅各布。不知道这是一件事。 ios 中,至少安装处理程序的顺序很重要:如果值处理程序安装在添加处理程序之前,那么仍然会调用添加回调。【参考方案4】:

这个问题有两种可能的解决方案,都不令人满意:

1) 给您的孩子加上时间戳,并且只请求时间戳大于“现在”值的孩子。这不是很好,因为您的应用程序的“现在”和“现在”之间可能存在同步问题,因为无论是将数据推送到 Firebase 或“现在”的 Firebase 服务器值。

2) 使用价值和添加的孩子。在 child_add 中看到的任何已在 value 中看到的重复项都可能被丢弃。这对于大型数据集是不可用的,因为它需要两次下载所有历史数据!

为什么不能有一个像“child_added”这样的命令,它永远不会给出所有的东西?

【讨论】:

请不要将此作为答案发布,除非您认为这是一个答案。 Kato 给出了两个类似问题的链接。如果那里给出的答案对您不起作用,请编辑您的问题(其下方有一个方便的编辑链接)以包含一个最小的代码示例,该示例显示您所做的事情并描述它出了什么问题。 我从加藤给出的两个链接中总结了我的收获,并将其作为答案发布。另外:链接到的解决方案之一有我试图避免的确切问题:大型数据集的缺失数据。我将在该线程中发布问题 (***.com/questions/19883736/…) 其实我的业力在50以下,不能评论。撇开必须两次下载数据的问题不谈,问题在于对于大型数据集,在 child_added 完成和 value 完成之间存在时间间隔。由于添加的子项仅在值完成后处理,因此您可能会丢失数据。 如果“解决方案”对您没有解决方案,请不要将它们作为答案发布。您始终有权使用其他信息更新/编辑您的问题。 我的问题已经充分说明,无需进一步解释。我已经总结了其他人为这个问题提供的解决方案,并解释了为什么它们并不完美。如果有“更好”的解决方案,欢迎任何人提供,我会指出它是正确的。

以上是关于如何使用 Firebase 将初始数据加载与增量子项分开?的主要内容,如果未能解决你的问题,请参考以下文章

React Redux仅从firebase加载一次数据

如何使用 vue 和 vuefire 加载 Firebase 数据?

如何将过去几天的数据从 Firebase 重新加载到 BigQuery?

Firebase 发布 apk 未加载数据

加载 Firebase 实时数据库时的 ProgressBar [重复]

如何从 Firebase 加载孩子