Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

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).

like image 512
RyanM Avatar asked Jun 22 '18 03:06

RyanM


People also ask

Can firestore update multiple documents matching a condition using one query?

Cloud Firestore does not support the equivalent of SQL's update queries.

What is a composite index firestore?

Composite indexes A composite index stores a sorted mapping of all the documents in a collection, based on an ordered list of fields to index. Note: You can have at most one array field per composite index. Cloud Firestore uses composite indexes to support queries not already supported by single-field indexes.


2 Answers

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); 
like image 133
abraham Avatar answered Sep 22 '22 14:09

abraham


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).

like image 20
Frank van Puffelen Avatar answered Sep 20 '22 14:09

Frank van Puffelen