Firebase 使用Firestore云函数计数
我喜欢使用firestore云函数计算子集合中的文档数 我的数据库看起来是这样的:groups/{groupId}/members/{memberId} 我喜欢计算每个组的成员数(memberId)。这意味着每个小组可以有不同数量的成员,他们可以增加或减少灵活性Firebase 使用Firestore云函数计数,firebase,google-cloud-firestore,google-cloud-functions,Firebase,Google Cloud Firestore,Google Cloud Functions,我喜欢使用firestore云函数计算子集合中的文档数 我的数据库看起来是这样的:groups/{groupId}/members/{memberId} 我喜欢计算每个组的成员数(memberId)。这意味着每个小组可以有不同数量的成员,他们可以增加或减少灵活性 我很高兴你的想法:-)。我想有两种可能的方法 1。直接清点收藏的文件 你可以使用类似的属性 这里的主要问题是成本,如果子集合包含大量文档:通过执行此查询,您将为读取子集合的每个文档收取费用 2。另一种方法是为每个子集合维护一些计数器 您
我很高兴你的想法:-)。我想有两种可能的方法 1。直接清点收藏的文件 你可以使用类似的属性 这里的主要问题是成本,如果子集合包含大量文档:通过执行此查询,您将为读取子集合的每个文档收取费用 2。另一种方法是为每个子集合维护一些计数器 您将基于分布式计数器编写两个云函数,,如Firebase文档项所示:。我们在下面的示例中使用3个碎片 首先,在
子collec
子集合中添加新单据时,云函数会增加计数器:
//....
const num_shards = 3;
//....
exports.incrementSubCollecCounter = functions
.firestore.document('groups/{groupId}/members/{memberId}')
.onCreate((snap, context) => {
const groupId = context.params.groupId;
const shard_id = Math.floor(Math.random() * num_shards).toString();
const shard_ref = admin
.firestore()
.collection('shards' + groupId)
.doc(shard_id);
if (!snap.data().counterIncremented) {
return admin.firestore().runTransaction(t => {
return t
.get(shard_ref)
.then(doc => {
if (!doc.exists) {
throw new Error(
'Shard doc #' +
shard_id +
' does not exist.'
);
} else {
const new_count = doc.data().count + 1;
return t.update(shard_ref, { count: new_count });
}
})
.then(() => {
return t.update(snap.ref, {
counterIncremented: true //This is important to have the Function idempotent, see https://cloud.google.com/functions/docs/bestpractices/tips#write_idempotent_functions
});
});
});
} else {
console.log('counterIncremented NOT NULL');
return null;
}
});
exports.decrementSubCollecCounter = functions
.firestore.document('groups/{groupId}/members/{memberId}')
.onDelete((snap, context) => {
const groupId = context.params.groupId;
const shard_id = Math.floor(Math.random() * num_shards).toString();
const shard_ref = admin
.firestore()
.collection('shards' + groupId)
.doc(shard_id);
return admin.firestore().runTransaction(t => {
return t.get(shard_ref).then(doc => {
if (!doc.exists) {
throw new Error(
'Shard doc #' +
shard_id +
' does not exist.'
);
} else {
const new_count = doc.data().count - 1;
return t.update(shard_ref, { count: new_count });
}
});
});
});
然后,当从子集合中删除文档时,第二个云函数将减少计数器:
//....
const num_shards = 3;
//....
exports.incrementSubCollecCounter = functions
.firestore.document('groups/{groupId}/members/{memberId}')
.onCreate((snap, context) => {
const groupId = context.params.groupId;
const shard_id = Math.floor(Math.random() * num_shards).toString();
const shard_ref = admin
.firestore()
.collection('shards' + groupId)
.doc(shard_id);
if (!snap.data().counterIncremented) {
return admin.firestore().runTransaction(t => {
return t
.get(shard_ref)
.then(doc => {
if (!doc.exists) {
throw new Error(
'Shard doc #' +
shard_id +
' does not exist.'
);
} else {
const new_count = doc.data().count + 1;
return t.update(shard_ref, { count: new_count });
}
})
.then(() => {
return t.update(snap.ref, {
counterIncremented: true //This is important to have the Function idempotent, see https://cloud.google.com/functions/docs/bestpractices/tips#write_idempotent_functions
});
});
});
} else {
console.log('counterIncremented NOT NULL');
return null;
}
});
exports.decrementSubCollecCounter = functions
.firestore.document('groups/{groupId}/members/{memberId}')
.onDelete((snap, context) => {
const groupId = context.params.groupId;
const shard_id = Math.floor(Math.random() * num_shards).toString();
const shard_ref = admin
.firestore()
.collection('shards' + groupId)
.doc(shard_id);
return admin.firestore().runTransaction(t => {
return t.get(shard_ref).then(doc => {
if (!doc.exists) {
throw new Error(
'Shard doc #' +
shard_id +
' does not exist.'
);
} else {
const new_count = doc.data().count - 1;
return t.update(shard_ref, { count: new_count });
}
});
});
});
这里,与解决方案1相比,因为我们有3个碎片,所以当您想知道子collec
子集合中的文档数时,您只需要读取3个文档
有关如何初始化分布式计数器的详细信息,请参阅文档。您必须为每个groupId
集合初始化一次(即admin.firestore().collection('shards'+groupId)
)我花了一段时间才开始工作,所以我想我应该将它共享给其他人使用:
'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;
});
你可以看看下面的问题/答案。它并没有完全回答你的问题,但非常接近@RenaudTarnec谢谢,但没有帮助,因为我需要一个无限和准确的文件数量。也许你还有别的想法?Thanks必须保留另一个包含计数的节点。添加节点时,递增计数器;删除节点时,递减计数器。这是一个很小的数据量,拥有“计数器”节点不会真正影响任何事情。谢谢,尝试了它,但收到错误消息:“ReferenceError:groupId未定义为”for line“。collection('shards'+groupId)”。知道吗?您应该验证groupId在const groupId=context.params.groupId行之后的值是否正确;通过执行console.log(groupId)并查看云函数日志中的输出,总是会出现以下错误-不确定原因。错误:碎片文档#0不存在。在进程的t.get.then.doc(/user_code/index.js:119:21)上。_tickDomainCallback(internal/process/next_tick.js:135:7)是的,正如我在回答的底部所说:«查看文档以了解如何初始化分布式计数器的详细信息。您必须为每个groupId集合初始化一次(即admin.firestore().collection('shards'+groupId)»。您必须学习并完全理解此文档在这种情况下使用shards实际上是个坏主意。阅读文档的成本更高,每秒写入的时间不会超过1次。请参阅我的回答: