Header logo.
A study of bugs
HomeArchiveTagsAboutFeed

Ecto search keyword interpolation

Updated on 2023-11-27:

Now I realize I didn't need to use a fragment. I could have used ilike.

The difference between ilike and like is that ilike is explicitly case-insensitive, whereas like could be case sensitive based on the database you are using.


Original post:

I was doing Dockyard's tutorial of Elixir and Phoenix. It took me an hour to figure out how to search a field for partial matching strings.

tl;dr, here's the code:

def search_posts(keyword) do
  search_pattern = "%#{keyword}%" # ❤️
  Repo.all(
    from p in Post,
    where:
      fragment("? LIKE ?",
          p.title,
          ^search_pattern)
  )
end

Inside an Ecto.Query statement, we need to use the caret to interpolate a variable. And this was novel to me. (Macros in Elixir enable innovative syntax, which is certainly powerful, but tends to be elusive to beginners.)

If we wrote SQL, it would be something like this, % being the wildcard for any number of chars.

SELECT * WHERE 'title' LIKE '%word%'

In order to send a query that looks something like the above, I used a fragment:

def search_posts(keyword) do
  Repo.all(
    from p in Post,
    where: fragment("? LIKE %?%", p.title, ^keyword)
  )
end

For some reason the SQL query after interpolation ended up looking like this.

SELECT p0."id", p0."title", p0."subtitle",
p0."content", p0."inserted_at",
p0."updated_at"
FROM "posts" AS p0
WHERE (p0."title" LIKE %$1%)

That's why I defensively used interpolation to get search_pattern on line ❤️ .