Вопрос:

Как получить количество документов в коллекции с Cloud Firestore

database firebase google-cloud-firestore

9673 просмотра

6 ответа

200 Репутация автора

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

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

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

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

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

Автор: justinbc820 Источник Размещён: 03.10.2017 08:58

Ответы (6)


30 плюса

27375 Репутация автора

Решение

В настоящее время у вас есть 3 варианта:

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

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

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

С помощью этого подхода вы можете использовать функции Cloud для обновления счетчика для каждого добавления и удаления из коллекции.

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

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

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

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

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

Автор: Dan McGrath Размещён: 03.10.2017 10:04

0 плюса

653 Репутация автора

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

// Example of performing an increment when item is added
module.exports.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

Автор: Saint Play Размещён: 25.01.2018 09:53

2 плюса

184 Репутация автора

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

this.afs.collection(myCollection).valueChanges().subscribe( values => console.log(values.length)); Здесь valuesнаходится массив всех элементов myCollection. Вам не нужны метаданные, поэтому вы можете valueChanges()напрямую использовать метод.

Автор: Johan Chouquet Размещён: 31.01.2018 10:22

1 плюс

1576 Репутация автора

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

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

db.collection('products').get().then(res => console.log(res.size))
Автор: Ben Cochrane Размещён: 21.03.2018 12:03

0 плюса

56 Репутация автора

Будьте осторожны, подсчитывая количество документов для больших коллекций с облачной функцией. Это немного сложнее с базой данных 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;
});

Причина в том, что каждый триггер для облачных огней должен быть идемпотентным, поскольку документация firestore говорит: 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 в объекте context. Если функция была обработана много раз для одного и того же события, идентификатор события будет одинаковым во всех случаях. К сожалению, в вашей базе данных должна быть коллекция событий.

Автор: Ferran Verdés Размещён: 25.10.2018 06:34

0 плюса

1 Репутация автора

const collection = await admin.firestore().collection("collection_name").get();
console.log(collection.size);
Автор: google ogiwara Размещён: 23.11.2018 01:57
Вопросы из категории :
32x32