database 如何使用 Cloud Firestore 获取集合中文档的数量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46553314/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get a count of number of documents in a collection with Cloud Firestore
提问by justinbc820
In Firestore, how can I get the total number of documents in a collection?
在 Firestore 中,如何获取集合中的文档总数?
For instance if I have
例如,如果我有
/people
/123456
/name - 'John'
/456789
/name - 'Jane'
I want to query how many people I have and get 2.
我想查询我有多少人并得到 2。
I could do a query on /people and then get the length of the returned results, but that seems a waste, especially because I will be doing this on larger datasets.
我可以对 /people 进行查询,然后获取返回结果的长度,但这似乎是一种浪费,尤其是因为我将在更大的数据集上执行此操作。
采纳答案by Dan McGrath
You currently have 3 options:
您目前有 3 个选择:
Option 1: Client side
选项 1:客户端
This is basically the approach you mentioned. Select all from collection and count on the client side. This works well enough for small datasets but obviously doesn't work if the dataset is larger.
这基本上就是你提到的方法。从集合中选择所有并在客户端计数。这对于小数据集来说效果很好,但如果数据集较大,则显然不起作用。
Option 2: Write-time best-effort
选项 2:写入时尽力而为
With this approach, you can use Cloud Functions to update a counter for each addition and deletion from the collection.
通过这种方法,您可以使用 Cloud Functions 为集合中的每次添加和删除更新计数器。
This works well for any dataset size, as long as additions/deletions only occur at the rate less than or equal to 1 per second. This gives you a single document to read to give you the almost currentcount immediately.
这适用于任何数据集大小,只要添加/删除仅以小于或等于每秒 1 的速率发生。这为您提供了一个可供阅读的文档,以便立即为您提供几乎当前的计数。
If need need to exceed 1 per second, you need to implement distributed counters per our documentation.
如果需要超过每秒 1 个,您需要根据我们的文档实现分布式计数器。
Option 3: Write-time exact
选项 3:写入时间精确
Rather than using Cloud Functions, in your client you can update the counter at the same time as you add or delete a document. This means the counter will also be current, but you'll need to make sure to include this logic anywhere you add or delete documents.
在您的客户端中,您可以在添加或删除文档的同时更新计数器,而不是使用 Cloud Functions。这意味着计数器也将是最新的,但您需要确保在添加或删除文档的任何地方都包含此逻辑。
Like option 2, you'll need to implement distributed counters if you want to exceed per second
与选项 2 一样,如果您想超过每秒,则需要实现分布式计数器
回答by Johan Chouquet
If you use AngulareFire2, you can do (assuming private afs: AngularFirestoreis injected in your constructor):
如果您使用 AngulareFire2,则可以执行以下操作(假设private afs: AngularFirestore已在构造函数中注入):
this.afs.collection(myCollection).valueChanges().subscribe( values => console.log(values.length));
Here, valuesis an array of all items in myCollection. You don't need metadata so you can use valueChanges()method directly.
这里,values是 中所有项目的数组myCollection。您不需要元数据,因此您可以valueChanges()直接使用方法。
回答by Ben Cochrane
Aggregations are the way to go (firebase functions looks like the recommended way to update these aggregations as client side exposes info to the user you may not want exposed) https://firebase.google.com/docs/firestore/solutions/aggregation
聚合是可行的方法(firebase 函数看起来是更新这些聚合的推荐方法,因为客户端向您可能不希望公开的用户公开信息)https://firebase.google.com/docs/firestore/solutions/aggregation
Another way (NOT recommended) which is not good for large lists and involves downloading the whole list: res.size like this example:
另一种方式(不推荐)不适合大型列表,需要下载整个列表: res.size 像这个例子:
db.collection('products').get().then(res => console.log(res.size))
回答by Ferran Verdés
Be careful counting number of documents for large collectionswith a cloud function. It is a little bit complex with firestore database if you want to have a precalculated counter for every collection.
使用云功能计算大型集合的文档数量时要小心。如果您想为每个集合预先计算一个计数器,那么 Firestore 数据库有点复杂。
Code like this doesn't work in this case:
在这种情况下,这样的代码不起作用:
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;
});
The reason is because every cloud firestore trigger has to be idempotent, as firestore documentation say: https://firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees
原因是因为每个云 Firestore 触发器都必须是幂等的,正如 Firestore 文档所说:https: //firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees
Solution
解决方案
So, in order to prevent multiple executions of your code, you need to manage with events and transactions. This is my particular way to handle large collection counters:
因此,为了防止您的代码多次执行,您需要管理事件和事务。这是我处理大型集合计数器的特殊方法:
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 cases here:
用例在这里:
/**
* 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'));
As you can see, the key to prevent multiple execution is the property called eventIdin the context object. If the function has been handled many times for the same event, the event id will be the same in all cases. Unfortunately, you must have "events" collection in your database.
如您所见,防止多次执行的关键是上下文对象中名为eventId的属性。如果该函数已针对同一事件多次处理,则事件 ID 在所有情况下都相同。不幸的是,您的数据库中必须有“事件”集合。
回答by Nikhil
Please check below answer I found on another thread. Your count should be atomic. Its required to use FieldValue.increment()function in such case.
请检查我在另一个线程上找到的以下答案。您的计数应该是原子的。在这种情况下需要使用FieldValue.increment()函数。
回答by Saint Play
Following Dan Answer: You can have a separated counter in your database and use Cloud Functions to maintain it. (Write-time best-effort)
按照 Dan 的回答:您可以在数据库中有一个单独的计数器并使用 Cloud Functions 来维护它。(写时尽力而为)
// 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!')
})
})
})
This code shows you the complete example of how to do it: https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7
此代码向您展示了如何执行此操作的完整示例:https: //gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7

