In Firestore, how can you do a compound query involving a key in a map without creating an index for every key?

Multi tool use
Multi tool use


In Firestore, how can you do a compound query involving a key in a map without creating an index for every key?



In Firestore, how can you do a compound query involving a key in a map without creating an index for every key?



For example, consider a collection which holds blog posts, and each blog post has categories.


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



In order to query posts in a particular category in a paginated way, we would do something like this:


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



But it seems that this would require a composite index (categories.<categoryId> and createdAt) for every single category. Is there any way around this?


categories.<categoryId>


createdAt



In my case, it isn't feasible to create composite indices for every category since categories are user-generated, and could easily exceed 200 (the limit for composite indices in Firestore).




3 Answers
3



This is doable by setting the value of each category to what you want to sort on. Firestore has a guide that covers this.


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

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



As far as I know Firestore should auto-generate those indexes. From the documentation page on arrays, lists, and sets:



Consider this alternative data structure, where each category is the key in a map and all values are true:


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



Now it's easy to query for all blog posts within a single category:


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



This technique relies on the fact that Cloud Firestore creates built-in indexes for all document fields, even fields in a nested map.



While the lefthand-side of your where condition may be variable, that doesn't change the fact that these indexes should auto-generated (as far as I can see).





Hey @Frank, thanks for the answer. You are correct in simple cases, however the issue arrises when using the orderBy clause, which is required in order to be able to use startAfter for paginating the query. It seems that as soon as you add orderBy, you need to create a composite index, but I'm hoping there is another way
– RyanM
Jun 22 at 3:53


orderBy


startAfter


orderBy





Aha... got it. That makes sense. And unfortunately I don't think there's any automatic index creation for that. The number of combinations would explode and go over the maximum number of indexes quickly. Sorry about that.
– Frank van Puffelen
Jun 22 at 4:07



Try restructuring your data store. Firebase documentation is very helpful here.



Cloud Firestore does not support the following types of queries:






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

R,X3aTop53Hz0oZL rjkC,h
Xc1c TjHiS 5KIiqplXxbRcwLZP7undZg0OHOsaTgkhiGq8FvWiAPOH

Popular posts from this blog

Rothschild family

Cinema of Italy