使用GraphQL Ruby进行批量加载具有组合主键的记录
简述
我将尝试批量加载复合主键记录。
我所在的团队几年前引入了GraphQL,因此使用了graphql-batch。但是我认为现在普遍使用graphql-ruby的dataloder,所以我会尝试在两者上都进行实现。
假设
假设有以下的表格,
考虑以下情景,查找视图器是否在以下查询中关注了用户。
query ($id: ID!) {
user(id: $id) { isFollowedByViewer }
}
GraphQL批处理的情况下
将其实现为Loaders::MultiColumnRecordLoader。
module Loaders
class MultiColumnRecordLoader < GraphQL::Batch::Loader
def initialize(model, columns:)
@model = model
@columns = columns
end
def perform(keys)
conditions = keys.map {|key| @columns.zip(key).to_h }
scoped = conditions.inject(@model.none) {|s, condition| s.or(@model.where(condition)) }
scoped.each do |record|
key = @columns.map {|column| record.public_send(column) }
fulfill(key, record)
end
keys.each do |key|
next if fulfilled?(key)
fulfill(key, nil)
end
end
end
end
呼叫将是以下的方式
module Types
class UserType < Types::BaseObject
field :is_followed_by_viewer, Boolean, resolver_method: :followed_by_viewer?
def followed_by_viewer?
return false unless viewer
loader = Loaders::MultiColumnRecordLoader.for(Follow, columns: %i[follower_id followee_id])
loader([viewer.id, object.id]).then(&:present?)
end
end
end
GraphQL Ruby – Dataloader 的情况
实现为MultiColumnRecordSource。
module Sources
class MultiColumnRecordSource < GraphQL::Dataloader::Source
def initialize(model, columns:)
@model = model
@columns = columns
end
def fetch(keys)
conditions = keys.map {|key| @columns.zip(key).to_h }
scoped = conditions.inject(@model.none) {|s, condition| s.or(@model.where(condition)) }
mapping = scoped.inject({}) do |record, m|
key = @columns.map {|column| record.public_send(column) }
m[key] = record
end
keys.map {|key| mapping.fetch(key, nil) }
end
end
end
呼叫可能如下所示。
module Types
class UserType < Types::BaseObject
field :is_followed_by_viewer, Boolean, resolver_method: :followed_by_viewer?
def followed_by_viewer?
return false unless viewer
loader = dataloader.with(Sources::MultiColumnRecordSource, Follow, %i[follower_id followee_id])
follow = loader.load([viewer.id, object.id])
follow.present?
end
end
end
个人感受
我更喜欢GraphQL Ruby中的Dataloader。