Cloud Firestore:存储和查询今天的多个UTC偏移的日期?

时间:2018-01-31 01:39:50

标签: google-cloud-firestore datetimeoffset

我正在使用Firestore编写一个Web应用程序,该应用程序需要能够显示"今天的热门帖子"在考虑不同时区的用户时,我无法正确查询。

日期作为UTC 0存储在数据库中,然后通过Moment.js调整到客户端当前用户的UTC偏移量。这可以正常工作。

添加新帖子时,我使用firebase.firestore.FieldValue.serverTimestamp()将当前服务器时间戳存储在名为timestamp的字段中,如下所示:

const collectionRef = db.collection('posts');
collectionRef.add({
  name: "Test Post",
  content: "Blah blah blah",
  timestamp: firebase.firestore.FieldValue.serverTimestamp(),
  likeCount: 0
});

然后在服务器上,我有一个在创建时运行的云功能,并将另一个字段添加到名为datestamp的文档中,这是UTC 0时间戳,但进行了调整,以便时间是一天的开始。该函数如下所示:

exports.updatePostDate = functions.firestore
  .document('posts/{postID}')
  .onCreate((event) => {
    const db = admin.firestore();
    const postRef = db.doc('post/'+event.params.postID);
    const postData = event.data.data();

    const startOfDay = moment(postData.timestamp).startOf('day').toDate();

    return postRef.update({
      datestamp: startOfDay
    });
  });

存储时间始终是一天开始的时间戳使我能够编写这样的查询,以查找所有帖子并在特定日期按人气排序:

const startOfDayUTC = moment.utc().startOf('day').toDate();
const postQuery = db.collection('posts')
                    .orderBy('likeCount', 'desc')
                    .orderBy('timestamp', 'desc')
                    .where('datestamp', '==', startOfDayUTC)
                    .limit(25);

问题是,根据用户的UTC偏移量,这可以在解析帖子的timestamp字段时显示具有两个不同日期的帖子。因此,即使查询正确地获取了datestamp所说的所有帖子,2018-01-30T00:00:00Z,timestamp的日期在解析后可能也不一样。以下是两个帖子的示例:

Post 2:
likeCount: 1
timestamp (UTC 0): 2018-01-30T06:41:58Z
timestamp (parsed to UTC-8): 2018-01-29T22:41:58-08:00
datestamp (UTC 0): 2018-01-30T00:00:00Z

Post 1:
likeCount: 0
timestamp (UTC 0): 2018-01-30T10:44:35Z
timestamp (parsed to UTC-8): 2018-01-30T02:44:35-08:00
datestamp (UTC 0): 2018-01-30T00:00:00Z

因此,您可以看到,在将datestamp调整为本地UTC后,帖子具有相同的timestamptimestamp字段最终会在两个不同的日期结束。< / p>

如果有人对此有解决方案,我将非常感激。

2 个答案:

答案 0 :(得分:3)

我认为在这种情况下最好避免使用函数,因为您现在可以执行复合查询。你可以简单地使用

query.where(date > lastMidnight).where(data < now).get().then(...)

可以说限制只属于一天的数据,并尝试将所有时间变量保存在UTC 0中,只需找到客户端的起点和当前时间,然后将它们转换为UTC0。

//get local time from midnight to now (local)
const now = new Date();
const lastMidnight = now.setHours(0,0,0,0);

//then convert those to UTC0 to pass on in your query to firestore
const lastMidNightUTC = new Date(lastMidnight + now.getTimezoneOffset() * 60000).toString();
const nowInUTC = new Date(now + now.getTimezoneOffset() * 60000).toString();

你可以获取你的数据(记住你需要制作一个索引或只需运行一次查询,firebase SDK将生成一个链接以在开发工具中创建索引 - > gt;控制台,为你服务)

    query.where(date > lastMidNightUTC).where(data < now).get().then(...)

答案 1 :(得分:0)

我提出了一个我真的不满意的解决方案......但它确实有效!

问题基本上是一个帖子可以在多个日期,具体取决于用户的位置。因为在这种情况下,我们还希望按timestamp以外的字段进行排序,我们无法使用范围查询来选择指定日期的帖子,因为您的第一个.orderBy必须在该字段上'(see Firestore docs)使用范围查询。

我的解决方案是将本地化的日期戳映射到相应的UTC偏移量。该对象包含每个UTC偏移量作为键,以及该偏移量时间内的帖子的日期戳。

示例帖子如下所示:

posts/{somepostid}
{
  name: "Test Post",
  content: "Blah blah blah",
  timestamp: Mon Jan 29 2018 21:37:21 GMT-0800 (PST),
  likeCount: 0,
  utcDatemap: {
    0: "2018-01-30,
    ...
    -480: "2018-01-29",
    ...
  }
}

字段utcDatemap是我们现在在查询中使用的字段:

const now = moment();
const datestamp = now.format("YYYY-MM-DD");
const utcOffset = now.utcOffset();
const utcDatemapField = 'utcDatemap.'+utcOffset;

const postQuery = db.collection('posts')
                    .orderBy('likeCount', 'desc')
                    .orderBy('timestamp', 'desc')
                    .where(utcDatemapField, '==', datestamp)
                    .limit(25);

现在帖子可以在两天内显示,具体取决于用户查询的位置。我们仍然可以将常规旧时间戳转换为用户在客户端上的本地时间。

这绝对不是一个理想的解决方案。对于上面的查询,我需要为utcDatemap中的每个键创建复合索引。我不确定复合索引的经验法则是什么,但我认为有39个索引用于简单的事情可能不是很好。

此外,我使用来自this post和utcDatemap对象的thomas-peter答案中的roughSizeOfObject函数进行了检查,其中所有字符串的日期戳大约为780字节,并且不像0.78kb很多,但你需要注意你使用像Firestore这样的服务传输了多少数据(0.78kb对于某个日期来说很多)。