vadimshvetsov
I’ve crawled couple related topics but still can’t implement reusable module for full-text search. I’m stuck with this module and can’t understand why it can’t find any row even when it has to. Looks like there is interpolation problem with creating multiple ts_vector but I’m not sure.
My goals:
- to abstract later search for other entities and have this api API
- to speed up this search with creating indexes while it has some joins (is it possible?) And is it normal to search within related tables?
- Consider to add tsv field with triggering updates and creating indexes and having to_tsvector value in db.
defmodule MyApp.Performer.Search do
import Ecto.Query
@fields ~w(bio first_name last_name phone email city)
@nullable_fields ~w(name)
defmacro tsquery(fields, terms, language \\ "english") do
quote do
fragment(
"to_tsvector(?, ?) @@ to_tsquery(?, ?)",
unquote(language),
unquote(fields),
unquote(language),
unquote(terms)
)
end
end
defmacro tsrankcd(fields, terms, language \\ "english") do
quote do
fragment(
"ts_rank_cd(to_tsvector(?, ?), to_tsquery(?, ?))",
unquote(language),
unquote(fields),
unquote(language),
unquote(terms)
)
end
end
def run(query, search_term) do
search_term = normalize(search_term)
fields = fields_to_tsvector_fields(@fields, @nullable_fields)
queryable =
from q in query,
join: p in assoc(q, :profile),
left_join: t in assoc(q, :topics),
where: tsquery(^fields, ^search_term),
order_by: [
desc: tsrankcd(^fields, ^search_term)
],
distinct: [q.id]
end
defp normalize(terms, operator \\ "&") do
terms
|> String.downcase()
|> String.trim()
|> String.replace(~r/\s+/u, " #{operator} ")
|> (&(&1 <> ":*")).()
end
def fields_to_tsvector_fields(non_nullable_fields, nullable_fields) do
nullable_fields
|> Enum.map(fn field -> "coalesce(#{field}, ' ')" end)
|> (&(non_nullable_fields ++ &1)).()
|> Enum.join(" || ' ' || ")
end
end
Would highly appreciate any advice or suggestion, thanks.
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
fuelen
Could you write few examples of SQL that you want to generate?
benwilson512
Ecto does not interpolate any values at all, ever. Rather it uses SQL parameters to avoid SQL injection. If you turn on debug logging you should be able to see the query that Ecto runs. Try running that in PSQL and tweaking the values until you get what you want, then adjust your ecto query accordingly.
thiagomajesk
Hi @vadimshvetsov! At work, we have implemented a small lib that helps us abstract what we need for full-text search. Take a look at our repo and see if it helps: GitHub - thiagomajesk/searchy: Full-text search capabilities for Ecto · GitHub.
vadimshvetsov
Hey, thank you all.
So this is the request. As I see it’s kinda hard to implement full text search in db query, maybe better to split them on query per table? I want to add tsvector field later for speed up things and it’s table specific.
Yep, I mean that mistake somewhere in my code, maybe because I don’t unquote variables when building
to_tsvectorvalue? Raw query works perfectly when I move SQL parameters to SQL query.Wow, that’s pretty solid, I will dig in. Is it possible to search things on many to many joins by related entity fields for example with this APIs?
thiagomajesk
I don’t know if I understood exactly what you need but as long as you have a
.
ts_vectorfield generated for the table you want to search, I guess you are good to go. Our use-case is very simplistic and the lib itself is just a thin wrapper that translates the queries according to the postgres docs. If this is not exactly what you need or it’s incomplete, feel free to open a discussion on the repo so we can talk about itvadimshvetsov
I’m wondering how to join search results of parent entity with joins and search inside join tables too.
So let’s say I have:
I want to search by
user.name,user.bioand joinedtopics.titleof thisuser.Is the best way look like this?
cpgo
I think it depends on how much complexity you want to put on the search optimization.
The query you posted might work, or you could create a materialized view with your tsvectors.
As most things, I would benchmark it before commiting to a more complex solution.
fuelen
I think you can avoid all that complexity with
fields_to_tsvector_fieldsfunction,@fieldsand@nullable_fieldsjust by usingconcat_ws.separatorwas moved to the last argument to have an ability to composeconcat_wscalls via pipe operatorthiagomajesk
Hello again @vadimshvetsov! Today at work we had to work with a similar problem.
Our use-case was searching in a many-to-many relationship that also has other filters specific to the table, and we solve it like this:
We had to use a named binding because the
to_tsqueryfunction had no clue of what binding was supposed to be applied since it’s positional. This allowed us to change which table we wanted to search in more complex queries.So, even though our lib does not support this specific use-case (as we noticed today), I hope to add this feature soon after we test the use-cases properly. In the meantime, I think you could build the query manually using something similar.
PS.: Bear in mind that we just use full-text search to simple scenario like searching one table at a time. If you care about performance, you might wanna look into this thread: Full text search over multiple related tables: indices and performance.
vadimshvetsov
Thanks a lot for helping me.
I’ve starred GitHub - thiagomajesk/searchy: Full-text search capabilities for Ecto · GitHub and will keep an eye on it. The reason why I’ve done this on my own is because I need to rank results and search in related tables.
So I’ve ended up with this one:
At first I’ve added
pg_termextension for incomplete word search:Then migrated searchable tables and add
tsvectorfield, trigger and index:Added
tsvectortype taken from GitHub - thiagomajesk/searchy: Full-text search capabilities for Ecto · GitHub source:Added
tsvectorfield to all searchable ecto schemas:Added
Search.Helpersmodule for convience:And finally added API for using in context:
I’m gonna star @thiagomajesk answer with
searchybecause it`s source code greatly led me to the final destination. Also I would like to mention this awesome answer - Search Bar Feature (Phoenix Searching) - #4 by idi527Hope this step-by-step example could help someone to get things done.