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()]
|
||||
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)
|
||||
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
|
||||
|
||||
@ -36,10 +60,34 @@ defmodule Memex.Notes do
|
||||
[%Note{title: "my 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,12 @@ 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))}
|
||||
def mount(%{"search" => search}, _session, socket) do
|
||||
{:ok, socket |> assign(search: search) |> display_notes()}
|
||||
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 +33,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 +58,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", value: @search) %>
|
||||
</.form>
|
||||
|
||||
<%= if @notes |> Enum.empty?() do %>
|
||||
<h1 class="self-center text-primary-500">
|
||||
<%= gettext("no notes found") %>
|
||||
|
@ -59,7 +59,7 @@ 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
|
||||
@ -79,7 +79,8 @@ 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
|
||||
|
@ -75,7 +75,7 @@ msgid "create invite"
|
||||
msgstr ""
|
||||
|
||||
#: 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
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "delete"
|
||||
@ -88,7 +88,7 @@ msgstr ""
|
||||
|
||||
#: lib/memex_web/live/context_live/index.html.heex:43
|
||||
#: 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/pipeline_live/index.html.heex:41
|
||||
#: lib/memex_web/live/pipeline_live/show.html.heex:35
|
||||
@ -117,7 +117,7 @@ msgstr ""
|
||||
msgid "new context"
|
||||
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
|
||||
msgid "new note"
|
||||
msgstr ""
|
||||
|
@ -15,7 +15,7 @@ msgstr ""
|
||||
msgid "%{title} created"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex_web/live/note_live/index.ex:48
|
||||
#: lib/memex_web/live/note_live/index.ex:57
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "%{title} deleted"
|
||||
msgstr ""
|
||||
@ -167,7 +167,7 @@ msgstr ""
|
||||
msgid "document your processes, attaching contexts to each step"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex_web/live/note_live/index.ex:24
|
||||
#: lib/memex_web/live/note_live/index.ex:23
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "edit %{title}"
|
||||
msgstr ""
|
||||
@ -267,7 +267,7 @@ msgstr ""
|
||||
msgid "no invites 😔"
|
||||
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
|
||||
msgid "no notes found"
|
||||
msgstr ""
|
||||
|
@ -142,7 +142,7 @@ msgid "are you sure you want to make %{invite_name} unlimited?"
|
||||
msgstr ""
|
||||
|
||||
#: 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
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "are you sure?"
|
||||
|
@ -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