【问题标题】:iOS - Firestore multiple orderBy and where clauses on indexes within a composite indexiOS - Firestore 复合索引中的索引上的多个 orderBy 和 where 子句
【发布时间】:2019-02-02 23:10:25
【问题描述】:

我正在尝试使用 Firestore 做三件事:

  • 获取“contentType”字段为“basic”的文档,
  • 获取在某个时间点之后创建的文档。 (示例中为凌晨 5 点。)
  • 根据“喜欢”计数对文档进行排序。

contents 集合中的文档如下所示(省略不必要的细节):

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

这是 iOS 代码:

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)

order(by: dateKey) 部分是必需的,因为 Firebase 需要它。否则会抛出异常,抱怨 where 子句和 orderby 子句不匹配。

我已经创建了一个显示contents contentType Ascending date Descending response.like Descending 的复合索引。

预期与结果

我希望文档按like 计数排序,并且所有文档都是“基本”类型并在今天凌晨 5 点之后创建。

相反,只应用前两个条件,而完全忽略第三个条件。两种条件的不同组合起作用。是这三个条件结合起来是行不通的。

所以我的问题是,由于 Firebase 文档没有说明有两个以上的多个 orderby 和 where 组合,这是一个错误还是不可能的事情?

【问题讨论】:

  • Firestore 查询只能执行范围查询或一键排序。在您的情况下,这意味着您必须要么dateKey 排序/过滤 获得按order 排序的结果,但您可以同时拥有两者。这已记录在 here 中,在 this videothis question 中进行了介绍。
  • 很抱歉,但我阅读了文档,问题中只有一个范围过滤器,尽管有两种。文档说一个范围过滤器,而不是排序。
  • where 子句有两个,但第一个是相等,而不是范围。我已经看到了你链接的问题,但它完全不同。
  • @FrankvanPuffelen 请检查我的 cmets。这不是重复的。
  • 您订购的是两个键 (.order(by: dateKey, descending: true).order(by: likeKey, descending: true)),由于 Todd 在我链接的视频中解释的原因,这 (afaik) 是不可能的。

标签: ios swift firebase google-cloud-firestore


【解决方案1】:

我找到了解决问题的方法。

原始查询需要三个字段的复合索引。所以date 上只有一个范围比较--contentType 仅用于相等性检查--dateresponse.like 上的两个排序,两者都构成复合索引.

相反,我决定在contents 文档中添加一个字段,如下所示:

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

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

新查询如下所示:

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() 只是根据当前日期创建一个yyyy-MM-dd 字符串。)

此查询需要两个复合索引:

tags Arrays response.like DescendingcontentType Ascending response.like Descending

幸运的是,这就像一个魅力。

添加信息 原始查询检查了某一天凌晨 5 点之后创建的文档的集合,对我来说,范围检查似乎是问题所在。

只要上面的Date.getDatabaseKey() 方法在5:00:00 到次日4:59:59 小时内生成同一天的键,这个新查询基本上具有效果一样。

【讨论】:

  • @FrankvanPuffelen 很高兴能跟进这个问题,无论原始查询确实是不支持的东西还是失败是一个错误。如果不支持,最好在文档中提供更多信息。
  • 这是否满足最初的问题? contentType = 'basic' 并且在凌晨 5 点之后创建 然后按赞排序?我没有看到任何会在凌晨 5 点之后提取范围数据的东西。我可能在新结构或查询中忽略了它。
  • @Jay 只要Date.getDatabaseKey() 生成相应的字符串键,它就应该可以工作。例如,直到凌晨 4:59:59,生成前一天的密钥,从凌晨 5:00:00 开始,生成具有相同日期的密钥。这应该与检查 5 AM 之后的时间戳具有相同的效果。其余的都是一样的。
  • 我可能应该将它添加到答案中。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-13
  • 2021-09-07
  • 1970-01-01
相关资源
最近更新 更多