Оптимизированный Извлечение данных, сгруппированных по другой Коллекции в Firestore Firebase - PullRequest
0 голосов
/ 11 апреля 2019

В моей базе данных 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 Ответ

1 голос
/ 11 апреля 2019

Поскольку Firebase взимает плату за каждый звонок, я не вижу такого масштабирования.

Firestore не взимает плату за «звонки». Он взимается за количество прочитанных документов и количество переданных данных. Если вам нужно прочитать все категории, то вы просто заплатите за ряд документов, прочитанных об этой коллекции. Неважно, сколько вызовов потребовалось вам, чтобы прочитать эти документы (исключение состоит в том, что запрос, который возвращает 0 результатов, все еще подвергается одному чтению).

Практически нет большой разницы между ожиданием 1 запроса, который возвращает N документов, или N запросов, каждый из которых возвращает 1 документ. Вы все еще ждете N документов в любом случае. С Firestore нет запросов, которые плохо масштабируются. Ограничивающим фактором является всегда , который будет основываться на количестве полученных документов, а не на количестве запросов. Ваш код будет в основном тратить время, просто ожидая передачи результатов. Конечно, существует практическая верхняя граница количества вещей, которые вы можете хранить в памяти, но количество обещаний не будет иметь большого значения.

Кроме того, я не понимаю, почему вы используете onSnapshot в getItemsByCategory. Похоже, вы просто хотите использовать get () вместо получения только одного результата в слушателе. Ваш код будет намного проще.

...