npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

firestore-declarative-indexes

v1.0.1

Published

Get your Firestore indexes scaling easier and working harder.

Downloads

5

Readme

🔖 firestore-declarative-indexes

Get your Firestore indexes scaling easier and working harder.

🚀 Installing

Using npm: npm install firestore-declarative-indexes

Using yarn: yarn add firestore-declarative-indexes

💣 Purpose:

When performing compound queries in Firestore (these are where you make non-trivial queries against multiple document fields), you are often required to create indexes. Indexes are used to "prepare" firestore to recognize and efficiently respond to your requests, by maintaing a backlog of how contents in your documents relate to one-another.

For larger applications, the number of indexes you require commonly scale with the number of use cases. Additionally, because it is common to repeatedly query using a particular collection, developers are often required to mostly recreate existing indexes but with minor variations. Both of these circumstances conspire against the development process in terms of time and maintainability. On top of this, compound queries help minimize the number of reads we need to perform on the client, since it is possible to take the approach to perform clientside filtering and deduping on the results from multiple requests; but this approach is costly and inefficient for large datasets.

This library aims to resolve these issues by providing you with the basic mechanisms to describe various permutations of your queries at a high level. This means you should only have to describe a a query just once, and have them easily scale to large permutations. In addition, because your indexes will be described algorithmically, the creation or modification of indexes will be much less prone to human error when compared to entry via the Firestore Indexes UI.

🔧 How does it work?

This library provides three bare-bones ways of describing a field; provided you express your Firestore Indexes using these structures, all possible permutations of hefty indexing schemes can be dynamically generated and scaled with ease.

The three building blocks of Declarative Indexes are outlined below:

Scalar

The Scalar Field is used to represent a single document field that you wish to query against. These can be likened to the individual fields you accumulate when building a compound query on Firestore.

Union

A Union Field represents the concatenation of a number of child fields. Wherever a Union is applied, its compiled equivalent will ensure that all child fields will be returned in order in the result set. Unions are useful for declarating higher-level "blobs" in your queries; these are structures that you find yourself re-using across different collections.

Compound (or 'concurrent')

The Compound Field guarantees that all of its child fields will never appear within the same Firestore Index; instead, it asserts that multiple Firestore Indexes must be created for each individual permutation of that field. Compound Fields are useful for when you need to query against multiple different aspects of a document attribute, for example the contents of an array or and object. It is possible to string together multiple Compound Fields for a single Declarative Index; just remember, since these operate using permutations, the resulting Firestore Indexes will grow exponentially!

💡 Example

Let's imagine we're making an app to purchase groceries, where we can filter recommendations based on a user's selected tags. After a lot of hard thought, we've come up with the collection of items below:

{items: [{
  name: 'pear',
  rating: 2.3,
  tags: [
    'horrid', 'ugly'
  ],
},
{
  name: 'carrot',
  rating: 3.4,
  tags: [
    'see-in-dark'
  ],
},
{
  name: 'apple',
  rating: 4.5,
  tags: [
    'delicious', 'red'
  ],
}]}

From the data, we can see that it is possible to have up to two tags assigned to an item. From a user's perspective, this means they could search through our items using either zero, one or two tags. To make things interesting, let's say it would also be possible to filter the returned results by rating, too.

To meet the conditions for satisfaction, we'd need to create the following indexes to account for all use cases:

  • [items], {rating, "ascending"}
  • [items], {rating, "ascending"}, {tags, "array-contains"},
  • [items], {rating, "ascending"}, {tags, "array-contains"}, {tags "array-contains"},
  • [items], {rating, "ascending"}, {tags, "array-contains"}, {tags "array-contains"}, {tags "array-contains"},

This doesn't seem too bad, but imagine we'd need to extend functionality to support up to ten different tags. This would start to get a little tedious! Instead, let's try to express the same contents using Declarative Indexing:

new Index(
 [
   new Scalar(
     {
       fieldPath: 'rating',
     },
   ),
   new Compound(
     [...Array(numberOfRatings + 1)]
       .map((e, i) => i)
       .map((i) => {
         return new Union(
           [...Array(i)]
             .map(() => 
               new Scalar(
                 {
                   fieldPath: 'tags',
                   arrayConfig: 'CONTAINS',
                 },
               ),
             ),
         );
       }),
   ),
 ],
 {
   collectionGroup: 'itemsCollection',
   queryScope: 'COLLECTION',
 },
);

This implementation produces the exact same result as the manual equivalent, with the added bonus of being dynamic. We can choose to index on as many fields as we'd like.

Could I nest Compounds within Unions, or vice-versa?

Absolutely! Check out some of the stress testing in index.test.js.

You can also find the demonstration above, just use npm test.

License

MIT