Currently I need to create a function to get all the users with registration date between a given start_date to a given end_date regardless of the year. This means that the user with registration date of ~D[2016-06-19] should appear for the function get_users(~D[2022-06-12], ~D[2022-07-30]).
So, I am trying to create a new_date(year of start_date, reg_d.month, reg_d.day) before comparing with the interval. Let’s assume that both the start_date and end_date is always on the same year.
def get_users(start_date, end_date) do
start_year = start_d.year
query = from u in User,
where ^Date.from_erl!({ start_year, (fragment(“date_part(‘month’, ?)”, u.reg_d)), (fragment(“date_part(‘day’, ?)”, u.reg_d)) }) > ^start_date
and ^Date.from_erl!({ start_year, (fragment(“date_part(‘month’, ?)”, u.reg_d)), (fragment(“date_part(‘day’, ?)”, u.reg_d)) }) < ^end_date
Repo.all(query)
end
When i write the above, it shows “CompileError undefined function fragment/2 Stacktrace: | (elixir 1.12.3) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3”
Ecto builds a whole query and then sends it to the database - you seem to be hoping to execute some Elixir for each matching row, which isn’t supported.
There are a couple different approaches you could take to solve this:
build a complex query using date_part and logical operators - basically re-implement date comparison by hand but without comparing the years.
pro: does everything in SQL
con: does everything in SQL; IIRC date_part isn’t particularly index-friendly
build a complex query by generating a copy of the supplied date range and combining the clauses with OR. In your example, it’d be checking “is reg_d between 2022-06-12 and 2022-07-30? What about 2021-06-12 and 2021-07-30, etc?”
pro: more index-friendly since it’s matching on the whole value of reg_d
con: if reg_d could be many many years ago, the query will get very large
change the data format: store a next_anniversary_d column that contains reg_d in the current year.
pro: the check becomes very very simple and index-friendly: “is next_anniversary_d in the supplied range?”
con: now you need code to update the anniversary column every year.
Thanks for the reply. I changed the data format of reg_d to the current year by creating a subquery. From there, I compared the new_reg_d with the start_date and end_date and it works.