Elixir: buscar existencia de elementos relacionados en código vs BDD
Estaba revisando este código:
@inactive_implementation_status [
:deprecated,
:versioned
]
defp validate_inactive_implementations(%{data: rule} = changeset) do
active_implementations? =
rule
|> TdDd.Repo.preload(:rule_implementations)
|> Map.get(:rule_implementations)
|> Enum.any?(&(!Enum.member?(@inactive_implementation_status, &1.status)))
if active_implementations? do
add_error(changeset, :rule_implementations, "active_implementations")
else
changeset
end
end
Aquí se comprueba si una regla («rule») contiene implementaciones («implementacions», «rule_implementations»). Lo cambié para que la existencia de la relación se compruebe por completo en BDD en lugar de usar Enum.member junto con Enum.any:
defp validate_inactive_implementations(%{data: %{id: rule_id}} = changeset) do
%{active_implementations?: active_implementations?} =
Implementation
|> where([ri], ri.rule_id == ^rule_id and ri.status not in ^@inactive_implementation_status)
|> select([ri], %{active_implementations?: count(ri) > 0})
|> Repo.one
if active_implementations? do
add_error(changeset, :rule_implementations, "active_implementations")
else
changeset
end
end
La diferencia de tiempo (excluyendo el if/else), medida en microsegundos con :timer.tc
, probando 10 ejecuciones de un test que llama indirectamente a validate_inactive_implementations
, es:
Preload + Enum.member + Enum.any SQL directo
11267 6317
13542 6903
10584 8762
11517 6130
10969 8834
12267 7878
15118 9899
11967 9894
11831 8691
11261 7291
El test es:
test "validates rule with a related inactive implementation can be deleted" do
%{rule: rule} = insert(:implementation, status: :versioned)
insert(:implementation,
deleted_at: DateTime.utc_now(),
status: :deprecated,
rule_id: rule.id
)
assert {:ok, rule} =
rule
|> Rule.delete_changeset()
|> Repo.update()
assert %{active: false, deleted_at: deleted_at} = rule
assert deleted_at !== nil
end