Skip to content

Commit

Permalink
Optimize workorders query by status
Browse files Browse the repository at this point in the history
  • Loading branch information
jyeshe authored and stuartc committed Nov 17, 2023
1 parent 61e8c06 commit 3d53175
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 5 deletions.
12 changes: 10 additions & 2 deletions lib/lightning/invocation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Lightning.Invocation do

import Ecto.Query, warn: false
import Lightning.Helpers, only: [coerce_json_field: 2]

alias Lightning.WorkOrder
alias Lightning.WorkOrders.SearchParams
alias Lightning.Repo
Expand Down Expand Up @@ -335,12 +336,19 @@ defmodule Lightning.Invocation do

def search_workorders_query(
%Project{id: project_id},
%SearchParams{} = search_params
%SearchParams{status: status_list} = search_params
) do
status_filter =
if SearchParams.all_statuses_set?(search_params) do
[]
else
status_list
end

base_query(project_id)
|> filter_by_workorder_id(search_params.workorder_id)
|> filter_by_workflow_id(search_params.workflow_id)
|> filter_by_statuses(search_params.status)
|> filter_by_statuses(status_filter)
|> filter_by_wo_date_after(search_params.wo_date_after)
|> filter_by_wo_date_before(search_params.wo_date_before)
|> filter_by_date_after(search_params.date_after)
Expand Down
15 changes: 13 additions & 2 deletions lib/lightning/workorders/search_params.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ defmodule Lightning.WorkOrders.SearchParams do
import Ecto.Changeset

@statuses ~w(pending running success failed crashed killed cancelled lost exception)
@statuses_set MapSet.new(@statuses, fn x -> String.to_existing_atom(x) end)
@search_fields ~w(body log)

defmacro status_list() do
quote do
unquote(@statuses)
end
end

@type t :: %__MODULE__{
status: [String.t()],
search_fields: [String.t()],
Expand Down Expand Up @@ -64,22 +71,26 @@ defmodule Lightning.WorkOrders.SearchParams do
end)
end

def all_statuses_set?(%{status: status_list}) do
MapSet.equal?(@statuses_set, MapSet.new(status_list))
end

defp from_uri(params) do
statuses =
Enum.map(params, fn {key, value} ->
if key in @statuses and value in [true, "true"] do
key
end
end)
|> Enum.filter(fn v -> v end)
|> Enum.reject(&is_nil/1)

search_fields =
Enum.map(params, fn {key, value} ->
if key in @search_fields and value in [true, "true"] do
key
end
end)
|> Enum.filter(fn v -> v end)
|> Enum.reject(&is_nil/1)

params
|> Map.put_new("status", statuses)
Expand Down
95 changes: 94 additions & 1 deletion test/lightning/invocation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ defmodule Lightning.InvocationTest do
alias Lightning.Invocation.Run
alias Lightning.Repo

require SearchParams

defp build_workflow(opts) do
job = build(:job)
trigger = build(:trigger)
Expand Down Expand Up @@ -336,7 +338,74 @@ defmodule Lightning.InvocationTest do
end

describe "search_workorders/3" do
test "returns paginated workorders" do
test "filters workorders by two statuses" do
project = insert(:project)
workflow = insert(:workflow, project: project)

insert_list(3, :workorder, workflow: workflow, state: :pending)
insert_list(2, :workorder, workflow: workflow, state: :crashed)
insert_list(2, :workorder, workflow: workflow, state: :failed)
insert_list(1, :workorder, workflow: workflow, state: :pending)
insert_list(1, :workorder, workflow: workflow, state: :crashed)

assert %{
page_number: 1,
page_size: 10,
total_entries: 7,
total_pages: 1,
entries: entries
} =
Lightning.Invocation.search_workorders(
project,
SearchParams.new(%{"status" => ["pending", "crashed"]}),
%{
page: 1,
page_size: 10
}
)

assert %{
:pending => 4,
:crashed => 3
} = Enum.map(entries, & &1.state) |> Enum.frequencies()
end

test "filters workorders by all statuses" do
project = insert(:project)
workflow = insert(:workflow, project: project)

count =
SearchParams.status_list()
|> Enum.map(fn status ->
insert(:workorder, workflow: workflow, state: status)
end)
|> Enum.count()

assert %{
page_number: 1,
page_size: 10,
total_entries: ^count,
total_pages: 1,
entries: entries
} =
Lightning.Invocation.search_workorders(
project,
SearchParams.new(%{"status" => SearchParams.status_list()}),
%{
page: 1,
page_size: 10
}
)

assert SearchParams.status_list() |> Enum.frequencies() ==
Enum.map(entries, & &1.state)
|> Enum.frequencies()
|> Map.new(fn {status, count} ->
{Atom.to_string(status), count}
end)
end

test "returns a sequence of workorders pages" do
project = insert(:project)
workflow = insert(:workflow, project: project)

Expand Down Expand Up @@ -754,6 +823,30 @@ defmodule Lightning.InvocationTest do
end
end

describe "search_workorders_query/2" do
test "ignores status filter when all statuses are queried" do
project = insert(:project)

query =
Lightning.Invocation.search_workorders_query(
project,
SearchParams.new(%{"status" => ["pending"]})
)

{sql, _value} = Repo.to_sql(:all, query)
assert sql =~ ~S["state" = ]

query =
Lightning.Invocation.search_workorders_query(
project,
SearchParams.new(%{"status" => SearchParams.status_list()})
)

{sql, _value} = Repo.to_sql(:all, query)
refute sql =~ ~S["state" = ]
end
end

describe "run logs" do
test "logs_for_run/1 returns an array of the logs for a given run" do
run =
Expand Down

0 comments on commit 3d53175

Please sign in to comment.