【发布时间】:2019-03-17 21:22:39
【问题描述】:
我使用 MongoDB 作为我的数据库和 GraphQL。我正在为我的模型使用 Mongoose。我意识到我的 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))
})
然后我的用户将被永久缓存,而不是基于每个请求。
在解析器中定义数据加载器
This seems more reasonable : one caching mechanism per request.
// 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)
}
}
}
但是,userLoader 现在在 Mongoose 模式的 friends 方法中是 undefined。那么让我们在解析器中移动架构!
// 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)
}
}
}
Mh ... 现在 Mongoose 抱怨第二个请求:解析器再次被调用,Mongoose 不喜欢使用相同的模型名称定义 2 个模型。
“虚拟填充”功能没有用,因为我什至不能告诉 Mongoose 通过数据加载器而不是直接通过数据库来获取模型。
问题
有人遇到过同样的问题吗?有人对如何结合使用 Mongoose 和 Dataloader 有建议吗?谢谢。
注意:我知道由于我的架构是“关系型”,我应该使用关系型数据库而不是 MongoDB。我不是做出那个选择的人。在我们可以迁移之前,我必须忍受它。
【问题讨论】:
标签: node.js mongodb caching mongoose graphql