【问题标题】:GraphQL Dataloader vs Mongoose PopulateGraphQL 数据加载器与猫鼬填充
【发布时间】:2019-03-11 02:39:53
【问题描述】:

为了执行类似连接的操作,我们可以同时使用 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),
    },
};

我试图查看是否有任何文章证明了哪种方法在性能、资源耗尽等方面更好,但我没有找到这两种方法的任何比较。

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

【问题讨论】:

  • 我不得不认为它们或多或少是相同的。您可能需要担心更重要的事情。
  • 感谢您的评论。是的,你是对的,当然还有很多其他方面需要关心。对于之前的方法,您有没有具体的改进或者更好的方法?

标签: node.js express mongoose graphql


【解决方案1】:

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

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

更公平的比较可能是使用populate,如您的问题所示,而不是为activities 添加解析器,如下所示:

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

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

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

关于相关说明...

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

【讨论】:

  • 假设在一个请求中我想获取任务数组,其中填充了每个活动字段。在这种情况下,使用数据加载器而不是填充不是更好吗?如果我有 100 个任务,populate 会请求 mongodb 100 次来获取活动。但是如果我使用数据加载器,它只会访问数据库一次。我知道您对 graphql 数据加载器的解释是什么意思。但我认为数据加载器仍然可以在非 graphql(express) 应用程序中受益,其中 populate(deep, with many arrays) 被大量使用。
  • 好吧,没关系。我用mongoose.set('debug', true) 做了一个测试。我认为当使用深度嵌套的子文档填充时,猫鼬会对数据库提出不必要的请求。但就像数据加载器一样,请求的数量是最少的。(深度长度 == 请求数)
猜你喜欢
  • 2019-08-29
  • 2020-08-27
  • 2015-07-13
  • 2014-04-19
  • 1970-01-01
  • 2015-07-13
  • 2016-10-10
  • 1970-01-01
相关资源
最近更新 更多