add search to notes
This commit is contained in:
parent
b95d3039bb
commit
f9be5229e7
@ -20,10 +20,34 @@ defmodule Memex.Notes do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec list_notes(User.t()) :: [Note.t()]
|
@spec list_notes(User.t()) :: [Note.t()]
|
||||||
def list_notes(%{id: user_id}) do
|
@spec list_notes(search :: String.t() | nil, User.t()) :: [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)
|
Repo.all(from n in Note, where: n.user_id == ^user_id, order_by: n.title)
|
||||||
end
|
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 """
|
@doc """
|
||||||
Returns the list of public notes for viewing
|
Returns the list of public notes for viewing
|
||||||
|
|
||||||
@ -36,10 +60,34 @@ defmodule Memex.Notes do
|
|||||||
[%Note{title: "my note"}, ...]
|
[%Note{title: "my note"}, ...]
|
||||||
"""
|
"""
|
||||||
@spec list_public_notes() :: [Note.t()]
|
@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)
|
Repo.all(from n in Note, where: n.visibility == :public, order_by: n.title)
|
||||||
end
|
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 """
|
@doc """
|
||||||
Gets a single note.
|
Gets a single note.
|
||||||
|
|
||||||
|
@ -3,13 +3,12 @@ defmodule MemexWeb.NoteLive.Index do
|
|||||||
alias Memex.{Notes, Notes.Note}
|
alias Memex.{Notes, Notes.Note}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, %{assigns: %{current_user: current_user}} = socket)
|
def mount(%{"search" => search}, _session, socket) do
|
||||||
when not (current_user |> is_nil()) do
|
{:ok, socket |> assign(search: search) |> display_notes()}
|
||||||
{:ok, socket |> assign(notes: Notes.list_notes(current_user))}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
{:ok, socket |> assign(notes: Notes.list_public_notes())}
|
{:ok, socket |> assign(search: nil) |> display_notes()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -34,7 +33,17 @@ defmodule MemexWeb.NoteLive.Index do
|
|||||||
defp apply_action(socket, :index, _params) do
|
defp apply_action(socket, :index, _params) do
|
||||||
socket
|
socket
|
||||||
|> assign(page_title: "notes")
|
|> assign(page_title: "notes")
|
||||||
|
|> assign(search: nil)
|
||||||
|> assign(note: 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
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -49,4 +58,22 @@ defmodule MemexWeb.NoteLive.Index do
|
|||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
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
|
end
|
||||||
|
@ -3,6 +3,17 @@
|
|||||||
<%= gettext("notes") %>
|
<%= gettext("notes") %>
|
||||||
</h1>
|
</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", value: @search) %>
|
||||||
|
</.form>
|
||||||
|
|
||||||
<%= if @notes |> Enum.empty?() do %>
|
<%= if @notes |> Enum.empty?() do %>
|
||||||
<h1 class="self-center text-primary-500">
|
<h1 class="self-center text-primary-500">
|
||||||
<%= gettext("no notes found") %>
|
<%= gettext("no notes found") %>
|
||||||
|
@ -59,7 +59,7 @@ defmodule MemexWeb.Router do
|
|||||||
|
|
||||||
live "/notes/new", NoteLive.Index, :new
|
live "/notes/new", NoteLive.Index, :new
|
||||||
live "/notes/:id/edit", NoteLive.Index, :edit
|
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/new", ContextLive.Index, :new
|
||||||
live "/contexts/:id/edit", ContextLive.Index, :edit
|
live "/contexts/:id/edit", ContextLive.Index, :edit
|
||||||
@ -79,7 +79,8 @@ defmodule MemexWeb.Router do
|
|||||||
pipe_through [:browser]
|
pipe_through [:browser]
|
||||||
|
|
||||||
live "/notes", NoteLive.Index, :index
|
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", ContextLive.Index, :index
|
||||||
live "/contexts/:id", ContextLive.Show, :show
|
live "/contexts/:id", ContextLive.Show, :show
|
||||||
|
@ -75,7 +75,7 @@ msgid "create invite"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/context_live/index.html.heex:53
|
#: lib/memex_web/live/context_live/index.html.heex:53
|
||||||
#: lib/memex_web/live/note_live/index.html.heex:32
|
#: lib/memex_web/live/note_live/index.html.heex:43
|
||||||
#: lib/memex_web/live/pipeline_live/index.html.heex:51
|
#: lib/memex_web/live/pipeline_live/index.html.heex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "delete"
|
msgid "delete"
|
||||||
@ -88,7 +88,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/memex_web/live/context_live/index.html.heex:43
|
#: lib/memex_web/live/context_live/index.html.heex:43
|
||||||
#: lib/memex_web/live/context_live/show.html.heex:40
|
#: lib/memex_web/live/context_live/show.html.heex:40
|
||||||
#: lib/memex_web/live/note_live/index.html.heex:23
|
#: lib/memex_web/live/note_live/index.html.heex:34
|
||||||
#: lib/memex_web/live/note_live/show.html.heex:27
|
#: lib/memex_web/live/note_live/show.html.heex:27
|
||||||
#: lib/memex_web/live/pipeline_live/index.html.heex:41
|
#: lib/memex_web/live/pipeline_live/index.html.heex:41
|
||||||
#: lib/memex_web/live/pipeline_live/show.html.heex:35
|
#: lib/memex_web/live/pipeline_live/show.html.heex:35
|
||||||
@ -117,7 +117,7 @@ msgstr ""
|
|||||||
msgid "new context"
|
msgid "new context"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/note_live/index.html.heex:41
|
#: lib/memex_web/live/note_live/index.html.heex:52
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "new note"
|
msgid "new note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -15,7 +15,7 @@ msgstr ""
|
|||||||
msgid "%{title} created"
|
msgid "%{title} created"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/note_live/index.ex:48
|
#: lib/memex_web/live/note_live/index.ex:57
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{title} deleted"
|
msgid "%{title} deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -167,7 +167,7 @@ msgstr ""
|
|||||||
msgid "document your processes, attaching contexts to each step"
|
msgid "document your processes, attaching contexts to each step"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/note_live/index.ex:24
|
#: lib/memex_web/live/note_live/index.ex:23
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "edit %{title}"
|
msgid "edit %{title}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -267,7 +267,7 @@ msgstr ""
|
|||||||
msgid "no invites 😔"
|
msgid "no invites 😔"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/note_live/index.html.heex:8
|
#: lib/memex_web/live/note_live/index.html.heex:19
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "no notes found"
|
msgid "no notes found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -142,7 +142,7 @@ msgid "are you sure you want to make %{invite_name} unlimited?"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/context_live/index.html.heex:51
|
#: lib/memex_web/live/context_live/index.html.heex:51
|
||||||
#: lib/memex_web/live/note_live/index.html.heex:29
|
#: lib/memex_web/live/note_live/index.html.heex:40
|
||||||
#: lib/memex_web/live/pipeline_live/index.html.heex:49
|
#: lib/memex_web/live/pipeline_live/index.html.heex:49
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "are you sure?"
|
msgid "are you sure?"
|
||||||
|
@ -1,17 +1,41 @@
|
|||||||
defmodule Memex.Repo.Migrations.CreateNotes do
|
defmodule Memex.Repo.Migrations.CreateNotes do
|
||||||
use Ecto.Migration
|
use Ecto.Migration
|
||||||
|
|
||||||
def change do
|
def up do
|
||||||
create table(:notes, primary_key: false) do
|
create table(:notes, primary_key: false) do
|
||||||
add :id, :binary_id, primary_key: true
|
add :id, :binary_id, primary_key: true
|
||||||
add :title, :string
|
add :title, :string
|
||||||
add :content, :text
|
add :content, :text
|
||||||
add :tags, {:array, :string}
|
add :tags, {:array, :citext}
|
||||||
add :visibility, :string
|
add :visibility, :string
|
||||||
|
|
||||||
add :user_id, references(:users, on_delete: :delete_all, type: :binary_id)
|
add :user_id, references(:users, on_delete: :delete_all, type: :binary_id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user