seva
I’m new to Ecto and Elixir, so sorry if this is something obvious.
I’m trying to filter on the join, e.g. get all authors with available books:
SELECT *
FROM "authors"
INNER JOIN "books"
ON "books"."author_id" = "authors"."id"
WHERE "books"."available" = TRUE;
Coming from Rails, this is something that’s fairy easy to achieve by merging a named scope into the join:
class Book < ApplicationRecord
belongs_to :author
scope :available, ->{ where(available: true) }
end
class Author < ApplicationRecord
has_many :books
scope :with_available_books, ->{ joins(:books).merge(Book.available) }
end
Calling Author.with_available_books, produces the required query, and I can reuse Book#available for Book queries as well.
I was able to construct initial sql with Ecto successfully:
from a in Author,
join: b in assoc(a, :books),
where: b.availabile == true)
However, I now need to refactor the b.availabile == true into Book module, so I can reuse it both for Author joins and queries on the Books.
Following the Dynamic query docs, I was able to partly solve this:
defmodule Hello.Book do
def available, do: dynamic([books: b], b.available == true)
end
# Works successfully:
Author
|> join(:inner, [a], assoc(a, :books), as: :books)
|> where(^Book.available)
However, getting available books (e.g. where(Book, ^Book.available)) throws:
** (Ecto.QueryError) unknown bind name `:books` in query:
from b0 in Hello.Book
…unless I remove the named binding from the composable function (in which case I can no longer use it on the join):
def available, do: dynamic([b], b.available == true)
Any pointers on how to get the composable query to play nice with both use cases would be appreciated!
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Showing Posts 1 to 2- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
MrDoops
TLDR; look into Named Bindings.
You can conditionally check and join in your required joins within a function using has_named_binding/2
e.g.
I usually try to avoid using Dynamic until the problem gets complex (dynamic field selection + joins + complex subquerying, etc) and prefer composing with pipe-able Ecto query functions when re-usability is desired.
seva
Thanks for the reply and for sharing the example @MrDoops.
Unfortunately, the example keeps book’s query logic (i.e.
b.available == true) within Authors#where_books_available, so I’m unable to reuse it to queryBooks.The example also repeats the query bit
b.available == truetwice. In my real case I have many decorating functions similar toavailable/0inBooksthat I need to chain and reuse together for either querying Books directly or as part of a join in Authors and other related tables. So the solution needs to be DRYer.I now managed to use
has_named_binding?to workaround my initial issue:This allows me to successfully use it for querying books:
as well as within joins:
However, my implementation of
available/1seems like an overkill, as there’s again repetition of the query logic (b.available == true). Plus there are many other similar composable functions, so suddenly a 1-line function becomes 7.So wondering if there might be a neater solution?