perf: add batch hrrr_profiles_for_contacts/1 to collapse N+1 per-contact queries into one DB round-trip
This commit is contained in:
parent
82e02ae0b8
commit
695bbf5cee
1 changed files with 73 additions and 0 deletions
|
|
@ -1738,6 +1738,79 @@ defmodule Microwaveprop.Weather do
|
|||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Batch version of `hrrr_profiles_for_path/1`. Queries all HRRR profiles
|
||||
for a list of contacts in a single DB round-trip, then maps results
|
||||
back per-contact. Returns a list of `{contact, [profile]}` tuples.
|
||||
"""
|
||||
@spec hrrr_profiles_for_contacts([map()]) :: [{map(), [HrrrProfile.t()]}]
|
||||
def hrrr_profiles_for_contacts(contacts) do
|
||||
for_result =
|
||||
for c <- contacts, c.pos1 != nil, {lat, lon} <- Radio.contact_path_points(c) do
|
||||
{{lat, lon, c.qso_timestamp}, c}
|
||||
end
|
||||
|
||||
points =
|
||||
Enum.uniq_by(for_result, fn {key, _} -> key end)
|
||||
|
||||
{keys, contact_map} =
|
||||
Enum.reduce(points, {[], %{}}, fn {{lat, lon, ts} = key, c}, {ks, cm} ->
|
||||
{[key | ks], Map.update(cm, c, [key], &[key | &1])}
|
||||
end)
|
||||
|
||||
index = find_nearest_hrrrs_batch(keys)
|
||||
|
||||
Enum.map(contacts, fn c ->
|
||||
point_keys = Map.get(contact_map, c, [])
|
||||
profiles = point_keys |> Enum.map(&Map.get(index, &1)) |> Enum.reject(&is_nil/1)
|
||||
{c, profiles}
|
||||
end)
|
||||
end
|
||||
|
||||
# Batch nearest-HRRR lookup: query all profiles in the union bounding
|
||||
# box once, then match each point to its nearest profile in Elixir.
|
||||
@spec find_nearest_hrrrs_batch([{float(), float(), DateTime.t()}]) :: %{
|
||||
{float(), float(), DateTime.t()} => HrrrProfile.t()
|
||||
}
|
||||
defp find_nearest_hrrrs_batch([]), do: %{}
|
||||
|
||||
defp find_nearest_hrrrs_batch(points) do
|
||||
d = 0.07
|
||||
margin_s = 3600
|
||||
|
||||
{lats, lons, times} = Enum.unzip(points)
|
||||
lat_min = Enum.min(lats) - d
|
||||
lat_max = Enum.max(lats) + d
|
||||
lon_min = Enum.min(lons) - d
|
||||
lon_max = Enum.max(lons) + d
|
||||
time_min = times |> Enum.min_by(&DateTime.to_unix/1) |> DateTime.add(-margin_s, :second)
|
||||
time_max = times |> Enum.max_by(&DateTime.to_unix/1) |> DateTime.add(margin_s, :second)
|
||||
|
||||
profiles =
|
||||
Repo.all(
|
||||
from(h in HrrrProfile,
|
||||
where:
|
||||
h.lat >= ^lat_min and h.lat <= ^lat_max and
|
||||
h.lon >= ^lon_min and h.lon <= ^lon_max and
|
||||
h.valid_time >= ^time_min and h.valid_time <= ^time_max,
|
||||
select: %{lat: h.lat, lon: h.lon, valid_time: h.valid_time, profile: h}
|
||||
)
|
||||
)
|
||||
|
||||
Map.new(points, fn {lat, lon, ts} ->
|
||||
nearest =
|
||||
Enum.min_by(
|
||||
profiles,
|
||||
fn p ->
|
||||
abs(p.lat - lat) + abs(p.lon - lon) + abs(DateTime.diff(p.valid_time, ts))
|
||||
end,
|
||||
fn -> nil end
|
||||
)
|
||||
|
||||
{{lat, lon, ts}, nearest && nearest.profile}
|
||||
end)
|
||||
end
|
||||
|
||||
@spec find_nearest_native_profile(float(), float(), DateTime.t()) ::
|
||||
HrrrNativeProfile.t() | nil
|
||||
def find_nearest_native_profile(lat, lon, timestamp) do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue