iOS - Firestore 复合索引中的索引上的多个 orderBy 和 where 子句

Posted

技术标签:

【中文标题】iOS - Firestore 复合索引中的索引上的多个 orderBy 和 where 子句【英文标题】:iOS - Firestore multiple orderBy and where clauses on indexes within a composite index 【发布时间】: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 video 和 this question 中进行了介绍。 很抱歉,但我阅读了文档,问题中只有一个范围过滤器,尽管有两种。文档说一个范围过滤器,而不是排序。 where 子句有两个,但第一个是相等,而不是范围。我已经看到了你链接的问题,但它完全不同。 @FrankvanPuffelen 请检查我的 cmets。这不是重复的。 您订购的是两个键 (.order(by: dateKey, descending: true).order(by: likeKey, descending: true)),由于 Todd 在我链接的视频中解释的原因,这 (afaik) 是不可能的。 【参考方案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 之后的时间戳具有相同的效果。其余的都是一样的。 我可能应该将它添加到答案中。谢谢。

以上是关于iOS - Firestore 复合索引中的索引上的多个 orderBy 和 where 子句的主要内容,如果未能解决你的问题,请参考以下文章

Pandas中xs()函数索引复合索引数据的不同切面数据(索引复合索引中需要的数据):索引列复合索引中的一个切面索引行复合索引中的一个切面

在不同“路径”上具有相同名称的firestore集合的索引

SQL Server创建复合索引时,复合索引列顺序对查询的性能影响

如何通过在 Firestore 中的查询从索引中获取多个文档到其他索引

有啥方法可以从 Firestore 中的数组更新特定索引

Flutter:更新列表中的特定索引(Firestore)