Aym*_*byk 3 caching mongoose mongodb node.js graphql
我使用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()
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的数据库中的一些示例数据:
[
{ 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] },
]
Run Code Online (Sandbox Code Playgroud)
当我执行以下 GraphQL 查询时:
{
users {
name
friends {
name
}
}
}
Run Code Online (Sandbox Code Playgroud)
每个用户被加载多次。我希望每个用户 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))
})
Run Code Online (Sandbox Code Playgroud)
那么我的用户将被永久缓存,而不是基于每个请求。
// 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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,userLoader现在在 Mongoose 模式undefined中的friends方法中。然后让我们在解析器中移动架构!
// 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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
嗯...现在猫鼬抱怨第二个请求:解析器再次被调用,猫鼬不喜欢用相同的模型名称定义两个模型。
“虚拟填充”功能没有用,因为我什至不能告诉 Mongoose 通过数据加载器而不是直接通过数据库来获取模型。
有没有人遇到过同样的问题?有人对如何结合使用 Mongoose 和 Dataloader 有任何建议吗?谢谢。
注意:我知道因为我的模式是“关系”的,所以我应该使用关系数据库而不是 MongoDB。我不是做出这个选择的人。在我们可以迁移之前,我必须忍受它。
将您的猫鼬模式保存在单独的模块中。您不想为每个请求创建架构——只是在第一次导入模块时。
const userSchema = new Schema({
name: String,
friendIds: [String]
})
const User = mongoose.model("User", userSchema)
module.exports = { User }
Run Code Online (Sandbox Code Playgroud)
如果需要,您还可以导出在同一模块中创建加载器的函数。但是请注意,我们不想导出加载器的实例,而只想导出一个返回一个的函数。
// ...
const getUserLoader = () => new DataLoader((userIds) => {
return User.find({ _id: { $in: userIds } }).execute()
})
module.exports = { User, getUserLoader }
Run Code Online (Sandbox Code Playgroud)
接下来,我们想在上下文中包含我们的加载器。这究竟是如何完成的将取决于您使用什么库来实际公开您的 graphql 端点。apollo-server例如,在 中,上下文作为配置的一部分传入。
new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
userLoader: getUserLoader()
}),
})
Run Code Online (Sandbox Code Playgroud)
这将确保我们为每个请求创建了一个新的加载器实例。现在,您的解析器可以像这样调用加载器:
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)
},
},
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2911 次 |
| 最近记录: |