FaunaDB集合Ref关联字段填充及分页查询实现方法
FaunaDB分页查询时填充Ref关联集合数据实现
你存的companyId本身就是FaunaDB原生Ref类型,不需要额外做ID转换,直接用FQL内置能力在数据库查询层一次性完成关联填充即可,完全避免应用层循环查导致的N+1性能问题,10万条user数据的量级下查询性能和普通单表分页没有明显差距。
方案1:常规分页场景直接关联(单页100条以内推荐)
分页拿到当前页的user数据后,遍历每条记录时直接拉取关联的company文档,和原user数据合并后返回,整个过程在数据库端执行,没有额外网络开销。
示例FQL:
Paginate( Documents(Collection("user")), { size: 20, // 按需调整单页条数 // after: 游标值 // 查下一页的时候把上一次返回的after值传进来即可 } ) .pipe( Lambda("userRefs", Map(Var("userRefs"), Lambda("userRef", Let( { userDoc: Get(Var("userRef")), companyDoc: Get(Select(["data", "companyId"], Var("userDoc"))) }, // 合并数据,保留原user所有字段,新增company字段存关联公司完整数据 Merge( Var("userDoc"), { data: Merge( Select(["data"], Var("userDoc")), { company: Var("companyDoc") } ) } ) ) ) ) ) )
这个查询返回的分页结构和普通单表分页完全一致,直接取返回结果里的after字段做下一页查询的入参就行,不需要改原有分页逻辑。
方案2:大页场景批量拉取优化(单页超过100条推荐)
如果单页条数设置得比较大,可以先把当前页所有user关联的company Ref提取去重,一次性批量拉取所有关联company后做映射匹配,比逐条Get的性能更高。
示例FQL:
Paginate( Documents(Collection("user")), { size: 100 } ) .pipe( Lambda("userRefs", Let( { // 批量拉取当前页所有user文档 userList: Map(Var("userRefs"), Lambda("ref", Get(Var("ref")))), // 提取所有company Ref并去重,避免重复拉取同一条company数据 companyRefs: Distinct( Map(Var("userList"), Lambda("user", Select(["data", "companyId"], Var("user")))) ), // 批量拉取company,组装成 Ref值->文档 的映射表 companyMap: ToObject( Map(Var("companyRefs"), Lambda("cRef", [ToString(Var("cRef")), Get(Var("cRef"))])) ) }, // 遍历user列表,从映射表匹配对应company数据合并 Map(Var("userList"), Lambda("user", Let( { currentCompany: Select( [ToString(Select(["data", "companyId"], Var("user")))], Var("companyMap") ) }, Merge( Var("user"), { data: Merge(Select(["data"], Var("user")), { company: Var("currentCompany") }) } ) ) ) ) ) ) )
注意事项
- 绝对不要在应用层做循环查询:先查当前页user列表,再循环N次请求数据库查每个user对应的company,这种N+1查询的网络开销极高,数据量上来之后延迟会完全不可用。
- 不需要返回company全量字段时,直接用
Select提取需要的字段即可,比如只需要公司名就写Select(["data", "companyName"], Get(Var("companyRef"))),能进一步降低查询延迟和返回包大小。 - 如果分页查询带筛选条件,一定要给筛选字段建对应索引,10万条数据量级下全表扫描性能很差,走索引的话分页查询速度会有量级提升。
内容的提问来源于stack exchange,提问作者Rajesh Gauswami
相关产品推荐
相关产品推荐

