Я использую 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кажется более разумным: один механизм кэширования на запрос.
// 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
теперь undefined
в методе friends
в схеме Mongoose.Давайте теперь переместим схему в резольвере!
// 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 не нравится, когда определяются две модели с одной и той же моделью.name.
Функция «виртуального заполнения» бесполезна, потому что я даже не могу сказать Mongoose, чтобы она выбирала модели через загрузчик данных, а не через базу данных напрямую.
Вопрос
У кого-нибудь была такая же проблема?У кого-нибудь есть предложения по использованию Mongoose и Dataloader в комбинации?Спасибо.
Примечание: я знаю, поскольку моя схема "реляционная", я должен использовать реляционную базу данных, а не MongoDB.Я был не один, чтобы сделать этот выбор.Я должен жить с этим, пока мы не сможем мигрировать.