my original syntax :
result =
Enum.map(
from(Repo, where: ^whitelisted_params)
|> where([schema], like(schema.nama, ^"#{nama}%"))
|> limit(^query_limit)
|> Repo.all(),
fn elem ->
elem |> Map.from_struct() |> Map.delete(:__meta__)
end
)
i want to add where([schema], like(schema.tgl, ^tgl))
if a conditional are true
tried :
result =
Enum.map(
from(Repo, where: ^whitelisted_params)
|> where([schema], like(schema.nama, ^"#{nama}%"))
|> (fn(n) -> tgl != "" |> where([schema], like(schema.tgl, ^tgl)) || "" end).()
|> limit(^query_limit)
|> Repo.all(),
fn elem ->
elem |> Map.from_struct() |> Map.delete(:__meta__)
end
)
error :
protocol Ecto.Queryable not implemented for true, the given module does not exist. This protocol is implemented for: Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple
thank you !
1 Like
|> (fn(n) -> tgl != "" |> where([schema], like(schema.tgl, ^tgl)) || "" end).()``
looks to me that you were trying to do something like this
|> (fn(query) ->
case tgl do
"" -> query
_ -> where(query, [schema], like(schema.tgl, ^tgl))
end
end).()
7 Likes
Yep, this is a pattern I use super often. I keep meaning to find a way to pipeline it better, but I just tend to have long repeated sets of this small āmutationā chunks (copy/pasted from my sources):
query =
case refine do
[pidm: pidm] when is_integer(pidm) -> where(query, [s], s.spriden_pidm == ^pidm)
[pidm: pidms] when is_list(pidms) -> where(query, [s], s.spriden_pidm in ^pidms)
[cnum: cnum] when is_binary(cnum) -> where(query, [s], s.spriden_id == ^cnum)
[cnum: cnums] when is_list(cnums) -> where(query, [s], s.spriden_id in ^cnums)
[id: id] when is_binary(id) -> where(query, [s], s.spriden_id == ^id)
[id: ids] when is_list(ids) -> where(query, [s], s.spriden_id in ^ids)
[] -> query
end
Obviously I donāt use the formatter because it absolutely destroys the readability of theseā¦ I wish it could be fixedā¦
3 Likes
thank you all for the response, before the answer from @peerreynders and @OvermindDL1 i use cond with multiple Ecto.Query condition
1 Like
I often have code like that:
Enum.reduce(opts, base_query, fn
{:category, category}, query -> from a in query, where: a.category == ^category
{:tag, tag}, query -> from a in query, where: a.tag == ^tag
_, query -> query
end)
2 Likes
Oh I have a TON of that too! Also a super common pattern, though my bodies in it tend to be many pages large to handle all the various options that it has when I use it so I figured it would make for an annoying example here. ^.^;
squery =
Enum.reduce(refine, squery, fn
{:pidm, a}, squery when a in [true, :all] ->
join(
squery,
:inner,
[course, section, dept],
student_course in DB.Banner.SFRSTCR,
student_course.sfrstcr_term_code == section.ssbsect_term_code and
student_course.sfrstcr_crn == section.ssbsect_crn
)
{:pidm, pidm}, squery when is_integer(pidm) ->
...
And so forth (these ones are formatted with the formatter due to their huge size).
Whenever I have a conditional in a pipeline (which happens all the time) I just write a separate private function, so in your pipeline it would look like this:
|> maybe_filter_by_tgl(schema, tgl)
Then a separate function:
defp maybe_filter_by_tgl(query, schema, tgl) do
if tgl != "", do: where(query, [schema], like(schema.tgl, ^tgl)), else: query
end
This is exactly the same as the anonymous function technique from @peerreynders responseā¦ for some reason I just find it easier to read (personal preference).
4 Likes
The issue then is having to carry a rather massive amount of state through, the examples I gave above are trivial compared to a lot of the conditionals, in addition it means Iād have to jump all over a file to see what the values can be instead of just looking at a single function linearly.
Personally Iām solidly in the private function camp - however in this case only a nudge seemed to be asked for - not a possible lecture from the āstyle policeā.
I find discontinuities in the pipe chain increase the cognitive load when reading the code.
3 Likes
Ingenious, thanks for sharing.
1 Like