How to partially match a string in ETS' match specs?

I am working on a small hobby project which uses ETS/(Am)nesia to learn more about how these systems work.

One thing I am struggling with, is the following:

A user might want to look up records by entering a postal code. Here in the Netherlands, postal codes have the format 1234AB that is, four (base-10) digits followed by two (always capital) letters. However, I want to allow users to be able to find all records having the postal code “1234AB”, “1234AC”, “1234DH”, etc… if they only enter “1234”. In more formal terms: I want users to find all postal codes that contain the prefix they entered.

In SQL this can be done by using a LIKE operation: SELECT * from `myTable` where `postalcode` LIKE '1234%'.
I was wondering if (how) something like this can be done using ETS’ Match Specs.

Any help would be greatly appreciated :blush:

1 Like

:ets.match_object(:your_table_name, {[numeric_part, :_]})
between [] is your unique key
the second part is the alphanumerical part

you create the table with f.e. :ets.new(:your_table_name, [:named_table, read_concurrency: true])

No idea if this is possible with the complete postcode in one field though.

AFAIK, matching a binary prefix is not supported with matchspecs. A couple of options I see:

  1. Consider splitting postal code into two fields - the integer part and the letter suffiix. Then you could match on the exact integer part, since matching the exact binary value is supported with matchspecs.
  2. Use charlists instead for the postal code, because you can match the head of a charlist, which allows you to select arbitrary prefix.
  3. You could hack with comparison operators >= and < to test whether a value is greater than or equal to “12345” and less than “12346”. As far as I can tell, that might work.
  4. Iterate through table records, fetch the postal code for each record, then if the code matches the required prefix fetch the desired fields.

To test what can work with matchspecs and what isn’t supported, you can play with :ets.fun2ms from the iex shell:

iex(1)> :ets.fun2ms(fn({key, "12345"} = el) -> key end)
[{{:"$1", "12345"}, [], [:"$1"]}]

iex(2)> :ets.fun2ms(fn({key, "12345" <> _} = el) -> key end)
Error: fun head contains bit syntax matching of variable '_', which cannot be translated into match_spec
5 Likes