

It means that in this case, you can do the following thing: |> where(, not is_nil(comments.id))īut you can't do this: |> where(, not is_nil(comments.id))Ĭomments were joined after posts, so you can pattern-match this resource only as the third element. |> join( :left,, _ in assoc(posts, :comments)) Here it is: def with_existing_comments(query \\ base()) do We might want to create a function that will return query able to filter users that have posts with comments. In our app there is also a Comment resource.

Our Queries module is still not perfect, and you'll soon find out why.

This makes it reusable which is a huge benefit. We have now two functions in Accounts context file that use UserQueries.with_existing_posts/1. With these changes, we're now able to finish the implementation of the er_has_posts? function: defmodule Queries.Accounts do Thanks to that you don't have to start the construction of queries pipe with UserQueries.base(). As you can see I also set the default value for the query argument in all of the functions. well, the base for all queries in a file which in most cases will be the resource module. There is one important function added here - base. To make our functions composable, we need to allow them to receive a query that will be then extended with other conditions: defmodule doĮnd def with_existing_posts(query \\ base()) do but it's not possible, as neither UserQueries.with_existing_posts/0 nor UserQueries.with_id/1 accepts query as an argument. Sounds great! But why is the last function not complete? This is what I wish I could do in this case: def user_has_posts?(user_id) do So far, we've been able to reduce Accounts responsibility in terms of Ecto.Query generation process to simple and effective delegation.
#Elixir ecto update
and update Accounts context: defmodule Queries.Accounts do alias The next step is to put our Ecto-related logic from context to the UserQueries module: defmodule do import Ecto.Query (In Curiosum, we also put other files in this folder, like user_validations, and user_specs, but this is a topic for another blog post). Notice, that I created a user folder here so that the user_queries file is scoped to the User resource. Let's create a file under this path: lib/accounts/user/user_queries.ex. We can design an approach that will totally separate context from Ecto.Query and deliver a module that is composable and reusable. |> where(, not is_nil(posts.id))Ĭurrently, the Accounts context's main responsibility is to compose Ecto.Query and pipe it to Repo. |> join( :left,, _ in assoc(user, :posts)) Let's imagine that the Accounts context looks like this: defmodule Queries.Accounts do import Ecto.Query To make this concept more feasible let’s create a sample app: mix phx.new queriesĪccounts.User mix Accounts.User accounts_users email:stringīlog.Post mix Blog.Post blog_posts content:text user_id:references:accounts_usersīlog.Comment mix Blog.Comment blog_comments content:text post_id:references:blog_postsĪs you can see, it’s a pretty easy and simplified structure – User has many posts and posts have many comments. With these two things in mind, we decided to extract resource queries into a file that has only one responsibility – to construct Ecto.Query for a specific resource.
