我有产品表,类别表和商店表,以及加入表product_shops和product_categories。
我想选择所有类别在conn.query_params中的产品,这些产品位于conn.query_params中指定的特定距离内的商店中。而且我希望几乎返回所选产品,类别和商店的每个领域。
我有这个:
get "/products" do
query = conn.query_params
point = %Geo.Point{coordinates: {String.to_float(query["longitude"]), String.to_float(query["latitude"])}, srid: 4326}
shops = within(Shop, point, String.to_float(query["distanceFromPlaceValue"]) * 1000) |> order_by_nearest(point) |> select_with_distance(point) |> Api.Repo.all
categories = Enum.map(query["categories"], fn(x) -> String.to_integer(x) end)
shop_ids = Enum.map(shops, fn(x) -> x.id end)
query1 = from p in Product,
join: ps in ProductShop, on: p.id == ps.p_id,
join: s in Shop, on: s.id == ps.s_id,
join: pc in ProductCategory, on: p.id == pc.p_id,
join: c in Category, on: c.id == pc.c_id,
where: Enum.member?(categories, c.id),
where: Enum.member?(shop_ids, s.id),
select: p, c, s,
group_by s,
order_by s.distance
在上面的代码中,shops
变量包含指定距离内的所有商店。
这是在shop.ex内部,以便从距离最近的商店订购所有商店:
def within(query, point, radius_in_m) do
{lng, lat} = point.coordinates
from(shop in query, where: fragment("ST_DWithin(?::geography, ST_SetSRID(ST_MakePoint(?, ?), ?), ?)", shop.point, ^lng, ^lat, ^point.srid, ^radius_in_m))
end
def order_by_nearest(query, point) do
{lng, lat} = point.coordinates
from(shop in query, order_by: fragment("? <-> ST_SetSRID(ST_MakePoint(?,?), ?)", shop.point, ^lng, ^lat, ^point.srid))
end
def select_with_distance(query, point) do
{lng, lat} = point.coordinates
from(shop in query, select: %{shop | distance: fragment("ST_Distance_Sphere(?, ST_SetSRID(ST_MakePoint(?,?), ?))", shop.point, ^lng, ^lat, ^point.srid)})
end
这是商店架构,并且在调用select_with_distance
变量时填充距离字段。
@derive {Poison.Encoder, only: [:name, :place_id, :point]}
schema "shops" do
field :name, :string
field :place_id, :string
field :point, Geo.Point
field :distance, :float, virtual: true
timestamps()
end
我当前的错误位于select: p, c, s
行,因为我不确定如何选择整个批次:
== Compilation error on file lib/api/router.ex ==
** (SyntaxError) lib/api/router.ex:153: syntax error before: c
(elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
还不确定group_by是否应该是商店。我只知道我想要返回所有的田地,只有独特的田地,按商店的邻近顺序排列。我觉得大部分时间都在那里,但有点卡在select
,group_by
和order_by
上。
编辑:感谢Dogbert在评论中解决了这两个问题。
目前的代码是:
products_shops_categories = from p in Product,
join: ps in ProductShop, on: p.id == ps.p_id,
join: s in Shop, on: s.id == ps.s_id,
join: pc in ProductCategory, on: p.id == pc.p_id,
join: c in Category, on: c.id == pc.c_id,
where: c.id in ^categories,
where: s.id in ^shop_ids,
select: {p, c, s}
group_by s,
order_by s.distance
我现在有这个错误:
** (Ecto.Query.CompileError) `order_by(s.distance())` is not a valid query expression.
* If you intended to call a database function, please check the documentation
for Ecto.Query to see the supported database expressions
* If you intended to call an Elixir function or introduce a value,
you need to explicitly interpolate it with ^
expanding macro: Ecto.Query.group_by/2
lib/api/router.ex:155: Api.Router.do_match/4
(elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1