Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preload with Ecto with subquery and using a join from the main query

Tags:

elixir

ecto

I'm using Ecto to request data from the database, and I've used the following code to preload the cats filtered by price_discount.

Item
|> join(:inner, [i], c in Cat, c.food_category_id == i.id)
|> where([i, c], is_nil(c.price_discount))
|> preload([i, c],
  [
    cats: c,
    cats: :houses
  ])

This works great, but now I want to order Houses by id, so I was hopping this work:

Item
|> join(:inner, [i], c in Cat, c.food_category_id == i.id)
|> where([i, c], is_nil(c.price_discount))
|> preload([i, c],
  [
    cats: c,
    cats: [
      houses: from(h in Houses, order_by: h.id)
    ]
  ])

But it doesn't, this is the error:

from(h in Houses, order_by: h.id()) is not a valid preload expression. preload expects an atom, a list of atoms or a keyword list with more preloads as values. Use ^ on the outermost preload to interpolate a value

The only post to work is to just use one of the following:

  • Use cats: c without the order
  • Use houses: from(h in Houses, order_by: h.id)

The following works, using ^:

Item
|> join(:inner, [i], c in Cat, c.food_category_id == i.id)
|> where([i, c], is_nil(c.price_discount))
|> preload([i, c],
  ^[
    cats: [
      houses: from(h in Houses, order_by: h.id)
    ]
  ])

Is this a bug in Ecto ?

like image 624
David Magalhães Avatar asked Nov 28 '17 15:11

David Magalhães


2 Answers

When you want to provide a custom query to a preload and also to its nested preloads, you can use the tuple syntax, where the first element is the main query and the second element is a list of preloads just like you'd have at the top level.

What you want can be achieved with this:

|> preload([i, c], [cats: {c, houses: ^from(h in Houses, order_by: h.id)}])
like image 129
Dogbert Avatar answered Oct 21 '22 05:10

Dogbert


The solution that I found was to split |> preload() in two.

Item
|> join(:inner, [i], c in Cat, c.food_category_id == i.id)
|> where([i, c], is_nil(c.price_discount))
|> preload([i, c], [cats: c])
|> preload([cats: [houses: from(h in Houses, order_by: h.id)]])
like image 36
David Magalhães Avatar answered Oct 21 '22 03:10

David Magalhães