Cloud Firestore: Storing and querying for today's date over multiple UTC offsets?

我的未来我决定 提交于 2019-12-03 17:26:30

I think it is better to avoid functions in this case as you can perform compound queries now. You can simply use

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

so to speak to limit data which only belongs to one day and try to keep all your time variables in UTC 0 and just find the start point and the current time both client side and convert them to 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();

and you can get your data (remember you need to make an index or just run the query once and firebase SDK will generate a link to create the index in dev tools -> console , for you)

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

I came up with a solution that I'm really not happy with... But it works!

The problem is fundamentally one post can be on more than one date, depending on the user's location. And since for this case we also want to order by a field other than timestamp we can't use a range query to select posts on a given date, because your first .orderBy must be on the field you're using a range query on (see Firestore docs).

My solution is to map localized datestamps to their corresponding UTC offset. The object contains every UTC offset as a key, and the post's datestamp in that offset's time.

An example post looks like this:

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

The field utcDatemap is the the one we use in our queries now:

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

Now posts can show up on two different days, depending on where the user is querying from. And we can still convert the regular old timestamp to the user's local time on the client.

This is definitely not an ideal solution. For the above query I needed to create composite indexes for every single key in utcDatemap. I'm not sure what the rules of thumb are with composite indexes, but I'm thinking having 39 indexes for something simple like this is probably not great.

Additionally I checked it out using the roughSizeOfObject function from thomas-peter's answer on this post and the utcDatemap object, with all it's string datestamps clocked in at roughly 780 bytes, and it's not like 0.78kb is a lot, but you do need to be mindful of how much data you're transferring with a service like Firestore (0.78kb is a lot for a date).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!