https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7

estore, как я могу получить общее количество документов в коллекции?

Например, если у меня есть

/people
    /123456
        /name - 'John'
    /456789
        /name - 'Jane'

Я хочу спросить, сколько людей у ​​меня есть и получить 2.

Я мог бы сделать запрос к / people и затем получить длину возвращаемых результатов, но это кажется пустой тратой, особенно потому, что я буду делать это для больших наборов данных.

 Ben Cochrane21 мар. 2018 г., 13:01
Я использую db.collection ('products'). Get (). Then (res => console.log (res.size)), который дает мне количество документов в этой коллекции, которое, кажется, работает

Ответы на вопрос(5)

большие коллекции с облачной функцией. Это немного сложно с базой данных Firestore, если вы хотите иметь предварительно рассчитанный счетчик для каждой коллекции.

Код в этом случае не работает в этом случае:

export const customerCounterListener = 
    functions.firestore.document('customers/{customerId}')
    .onWrite((change, context) => {

    // on create
    if (!change.before.exists && change.after.exists) {
        return firestore
                 .collection('metadatas')
                 .doc('customers')
                 .get()
                 .then(docSnap =>
                     docSnap.ref.set({
                         count: docSnap.data().count + 1
                     }))
    // on delete
    } else if (change.before.exists && !change.after.exists) {
        return firestore
                 .collection('metadatas')
                 .doc('customers')
                 .get()
                 .then(docSnap =>
                     docSnap.ref.set({
                         count: docSnap.data().count - 1
                     }))
    }

    return null;
});

Причина в том, что каждый триггер облачного пожарного хранилища должен быть идемпотентным, как сказано в документации пожарного хранилища:https://firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees

Решение

Итак, чтобы предотвратить многократное выполнение вашего кода, вам нужно управлять событиями и транзакциями. Это мой особый способ обработки больших счетчиков коллекции:

const executeOnce = (change, context, task) => {
    const eventRef = firestore.collection('events').doc(context.eventId);

    return firestore.runTransaction(t =>
        t
         .get(eventRef)
         .then(docSnap => (docSnap.exists ? null : task(t)))
         .then(() => t.set(eventRef, { processed: true }))
    );
};

const documentCounter = collectionName => (change, context) =>
    executeOnce(change, context, t => {
        // on create
        if (!change.before.exists && change.after.exists) {
            return t
                    .get(firestore.collection('metadatas')
                    .doc(collectionName))
                    .then(docSnap =>
                        t.set(docSnap.ref, {
                            count: ((docSnap.data() && docSnap.data().count) || 0) + 1
                        }));
        // on delete
        } else if (change.before.exists && !change.after.exists) {
            return t
                     .get(firestore.collection('metadatas')
                     .doc(collectionName))
                     .then(docSnap =>
                        t.set(docSnap.ref, {
                            count: docSnap.data().count - 1
                        }));
        }

        return null;
    });

Варианты использования здесь:

/**
 * Count documents in articles collection.
 */
exports.articlesCounter = functions.firestore
    .document('articles/{id}')
    .onWrite(documentCounter('articles'));

/**
 * Count documents in customers collection.
 */
exports.customersCounter = functions.firestore
    .document('customers/{id}')
    .onWrite(documentCounter('customers'));

Как видите, ключом для предотвращения многократного выполнения является свойство, называемоеEVENTID в объекте контекста. Если функция была обработана много раз для одного и того же события, идентификатор события будет одинаковым во всех случаях. К сожалению, у вас должна быть коллекция "events" в вашей базе данных.

После Дана Ответ: Вы можете иметь отдельный счетчик в своей базе данных и использовать облачные функции для его обслуживания. (Лучшее время написания)

// Example of performing an increment when item is added
module..incrementIncomesCounter = collectionRef.onCreate(event => {
  const counterRef = event.data.ref.firestore.doc('counters/incomes')

  counterRef.get()
  .then(documentSnapshot => {
    const currentCount = documentSnapshot.exists ? documentSnapshot.data().count : 0

    counterRef.set({
      count: Number(currentCount) + 1
    })
    .then(() => {
      console.log('counter has increased!')
    })
  })
})

Этот код показывает вам полный пример того, как это сделать:https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7

вы можете сделать (при условииprivate afs: AngularFirestore вводится в ваш конструктор):

this.afs.collection(myCollection).valueChanges().subscribe( values => console.log(values.length));

Вот,values это массив всех элементов вmyCollection, Вам не нужны метаданные, чтобы вы могли использоватьvalueChanges() метод напрямую.

 Sharan Mohandas01 мар. 2018 г., 08:57
Что если коллекция содержит миллионы документов?

об обновления этих агрегаций, поскольку клиентская сторона предоставляет информацию пользователю, которого вы, возможно, не хотите раскрывать)https://firebase.google.com/docs/firestore/solutions/aggregation

Другой способ (НЕ рекомендуется), который не подходит для больших списков и включает загрузку всего списка: res.size, как в этом примере:

db.collection('products').get().then(res => console.log(res.size))
 justinbc82028 мар. 2018 г., 22:01
Это требует, чтобы вы загрузили всю коллекцию продуктов, чтобы получить размер. Это может быть крайне вредно для больших коллекций
 Ben Cochrane29 мар. 2018 г., 02:35
@ justinbc820 Вы правы - я изменил свой ответ, чтобы рекомендовать агрегации вместоfirebase.google.com/docs/firestore/solutions/aggregation
Решение Вопроса

Вариант 1: Клиентская сторона

Это в основном подход, который вы упомянули. Выбрать все из коллекции и рассчитывать на стороне клиента. Это работает достаточно хорошо для небольших наборов данных, но, очевидно, не работает, если набор данных больше.

Вариант 2: лучшее время записи

При таком подходе вы можете использовать облачные функции для обновления счетчика для каждого добавления и удаления из коллекции.

Это хорошо работает для любого размера набора данных, если добавления / удаления происходят только со скоростью, меньшей или равной 1 в секунду. Это дает вам один документ для чтения, чтобы дать вампочти текущий считать сразу.

Если потребность должна превышать 1 в секунду, вам необходимо реализоватьраспределенные счетчики согласно нашей документации.

Вариант 3: точное время записи

Вместо использования облачных функций в вашем клиенте вы можете обновить счетчик одновременно с добавлением или удалением документа. Это означает, что счетчик также будет текущим, но вам нужно обязательно включать эту логику в любое место, куда вы добавляете или удаляете документы.

Как и в варианте 2, вам нужно будет реализовать распределенные счетчики, если вы хотите увеличить в секунду

 saricden30 дек. 2017 г., 08:33
Просто интересно, есть ли другие способы сделать это в будущем? Что-то простое, например, агрегатная функция SQL COUNT, которая будет работать с большими наборами данных при сохранении хорошей производительности?
 Dan McGrath22 февр. 2018 г., 22:26
@saricden COUNT фактический агрегат имеет много потенциальных проблем производительности. Система Cloud Firestore предназначена для операций, которые сохраняют одинаковые характеристики производительности независимо от размера набора данных, чего нет в COUNT. Мы ищем варианты в будущем, которые найдут баланс.
 Alan Nelson10 авг. 2018 г., 15:09
Почему я не подумал о варианте 3 !! Мое приложение использует 5 осколков для каждого документа, умножая количество операций чтения на 6 каждый раз, когда я получаю документ. Это было действительно неэффективно и стоило слишком много денег. Вариант 3 позволяет мне обновлять общее количество документов каждый раз при увеличении сегмента. Это стоит 2 записи вместо 1, но это действительно стоит на стороне чтения.
 Paul09 дек. 2018 г., 00:20
@DanMcGrath Я новичок в FireStore, но, как я понимаю, вариант 3 проблематичен для всей стороны безопасности клиента. Скажи, что мне может понравиться пост. Но я должен быть в состоянии обновить счетчик. Так что если я злонамеренный пользователь, я могу установить любое значение. Есть ли решение для этого?

Ваш ответ на вопрос