google-cloud-firestorealgoliareact-instantsearch

How to structure a firestore collection for items with variants, when using Algolia Search?


So as the title implies I've got an items collection in firestore that looks like this:

ParentCollection
    items
        ->docId_1
            ->name: 'Shirt'
            ->price: 5
            ->tags: ['tag1', 'tag2', 'tag3']
            ->attribute1: 'blah'
            ->attribute2: 'blahblah'

However, I need these items to have variants, such as different sizes/color/etc. In this structure the only way is to have a completely new item for each variant. Which isn't ideal,

Here's my current thinking on the requirements:

My current plan is this:

ParentCollection

    items
        ->parentDocId_1
            ->name: 'Shirt'
            ->variants : [{
                size: S,
                available: true,
                variantDocId: variantDocId_1
                },{
                size: L,
                available: false,
                variantDocId: variantDocId_2
                }]
            ->tags: ['tag1', 'tag2', 'tag3']
            ->attribute1: 'blah'
            ->attribute2: 'blahblah'

                SubCollection
                    variants
                        ->variantDocId_1
                            ->size: 'S'
                            ->price: 5
                            ->parentDocId: parentDocId_1
                        ->variantDocId_2
                            ->size: 'L'
                            ->price: 10
                            ->parentDocId: parentDocId_1

I can see a couple issues with this.

  1. It will require extra db calls since Firestore cannot get the sub Collection when requesting the parent collection.

  2. A big problem is that I need to attach an available attribute to the items, previously I had it in the item collection level but now that I have variants I'll need to put it on each variant. If I have it in the variants array at the parent collection level, I don't think I can filter for available in algolia search anymore as there will be multiple values for the one Item. If I put available down in the variants sub collection, that is not indexed by algolia as it's only indexing documents in the Items collection. Not sure what the solution is here.

I'd really prefer not to have a sub collection at all, I suppose I could do the above without a sub collection and just merge all the unique attributes into the variants array. But then I won't have a unique docId for each variant, and I'm pretty sure I need that (not totally sure yet). Also it doesn't fix my available attribute issue.

Any thoughts on how to properly do this? Is there a way to do it without a sub collection?


Solution

  • Turns out the solution to this issue is more to do with algolia search than firestore. In order to achieve what I want I can keep my firestore database very close to what it was originally (no sub collection necessary), I just need to add an array of objects that contains the information unique to each variant (variants: []) as well as an identifier to bind the individual documents together as variants of each other (distinct: 12345).

    The key here is aloglia's distinct feature which allows de duplication of items that are bound together by a specific key. So in the example below I tie the three variants of the Shirt item together by the distinct: 12345 field. You have to go into the aloglia dashboard to turn on distinct and set the key name. Now only one of the variants will show when searching (which depends on custom rankings or filters), but I will have access to the info of all them via the variants field. This allows me to have unique id's for each variant as well has have all the attributes for each be filterable within algolia search. It also allows me to build a selection dropdown to choose which variant the user would like to interact with. One caveat is that there is redundancy added, and when updating an item variant it will require a cloud function to propagate the change across all variants. But it works and Problem solved!

    ParentCollection
        items
            ->docId_1
                ->name: 'Shirt,
                ->tags: ['tag1', 'tag2', 'tag3']
                ->attribute1: 'blah'
                ->attribute2: 'blahblah'
                ->distinct: 12345
                ->variants: [
                    {
                     size: 'S',
                     available: true,
                     price: 5, 
                     docId: docId_1
                    },
                    {
                     size: 'M',
                     available: false,
                     price: 10,
                     docId: docId_2
                    },
                    {
                     size: 'L',
                     available: true,
                     price: 15, 
                     docId: docId_3
                    }]
            ->docId_2
                ->name: 'Shirt'
                ->tags: ['tag1', 'tag2', 'tag3']
                ->attribute1: 'blah'
                ->attribute2: 'blahblah'
                ->distinct: 12345
                ->variants: [
                    {
                     size: 'S',
                     available: true,
                     price: 5, 
                     docId: docId_1
                    },
                    {
                     size: 'M',
                     available: false,
                     price: 10,
                     docId: docId_2
                    },
                    {
                     size: 'L',
                     available: true,
                     price: 15, 
                     docId: docId_3
                    }]
            ->docId_3
                ->name: 'Shirt'
                ->tags: ['tag1', 'tag2', 'tag3']
                ->attribute1: 'blah'
                ->attribute2: 'blahblah'
                ->distinct: 12345
                ->variants: [
                    {
                     size: 'S',
                     available: true,
                     price: 5, 
                     docId: docId_1
                    },
                    {
                     size: 'M',
                     available: false,
                     price: 10,
                     docId: docId_2
                    },
                    {
                     size: 'L',
                     available: true,
                     price: 15, 
                     docId: docId_3
                    }]