在Firestore中查询具有今天之前时间戳的项目

时间:2019-02-12 06:58:35

标签: firebase google-cloud-firestore

在新的Cloud Firestore中,您可以在对象创建期间使用以下行在我的消息对象上设置服务器时间戳:

showtime: firebase.firestore.FieldValue.serverTimestamp()

这对于来自任何用户的消息很有用,因为它不依赖于每个用户的本地时钟。

我面临的挑战是随后查询和设置表演时间字段。我要做的是查找显示时间早于“现在”的任何消息。在我的应用程序中,许多消息被推送到未来。我只希望将今天之前有放映时间的放回并显示。

这是我的查询:

  var query = firebase.firestore()
                  .collection('messages')
                  .where('owner','==',userID)
                  .where('showtime','<',timenow)
                  .orderBy('showtime', 'desc')
                  .limit(25);

面临的挑战是,我不知道如何获取查询中使用的当前时间(在服务器上)。在Firebase中有一个类型为timestamp的now()调用,但是我不确定如何调用它,并且我不确定基于此处的其他一些问题,即Firebase时间戳是否与Cloud Firestore时间戳匹配!

所以问题是:如何将名为timenow的变量设置为服务器上的当前时间,并在查询中使用它来提取25条之前的消息? (使用客户端上的Javascript,以及使用服务器上功能的额外功劳)

一个快速跟进的问题是我如何将放映时间时间戳更新为比现在晚一个星期?

希望我们在Stackoverflow上有一些Firebase / Cloud Firestore专家!

**以下是对功能请求的警告的答案:在Firebase中调用服务器以请求当前时间戳,以便客户端代码可以使用一个标准时钟。 **

4 个答案:

答案 0 :(得分:0)

根据Alex Dunlop的出色回答,我处于类似的情况,当我意识到Firestore根据两种设计原则(或者至少是我的分析)进行操作时,这是有道理的

  1. 重点在于将实时更新流式传输给您的简单查询。为此,设计使查询的复杂性受到限制。这会负担Firestore数据库服务器的负载,并使其能够快速流式传输(有效地重新运行)查询。
    由于查询语言的局限性以及“服务器端”运算符很少(FieldValue是少数几种),Google可以毫不留情地优化查询。

  2. 开发人员需要进行许多更复杂的操作。要求开发人员启动云功能,而不是在Firestore中实现它们,从而使他们对代码的额外复杂性负责(在代码和成本方面)。

如果您来自于Postgres之类的常规数据库设计,那么缺少查询表达能力和服务器端操作将是惊人的。如果您想到Firestore用例和原理,那一切都是有道理的。

答案 1 :(得分:0)

尝试通过数据验证的Firestore安全规则:

  match /messages/{msgId} {
    // optionally: request.time + someBuffer
    allow read: if request.time > resource.data.showtime;
  }

我认为您不希望信任客户端(因为您提到要阻止客户端访问将来的放映时间)。除了更改时钟外,他们还可以编辑javascript。

如果客户端提供的Date.now()与Firestore规则request.time之间存在某些差异,则可能需要一个缓冲区来使查询无效,尤其是如果request.time早于客户端日期的话,那么查询将包含超出有效范围的文档并失败。

答案 2 :(得分:0)

检查空快照上的读取时间戳似乎有效:

ids

同意其他评论之一,即此额外调用可能会很昂贵,但这应该适用于准确的“不早于”服务器时间对于正确性很重要的情况。

答案 3 :(得分:-1)

您是正确的serverTimestamp()正是为了在服务器上获取时间戳,而不是依赖于用户本地时钟。需要注意的一件事是,一般来说,发送消息并从用户本地时钟获取时间戳是可以的,因为消息时间戳对时间不是很敏感。当然,您想知道何时发送该消息,但是在1-2秒之内,在大多数情况下这不是问题。

如果要在客户端进行查询,则查询不应基于服务器时间,而应基于客户端。因为它是客户端查询,而不是服务器查询。

这是您要执行的客户端查询。

const currentTime = new Date();
const query = firebase.firestore()
                  .collection('messages')
                  .where('owner','==',userID)
                  .where('showtime','<',currentTime)
                  .orderBy('showtime', 'desc')
                  .limit(25);

此查询将在客户端当前时间之后获得25条带有“ showtime”的消息。

现在,如果消息需要非常时间敏感,并且您确实确实需要消息基于服务器时间戳,那么我建议您不要像上面那样在客户端上进行查询,而是设置一个云函数api。 如果您以前从未使用过,则可以查看用于直接调用云函数的firebase文档。

这是您希望云功能看起来像的样子:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp()

exports.getUserMessages = functions.https.onCall((data, context) => {
    const uid = context.auth.uid;
    const firestore = admin.firestore();
    firestore.collection('messages')
             .where('owner', '==', uid)
             .where('showtime', '<', new Date())
             .orderBy('showtime', 'desc')
             .limit(25)
             .get()
             .then(snapshot => {
                return snapshot;
             });
});

这将基于服务器时间戳获取消息。要注意的一件事是,除非您需要使它对时间极为敏感,但这不是一个好主意。因为每次调用此呼叫都必须执行一个额外的不必要的呼叫。因为您要调用云功能,然后要查询Firestore数据库。 我建议您不要基于服务器时间,而应基于客户端时间戳。在100的99倍中,客户端与服务器之间的时间差不值得您执行额外的两次通话,尤其是当您考虑在拥有更多用户时考虑扩大所有功能时。

希望能回答您的问题:)