estructura - Firebase recuento de colección de almacén de fuego
firebase firestore set (10)
Actualización (abril de 2019) - FieldValue.increment (ver solución de colección grande)
Como con muchas preguntas, la respuesta es: depende .
Debe tener mucho cuidado al manejar grandes cantidades de datos en el front-end. Además de hacer que su front-end se sienta lento, Firestore también le cobra $ 0.60 por millón de lecturas que realiza.
Colección pequeña (menos de 100 documentos)
Úselo con cuidado: la experiencia del usuario frontend puede recibir un golpe
Manejar esto en el front end debería estar bien siempre que no esté haciendo demasiada lógica con esta matriz devuelta.
db.collection(''...'').get().then(snap => {
size = snap.size // will return the collection size
});
Colección mediana (100 a 1000 documentos)
Úselo con cuidado: las invocaciones de lectura de Firestore pueden costar mucho
Manejar esto en el front end no es factible ya que tiene demasiado potencial para ralentizar el sistema de los usuarios. Deberíamos manejar este lado del servidor lógico y solo devolver el tamaño.
El inconveniente de este método es que todavía está invocando lecturas de almacén de incendios (igual al tamaño de su colección), que a la larga puede terminar costándole más de lo esperado.
Función de la nube:
...
db.collection(''...'').get().then(snap => {
res.status(200).send({length: snap.size});
});
Interfaz:
yourHttpClient.post(yourCloudFunctionUrl).toPromise().then(snap => {
size = snap.length // will return the collection size
})
Gran colección (más de 1000 documentos)
La solución más escalable
FieldValue.increment ()
A partir de abril de 2019, Firestore ahora permite aumentar los contadores, de forma completamente atómica y sin leer los datos antes . Esto garantiza que tengamos los valores de contador correctos incluso cuando se actualizan desde múltiples fuentes simultáneamente (previamente resueltas mediante transacciones), al tiempo que se reduce la cantidad de lecturas de bases de datos que realizamos.
Al escuchar cualquier documento eliminado o creado, podemos agregar o eliminar de un campo de recuento que se encuentra en la base de datos.
Vea los documentos de la tienda de incendios: contadores distribuidos O eche un vistazo a la agregación de datos por Jeff Delaney. Sus guías son realmente fantásticas para cualquiera que use AngularFire, pero sus lecciones también deberían trasladarse a otros marcos.
Función de la nube:
export const documentWriteListener =
functions.firestore.document(''collection/{documentUid}'')
.onWrite((change, context) => {
if (!change.before.exists) {
// New document Created : add one to count
db.doc(docRef).update({numberOfDocs: FieldValue.increment(1)});
} else if (change.before.exists && change.after.exists) {
// Updating existing document : Do nothing
} else if (!change.after.exists) {
// Deleting document : subtract one from count
db.doc(docRef).update({numberOfDocs: FieldValue.increment(-1)});
}
return;
});
Ahora en la interfaz puede consultar este campo numberOfDocs para obtener el tamaño de la colección.
¿Es posible contar cuántos elementos tiene una colección usando la nueva base de datos Firebase, Firestore?
Si es así, ¿cómo hago eso?
Estoy de acuerdo con @Matthew, costará mucho si realiza dicha consulta.
[CONSEJO PARA DESARROLLADORES ANTES DE COMENZAR SUS PROYECTOS]
Como hemos previsto esta situación al principio, en realidad podemos hacer una colección, a saber, contadores con un documento para almacenar todos los contadores en un campo con
number
tipo.
Por ejemplo:
Para cada operación CRUD en la colección, actualice el documento de contador:
- Cuando crea una nueva colección / subcolección: (+1 en el contador) [1 operación de escritura]
- Cuando elimina una colección / subcolección: (-1 en el contador) [1 operación de escritura]
- Cuando actualice una colección / subcolección existente, no haga nada en el documento del contador: (0)
- Cuando lea una colección / subcolección existente, no haga nada en el documento del contador: (0)
La próxima vez, cuando desee obtener el número de recopilación, solo necesita consultar / señalar el campo del documento. [1 operación de lectura]
Además, puede almacenar el nombre de las colecciones en una matriz, pero esto será complicado, la condición de la matriz en firebase se muestra a continuación:
// we send this
[''a'', ''b'', ''c'', ''d'', ''e'']
// Firebase stores this
{0: ''a'', 1: ''b'', 2: ''c'', 3: ''d'', 4: ''e''}
// since the keys are numeric and sequential,
// if we query the data, we get this
[''a'', ''b'', ''c'', ''d'', ''e'']
// however, if we then delete a, b, and d,
// they are no longer mostly sequential, so
// we do not get back an array
{2: ''c'', 4: ''e''}
Entonces, si no va a eliminar la colección, en realidad puede usar la matriz para almacenar la lista de nombres de colecciones en lugar de consultar toda la colección cada vez.
¡Espero eso ayude!
Hasta donde sé, no hay una solución integrada para esto y solo es posible en el nodo sdk en este momento. Si tienes un
db.collection (''someCollection'')
puedes usar
.select ([campos])
para definir qué campo desea seleccionar. Si hace una selección vacía (), solo obtendrá una matriz de referencias de documentos.
ejemplo:
db.collection(''someCollection'').select().get().then( (snapshot) => console.log(snapshot.docs.length) );
¡Esta solución es solo una optimización para el peor de los casos de descarga de todos los documentos y no se escala en grandes colecciones!
También eche un vistazo a esto:
Cómo obtener un recuento de la cantidad de documentos en una colección con Cloud Firestore
Incremente un contador usando admin.firestore.FieldValue.increment :
exports.onInstanceCreate = functions.firestore.document(''projects/{projectId}/instances/{instanceId}'')
.onCreate((snap, context) =>
db.collection(''projects'').doc(context.params.projectId).update({
instanceCount: admin.firestore.FieldValue.increment(1),
})
);
exports.onInstanceDelete = functions.firestore.document(''projects/{projectId}/instances/{instanceId}'')
.onDelete((snap, context) =>
db.collection(''projects'').doc(context.params.projectId).update({
instanceCount: admin.firestore.FieldValue.increment(-1),
})
);
En este ejemplo, incrementamos un campo
instanceCount
en el proyecto cada vez que se agrega un documento a la subcolección de
instances
.
Si el campo aún no existe, se creará e incrementará a 1.
El incremento es transaccional internamente, pero debe usar un contador distribuido si necesita aumentar con más frecuencia que cada 1 segundo.
A menudo es preferible implementar
onCreate
y
onDelete
lugar de
onWrite
ya que llamará a
onWrite
para obtener actualizaciones, lo que significa que está gastando más dinero en invocaciones de funciones innecesarias (si actualiza los documentos de su colección).
La forma más sencilla de hacerlo es leer el tamaño de una "consulta instantánea".
db.collection("cities").get().then(function(querySnapshot) {
console.log(querySnapshot.size);
});
También puede leer la longitud de la matriz de documentos dentro de "querySnapshot".
querySnapshot.docs.length;
O si un "querySnapshot" está vacío leyendo el valor vacío, que devolverá un valor booleano.
querySnapshot.empty;
Me tomó un tiempo hacer que esto funcionara en base a algunas de las respuestas anteriores, así que pensé en compartirlo para que otros lo usen. Espero que sea útil.
''use strict'';
const functions = require(''firebase-functions'');
const admin = require(''firebase-admin'');
admin.initializeApp();
const db = admin.firestore();
exports.countDocumentsChange = functions.firestore.document(''library/{categoryId}/documents/{documentId}'').onWrite((change, context) => {
const categoryId = context.params.categoryId;
const categoryRef = db.collection(''library'').doc(categoryId)
let FieldValue = require(''firebase-admin'').firestore.FieldValue;
if (!change.before.exists) {
// new document created : add one to count
categoryRef.update({numberOfDocs: FieldValue.increment(1)});
console.log("%s numberOfDocs incremented by 1", categoryId);
} else if (change.before.exists && change.after.exists) {
// updating existing document : Do nothing
} else if (!change.after.exists) {
// deleting document : subtract one from count
categoryRef.update({numberOfDocs: FieldValue.increment(-1)});
console.log("%s numberOfDocs decremented by 1", categoryId);
}
return 0;
});
No hay una opción directa disponible.
No puede hacer
db.collection("CollectionName").count()
.
A continuación se muestran las dos formas en que puede encontrar el recuento de la cantidad de documentos dentro de una colección.
1: - Obtenga todos los documentos de la colección y luego obtenga su tamaño. (No es la mejor solución)
db.collection("CollectionName").get().subscribe(doc=>{
console.log(doc.size)
})
Al usar el código anterior, las lecturas de sus documentos serán iguales al tamaño de los documentos dentro de una colección y esa es la razón por la cual uno debe evitar el uso de la solución anterior.
2: - Cree un documento separado en su colección que almacenará el número de documentos en la colección. (La mejor solución)
db.collection("CollectionName").doc("counts")get().subscribe(doc=>{
console.log(doc.count)
})
Arriba creamos un documento con nombres de conteos para almacenar toda la información de conteo. Puede actualizar el documento de conteo de la siguiente manera: -
- Crear disparadores de almacén de incendios en los recuentos de documentos
- Incremente la propiedad de conteo del documento de conteo cuando se crea un nuevo documento.
- Disminuya la propiedad de conteo del documento de conteo cuando se elimina un documento.
wrt price (Document Read = 1) y recuperación rápida de datos, la solución anterior es buena.
No, no hay soporte integrado para consultas de agregación en este momento. Sin embargo, hay algunas cosas que podrías hacer.
El primero está documentado aquí . Puede usar transacciones o funciones en la nube para mantener información agregada:
Este ejemplo muestra cómo usar una función para realizar un seguimiento del número de clasificaciones en una subcolección, así como la clasificación promedio.
exports.aggregateRatings = firestore
.document(''restaurants/{restId}/ratings/{ratingId}'')
.onWrite(event => {
// Get value of the newly added rating
var ratingVal = event.data.get(''rating'');
// Get a reference to the restaurant
var restRef = db.collection(''restaurants'').document(event.params.restId);
// Update aggregations in a transaction
return db.transaction(transaction => {
return transaction.get(restRef).then(restDoc => {
// Compute new number of ratings
var newNumRatings = restDoc.data(''numRatings'') + 1;
// Compute new average rating
var oldRatingTotal = restDoc.data(''avgRating'') * restDoc.data(''numRatings'');
var newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;
// Update restaurant info
return transaction.update(restRef, {
avgRating: newAvgRating,
numRatings: newNumRatings
});
});
});
});
La solución que mencionó jbb también es útil si solo desea contar documentos con poca frecuencia.
Asegúrese de usar la instrucción
select()
para evitar descargar todos los documentos (eso es mucho ancho de banda cuando solo necesita un recuento).
select()
solo está disponible en los SDK del servidor por ahora, por lo que la solución no funcionará en una aplicación móvil.
Tenga cuidado al contar el número de documentos para grandes colecciones . 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.
firebaseFirestore.collection("...").addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
int Counter = documentSnapshots.size();
}
});