为了执行类似连接的操作,我们可以同时使用 GraphQL 和 Mongoose 来实现这一目标。
在问任何问题之前,我想给出以下任务/事件示例(此代码均未经过测试,只是为了示例而给出):
Task {
_id,
title,
description,
activities: [{ //Of Activity Type
_id,
title
}]
}
在 mongoose 中,我们可以使用 populate 检索与任务相关的事件。方法,像这样:
const task = await TaskModel.findbyId(taskId).populate('activities');
使用 GraphQL 和 Dataloader,我们可以获得相同的结果:
const DataLoader = require('dataloader');
const getActivitiesByTask = (taskId) => await ActivityModel.find({task: taskId});
const dataloaders = () => ({
activitiesByTask: new DataLoader(getActivitiesByTask),
});
// ...
// SET The dataloader in the context
// ...
//------------------------------------------
// In another file
const resolvers = {
Query: {
Task: (_, { id }) => await TaskModel.findbyId(id),
},
Task: {
activities: (task, _, context) => context.dataloaders.activitiesByTask.load(task._id),
},
};
我试图查看是否有任何文章说明哪种方法在性能、资源耗尽等方面更好,但我没有找到这两种方法的任何比较。
任何见解都会有所帮助,谢谢!
最佳答案
重要的是要注意数据加载器不仅仅是数据模型的接口(interface)。虽然数据加载器被吹捧为“针对各种远程数据源的简化且一致的 API”——它们与 GraphQL 结合使用时的主要好处在于能够在单个请求的上下文中实现缓存和批处理。这种功能在处理潜在冗余数据的 API 中很重要(想想查询用户和每个用户的 friend ——很有可能多次重新获取同一用户)。
另一方面,mongoose 的populate
方法实际上只是聚合多个MongoDB 请求的一种方式。从这个意义上说,比较两者就像比较苹果和橙子。
更公平的比较可能是使用 populate
如您的问题所示,而不是按照以下行为 activities
添加解析器:
activities: (task, _, context) => Activity.find().where('id').in(task.activities)
无论哪种方式,问题都归结为您是在父解析器中加载所有数据,还是让解析器进一步向下做一些工作。 因为仅针对请求中包含的字段调用解析器,所以这两种方法之间可能会对性能产生重大影响。
如果请求 activities
字段,两种方法将在服务器和数据库之间进行相同次数的往返——性能差异可能很小。但是,您的请求可能根本不包含 activities
字段。在这种情况下,永远不会调用 activities
解析器,我们可以通过创建一个单独的 activities
解析器并在那里完成工作来保存一个或多个数据库请求。
在相关说明中...
据我所知,在 MongoDB 中使用 $lookup
之类的方法聚合查询通常比仅使用 populate
的性能要差(关于这一点的一些对话可以在 here 中找到) .然而,在关系数据库的上下文中,在考虑上述方法时还需要考虑其他注意事项。那是因为您在父解析器中的初始提取可以使用连接来完成,这通常比发出单独的数据库请求要快得多。这意味着以降低无事件字段查询的速度为代价,您可以使其他查询显着加快。
关于node.js - GraphQL 数据加载器与 Mongoose 填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52668154/