使用Lighthouse GraphQL和Laravel时,访问器的N+1查询出现问题



我对旧的N+1问题有点意见。我正在运行Lighthouse 4.8、Laravel 5.8,并且在我的配置中将batched_queriesbatchload_relations设置为true。我的模式中有hasManyhasOne集(有点匿名(:

type Team {
id: Int!
Y_resource_id: Int
X_resource_id: Int
name: String
description: String
start_date: Date
cost: Float
Y: Resource @hasOne
X: Resource @hasOne
members(
where: _ @whereConditions
hasDepartmentMember: _ @whereHasConditions(columns: ["z_id","start_date"])
orderBy: [OrderByClause!] @orderBy
): [TeamMember] @hasMany @is_logged_in
options: [TeamResourceOption] @hasMany
optionIds: [Int!]
}

我使用的是多个访问器函数,它们利用了这些关系,以及一些自定义解析器和本地作用域。这些似乎是导致N+1问题的原因,例如,当我在团队中加载时,出现N+1问题之一的php查询是这样的(在尝试加载团队并填充信息后,在团队模型上(:

public function getHasTeamLeaderAttribute(): bool
{
return $this->members()->teamLeaders()->count() > 0;
}

这是members((函数:

public function members(): HasMany
{
return $this->hasMany(TeamMember::class);
}

以及团队成员类:

public function scopeTeamLeaders($query)
{
return $query->where('team_members.group_role', '=', TeamMember::ROLE_TEAM_LEADER);
}

时钟显示这是2个N+1问题(我称之为两次(:

N+1 queries: AppModelsTeamsTeam::AppModelsTeamsTeamMember loaded 4 times.
Team.php:254
N+1 queries: AppModelsTeamsTeam::AppModelsTeamsTeamMember loaded 2 times.
Team.php:254

我曾尝试在访问器中使用loadMissing在teamLeaders中加载,但这似乎增加了N+1查询,我也尝试过用$加载模型以及我需要的关系,但这会使整个应用程序变得更慢(我认为是因为它现在正在不需要的地方自动加载(。

这是一个继承的项目,我不完全熟悉GraphQL,所以任何指向正确方向的指针都会很棒。谢谢

我不确定这与GraphQL或Lighthouse有什么关系,因为您显示的类型似乎没有直接的has_team_leader属性。。。但是:

再多的急切加载也无法解决这里的N+1问题。

问题是这个代码:

public function getHasTeamLeaderAttribute(): bool
{
return $this->members()->teamLeaders()->count() > 0;
}

由于它使用关系方法而不是访问器,因此它将始终执行查询,$this->members->count() > 0可以在这里提供帮助(并急于加载members(,但这也不理想,也不使用teamLeaders()作用域。

Laravel有一种方法可以解决这个问题:计算相关模型。

您可以使用它来急切地加载团队领导者的数量,并在getter中使用它进行检查。它可能看起来像这样:

// where the query is executed:
$teams->loadCount(['members as team_leaders_count' => function ($query) {
$query->teamLeaders();
}])
// and in your getter:
public function getHasTeamLeaderAttribute(): bool
{
// Use `team_leaders_count` if loaded, if not fallback to the non-optimal query
return ($this->team_leaders_count ?? $this->members()->teamLeaders()->count()) > 0;
}

您也可以指示Lighthouse用@withCount为您进行急切的加载,但由于我没有看到has_team_leaders在模式中实际调用的位置,我不知道该将其添加到哪里,但知道这是可能的可能会很有用。

最新更新