Search code examples
elixirphoenix-frameworkecto

Ecto join on multiple conditions


Assuming I have three models:

user: has_one wallet
wallet: belongs_to user
transactions: belongs_to wallet

I am trying to get all the transactions for a given user. The SQL I can use is the following:

SELECT
   * 
FROM
   transactions AS t 
   JOIN
      wallets AS w 
      ON w.user_id = 1 
      AND w.id = t.wallet_id

Now in Ecto the following works but it does not replicate the query above:

wallet = assoc(user, :wallet)
q = from t in Transaction,
join: w in ^wallet,
where: t.wallet_id == w.id,
order_by: [desc: t.id],
select: t

I can't find any docs for creating assocs with AND cases. I've tried:

join: w in (^wallet and assoc(t, :wallet)),

but that results in a compile error. The goal would be to write this query on assoc only without manual id joins to let the relational abstraction stay within the models.

EDIT

following @daniel's suggestion and looking at dynamic/2 docs, I managed to build the query with multiple join conditions, by supplying extra conditions in the on:

id = user.id
q = from t in Transaction,
join: w in assoc(t, :wallet),
on: w.user_id == ^id,
order_by: [desc: t.id],
select: t

that snipped produces the following:

SELECT t0."id" 
  FROM "transactions" AS t0 
  INNER JOIN "wallets" AS w1 
    ON (w1."id" = t0."wallet_id") 
    AND (w1."user_id" = $1)

Solution

  • I will assume:

    schema "transactions" do
      belongs_to :wallet, Wallet
      timestamps()
    end
    
    schema "wallet" do
      belongs_to :user, User
      has_many :transactions, Transaction
      timestamps()
    end
    
    schema "user" do
      has_one :wallet, Wallet
      timestamps()
    end
    

    Your query should look the following:

    def user_transactions_query(id) do
      from tr in Transaction,
        join: wallet in assoc(tr, :wallet),
        join: user in assoc(wallet, :user),
        where: user.id == ^id
    end