原生GraphQL分页实现指引求助(mongoose/MongoDB+ES5)
原生GraphQL分页(mongoose/MongoDB + ES5)Resolver实现指南
我完全懂你找不到非框架绑定的GraphQL分页教程的苦恼——大部分资源都盯着Apollo、Relay这些工具,纯原生的实现细节确实藏得深。既然你已经搞定了查询端的配置,那咱们直接聚焦resolver的核心实现,给你两个最常用的分页方案和代码示例:
1. 偏移分页(Offset-based Pagination)
适合数据量不大、需要页码跳转的场景,核心用skip和limit实现。
示例Resolver(ES5)
假设你有一个Post模型,查询定义接受limit(每页条数)和offset(跳过的条数)参数:
var Post = require('./models/Post'); // 对应GraphQL查询的resolver function resolvePosts(root, args) { // 处理默认值,避免参数为空 var limit = args.limit || 10; var offset = args.offset || 0; // 并行查询数据列表和总条数 return Promise.all([ Post.find() .skip(offset) .limit(limit) .exec(), Post.countDocuments().exec() ]).then(function(results) { var posts = results[0]; var totalCount = results[1]; // 返回符合查询定义的结构,比如包含列表和分页信息 return { nodes: posts, totalCount: totalCount, hasNextPage: offset + limit < totalCount }; }); }
2. 游标分页(Cursor-based Pagination)
适合大数据量场景,避免偏移分页的性能问题(skip会扫描前面所有数据),核心用唯一有序字段(比如_id、createdAt)作为游标。
示例Resolver(ES5)
假设查询定义接受first(取前N条)和after(游标,即最后一条数据的_id)参数:
var Post = require('./models/Post'); function resolvePosts(root, args) { var first = args.first || 10; var after = args.after; var query = {}; // 如果有游标,只查询游标之后的数据 if (after) { query._id = { $gt: after }; } return Post.find(query) .sort({ _id: 1 }) // 按_id升序,保证游标有序 .limit(first + 1) // 多查一条,用来判断是否有下一页 .exec() .then(function(posts) { var hasNextPage = posts.length > first; // 去掉多查的那条,返回给前端 var nodes = hasNextPage ? posts.slice(0, first) : posts; // 获取最后一条的_id作为下一页的游标 var endCursor = nodes.length > 0 ? nodes[nodes.length - 1]._id : null; return { nodes: nodes, pageInfo: { hasNextPage: hasNextPage, endCursor: endCursor } }; }); }
关键注意事项
- ES5适配:所有函数用
function声明,变量用var,避免ES6+语法;mongoose的异步操作优先用Promise,比回调更清晰。 - 数据结构匹配:resolver返回的结构必须和你定义的GraphQL类型一致,比如上面的
pageInfo、nodes要对应你的Schema定义。 - 游标字段选择:如果不用
_id,可以选createdAt这类有序字段,但要保证字段唯一(或者结合唯一字段),避免出现重复数据或漏数据。
内容的提问来源于stack exchange,提问作者NetOperator Wibby
相关产品推荐
相关产品推荐

