query number firestore collection database firebase google-cloud-firestore

database - collection - firestore number of documents



Cómo obtener un recuento de la cantidad de documentos en una colección con Cloud Firestore (5)

En Firestore, ¿cómo puedo obtener el número total de documentos en una colección?

Por ejemplo si tengo

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

Quiero consultar cuántas personas tengo y obtener 2.

Podría hacer una consulta en / people y luego obtener la longitud de los resultados devueltos, pero eso parece un desperdicio, especialmente porque lo haré en conjuntos de datos más grandes.


Actualmente tienes 3 opciones:

Opción 1: lado del cliente

Este es básicamente el enfoque que mencionaste. Seleccione todo de la colección y cuente en el lado del cliente. Esto funciona lo suficientemente bien para conjuntos de datos pequeños, pero obviamente no funciona si el conjunto de datos es más grande.

Opción 2: mejor esfuerzo en tiempo de escritura

Con este enfoque, puede usar Cloud Functions para actualizar un contador para cada adición y eliminación de la colección.

Esto funciona bien para cualquier tamaño de conjunto de datos, siempre que las adiciones / eliminaciones solo ocurran a una velocidad menor o igual a 1 por segundo. Esto le da un solo documento para leer para darle el recuento casi actual de inmediato.

Si necesita exceder 1 por segundo, debe implementar contadores distribuidos según nuestra documentación .

Opción 3: tiempo de escritura exacto

En lugar de usar Cloud Functions, en su cliente puede actualizar el contador al mismo tiempo que agrega o elimina un documento. Esto significa que el contador también estará actualizado, pero deberá asegurarse de incluir esta lógica en cualquier lugar donde agregue o elimine documentos.

Al igual que la opción 2, deberá implementar contadores distribuidos si desea exceder por segundo


Las agregaciones son el camino a seguir (las funciones de firebase se parecen a la forma recomendada de actualizar estas agregaciones ya que el lado del cliente expone información al usuario que quizás no desee exponer) https://firebase.google.com/docs/firestore/solutions/aggregation

Otra forma (NO recomendada) que no es buena para listas grandes y consiste en descargar la lista completa: res.size como este ejemplo:

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


Si usa AngulareFire2, puede hacer (suponiendo private afs: AngularFirestore se inyecta en su constructor):

db.collection(''products'').get().then(res => console.log(res.size))

Aquí, values es una matriz de todos los elementos en myCollection . No necesita metadatos para poder usar el método valueChanges() directamente.


Tenga cuidado al contar el número de documentos para grandes colecciones con una función en la nube. Es un poco complejo con la base de datos de firestore si desea tener un contador precalculado para cada colección.

Un código como este no funciona en este caso:

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; });

La razón es porque cada desencadenante del almacén de incendios de la nube debe ser idempotente, como dice la documentación del almacén de incendios: https://firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees

Solución

Por lo tanto, para evitar múltiples ejecuciones de su código, debe administrar eventos y transacciones. Esta es mi forma particular de manejar grandes contadores de colección:

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; });

Use los casos aquí:

/** * 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''));

Como puede ver, la clave para evitar la ejecución múltiple es la propiedad llamada eventId en el objeto de contexto. Si la función se ha manejado muchas veces para el mismo evento, la identificación del evento será la misma en todos los casos. Desafortunadamente, debe tener una colección de "eventos" en su base de datos.


Siguiente Dan Answer : Puede tener un contador separado en su base de datos y usar Cloud Functions para mantenerlo. ( Tiempo de escritura mejor esfuerzo )

// 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!'') }) }) })

Este código le muestra el ejemplo completo de cómo hacerlo: https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7