GraphQL中如何优化列表与数量查询?避免重复数据库请求
优化GraphQL字段查询:单次数据库请求获取列表与数量
当然可以实现!你完全不需要两次查询数据库就能同时提供states列表和statesQuantity计数,还能保持两个字段的独立性。这里有几种实用方案,按推荐度排序:
方案1:缓存查询结果到父对象(最简单直接)
核心思路是:第一次查询states列表后,把结果临时缓存到父对象sysCountry上,statesQuantity直接读取缓存列表的长度。如果用户只请求statesQuantity,我们也可以单独执行更高效的count查询,避免不必要的列表读取。
const xType = new GraphQLObjectType({ name: 'SysCountry', description: 'SysCountry API', fields: () => ({ states: { type: new GraphQLList(sysCountryStateType), async resolve(sysCountry) { // 优先返回已缓存的结果 if (sysCountry._cachedStates) { return sysCountry._cachedStates; } // 查询列表并缓存 const states = await SysCountryStateModel.find({ sysCountryId: sysCountry._id }).exec(); sysCountry._cachedStates = states; return states; } }, statesQuantity: { type: GraphQLInt, async resolve(sysCountry) { // 如果已经缓存了列表,直接取长度 if (sysCountry._cachedStates) { return sysCountry._cachedStates.length; } // 如果只请求了数量,执行更轻量的count查询 return SysCountryStateModel.count({ sysCountryId: sysCountry._id }).exec(); } } }) });
这个方案的好处是:
- 当同时请求两个字段时,只触发一次数据库查询
- 当仅请求
statesQuantity时,用更高效的count操作,避免加载整个列表
方案2:利用GraphQL Info参数精准判断请求字段
如果想进一步优化,可以通过GraphQL的info参数判断当前请求的字段集合,动态选择查询策略:
const xType = new GraphQLObjectType({ name: 'SysCountry', description: 'SysCountry API', fields: () => ({ states: { type: new GraphQLList(sysCountryStateType), async resolve(sysCountry, _, __, info) { const states = await SysCountryStateModel.find({ sysCountryId: sysCountry._id }).exec(); // 如果请求了statesQuantity,提前缓存结果 const isQuantityRequested = info.fieldNodes.some(node => node.name.value === 'statesQuantity'); if (isQuantityRequested) { sysCountry._cachedStates = states; } return states; } }, statesQuantity: { type: GraphQLInt, async resolve(sysCountry, _, __, info) { // 检查缓存 if (sysCountry._cachedStates) { return sysCountry._cachedStates.length; } // 检查是否同时请求了states const isStatesRequested = info.fieldNodes.some(node => node.name.value === 'states'); if (!isStatesRequested) { // 仅请求数量时用count查询 return SysCountryStateModel.count({ sysCountryId: sysCountry._id }).exec(); } // 兜底:如果states还没查询,先获取列表再返回长度 const states = await SysCountryStateModel.find({ sysCountryId: sysCountry._id }).exec(); sysCountry._cachedStates = states; return states.length; } } }) });
这个方案更灵活,能根据用户的实际请求动态调整查询逻辑,最大化性能。
方案3:MongoDB聚合查询(适合复杂场景)
如果你的业务需要分页、筛选等复杂操作,可以用MongoDB的$facet聚合操作,一次查询同时获取列表和总数:
const xType = new GraphQLObjectType({ name: 'SysCountry', description: 'SysCountry API', fields: () => ({ states: { type: new GraphQLList(sysCountryStateType), async resolve(sysCountry) { const [result] = await SysCountryStateModel.aggregate([ { $match: { sysCountryId: sysCountry._id } }, // 可以在这里添加分页、排序、筛选逻辑 { $facet: { states: [{ $skip: 0 }, { $limit: 100 }], // 示例分页 total: [{ $count: 'quantity' }] }} ]).exec(); // 缓存总数 sysCountry._cachedStatesCount = result.total[0]?.quantity || 0; return result.states; } }, statesQuantity: { type: GraphQLInt, async resolve(sysCountry) { if (sysCountry._cachedStatesCount !== undefined) { return sysCountry._cachedStatesCount; } // 仅请求数量时回退到count查询 return SysCountryStateModel.count({ sysCountryId: sysCountry._id }).exec(); } } }) });
这种方式适合需要对列表做复杂处理的场景,一次聚合就能完成所有数据准备工作。
内容的提问来源于stack exchange,提问作者Nelson La Rocca
相关产品推荐
相关产品推荐

