This commit is contained in:
parent
ec2fe32afe
commit
4a5c09d819
@ -17,10 +17,34 @@ defmodule Memex.Notes do
|
||||
|
||||
"""
|
||||
@spec list_notes(User.t() | nil) :: [Note.t()]
|
||||
def list_notes(%{id: user_id}) do
|
||||
@spec list_notes(search :: String.t() | nil, User.t() | nil) :: [Note.t()]
|
||||
def list_notes(search \\ nil, user)
|
||||
|
||||
def list_notes(search, %{id: user_id}) when search |> is_nil() or search == "" do
|
||||
Repo.all(from n in Note, where: n.user_id == ^user_id, order_by: n.title)
|
||||
end
|
||||
|
||||
def list_notes(search, %{id: user_id}) when search |> is_binary() do
|
||||
trimmed_search = String.trim(search)
|
||||
|
||||
Repo.all(
|
||||
from n in Note,
|
||||
where: n.user_id == ^user_id,
|
||||
where:
|
||||
fragment(
|
||||
"search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
|
||||
^trimmed_search
|
||||
),
|
||||
order_by: {
|
||||
:desc,
|
||||
fragment(
|
||||
"ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
|
||||
^trimmed_search
|
||||
)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list of public notes for viewing
|
||||
|
||||
@ -30,10 +54,34 @@ defmodule Memex.Notes do
|
||||
[%Note{}, ...]
|
||||
"""
|
||||
@spec list_public_notes() :: [Note.t()]
|
||||
def list_public_notes do
|
||||
@spec list_public_notes(search :: String.t() | nil) :: [Note.t()]
|
||||
def list_public_notes(search \\ nil)
|
||||
|
||||
def list_public_notes(search) when search |> is_nil() or search == "" do
|
||||
Repo.all(from n in Note, where: n.visibility == :public, order_by: n.title)
|
||||
end
|
||||
|
||||
def list_public_notes(search) when search |> is_binary() do
|
||||
trimmed_search = String.trim(search)
|
||||
|
||||
Repo.all(
|
||||
from n in Note,
|
||||
where: n.visibility == :public,
|
||||
where:
|
||||
fragment(
|
||||
"search @@ to_tsquery(websearch_to_tsquery(?)::text || ':*')",
|
||||
^trimmed_search
|
||||
),
|
||||
order_by: {
|
||||
:desc,
|
||||
fragment(
|
||||
"ts_rank_cd(search, to_tsquery(websearch_to_tsquery(?)::text || ':*'), 4)",
|
||||
^trimmed_search
|
||||
)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single note.
|
||||
|
||||
|
@ -3,13 +3,8 @@ defmodule MemexWeb.NoteLive.Index do
|
||||
alias Memex.{Notes, Notes.Note}
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, %{assigns: %{current_user: current_user}} = socket)
|
||||
when not (current_user |> is_nil()) do
|
||||
{:ok, socket |> assign(notes: Notes.list_notes(current_user))}
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, socket |> assign(notes: Notes.list_public_notes())}
|
||||
{:ok, socket |> assign(search: nil) |> display_notes()}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@ -34,7 +29,17 @@ defmodule MemexWeb.NoteLive.Index do
|
||||
defp apply_action(socket, :index, _params) do
|
||||
socket
|
||||
|> assign(page_title: "notes")
|
||||
|> assign(search: nil)
|
||||
|> assign(note: nil)
|
||||
|> display_notes()
|
||||
end
|
||||
|
||||
defp apply_action(socket, :search, %{"search" => search}) do
|
||||
socket
|
||||
|> assign(page_title: "notes")
|
||||
|> assign(search: search)
|
||||
|> assign(note: nil)
|
||||
|> display_notes()
|
||||
end
|
||||
|
||||
@impl true
|
||||
@ -49,4 +54,22 @@ defmodule MemexWeb.NoteLive.Index do
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
||||
{:noreply, socket |> push_patch(to: Routes.note_index_path(Endpoint, :index))}
|
||||
end
|
||||
|
||||
def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do
|
||||
{:noreply, socket |> push_patch(to: Routes.note_index_path(Endpoint, :search, search_term))}
|
||||
end
|
||||
|
||||
defp display_notes(%{assigns: %{current_user: current_user, search: search}} = socket)
|
||||
when not (current_user |> is_nil()) do
|
||||
socket |> assign(notes: Notes.list_notes(search, current_user))
|
||||
end
|
||||
|
||||
defp display_notes(%{assigns: %{search: search}} = socket) do
|
||||
socket |> assign(notes: Notes.list_public_notes(search))
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,17 @@
|
||||
<%= gettext("notes") %>
|
||||
</h1>
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={:search}
|
||||
phx-change="search"
|
||||
phx-submit="search"
|
||||
phx-debounce="500"
|
||||
class="self-stretch flex flex-col items-stretch"
|
||||
>
|
||||
<%= text_input(f, :search_term, class: "input input-primary") %>
|
||||
</.form>
|
||||
|
||||
<%= if @notes |> Enum.empty?() do %>
|
||||
<h1 class="self-center text-primary-500">
|
||||
<%= gettext("no notes found") %>
|
||||
|
@ -59,15 +59,15 @@ defmodule MemexWeb.Router do
|
||||
|
||||
live "/notes/new", NoteLive.Index, :new
|
||||
live "/notes/:id/edit", NoteLive.Index, :edit
|
||||
live "/notes/:id/show/edit", NoteLive.Show, :edit
|
||||
live "/note/:id/edit", NoteLive.Show, :edit
|
||||
|
||||
live "/contexts/new", ContextLive.Index, :new
|
||||
live "/contexts/:id/edit", ContextLive.Index, :edit
|
||||
live "/contexts/:id/show/edit", ContextLive.Show, :edit
|
||||
live "/context/:id/show/edit", ContextLive.Show, :edit
|
||||
|
||||
live "/pipelines/new", PipelineLive.Index, :new
|
||||
live "/pipelines/:id/edit", PipelineLive.Index, :edit
|
||||
live "/pipelines/:id/show/edit", PipelineLive.Show, :edit
|
||||
live "/pipeline/:id/edit", PipelineLive.Show, :edit
|
||||
|
||||
get "/users/settings", UserSettingsController, :edit
|
||||
put "/users/settings", UserSettingsController, :update
|
||||
@ -79,13 +79,14 @@ defmodule MemexWeb.Router do
|
||||
pipe_through [:browser]
|
||||
|
||||
live "/notes", NoteLive.Index, :index
|
||||
live "/notes/:id", NoteLive.Show, :show
|
||||
live "/notes/:search", NoteLive.Index, :search
|
||||
live "/note/:id", NoteLive.Show, :show
|
||||
|
||||
live "/contexts", ContextLive.Index, :index
|
||||
live "/contexts/:id", ContextLive.Show, :show
|
||||
live "/context/:id", ContextLive.Show, :show
|
||||
|
||||
live "/pipelines", PipelineLive.Index, :index
|
||||
live "/pipelines/:id", PipelineLive.Show, :show
|
||||
live "/pipeline/:id", PipelineLive.Show, :show
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,17 +1,41 @@
|
||||
defmodule Memex.Repo.Migrations.CreateNotes do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
def up do
|
||||
create table(:notes, primary_key: false) do
|
||||
add :id, :binary_id, primary_key: true
|
||||
add :title, :string
|
||||
add :content, :text
|
||||
add :tags, {:array, :string}
|
||||
add :tags, {:array, :citext}
|
||||
add :visibility, :string
|
||||
|
||||
add :user_id, references(:users, on_delete: :delete_all, type: :binary_id)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
flush()
|
||||
|
||||
execute """
|
||||
CREATE FUNCTION immutable_array_to_string(text[], text)
|
||||
RETURNS text LANGUAGE sql IMMUTABLE as $$SELECT array_to_string($1, $2)$$
|
||||
"""
|
||||
|
||||
execute """
|
||||
ALTER TABLE notes
|
||||
ADD COLUMN search tsvector
|
||||
GENERATED ALWAYS AS (
|
||||
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
|
||||
setweight(to_tsvector('english', coalesce(immutable_array_to_string(tags, ' '), '')), 'B') ||
|
||||
setweight(to_tsvector('english', coalesce(content, '')), 'C')
|
||||
) STORED
|
||||
"""
|
||||
|
||||
execute("CREATE INDEX notes_trgm_idx ON notes USING GIN (search)")
|
||||
end
|
||||
|
||||
def down do
|
||||
drop table(:notes)
|
||||
execute("DROP FUNCTION immutable_array_to_string(text[], text)")
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user