在Firestore中,如何在不为每个键创建索引的情况下对地图中的键进行复合查询?

时间:2018-06-22 03:28:42

标签: javascript firebase google-cloud-firestore

在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中复合索引的限制)。

4 个答案:

答案 0 :(得分:6)

这可以通过将每个类别的值设置为要排序的内容来实现。 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);

答案 1 :(得分:2)

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

  

考虑这种替代数据结构,其中每个类别都是映射中的键,所有值均为true:

// 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为所有文档字段甚至是嵌套地图中的字段创建内置索引的事实。

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

答案 2 :(得分:1)

尝试重组数据存储。 Firebase documentation在这里非常有帮助。

  

查询限制

     

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

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

答案 3 :(得分:1)

现在,Firestore允许array-contains运算符。
如果要过滤包含特定值的文档,请尝试此操作。

<强>首先,改变地图的字段阵列字段。

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运算符的正式文档。