ETS/matchspecs - Matching on struct attributes?

Hi

I have an ETS set table with a id -> struct row structure. I’d like to allow filtering on the structs in the table.

I imagine something like:

:ets.fun2ms(fn {_, struct} when struct.my_key == "something" -> {struct} end)

Is it possible to have erlang/ETS matchspecs that match against struct fields?

The key is not limited to a unique element, You can use a tuple key and filter on it…

Eg. You can even do this.

{id, struct} → struct

My question is more related to how exactly (if possible) the Erlang matchspecs will look when selecting/matching for a struct’s fields

Updated post with an example

I just modified example from Elixir School.

iex> :ets.new(:user_lookup, [:set, :protected, :named_table])
:user_lookup
iex> :ets.insert(:user_lookup, {{"doomspork", YdoDb.Gaming.Player}, "Sean", ["Elixir", "Ruby", "Java"]})
true
iex> :ets.match(:user_lookup, {{:"$1", YdoDb.Gaming.Player}, :"$2", :"$3"})
[["doomspork", "Sean", ["Elixir", "Ruby", "Java"]]]
iex> :ets.insert(:user_lookup, {{"doomsday", YdoDb.Gaming.Player}, "Sean2", ["Elixir", "Ruby"]})        
true
iex> :ets.insert(:user_lookup, {{"doomsday", YdoDb.Gaming.Game}, "Game", ["Elixir", "Ruby"]})     
true
iex> :ets.match(:user_lookup, {{:"$1", YdoDb.Gaming.Player}, :"$2", :"$3"})                     
[["doomspork", "Sean", ["Elixir", "Ruby", "Java"]],
 ["doomsday", "Sean2", ["Elixir", "Ruby"]]]
iex> :ets.match(:user_lookup, {{:"$1", YdoDb.Gaming.Game}, :"$2", :"$3"})  
[["doomsday", "Game", ["Elixir", "Ruby"]]]

As You can see, the first tuple element, is a tuple, which contains a structure.

On the match side, I filter with the structure, and retrieve 3 elements.

Is that what You are looking for?

With your example I see what You want more precisely.

The problem is more with the guard clause.

when struct.my_key == “something”

UPDATE: Maybe try this

iex> fun = :ets.fun2ms(fn {_, %Struct{key: key}} = record when key==“something” → record end)

Yeah this is what I want. I tried your suggestion by it does not work for the match specs

I tried this function…

iex> fun = :ets.fun2ms(fn {_, %YdoDb.Gaming.Player{name: name}} = player when name=="Honinbo Doetsu" -> player end)     
[{{:_, %{__struct__: YdoDb.Gaming.Player, name: :"$1"}},
  [{:==, :"$1", "Honinbo Doetsu"}], [:"$_"]}]
iex> :ets.select(:lookup, fun)                                                                                     
[{1,
  %YdoDb.Gaming.Player{__meta__: #Ecto.Schema.Metadata<:loaded, "players">,
   black_games: #Ecto.Association.NotLoaded<association :black_games is not loaded>,
   id: 1, inserted_at: ~N[2017-09-05 10:30:42.076992], name: "Honinbo Doetsu",
   updated_at: ~N[2017-09-05 10:30:42.081534],
   white_games: #Ecto.Association.NotLoaded<association :white_games is not loaded>}}]

And it is working for me.

But the match spec was not what You wanted. So I changed the example.

Note: the struct does not have to be the key, it can be the value.

3 Likes

Sweet that worked, thanks!