Odd or_where behavior, can someone explain this?

Have a look at this:

  def get_patient!(id, user_id) do

    case CurrentUser.is_manager(user_id) do
      true ->  Repo.get!(Patient, id) # manager can access all without ACL
      false ->  Patient # normal user, check if current_user is listed in patient's ACL
      |> where([p], fragment("? = '{}'", p.acl)) # A -> ACL array is empty, so this row is open for all users..
      |> or_where([p], fragment("? @> ?::bigint[]", p.acl, ^[user_id])) # B -> user_id is listed in the ACL array
      |> where(id: ^id) # C -> match patient record itself
      |> Repo.one
    end

  end

This works as expected (discovered this after wasting some time on it…)

However, if I place statement C first (before A and B), I don’t get the expected results.

I this normal when using or_where? Thank you.

It’s normal. Here are there heuristics:

where - if no filters came before, emit just the condition. if some filters came before, combine them with this new condition using AND boolean operator

:or_where - if no filters came before, emit just the condition. if some filters came before, wrap them in brackets and combine them with this new condition using OR operator

It’s the same way you should think about it using regular boolean logic. Order matters

1 Like

Is there a way to specify (in pipe) that A and B should be a set and C another (I mean to wrap them somehow with some code so C can be placed first)?

Is there a way to specify explicitly that [C AND [A OR B]]?

where(A) |> or_where(B) |> where(C)

just need to remember or_where is OR everything that came behind it.

1 Like

If you want to control precedance manually you can use the dynamic macro to build up conditions: Ecto.Query — Ecto v3.7.0

With multiple where or or_where ordering determines precedance.

2 Likes