GraphQL Dataloader vs猫鼬填充

时间:2018-10-05 14:44:26

标签: node.js express mongoose graphql

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

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

Task {
  _id,
  title,
  description,
  activities: [{ //Of Activity Type
    _id,
    title
  }]
}

在猫鼬中,我们可以使用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),
    },
};

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

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

1 个答案:

答案 0 :(得分:5)

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

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

更公平的比较可能是使用问题中所示的populate,而不是按照以下方式为activities添加解析器:

activities: (task, _, context) => Activity.find().where('id').in(task.activities)

无论哪种方式,问题都归结到您是在父解析器中加载所有数据,还是让解析器进一步进行某些工作。 因为仅要求解析器请求包含在请求中的字段,所以这两种方法之间的性能可能会受到重大影响。

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

相关说明...

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