使用Ecto从其他表中预加载order_by

时间:2018-11-20 17:05:42

标签: elixir ecto

我对某些预紧力的顺序有疑问。

基本上,我有一个类似Restaurant > FoodItem > OptionGroup的结构。 OptionGroupRestaurant关联,并且可以与FoodItem(具有many_to_many FoodItemOptionGroup的表)关联。

food_item.ex中,我有以下内容:

many_to_many(
  :option_groups,
  OptionGroup,
  join_through: FoodItemOptionGroup,
  on_delete: :delete_all
)

我允许前端使用所需的预加载来查询API,因此它们可以执行以下操作:?include=food_item.option_groups。该函数迭代并创建一个预加载结构。这是核心部分:

is_atom(value) ->
    case value do
      :food_items ->
        [{:food_items, from(p in FoodItem, order_by: [p.id])}]

      :option_groups ->
        [{:option_groups, from(og in OptionGroup, order_by: og.id)}]

      :food_extras ->
        [{:food_extras, from(fe in FoodExtra, order_by: fe.id)}]
    end

最近,我已经实现了一个row_weight,它允许我按其值进行排序。在FoodItem上很简单,只需添加order_by即可完成。在OptionGroups上,情况很复杂。因为row_weight在关联many_to_many FoodItemOptionGroup上,所以预加载无法正常工作。

is_atom(value) ->
    case value do
      :food_items ->
        [{:food_items, from(p in FoodItem, order_by: [p.row_weight, p.id])}]

      :option_groups ->
        [
          {
            :option_groups,

            OptionGroup
            |> join(
              :inner,
              [og],
              fiog in Ketchup.FoodItemOptionGroup,
              og.id == fiog.option_group_id
            )
            |> order_by([og, fiog], [fiog.row_weight, og.id])
          }
        ]

      :food_extras ->
        [{:food_extras, from(fe in FoodExtra, order_by: fe.id)}]
    end

这将导致多个option_groups与它们与其他food_items的关联数重复。预接缝只能选择正确的选项,例如TemperatureSpice,还可以选择其他所有的TemperatureSpice(它们共享相同的id因为是其他food_items中的一些)。

存在使用group_by的可能解决方案,因为我认为它不会影响输出OptionGroup

OptionGroup 
|> join(:inner, [og], fiog in Ketchup.FoodItemOptionGroup, og.id == fiog.option_group_id) 
|> order_by([og, fiog], [fiog.row_weight, og.id]) 
|> group_by([og, fiog], [og.id, fiog.row_weight]) 

但是如果作为预加载运行,则会出现以下错误。如果作为标准查询运行,它确实会检索正确的OptionGroups

** (Postgrex.Error) ERROR 42803 (grouping_error): column "f2.id" must appear in the GROUP BY clause or be used in an aggregate function

在第一个示例中,看起来它需要food_item_id进行过滤,但是唯一的方法是使用function in preload

fn food_items_ids ->
  OptionGroup
  |> join(:inner, [og], fiog in Ketchup.FoodItemOptionGroup,
      og.id == fiog.option_group_id and fiog.food_item_id in ^food_items_ids)
  |> order_by([og, fiog], [fiog.row_weight, og.id])
  |> Ketchup.Repo.all()
end

但是在这种情况下,预加载将仅添加与OptionGroup具有相同id的{​​{1}}。

我想我在这里没有选择的余地,我在想一个可能没有选择要选择的预加载的选项的正确查询。

编辑

这是我要发送给Ecto的预载:

FoodItem

GET ../endpoint1?include=option_groups

我在这里显示的内容没有区别,但是区别在于它将显示所有OptionGroup,但不会排序它们,因为row_weight在所有它们中均不可用,当我只想要一个与{{1 }}具有[ option_groups: #Ecto.Query<from o in Ketchup.OptionGroup, join: f in Ketchup.FoodItemOptionGroup, on: o.id == f.option_group_id, order_by: [asc: f.row_weight, asc: o.id]> ]

FoodItem

row_weight

EDIT2

因此,我最终使用了查询。将来我将尝试实现GraphQL。

GET ../endpoint2?include=food_items.option_groups.food_extras

0 个答案:

没有答案