GraphQL Dataloader vs猫鼬填充

Str*_*der 3 mongoose node.js express graphql

为了执行类似连接的操作,我们可以同时使用GraphQL和Mongoose来实现这一目的。

在问任何问题之前,我想举一个下面的“任务/活动”示例(此代码没有经过测试,仅出于示例的目的而给出):

Task {
  _id,
  title,
  description,
  activities: [{ //Of Activity Type
    _id,
    title
  }]
}
Run Code Online (Sandbox Code Playgroud)

在猫鼬中,我们可以使用populate方法检索与任务相关的活动,如下所示:

const task = await TaskModel.findbyId(taskId).populate('activities');
Run Code Online (Sandbox Code Playgroud)

使用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),
    },
};
Run Code Online (Sandbox Code Playgroud)

我试图查看是否有文章说明哪种方法在性能,资源枯竭等方面更好,但是我找不到这两种方法的比较。

任何见解都会有所帮助,谢谢!

Dan*_*den 5

重要的是要注意,数据加载器不仅是数据模型的接口。尽管数据加载器被吹捧为“基于各种远程数据源的简化且一致的API”,但与GraphQL结合使用时,它们的主要优势在于能够在单个请求的上下文中实现缓存和批处理。此类功能在处理潜在冗余数据的API中很重要(请考虑一下查询用户和每个用户的朋友-多次重访同一用户的可能性很大)。

另一方面,猫鼬的populate方法实际上只是聚合多个MongoDB请求的一种方式。从这个意义上讲,比较两者就像比较苹果和橘子。

可以按照populate问题中的说明进行更公平的比较,而不是按照以下方式添加解析器activities

activities: (task, _, context) => Activity.find().where('id').in(task.activities)
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,问题都取决于您是否将所有数据加载到父解析器中,还是让更深层次的解析器完成一些工作。因为仅针对请求中包含的字段调用解析器,所以这两种方法之间的性能可能会受到重大影响。

如果activities请求该字段,则这两种方法将使服务器和数据库之间的往返次数相同-性能差异可能很小。但是,您的请求可能根本不包含该activities字段。在这种情况下,activities将永远不会调用解析器,并且我们可以通过创建一个单独的activities解析器并在那里进行工作来保存一个或多个数据库请求。

在相关说明中...

据我了解,使用类似的$lookup方法在MongoDB中聚合查询通常比仅使用性能要差populate一些(关于此点的一些对话可以在此处找到)。但是,在关系数据库的上下文中,在考虑上述方法时还需要考虑一些其他因素。那是因为您可以使用联接完成在父解析器中的初始访存,这通常比发出单独的数据库请求要快得多。这意味着要以使“非活动性”字段查询变慢为代价,可以使其他查询变快得多。