Skip to content

Commit

Permalink
Merge pull request #35 from curiosum-dev/release/0.3.0
Browse files Browse the repository at this point in the history
Release/0.3.0
  • Loading branch information
arturz authored Sep 26, 2023
2 parents 60f700f + 7633fef commit a7effc0
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 83 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ by adding `kanta` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:kanta, "~> 0.2.2"},
{:kanta, "~> 0.3.0"},
{:gettext, git: "[email protected]:ravensiris/gettext.git", branch: "runtime-gettext"}
]
end
Expand Down Expand Up @@ -140,11 +140,11 @@ defmodule MyApp.Repo.Migrations.AddKantaTranslationsTable do
use Ecto.Migration

def up do
Kanta.Migration.up(version: 1, prefix: prefix()) # Prefix is needed if you are using multitenancy with i.e. triplex
Kanta.Migration.up(version: 2, prefix: prefix()) # Prefix is needed if you are using multitenancy with i.e. triplex
end

def down do
Kanta.Migration.down(version: 1, prefix: prefix()) # Prefix is needed if you are using multitenancy with i.e. triplex
Kanta.Migration.down(version: 2, prefix: prefix()) # Prefix is needed if you are using multitenancy with i.e. triplex
end
end
```
Expand Down Expand Up @@ -183,6 +183,8 @@ In the `application.ex` file of our project, we add Kanta and its configuration
Inside your `router.ex` file we need to connect the Kanta panel using the kanta_dashboard macro.

```elixir
import KantaWeb.Router

scope "/" do
  pipe_through :browser

Expand Down
5 changes: 0 additions & 5 deletions lib/kanta/gettext/repo.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
defmodule Kanta.Gettext.Repo do
@behaviour Gettext.Repo

alias Kanta.Translations.{
Context,
Domain,
Expand All @@ -12,12 +10,10 @@ defmodule Kanta.Gettext.Repo do

alias Kanta.Translations

@impl Gettext.Repo
def init(_) do
__MODULE__
end

@impl Gettext.Repo
def get_translation(locale, domain, msgctxt, msgid, opts) do
default_locale = Application.get_env(:kanta, :default_locale) || "en"

Expand Down Expand Up @@ -56,7 +52,6 @@ defmodule Kanta.Gettext.Repo do
end
end

@impl Gettext.Repo
def get_plural_translation(
locale,
domain,
Expand Down
2 changes: 1 addition & 1 deletion lib/kanta/migrations/postgresql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Kanta.Migrations.Postgresql do
use Ecto.Migration

@initial_version 1
@current_version 1
@current_version 2
@default_prefix "public"

@doc false
Expand Down
4 changes: 2 additions & 2 deletions lib/kanta/migrations/postgresql/v01.ex
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ defmodule Kanta.Migrations.Postgresql.V01 do

execute """
ALTER TABLE #{prefix}.#{@kanta_messages}
ADD COLUMN searchable tsvector
ADD COLUMN IF NOT EXISTS searchable tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(msgid, '')), 'A')
) STORED;
"""

execute """
CREATE INDEX #{@kanta_messages}_searchable_idx ON #{prefix}.#{@kanta_messages} USING gin(searchable);
CREATE INDEX IF NOT EXISTS #{@kanta_messages}_searchable_idx ON #{prefix}.#{@kanta_messages} USING gin(searchable);
"""

create_if_not_exists unique_index(@kanta_messages, [:context_id, :domain_id, :msgid])
Expand Down
46 changes: 46 additions & 0 deletions lib/kanta/migrations/postgresql/v02.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule Kanta.Migrations.Postgresql.V02 do
@moduledoc """
Kanta V2 Migrations
"""

use Ecto.Migration

@default_prefix "public"
@kanta_singular_translations "kanta_singular_translations"
@kanta_plural_translations "kanta_plural_translations"

def up(opts) do
Kanta.Migration.up(version: 1)
up_fuzzy_search(opts)
end

def down(opts) do
down_fuzzy_search(opts)
Kanta.Migration.down(version: 1)
end

def up_fuzzy_search(opts) do
prefix = Map.get(opts, :prefix, @default_prefix)

[@kanta_plural_translations, @kanta_singular_translations]
|> Enum.each(fn table_name ->
execute """
ALTER TABLE #{prefix}.#{table_name}
ADD COLUMN IF NOT EXISTS searchable tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('simple', coalesce(translated_text, '')), 'A')
) STORED;
"""

execute """
CREATE INDEX IF NOT EXISTS #{table_name}_searchable_idx ON #{prefix}.#{table_name} USING gin(searchable);
"""
end)

execute("CREATE EXTENSION IF NOT EXISTS unaccent;")
end

def down_fuzzy_search(_opts) do
execute("DROP EXTENSION IF EXISTS unaccent;")
end
end
91 changes: 88 additions & 3 deletions lib/kanta/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Kanta.Query do
quote do
import Ecto.Query

alias Kanta.Migrations.Postgresql
alias Kanta.Repo

# Returns the base for resource query with binding.
Expand Down Expand Up @@ -134,6 +135,36 @@ defmodule Kanta.Query do
)
end

@doc """
Joins resource with another resource.
*Important!* The joining has to be defined by join_resource/3 function. Example:
```
defp join_resource(query, :articles, opts) do
query
|> join(:left, [user: u], _ in assoc(u, :articles), as: :article)
end
```
First argument is a query and second argument is pattern-matched atom.
## Examples
iex> Kanta.Accounts.UserQueries.with_join(:articles)
#Ecto.Query<from u0 in Kanta.Accounts.User, as: :user,
left_join: u1 in assoc(u0, :articles), as: :article>
"""
@spec with_join(Ecto.Query.t(), atom()) :: Ecto.Query.t()
@spec with_join(Ecto.Query.t(), atom(), keyword()) :: Ecto.Query.t()
def with_join(query \\ base(), resource_name, opts \\ []) when is_atom(resource_name) do
if has_named_binding?(query, resource_name) do
query
else
join_resource(query, resource_name, opts)
end
end

@doc """
Filters given resource by specific criterias. Filters should be pass to the function as map `%{"field_name" => filter_value}`.
It supports associations: `%{"association" => %{"field_name" => filter_value}}`
Expand Down Expand Up @@ -292,18 +323,59 @@ defmodule Kanta.Query do
def search_query(query, nil), do: query
def search_query(query, ""), do: query

def search_query(query, search) do
def search_query(query, search_term) do
repo = Repo.get_repo()

if Postgresql.migrated_version(%{repo: repo}) >= 2 do
search_query_fuzzy(query, search_term)
else
search_query_legacy(query, search_term)
end
end

defmacrop form_search_query(search_term) do
quote do
fragment(
"SELECT to_tsquery(string_agg(unaccent(lexeme) || ':*', ' & ' order by positions)) FROM unnest(to_tsvector(?))",
unquote(search_term)
)
end
end

defmacrop ts_rank(left, right) do
quote do
fragment("ts_rank(?, ?)", unquote(left), unquote(right))
end
end

defp search_query_fuzzy(query, search_term) do
query
|> or_where(
[{unquote(opts[:binding]), resource}],
fragment("? @@ ?", resource.searchable, form_search_query(^search_term))
)
|> order_by(
[{unquote(opts[:binding]), resource}],
desc:
ts_rank(
resource.searchable,
form_search_query(^search_term)
)
)
end

defp search_query_legacy(query, search_term) do
from(s in unquote(opts[:module]),
where:
fragment(
"searchable @@ websearch_to_tsquery(?)",
^search
^search_term
),
order_by: {
:desc,
fragment(
"ts_rank_cd(searchable, websearch_to_tsquery(?), 4)",
^search
^search_term
)
}
)
Expand All @@ -321,6 +393,19 @@ defmodule Kanta.Query do
where: is_nil(q.deleted_by)
)
end

defmacro null_or_empty(field) do
quote do
fragment("(? = '') IS NOT FALSE", unquote(field))
end
end

@spec join_resource(Ecto.Query.t(), atom(), keyword()) :: no_return()
defp join_resource(_query, _, _opts) do
raise(ArgumentError, message: "wrong join criteria")
end

defoverridable join_resource: 3
end
end
end
1 change: 0 additions & 1 deletion lib/kanta/translations/context/finders/get_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ defmodule Kanta.Translations.Contexts.Finders.GetContext do
defp find_in_database(params) do
base()
|> filter_query(params[:filter])
|> search_query(params[:search])
|> preload_resources(params[:preloads] || [])
|> one()
|> case do
Expand Down
1 change: 0 additions & 1 deletion lib/kanta/translations/context/finders/list_contexts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ defmodule Kanta.Translations.Contexts.Finders.ListContexts do
def find(params \\ []) do
base()
|> filter_query(params[:filter])
|> search_query(params[:search])
|> preload_resources(params[:preloads] || [])
|> paginate(params[:page], params[:per_page])
end
Expand Down
1 change: 0 additions & 1 deletion lib/kanta/translations/domain/finders/get_domain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ defmodule Kanta.Translations.Domains.Finders.GetDomain do
defp find_in_database(params) do
base()
|> filter_query(params[:filter])
|> search_query(params[:search])
|> preload_resources(params[:preloads] || [])
|> one()
|> case do
Expand Down
1 change: 0 additions & 1 deletion lib/kanta/translations/domain/finders/list_domains.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ defmodule Kanta.Translations.Domains.Finders.ListDomains do
def find(params \\ []) do
base()
|> filter_query(params[:filter])
|> search_query(params[:search])
|> preload_resources(params[:preloads] || [])
|> paginate(params[:page], params[:per_page])
end
Expand Down
1 change: 0 additions & 1 deletion lib/kanta/translations/locale/finders/get_locale.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ defmodule Kanta.Translations.Locale.Finders.GetLocale do
defp find_in_database(params) do
base()
|> filter_query(params[:filter])
|> search_query(params[:search])
|> preload_resources(params[:preloads] || [])
|> one()
|> case do
Expand Down
1 change: 0 additions & 1 deletion lib/kanta/translations/locale/finders/list_locales.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ defmodule Kanta.Translations.Locale.Finders.ListLocales do
def find(params \\ []) do
base()
|> filter_query(params[:filter])
|> search_query(params[:search])
|> preload_resources(params[:preloads] || [])
|> paginate(params[:page], params[:per_page])
end
Expand Down
Loading

0 comments on commit a7effc0

Please sign in to comment.