Reputation: 3920
I have some tuples stored in an Elixir :ets table with the following format,
{id, "first_name", "last_name"}
I'm looking to search the ETS table on both first and last name given a search term. For example if someone's first name was Alice, the search would return this tuple for "Al", "Ali", "Alic", etc.
So far I've tried:
:ets.match_object(table_name, {:_, :_, "last_name"})
which works when it matches exactly on "last_name" but if I have "last" or some shorter permutation in the search string it does not return a result.
I have also looked at search vectors, ets.match, ets.lookup and a few other ways to perform the search. Is there some built in, or other Elixiry way to do this type of string searching?
Upvotes: 1
Views: 897
Reputation: 121010
One might use a Swiss knife of ets
lookups, :ets.foldl/3
.
:ets.foldl(fn
{_, "Al" <> _, _} = y, acc -> [y | acc] # firstname
{_, _, "Al" <> _} = y, acc -> [y | acc] # lastname
_, acc -> acc # no match
end, [], table)
Please note, this is less efficient, compared to the solution proposed by @MikhailAksenov because the search is done in the calling process and hence all the data has to be transferred there, instead of being filtered “inside” the ets
.
Upvotes: 1
Reputation: 571
Rememeber that you have universal comparison rules in Erlang which means you can compare strings "Alice"
and "Al"
since they're just lists.
Now to the fun part:
table = :ets.new(:table, [:protected, :set])
:ets.insert(table, {1, "Alice", "Dannings"})
:ets.insert(table, {2, "Ali", "Aaqib"})
:ets.insert(table, {3, "Bob", "Dannings"})
:ets.insert(table, {4, "Amanda", "Clarke"})
:ets.insert(table, {5, "Al", "Clarke"})
# now let's explore elixir comparison rules
"Alice" >= "Al"
"Alice" < "Am"
# based on rules above we can make a selector which will be equivalent to starts_with?
selector = :ets.fun2ms(
fn({_, x, _} = y) # match all three element tuples
when # where
x >= "Al" and x < "Am" # second element is greater or equal to "Al" and less than "Am" ("m" is the next letter in the alphabet)
-> y end)
:ets.select(table, selector)
#[{1, "Alice", "Dannings"}, {2, "Ali", "Aaqib"}, {5, "Al", "Clarke"}]
This is hacky, though you can "search" by the beginning of the string.
If you want to use it in runtime, you must either set @compile {:parse_transform, :ms_transform}
at the beginning of your module or use library like https://github.com/ericmj/ex2ms to make sure you are generating match specs (selector) properly.
Upvotes: 3