Ecto IN to match substring

Trying to match a partial string against an array of full strings. Here’s where I’m stuck:

full_names = ["Larry King", "Jessica Jackson", "Billy Idol", "Ray Charles"]
from(p in Person, where: p.first_name in ^full_names) |> Repo.all

First name wont match because it’s looking for an exact match. How can I go about this?

Been messing with ARRAY_TO_STRING with an example like this as raw sql but haven’t gotten anything to work.

SELECT first_name
  FROM people
  WHERE first_name SIMILAR TO ARRAY_TO_STRING((SELECT full_name FROM leaders), ',')

You’ll have to use Fragment

Heres what I came up with.

# Convert list of full names to a list of first names
full_names = ["Larry King", "Jessica Jackson", "Billy Idol", "Ray Charles"]
first_names = 
  Enum.map(full_names, fn full_name -> 
    full_name
    |> String.split(" ")
    |> List.first()
  end)

# Dynamically return an ecto `or/2` statement based on how many entries in your list
# in our case 4
conditions = Enum.reduce(first_names, false, fn name, acc ->
  dynamic([d], d.first_name == ^name or ^acc)
end)

# Query that returns all matches
matches =
  from(d in leaders,
    select: d.first_name,
    where: ^conditions
    )
  |> Repo.all()

The example doesn’t exactly correspond but the idea is the same. Struggled a lot with this one but I learned how to use Ecto.Query.dynamic/2 with Enum.reduce/3 and there’s a ton of potential there.

# The query builds as
Ecto.Query<from d0 in leaders,
 where: d0.first_name == ^"Larry" or (d0.first_name == ^"Jessica" or (d0.first_name == ^"Billy" or (d0.first_name == ^"Ray" or ^false))),
 select: d0.first_name>
2 Likes