feat(search): use postgres native fulltext search
All checks were successful
Build / build (push) Successful in 3m46s

This commit is contained in:
Daniel Kempkens 2023-10-09 21:51:53 +02:00
parent 5397833935
commit d40db4ca4c
Signed by: daniel
SSH key fingerprint: SHA256:Ks/MyhQYcPRQiwMKLAKquWCdCPe3JXlb1WttgnAoSeM
3 changed files with 67 additions and 12 deletions

View file

@ -46,8 +46,6 @@ defmodule BdfrBrowser.Comment do
def search(str), do: search(str, nil) def search(str), do: search(str, nil)
def search(str, subreddits) when is_nil(subreddits) do def search(str, subreddits) when is_nil(subreddits) do
search_str = "%#{str}%"
from(c in __MODULE__, from(c in __MODULE__,
join: p in assoc(c, :post), join: p in assoc(c, :post),
join: s in assoc(p, :subreddit), join: s in assoc(p, :subreddit),
@ -62,15 +60,15 @@ defmodule BdfrBrowser.Comment do
post_title: p.title, post_title: p.title,
post_date: fragment("to_char(?, 'YYYY-MM')", p.posted_at) post_date: fragment("to_char(?, 'YYYY-MM')", p.posted_at)
}, },
where: ilike(c.body, ^search_str), where:
fragment("? @@ websearch_to_tsquery('english', ?)", c.searchable_content, ^str) or
fragment("? @@ websearch_to_tsquery('german', ?)", c.searchable_content, ^str),
order_by: [desc: c.posted_at], order_by: [desc: c.posted_at],
group_by: [c.id, p.id, s.name] group_by: [c.id, p.id, s.name]
) )
end end
def search(str, subreddits) when is_list(subreddits) do def search(str, subreddits) when is_list(subreddits) do
search_str = "%#{str}%"
from(c in __MODULE__, from(c in __MODULE__,
join: p in assoc(c, :post), join: p in assoc(c, :post),
join: s in assoc(p, :subreddit), join: s in assoc(p, :subreddit),
@ -85,7 +83,10 @@ defmodule BdfrBrowser.Comment do
post_title: p.title, post_title: p.title,
post_date: fragment("to_char(?, 'YYYY-MM')", p.posted_at) post_date: fragment("to_char(?, 'YYYY-MM')", p.posted_at)
}, },
where: s.name in ^subreddits and ilike(c.body, ^search_str), where:
s.name in ^subreddits and
(fragment("? @@ websearch_to_tsquery('english', ?)", c.searchable_content, ^str) or
fragment("? @@ websearch_to_tsquery('german', ?)", c.searchable_content, ^str)),
order_by: [desc: c.posted_at], order_by: [desc: c.posted_at],
group_by: [c.id, p.id, s.name] group_by: [c.id, p.id, s.name]
) )

View file

@ -123,8 +123,6 @@ defmodule BdfrBrowser.Post do
def search(str), do: search(str, nil) def search(str), do: search(str, nil)
def search(str, subreddits) when is_nil(subreddits) do def search(str, subreddits) when is_nil(subreddits) do
search_str = "%#{str}%"
from(p in __MODULE__, from(p in __MODULE__,
left_join: c in assoc(p, :comments), left_join: c in assoc(p, :comments),
join: s in assoc(p, :subreddit), join: s in assoc(p, :subreddit),
@ -137,15 +135,15 @@ defmodule BdfrBrowser.Post do
subreddit: s.name, subreddit: s.name,
date: fragment("to_char(?, 'YYYY-MM')", p.posted_at) date: fragment("to_char(?, 'YYYY-MM')", p.posted_at)
}, },
where: ilike(p.title, ^search_str) or ilike(p.selftext, ^search_str), where:
fragment("? @@ websearch_to_tsquery('english', ?)", p.searchable_content, ^str) or
fragment("? @@ websearch_to_tsquery('german', ?)", p.searchable_content, ^str),
order_by: [desc: p.posted_at], order_by: [desc: p.posted_at],
group_by: [p.id, s.name] group_by: [p.id, s.name]
) )
end end
def search(str, subreddits) when is_list(subreddits) do def search(str, subreddits) when is_list(subreddits) do
search_str = "%#{str}%"
from(p in __MODULE__, from(p in __MODULE__,
left_join: c in assoc(p, :comments), left_join: c in assoc(p, :comments),
join: s in assoc(p, :subreddit), join: s in assoc(p, :subreddit),
@ -158,7 +156,10 @@ defmodule BdfrBrowser.Post do
subreddit: s.name, subreddit: s.name,
date: fragment("to_char(?, 'YYYY-MM')", p.posted_at) date: fragment("to_char(?, 'YYYY-MM')", p.posted_at)
}, },
where: s.name in ^subreddits and (ilike(p.title, ^search_str) or ilike(p.selftext, ^search_str)), where:
s.name in ^subreddits and
(fragment("? @@ websearch_to_tsquery('english', ?)", p.searchable_content, ^str) or
fragment("? @@ websearch_to_tsquery('german', ?)", p.searchable_content, ^str)),
order_by: [desc: p.posted_at], order_by: [desc: p.posted_at],
group_by: [p.id, s.name] group_by: [p.id, s.name]
) )

View file

@ -0,0 +1,53 @@
defmodule BdfrBrowser.Repo.Migrations.AddPostCommentFulltextSearch do
use Ecto.Migration
def up do
# Posts
execute """
ALTER TABLE posts
ADD COLUMN searchable_content tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('german', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(selftext, '')), 'B') ||
setweight(to_tsvector('german', coalesce(selftext, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(author, '')), 'C')
) STORED;
"""
execute """
CREATE INDEX posts_searchable_content_index ON posts USING gin(searchable_content);
"""
# Comments
execute """
ALTER TABLE comments
ADD COLUMN searchable_content tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(body, '')), 'A') ||
setweight(to_tsvector('german', coalesce(body, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(author, '')), 'B')
) STORED;
"""
execute """
CREATE INDEX comments_searchable_content_index ON comments USING gin(searchable_content);
"""
end
def down do
# Posts
drop index("posts", [:searchable_content])
alter table(:posts) do
remove :searchable_content
end
# Comments
drop index("comments", [:searchable_content])
alter table(:comments) do
remove :searchable_content
end
end
end