在Firebase的Firestore中优化检索由另一个集合分组的数据

时间:2019-04-10 23:34:30

标签: javascript firebase promise google-cloud-firestore

我的Firestore数据库中有两个集合,等效于:

// ItemCategories collection
{
    id_category_1: {
        name: 'Category 1',
    },
    id_category_2: {
        name: 'Category 2',
    },
    id_category_3: {
        name: 'Category 3',
    },
    // ...
}

// Items collection
{
    id_item_1: {
        CategoryId: 'id_category_1',
        name: 'Item 1',
    },
    id_item_2: {
        CategoryId: 'id_category_1',
        name: 'Item 2',
    },
    id_item_3: {
        CategoryId: 'id_category_3',
        name: 'Item 3',
    },
    id_item_4: {
        CategoryId: 'id_category_2',
        name: 'Item 4',
    },
    // ...
}

我想检索和格式化我的项目,以按类别进行分隔,例如:

const ItemList = {
    'Category 1': [
        {
            id: 'id_item_1',
            CategoryId: 'id_category_1',
            name: 'Item 1',
        },
        {
            id: 'id_item_2',
            CategoryId: 'id_category_1',
            name: 'Item 2',
        },
    ],
    'Category 2': [
        {
            id: 'id_item_4',
            CategoryId: 'id_category_2',
            name: 'Item 4',
        },
    ],
    'Category 3': [
        {
            id: 'id_item_3',
            CategoryId: 'id_category_3',
            name: 'Item 3',
        },
    ],
};

我目前正在处理一堆承诺:

// Function to retrieve Items for a given CategoryId
const getItemsByCategory = async CategoryId => {
    const Items = await new Promise(resolve => {
        firebase
            .firestore()
            .collection('items')
            .where('CategoryId', '==', CategoryId)
            .orderBy('name', 'ASC')
            .onSnapshot(querySnapshot => {
                const values = [];
                querySnapshot.forEach(doc => {
                    values.push({
                        ...doc.data(),
                        key: doc.id,
                    });
                });
                resolve(values);
            });
    });
    return Items;
};

// Function to actually get all the items, formatted as wanted
export const getItemList = () => {
    return dispatch => { // I'm in a Redux Action
        const Items = {};

        firebase
            .firestore()
            .collection('itemCategories')
            .orderBy('name', 'ASC')
            .get() // Categories can't be changed
            .then(querySnapshot => {
                const Promises = [];
                querySnapshot.forEach(doc => {
                    const category = doc.data().name;

                    const P = new Promise(resolve => {
                        getItemsByCategory(doc.id).then(values => {
                            const result = {
                                category,
                                values,
                            };
                            resolve(result);
                        });
                    });

                    Promises.push(P);
                });

                Promise.all(Promises).then(values => {
                    values.forEach(v => {
                        Items[v.category] = v.values;
                    });

                    // Here I have the formatted items
                    console.log(Items); 
                    //dispatch(setItemList(Items)); // Redux stuff
                });
            });
    }
};

此代码有效,但我认为应该有一些方法可以对其进行优化。我至少可以看到两个问题:

  • 此代码进行(N + 1)次Firestore调用 (其中N是类别数)。由于Firebase会为每个呼叫收费,因此我看不到这种扩展。

  • 我们必须等待N个诺言才能实际显示数据;如果类别数量增加太多,执行时间可能会成为问题。

有什么比我要解决的更好的解决方案了吗?

1 个答案:

答案 0 :(得分:1)

  

由于Firebase会为每个呼叫收费,因此我看不到这种扩展。

Firestore不根据“通话”收费。它收取读取的文档数量和传输的数据量。如果您需要阅读所有类别,则只需为阅读有关该馆藏的许多文件付费。读取您的文档多少次调用都没有关系(例外情况是返回0的查询仍然会读取一次)。

在实践中,等待1个返回N个文档的查询或N个分别返回1个文档的查询之间没有太大区别。您仍然只是在等待N种文档。使用Firestore,不会出现无法很好扩展的查询。限制因素总是将取决于收到的文档数,而不是查询数。您的代码将主要花费时间等待结果转移。当然,在内存中可以容纳的事物数量有一个实用的上限,但是Promise的数量并不会太大。

除此之外,我不明白您为什么在onSnapshot中使用getItemsByCategory。似乎您只想使用get()而不是在侦听器中仅接收一个结果。这样,您的代码就会简单得多。