Added a search method using postgres fulltext search
This commit is contained in:
parent
00f7aef8dc
commit
ed759e5e09
6 changed files with 99 additions and 2 deletions
|
|
@ -47,6 +47,42 @@ defmodule Pinchflat.Media do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of media_items that match the search term. Adds a `matching_search_term`
|
||||
virtual field to the result set.
|
||||
|
||||
Returns [%MediaItem{}, ...].
|
||||
|
||||
TODO: test limit
|
||||
"""
|
||||
def search(search_term, opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit, 50)
|
||||
|
||||
from(mi in MediaItem,
|
||||
where: fragment("searchable @@ websearch_to_tsquery(?)", ^search_term),
|
||||
select_merge: %{
|
||||
matching_search_term:
|
||||
fragment(
|
||||
"""
|
||||
ts_headline(
|
||||
'english',
|
||||
CONCAT(title, ' ', description),
|
||||
websearch_to_tsquery(?),
|
||||
'StartSel=[PF_HIGHLIGHT],StopSel=[/PF_HIGHLIGHT]'
|
||||
)
|
||||
""",
|
||||
^search_term
|
||||
)
|
||||
},
|
||||
order_by: {
|
||||
:desc,
|
||||
fragment("ts_rank_cd(searchable, websearch_to_tsquery(?), 0)", ^search_term)
|
||||
},
|
||||
limit: ^limit
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single media_item.
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ defmodule Pinchflat.Media.MediaItem do
|
|||
# Will very likely revisit because I can't leave well-enough alone.
|
||||
field :subtitle_filepaths, {:array, {:array, :string}}, default: []
|
||||
|
||||
field :matching_search_term, :string, virtual: true
|
||||
|
||||
belongs_to :source, Source
|
||||
|
||||
has_one :metadata, MediaMetadata, on_replace: :update
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Pinchflat.Repo.Migrations.AddSearchFieldToMediaItems do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
execute """
|
||||
ALTER TABLE media_items
|
||||
ADD COLUMN searchable tsvector
|
||||
GENERATED ALWAYS AS (
|
||||
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
|
||||
setweight(to_tsvector('english', coalesce(description, '')), 'B')
|
||||
) STORED;
|
||||
"""
|
||||
|
||||
execute """
|
||||
CREATE INDEX media_items_searchable_idx ON media_items USING gin(searchable);
|
||||
"""
|
||||
end
|
||||
|
||||
def down do
|
||||
execute """
|
||||
DROP INDEX media_items_searchable_idx;
|
||||
"""
|
||||
|
||||
alter table(:media_items) do
|
||||
remove :searchable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -60,7 +60,7 @@ defmodule Pinchflat.MediaClient.VideoDownloaderTest do
|
|||
test "it sets the media_downloaded_at", %{media_item: media_item} do
|
||||
assert media_item.media_downloaded_at == nil
|
||||
assert {:ok, updated_media_item} = VideoDownloader.download_for_media_item(media_item)
|
||||
assert DateTime.diff(DateTime.utc_now(), updated_media_item.media_downloaded_at) < 1
|
||||
assert DateTime.diff(DateTime.utc_now(), updated_media_item.media_downloaded_at) < 2
|
||||
end
|
||||
|
||||
test "it extracts the title", %{media_item: media_item} do
|
||||
|
|
|
|||
|
|
@ -183,6 +183,37 @@ defmodule Pinchflat.MediaTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "search/1" do
|
||||
setup do
|
||||
media_item =
|
||||
media_item_fixture(%{
|
||||
title: "The quick brown fox",
|
||||
description: "jumps over the lazy dog"
|
||||
})
|
||||
|
||||
{:ok, %{media_item_id: media_item.id}}
|
||||
end
|
||||
|
||||
test "searches based on title", %{media_item_id: media_item_id} do
|
||||
assert [%{id: ^media_item_id}] = Media.search("quick")
|
||||
end
|
||||
|
||||
test "searches based on description", %{media_item_id: media_item_id} do
|
||||
assert [%{id: ^media_item_id}] = Media.search("lazy")
|
||||
end
|
||||
|
||||
test "adds a matching_search_term attribute with the relevant text" do
|
||||
assert [res] = Media.search("quick")
|
||||
assert String.contains?(res.matching_search_term, "The [PF_HIGHLIGHT]quick[/PF_HIGHLIGHT] brown fox")
|
||||
end
|
||||
|
||||
test "optionall lets you specify a limit" do
|
||||
media_item_fixture(%{title: "The small gray dog"})
|
||||
|
||||
assert [_] = Media.search("dog", limit: 1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_media_item!/1" do
|
||||
test "it returns the media_item with given id" do
|
||||
media_item = media_item_fixture()
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ defmodule Pinchflat.Tasks.SourceTasksTest do
|
|||
SourceTasks.index_media_items(source)
|
||||
source = Repo.reload!(source)
|
||||
|
||||
assert DateTime.diff(DateTime.utc_now(), source.last_indexed_at) < 1
|
||||
assert DateTime.diff(DateTime.utc_now(), source.last_indexed_at) < 2
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue