繁体   English   中英

如何在GraphQL和DataLoader中使用Mongoose?

How to use Mongoose with GraphQL and DataLoader?

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我使用MongoDB作为数据库和GraphQL 我在模型上使用猫鼬 我意识到我的GraphQL查询速度很慢,因为一次又一次地加载相同的文档。 我想使用DataLoader解决我的问题,但是我不知道如何。

假设我有以下架构,描述了与朋友一起的用户:

// mongoose schema
const userSchema = new Schema({
  name: String,
  friendIds: [String],
})

userSchema.methods.friends = function() {
  return User.where("_id").in(this.friendIds)
}

const User = mongoose.model("User", userSchema)

// GraphQL schema
const graphqlSchema = `
  type User {
    id: ID!
    name: String
    friends: [User]
  }

  type Query {
    users: [User]
  }
`

// GraphQL resolver
const resolver = {
  Query: {
    users: () => User.find()
  }
}

这是我数据库中的一些示例数据:

[
  { id: 1, name: "Alice", friendIds: [2, 3] },
  { id: 2, name: "Bob", friendIds: [1, 3] },
  { id: 3, name: "Charlie", friendIds: [2, 4, 5] },
  { id: 4, name: "David", friendIds: [1, 5] },
  { id: 5, name: "Ethan", friendIds: [1, 4, 2] },
]

当我执行以下GraphQL查询时:

{
  users {
    name
    friends {
      name
    }
  }
}

每个用户加载多次。 我希望每个用户Mongoose文档仅加载一次。

什么不起作用

定义“全局”数据加载器以获取朋友

如果我将friends方法更改为:

// mongoose schema
const userSchema = new Schema({
  name: String,
  friendIds: [String]
})

userSchema.methods.friends = function() {
  return userLoader.load(this.friendIds)
}

const User = mongoose.model("User", userSchema)

const userLoader = new Dataloader(userIds => {
  const users = await User.where("_id").in(userIds)
  const usersMap = new Map(users.map(user => [user.id, user]))
  return userIds.map(userId => usersMap.get(userId))
})

那么我的用户将被永久缓存,而不是基于每个请求。

在解析器中定义数据加载器

这似乎更合理:每个请求一种缓存机制。

// GraphQL resolver
const resolver = {
  Query: {
    users: async () => {
      const userLoader = new Dataloader(userIds => {
        const users = await User.where("_id").in(userIds)
        const usersMap = new Map(users.map(user => [user.id, user]))
        return userIds.map(userId => usersMap.get(userId))
      })
      const userIds = await User.find().distinct("_id")
      return userLoader.load(userIds)
    }
  }
}

但是,现在在Mongoose模式的friends方法中undefined userLoader 然后,让我们在解析器中移动模式!

// GraphQL resolver
const resolver = {
  Query: {
    users: async () => {
      const userLoader = new Dataloader(userIds => {
        const users = await User.where("_id").in(userIds)
        const usersMap = new Map(users.map(user => [user.id, user]))
        return userIds.map(userId => usersMap.get(userId))
      })
      const userSchema = new Schema({
        name: String,
        friendIds: [String]
      })
      userSchema.methods.friends = function() {
        return userLoader.load(this.friendIds)
      }
      const User = mongoose.model("User", userSchema)
      const userIds = await User.find().distinct("_id")
      return userLoader.load(userIds)
    }
  }
}

嗯...现在,Mongoose在第二个请求上抱怨:解析器再次被调用,Mongoose不喜欢使用相同的模型名称定义两个模型。

“虚拟填充”功能没有用,因为我什至不能告诉Mongoose通过数据加载器而不是直接通过数据库获取模型。

有人遇到过同样的问题吗? 有没有人建议如何结合使用Mongoose和Dataloader? 谢谢。

注意:我知道因为我的架构是“关系的”,所以我应该使用关系数据库而不是MongoDB。 我不是那种选择的人。 在我们可以迁移之前,我必须忍受它。

1 个回复

将您的猫鼬模式保存在单独的模块中。 您不想在每个请求时都创建架构,而只是在第一次导入模块时。

const userSchema = new Schema({
  name: String,
  friendIds: [String]
})
const User = mongoose.model("User", userSchema)

module.exports = { User }

如果需要,您还可以导出在同一模块中创建加载程序的函数。 但是请注意,我们不想导出一个加载程序的实例,而只是要导出一个实例的函数。

// ...
const getUserLoader = () => new DataLoader((userIds) => {
  return User.find({ _id: { $in: userIds } }).execute()
})
module.exports = { User, getUserLoader }

接下来,我们要在上下文中包含我们的加载器。 具体执行方式将取决于您使用哪个库来实际显示graphql端点。 例如,在apollo-server ,上下文作为配置的一部分传入。

new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req }) => ({
        userLoader: getUserLoader()
    }),
})

这将确保我们为每个请求创建一个新的加载器实例。 现在,您的解析器可以像这样调用加载程序:

const resolvers = {
  Query: {
    users: async (root, args, { userLoader }) => {
      // Our loader can't get all users, so let's use the model directly here
      const allUsers = await User.find({})
      // then tell the loader about the users we found
      for (const user of allUsers) {
        userLoader.prime(user.id, user);
      }
      // and finally return the result
      return allUsers
    }
  },
  User: {
    friends: async (user, args, { userLoader }) => {
      return userLoader.loadMany(user.friendIds)
    },
  },
}
1 如何在Mongoose中使用DataLoader

我正在尝试与Mongoose一起构建以下DataLoader用例: 加载多个PurchaseOrders时,我面临以下问题: 在DataLoader的ids参数中多次调用一个customer_id。 因此,在连续调用中,对我的加载程序的几个请求都调用了一个示例ID 5cee8 ...

2 在graphql-dotnet中使用dataLoader进行多对多

我们如何使用 dataLoader 实现多对多? 假设我想获取有联系人的公司(并且一个联系人可以有多个公司)。 我有一个 companyLinks 作为中间表(带有 companyId/contactId)。 当公司只有 1 个联系人时,我有这个工作: 但是如果一个联系人有超过 1 家公司呢 ...

3 为什么我们在 Mongoose 中使用 GraphQL

我目前对为什么在开发 Web 应用程序的后端时创建两个相同的模式感到困惑,一个用于 mongoose,另一个用于 GraphQL。 为什么我们不能只使用这些 API 之一? 为什么我们同时使用两者,两者的目的是什么? ...

4 如何在 Dataloader 中使用 Batchsampler

我需要在 pytorch DataLoader使用BatchSampler而不是多次调用数据集的__getitem__ (远程数据集,每个查询都很贵)。 我无法理解如何将批处理采样器用于任何给定的数据集。 例如 我不明白,也没有在网上或火炬文档中找到任何示例,是我如何使用我的get_batch ...

5 如何在GraphQL中使用Mongoose显示数组的内容?

我有以下两种模型: 我有一个GraphQL查询,它列出了事件和创建者信息(_id,电子邮件,密码),因为您可以看到它们之间的关系。 关键是:如何在此查询中显示createdEvents的内容? 我在下面尝试了此代码,但即使它不为空,它也会返回一个空数组(我已经硬编码了创建者的ID,以将事件分配 ...

6 如何在ammap上使用Dataloader

我想使用dataloader的功能ammap但我couldt收获。 这是我的尝试方法: json url没有问题,该url返回如下json数据: 我想用作地图total上的value 。 如何在dataloader上使用dataloader加载器? 谢谢 ...

7 关于 GraphQL 的建议以及可能使用 DataLoader 或 ORM

我希望对我正在做的事情有一些见解和可能的建议。 我目前已经构建了一个解析器,它可以获取例如帖子列表。 如 但是注意到这种方法不会很好,因为我们希望获得的数据类型是 为了执行这样的任务,我可能需要运行多个查询,我的问题就在这里。 是否建议使用某种形式的 ORM 来轻松连接one-to-one或 ...

8 在 GraphQL 服务器设置中何时使用 Redis 以及何时使用 DataLoader

我已经在 GraphQL 服务器上工作了一段时间,虽然我了解大部分方面,但我似乎无法掌握缓存。 谈到缓存,我看到提到了 DataLoader 和 Redis,但我不清楚什么时候应该使用什么以及如何使用它们。 我认为 DataLoader 在字段级别上更多地用于解决 n+1 问题? 我猜 Redi ...

9 GraphQL猫鼬使用Dataloader限制批处理结果

我的用户和照片之间有关联。 每当我为所有用户获取信息时,我也都希望为每个用户获取前5张照片。 这是我的架构: 所以在我的解析器中,我有: 因此,使用数据加载器,我可以在一个请求中批量处理所有照片,但是我找不到一种方法来限制每个用户的照片数量。 ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM