if Code.ensure_loaded?(Ecto.Query.API) do
defmodule QuackDB.Ecto.FTS do
@moduledoc """
DuckDB full-text search expression helpers for Ecto queries.
Create/drop indexes with `QuackDB.FTS`, then use these macros in
normal Ecto queries.
"""
defmacro match_bm25(schema, id, query) when is_binary(schema) do
quote do
fragment(unquote(~s|"#{schema}".match_bm25(?, ?)|), unquote(id), unquote(query))
end
end
defmacro match_bm25({:^, _, [schema]}, id, query) do
quote do
fragment("?.match_bm25(?, ?)", identifier(^unquote(schema)), unquote(id), unquote(query))
end
end
defmacro match_bm25(id, query, options) do
fields = Keyword.get(options, :fields)
k = Keyword.get(options, :k)
b = Keyword.get(options, :b)
conjunctive = Keyword.get(options, :conjunctive)
cond do
fields && k && b && !is_nil(conjunctive) ->
quote do
fragment(
"match_bm25(?, ?, fields := ?, k := ?, b := ?, conjunctive := ?)",
unquote(id),
unquote(query),
unquote(fields),
unquote(k),
unquote(b),
unquote(conjunctive)
)
end
fields ->
quote do
fragment(
"match_bm25(?, ?, fields := ?)",
unquote(id),
unquote(query),
unquote(fields)
)
end
k || b || !is_nil(conjunctive) ->
raise ArgumentError,
"QuackDB.Ecto.FTS.match_bm25/3 requires :fields when passing BM25 options"
true ->
quote do
fragment("match_bm25(?, ?)", unquote(id), unquote(query))
end
end
end
defmacro match_bm25(id, query) do
quote do
fragment("match_bm25(?, ?)", unquote(id), unquote(query))
end
end
defmacro bm25(id, query) do
quote do
match_bm25(unquote(id), unquote(query))
end
end
defmacro bm25(schema, id, query) do
quote do
match_bm25(unquote(schema), unquote(id), unquote(query))
end
end
defmacro search_score(id, query) do
quote do
match_bm25(unquote(id), unquote(query))
end
end
defmacro search_score(schema, id, query) do
quote do
match_bm25(unquote(schema), unquote(id), unquote(query))
end
end
defmacro stem(text) do
quote do
fragment("stem(?)", unquote(text))
end
end
defmacro stem(text, stemmer) do
quote do
fragment("stem(?, ?)", unquote(text), unquote(stemmer))
end
end
end
end