【问题标题】:Firestore query: Order by count after a certain dateFirestore 查询:在某个日期后按计数排序
【发布时间】:2021-11-02 15:18:17
【问题描述】:

假设有大量这样的文档集合:

{
  createdAt: 1630789008561, // timestamp
  count: 5
},
{
  createdAt: 1630788666511, // timestamp
  count: 50
},
...

我想查询在某个时间戳之后创建的计数最多的 50 个文档。

一个简单的解决方案是查询此特定时间戳之后的所有文档,然后自行排序。但是我不能,因为我的数据库太大了。

我的第一次查询尝试是:

db.collection("docs")
  .where("createdAt", ">", timestamp)
  .orderBy("count", "desc")
  .limit(50)
  .get();

此查询在 Firestore 中不起作用并抛出:

错误:3 INVALID_ARGUMENT:不等式过滤器属性和第一排序顺序必须相同:createdAt 和 count`

然后我尝试了:

db.collection("docs")
  .where("createdAt", ">", timestamp)
  .orderBy("createdAt", "desc")
  .orderBy("count", "desc")
  .limit(50)
  .get()

这个查询对我不起作用,因为它的作用是在时间戳之后获取 50 个文档,然后按计数对它们进行排序,以防它们具有相同的时间戳,这不是我想要的。

修改为什么这个查询不起作用:

如果我有 150 个文档:

  • 在第 1 天创建了 50 个,计数为 1
  • 在第 2 天创建了 50 个,计数为 2
  • 在第 3 天创建了 50 个,计数为 1

如果我使用时间戳为 0 的查询,我将返回上次创建的 50 个文档(因为我使用的是 .orderBy("createdAt", "desc")),这些文档的计数为 1,显然不是计数最高的文档。 如果你改用asc,你会得到第一个文档,但仍然不是最高计数。 我想要一个查询,如果时间戳为 0,它将给我 50 个文档,计数为 2。

我怎样才能实现我想要的?

编辑我当前使用的解决方案(我想更改):

由于我的时间范围是一天,所以我在每个文档中添加一个day 字段,如下所示:

{
  day: '2021-09-06',
  createdAt: 1630789008561, // timestamp
  count: 5
},
{
  day: '2021-09-05',
  createdAt: 1630788666511, // timestamp
  count: 50
},
...

然后我可以像这样进行相等性检查:

db
  .collection("docs")
  .where("day", "==", '2021-09-05')
  .orderBy("count", "desc")
  .limit(50)
  .get();

这可行,但这不是很好,因为每次我的时间范围发生变化时,我都需要更新我的数据库架构并使用新字段更新每个文档。

【问题讨论】:

    标签: javascript firebase google-cloud-platform google-cloud-firestore


    【解决方案1】:

    返回按计数排序的前 50 个文档。然后在设备上按时间戳过滤。

    QuerySnapshot<Map<String, dynamic>> query = await db
        .collection("docs")
        .orderBy("count", "desc")
        .limit(50)
        .get();
    List docs = query.docs.map((e) => e.data()).toList();
    // then filter by timestamp on the device
    docs.removeWhere((element) => element['createdAt'] < timestamp);
    

    这不能通过使用 where 查询来完成,因为无法绕过这个 limitation

    如果您包含具有范围比较(、>=)的过滤器,则您的第一个排序必须在同一字段中:

    【讨论】:

    • 此查询将获取总体计数最高的文档,然后您(偶然)会选择所需时间段内的任何文档:这不是我需要的,因为如果 50 个文档与最高计数不在所需的时间段内,那么最后你什么也得不到。
    • 我理解你的意思,但是由于我指出的限制,没有办法得到你想要的东西。另一种选择是在时间戳之后获取所有文档,然后按设备上的计数进行过滤。
    • 不幸的是,我在写我的帖子时意识到了限制,我正在寻找这个用例的解决方案。在具有数百万个文档的真实应用程序中,您的其他选择是不可能的。
    【解决方案2】:

    这是正确的查询。

    db.collection("docs")
     .where("createdAt", ">", timestamp)
     .orderBy("createdAt", "desc")
     .orderBy("count", "desc")
     .limit(50)
     .get()
    

    但您需要设置 Firestore 复合索引。

    【讨论】:

    • 我确实创建了一个索引,但这个查询并不能满足我的需要。它在日期之后获取 50 个文档,然后按计数对它们进行排序(如果它们具有相同的时间戳)。因此,您最终不会得到该期间数量最多的文档。
    猜你喜欢
    • 2021-01-12
    • 2015-05-08
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 1970-01-01
    • 2021-11-14
    • 2018-04-10
    相关资源
    最近更新 更多