Skip to content

Commit 6c6f811

Browse files
committed
Code for step 4
1 parent 9477652 commit 6c6f811

File tree

8 files changed

+155
-1
lines changed

8 files changed

+155
-1
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
defmodule ElixirMonitoringProm.Breweries do
2+
alias ElixirMonitoringProm.{Breweries.Brewery, Repo, ZipCodes}
3+
import Ecto.Query
4+
5+
def get_breweries_in_radius(zip_code_to_search, radius_in_miles) do
6+
zip_codes_in_radius =
7+
zip_code_to_search
8+
|> ZipCodes.get_zip_codes_in_radius(radius_in_miles)
9+
|> case do
10+
{:ok, zip_codes} -> Enum.map(zip_codes, & &1.zip_code)
11+
error -> error
12+
end
13+
14+
query =
15+
from brewery in Brewery,
16+
where: brewery.zip_code in ^zip_codes_in_radius
17+
18+
Repo.all(query)
19+
end
20+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
defmodule ElixirMonitoringProm.Breweries.Brewery do
2+
use Ecto.Schema
3+
4+
import Ecto.Changeset
5+
6+
alias __MODULE__
7+
8+
@derive {Jason.Encoder, only: ~w(brand beers zip_code)a}
9+
schema "breweries" do
10+
field :brand, :string
11+
field :beers, {:array, :string}
12+
field :zip_code, :string
13+
end
14+
15+
def changeset(%Brewery{} = brewery, attrs \\ %{}) do
16+
all_fields = [:brand, :beers, :zip_code]
17+
18+
brewery
19+
|> cast(attrs, all_fields)
20+
|> validate_required(all_fields)
21+
end
22+
end
23+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
defmodule ElixirMonitoringProm.ZipCodes do
2+
alias ElixirMonitoringProm.{Repo, ZipCodes.ZipCode}
3+
4+
def get_zip_codes_in_radius(zip_code, radius_in_miles) do
5+
# Our raw Postgres query to get all the zip codes within a radius
6+
query =
7+
[
8+
"WITH target AS (SELECT point AS p FROM zip_codes WHERE zip_code = $1::varchar)",
9+
"SELECT id, zip_code, city, state, timezone, dst, point FROM zip_codes JOIN target ON true",
10+
"WHERE ST_DWithin(p::geography, zip_codes.point::geography, $2::double precision)"
11+
]
12+
|> Enum.join(" ")
13+
14+
# The arguments we are passing to the query
15+
args = [zip_code, miles_to_meters(radius_in_miles)]
16+
17+
# Since we used a raw SQL query, we'll need to manually (for more information
18+
# see https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.html#query/4)
19+
case Repo.query(query, args, log: true) do
20+
{:ok, %Postgrex.Result{columns: cols, rows: rows}} ->
21+
results =
22+
Enum.map(rows, fn row ->
23+
Repo.load(ZipCode, {cols, row})
24+
end)
25+
26+
{:ok, results}
27+
28+
_ ->
29+
{:error, :not_found}
30+
end
31+
end
32+
33+
defp miles_to_meters(miles), do: miles * 1609.344
34+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule ElixirMonitoringPromWeb.BreweryController do
2+
use ElixirMonitoringPromWeb, :controller
3+
4+
alias ElixirMonitoringProm.Breweries
5+
6+
def index(conn, %{"zip_code" => zip_code, "mile_radius" => radius}) do
7+
results = Breweries.get_breweries_in_radius(zip_code, String.to_integer(radius))
8+
9+
json(conn, results)
10+
end
11+
12+
def index(conn, _) do
13+
conn
14+
|> json(%{
15+
error: "\"zip_code\" and \"mile_radius\" are both required fields"
16+
})
17+
end
18+
end

lib/elixir_monitoring_prom_web/router.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ defmodule ElixirMonitoringPromWeb.Router do
1313
plug :accepts, ["json"]
1414
end
1515

16+
scope "/api", ElixirMonitoringPromWeb do
17+
pipe_through :api
18+
19+
get "/breweries", BreweryController, :index
20+
end
21+
1622
scope "/", ElixirMonitoringPromWeb do
1723
pipe_through :browser
1824

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ defmodule ElixirMonitoringProm.MixProject do
4444
{:jason, "~> 1.0"},
4545
{:plug_cowboy, "~> 2.0"},
4646
{:geo, "~> 3.1.0"},
47-
{:geo_postgis, "~> 3.1.0"}
47+
{:geo_postgis, "~> 3.1.0"},
48+
{:faker, "~> 0.12.0"}
4849
]
4950
end
5051

mix.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
77
"ecto": {:hex, :ecto, "3.1.7", "fa21d06ef56cdc2fdaa62574e8c3ba34a2751d44ea34c30bc65f0728421043e5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
88
"ecto_sql": {:hex, :ecto_sql, "3.1.6", "1e80e30d16138a729c717f73dcb938590bcdb3a4502f3012414d0cbb261045d8", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0 or ~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
9+
"faker": {:hex, :faker, "0.12.0", "796cbac868c86c2df6f273ea4cdf2e271860863820e479e04a374b7ee6c376b6", [:mix], [], "hexpm"},
910
"file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"},
1011
"geo": {:hex, :geo, "3.1.0", "727e005262430d037e870ff364e65d80ca5ca21d5ac8eddd57a1ada72c3f83b0", [:mix], [], "hexpm"},
1112
"geo_postgis": {:hex, :geo_postgis, "3.1.0", "d06c8fa5fd140a52a5c9dab4ad6623a696dd7d99dd791bb361d3f94942442ff9", [:mix], [{:geo, "~> 3.1", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm"},
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
defmodule ElixirMonitoringProm.Repo.Migrations.BreweryTable do
2+
use Ecto.Migration
3+
4+
alias ElixirMonitoringProm.{Breweries.Brewery, Repo, ZipCodes.ZipCode}
5+
alias Faker.{Beer, Company}
6+
7+
import Ecto.Query
8+
9+
def up do
10+
create table(:breweries) do
11+
add :brand, :string
12+
add :beers, {:array, :string}
13+
add :zip_code, :string
14+
end
15+
16+
create index(:breweries, [:zip_code])
17+
18+
# Flush the database changes so that we can populate the tables with dummy data
19+
flush()
20+
21+
Faker.start()
22+
23+
# Go through all of the zip codes in WA state and create between 1-3 brewers
24+
ZipCode
25+
|> Repo.all()
26+
|> Enum.each(fn %ZipCode{zip_code: zip_code} ->
27+
num_breweries = Enum.random(1..3)
28+
generate_breweries(zip_code, num_breweries)
29+
end)
30+
end
31+
32+
defp generate_breweries(_zip_code, 0), do: :ok
33+
34+
defp generate_breweries(zip_code, count) do
35+
attrs = %{
36+
brand: Company.name() <> " Brewers",
37+
beers: [Beer.name(), Beer.name()],
38+
zip_code: zip_code
39+
}
40+
41+
%Brewery{}
42+
|> Brewery.changeset(attrs)
43+
|> Repo.insert()
44+
45+
generate_breweries(zip_code, count - 1)
46+
end
47+
48+
def down do
49+
drop table(:breweries)
50+
end
51+
end

0 commit comments

Comments
 (0)