Ecto - 从5表连接中的所有表中选择所有字段

时间:2017-05-29 06:53:54

标签: elixir ecto elixir-framework

我有产品表,类别表和商店表,以及加入表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是否应该是商店。我只知道我想要返回所有的田地,只有独特的田地,按商店的邻近顺序排列。我觉得大部分时间都在那里,但有点卡在selectgroup_byorder_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

0 个答案:

没有答案