【问题标题】:In Firestore, how can you do a compound query involving a key in a map without creating an index for every key?在 Firestore 中,如何在不为每个键创建索引的情况下执行涉及映射中键的复合查询?
【发布时间】:2018-12-01 11:27:27
【问题描述】:

在 Firestore 中,如何在不为每个键创建索引的情况下执行涉及映射中键的复合查询?

例如,考虑一个包含博客文章的集合,每个博客文章都有类别。

Post {
    title: ..
    ...
    categories: {
        cats: true
        puppies: true
    }   
}

为了以分页方式查询特定类别的帖子,我们会这样做:

let query = db.collection(`/posts`)
    .where(`categories.${categoryId}`, '==', true)
    .orderBy('createdAt')
    .startAfter(lastDate)
    .limit(5);

但这似乎需要每个类别的复合索引(categories.<categoryId>createdAt)。有没有办法解决这个问题?

在我的例子中,为每个类别创建复合索引是不可行的,因为类别是用户生成的,并且很容易超过 200(Firestore 中复合索引的限制)。

【问题讨论】:

    标签: javascript firebase google-cloud-firestore


    【解决方案1】:

    这可以通过将每个类别的值设置为您想要排序的值来实现。 Firestore 有一个 guide 涵盖了这一点。

    Post {
        title: ..
        ...
        categories: {
            cats: createdAt
            puppies: createdAt
        }   
    }
    
    let query = db.collection(`/posts`)
        .where(`categories.${categoryId}`, '>', 0)
        .orderBy(`categories.${categoryId}`)
        .startAfter(lastDate)
        .limit(5);
    

    【讨论】:

    • 看起来有点奇怪,但这是目前唯一的解决方法。
    • 更不用说这仅适用于两个参数,因此如果您要向查询添加另一个参数,这将不起作用。我希望 Firebase 添加该功能。但这是解决问题的好方法,谢谢
    • 你不需要where 来工作,你可以完全忽略它。
    【解决方案2】:

    据我所知,Firestore 应该自动生成这些索引。来自documentation page on arrays, lists, and sets

    考虑这种替代数据结构,其中每个类别都是地图中的键,所有值都为真:

    // Sample document in the 'posts' collection
    {
        title: "My great post",
        categories: {
            "technology": true,
            "opinion": true,
            "cats": true
        }
    }
    

    现在可以轻松查询单个类别中的所有博客文章:

    // Find all documents in the 'posts' collection that are
    // in the 'cats' category.
    db.collection('posts')
        .where('categories.cats', '==', true)
        .get()
        .then(() => {
            // ...
        });
    )
    

    这种技术依赖于 Cloud Firestore 为所有文档字段创建内置索引,甚至是嵌套地图中的字段

    虽然 where 条件的左侧可能是可变的,但这并不会改变这些索引应该自动生成的事实(据我所知)。

    【讨论】:

    • 嘿@Frank,感谢您的回答。在简单的情况下您是正确的,但是在使用 orderBy 子句时会出现问题,这是为了能够使用 startAfter 对查询进行分页所必需的。好像只要加了orderBy,就需要创建一个复合索引,不过我希望有别的办法
    • 啊哈……明白了。这就说得通了。不幸的是,我认为没有任何自动索引创建。组合的数量会爆炸并迅速超过索引的最大数量。对此感到抱歉。
    • 嗨@FrankvanPuffelen 我们如何编写查询 db.collection('posts').where('roles.${uid}', '==', 'EDITOR' || 'VIEWER ')?
    • @choopage-JekBao 如文档中所述(此问题的答案中提供的链接的底部页面)逻辑或查询。在这种情况下,您应该为每个 OR 条件创建一个单独的查询,并在您的应用中合并查询结果
    【解决方案3】:

    现在 Firestore 允许使用 array-contains 运算符。
    如果你想过滤包含特定值的文档,试试这个。

    首先,将 Map 字段更改为 Array 字段。

    Post {
        title: ..
        ...
        categories: [
            cats,
            puppies
        ]
    }
    

    其次,对每个不同的字段使用array-containsorderBy

    let query = db.collection(`/posts`)
        .where('categories', 'array-contains', 'cats')
        .orderBy('createdAt')
        .startAfter(lastDate)
        .limit(5);
    

    你可以从here查看关于array-contains操作符的官方文档。

    【讨论】:

    • 很遗憾,您目前无法链接array-contains
    【解决方案4】:

    尝试重构您的数据存储。 Firebase documentation 在这里很有帮助。

    查询限制

    Cloud Firestore 不支持以下类型的查询:

    • 在不同字段上使用范围过滤器进行查询,如上一节所述。
    • 跨多个集合或子集合的单个查询。每个查询都针对单个文档集合运行。更多 有关数据结构如何影响查询的信息,请参阅 Choose a Data Structure.
    • 单个数组成员的查询。但是,您可以使用 Working with Arrays, Lists, and Sets 中的技术对类似数组的数据进行建模和查询。
    • 逻辑或查询。在这种情况下,您应该为每个 OR 条件创建一个单独的查询,并在您的应用中合并查询结果。
    • 带有 != 子句的查询。在这种情况下,您应该将查询拆分为大于查询和小于查询。例如,虽然 不支持查询子句 where("age", "!=", "30"),可以 通过组合两个查询获得相同的结果集,一个带有子句 where("age", "", 30) 的子句。

    【讨论】:

      【解决方案5】:

      如果您在 doc id 上创建复合索引,则可以在多个 where 子句上排序,而无需创建动态索引,然后按它排序。

      posts/field__2ls2kdlsk2
      posts/field__3ksl2fjes2
      posts/field__j23kslewsl
      

      你的帖子文档看起来像${field}__${createId()}:

      let query = db.collection(`/posts`)
          .where(`categories.${categoryId}`, '==', true)
          .where(`categories.${categoryID2}`, '==', true)
          .orderBy(firebase.firestore.FieldPath.documentId())
          .startAfter(docID)
          .limit(5);
      

      J

      【讨论】:

        猜你喜欢
        • 2018-06-11
        • 1970-01-01
        • 2020-01-22
        • 1970-01-01
        • 2019-12-21
        • 1970-01-01
        • 1970-01-01
        • 2023-02-13
        • 1970-01-01
        相关资源
        最近更新 更多