我有一个companies
的集合,并且在每个公司文档中,我都有一个appointments
的集合。我想在一个云函数中循环访问所有appointments
中的所有companies
,所以我在使用以下集合组查询:
db.collectionGroup('appointments')
.get()
.then((querySnapshot: any) => {
querySnapshot.forEach((appointmentDoc: any) => {
const appointment: Appointment = appointmentDoc.data();
appointmentDoc.ref.parent.parent.get().then((companyDoc: any) => {
const company: Company = companyDoc.data();
...
});
});
});
如您所见,在每次迭代中,我也都获得了约会来自的公司的数据。这可行,但我担心性能。如果我有500个约会,那么此方法基本上不是对数据库进行501调用(对约会进行1次调用,然后获取所有500个约会的公司数据)吗?有没有一种更好的方法可以访问该父数据,所以我不会进行所有这些额外的调用?如果我能够以一种可扩展的方式做到这一点,那就太好了。
答案 0 :(得分:1)
无法与db.collection.aggregate([
/** Filter docs based on criteria */
{
$match: {
passport: 123
}
},
/** Transform fields into required form */
{
$project: {
name: "$nombre",
lastname: "$apellido"
}
}
])
集合中的文档同时获取父文档。
您唯一可以做的就是将文档ID分成10个批次,然后对它们进行IN
query。但是我怀疑是否值得付出努力,因为电汇流量可能几乎相同。
请注意,尽管性能通常与通话次数并不线性相关,所以请在尝试对其进行优化之前进行测试。另请参见Google Firestore - how to get document by multiple ids in one round trip?。
也:考虑一下为什么一次需要500个文档。通常,您将需要加载筛选的数据,这似乎还很多。有关Firestore中数据建模的一般提示,我推荐Getting to know Cloud Firestore的第一集。
答案 1 :(得分:1)
Firestore实际上不会根据查询数向您收费。它基于文档读取次数。因此,如果您有500个约会,那么您的代码将要读取1000个文档,因为对于每个约会文档,它都读取一次公司文档。
您可以做的只是对每个公司文档只读取一次总计,而对于该公司的每次约会都不会读取一次 。您可以使用以下方法在内存中维护缓存:
// cache of companies identified by their document ID
const companies: { [key: string]: Company } = {}
db.collectionGroup('appointments')
.get()
.then((querySnapshot: any) => {
querySnapshot.forEach((appointmentDoc: any) => {
const appointment: Appointment = appointmentDoc.data();
const parentRef = appointmentDoc.ref.parent.parent
const companyId = parentRef.id
let company: Company
if (companies[companyId]) {
company = companies[companyId]
// work with cached company here
}
else {
parentRef.get().then((companyDoc: any) => {
company: Company = companyDoc.data();
companies[companyId] = company
// work with queried company here
});
}
});
});
尽管这是不完整的,因为内部查询仍然是异步的,并且将继续以约会迭代器可以运行的速度查询公司。您将必须以某种方式对内部查询进行序列化,或者按公司ID对约会进行分组,然后对这些组进行迭代,以免多次获取公司文档。
但是我希望您能想到使用内存缓存可以节省文档读取次数。
答案 2 :(得分:1)
我使用的是非常分层的结构,看起来会遇到类似的问题,但是...
...对于像Firestore这样的NoSQL数据库,您必须放弃DRY的SQL要求。如果数据是静态的(例如,您实际需要进行约会的任何“公司”数据),则您绝对可以并且应该复制该数据。
例如,您可以很简单地在约会文档中添加以下结构:
appointmentSchema = {
....
....
company: {
id: {string},
name: {string},
location: {string}
}
}
是的,这使用存储空间。所以? Firestore通常不收取少量额外存储空间的费用,而收取新副本的费用 。由于此数据不会动态更改,因此在创建约会文档时将其添加到约会文档中效率更高。
文件提取应保留用于动态数据。
答案 3 :(得分:0)
另一点:文档的refPath是一个字符串,代表完全限定的'/'分隔的文档路径:
root/topcollection/topdocumentId/nextcollection/nextdocumentId/bottomcollection/bottomdocumentId
...,您可以直接解析此字符串以在文档路径的 up 任意位置找到集合名称和documentId。我也经常使用它。