iOS - Firestore multiple orderBy and where clauses

2019-08-17 17:27发布

I'm trying to do three things using Firestore:

  • fetch documents whose "contentType" field is "basic",
  • fetch documents that are created past a certain point in time. (5AM in the example.)
  • order documents according to the "like" count.

The documents in the contents collection look like this (leaving out unnecessary details):

{
  date: Timestamp;
  contentType: string;
  response: {
    like: Number;
  };
}

And here is the iOS code:

let dateKey = "date"
let likeKey = "response.like"
let startDate = Date().setLocalHour(5)
let timestamp = Timestamp(date: startDate)

Firestore.firestore()
    .collection(path: .contents)
    .whereField(.contentType, isEqualTo: "basic")
    .whereField(dateKey, isGreaterThanOrEqualTo: timestamp)
    .order(by: dateKey, descending: true)
    .order(by: likeKey, descending: true)
    .limit(to: Constant.fetchLimit)

The order(by: dateKey) part is only necessary because Firebase demands it. Otherwise, an exception will be thrown, complaining that the where clause and the orderby clauses don't match.

I already created a composite index that says contents contentType Ascending date Descending response.like Descending.

Expectation & Results

I'm expecting the documents to be ordered by the like count, and all documents to be "basic" type and created past 5 a.m. of today.

Instead, only the first two conditions are applied, and the third is completely ignored. Different mixes of two conditions work. It's the three conditions combined that is not working.

So my questions is, since Firebase documents don't say anything about having more than two multiple orderby & where combinations, is this a bug or something that's just not possible?

1条回答
祖国的老花朵
2楼-- · 2019-08-17 18:24

I found a workaround to the problem.

The original query required a composite index of three fields. So there is only one range comparison on date--contentType is used only for equality check--and two ordering on date and response.like, both of which compose the composite index.

Instead I decided to add a field in the contents document like so:

{
  tags: string[]; // the new field.

  date: Timestamp;
  contentType: string;
  response: {
    like: Number;
  };
}

And the new query looks like this:

Firestore.firestore()
    .collection(path: .contents)
    .whereField(.tags, arrayContains: Date.getDatabaseKey())
    .whereField(.contentType, isEqualTo: "basic")
    .order(by: likeKey, descending: true)
    .limit(to: Constant.fetchLimit)      

(Date.getDatabaseKey() just creates a yyyy-MM-dd string based on the current date.)

This query requires two composite indexes:

tags Arrays response.like Descending and contentType Ascending response.like Descending.

Fortunately, this works like a charm.

Added Info The original query checked the collection for documents created after 5 AM of a certain day, and to me the range check seemed to be the problem.

As long as the Date.getDatabaseKey() method above generates a key with the same day for hours 5:00:00 to 4:59:59 of the next day, this new query has basically the same effect.

查看更多
登录 后发表回答