Mnesia qlc queries with filters error (:undefined_record)

Using Mnesia recently, I am trying to move from match_spec to qlc queries in order to query multiple tables at once. I am facing an error trying to apply qualifiers for filtering my results :

require Qlc

defmodule TestSchema do
  defstruct id: nil, field: nil
end

:mnesia.start()

:mnesia.create_table(:test, [
  ram_copies: [node()],
  record_name: TestSchema,
  attributes: [:id, :field],
  type: :ordered_set
])

Qlc.q("[A || A <- mnesia:table('test'), A#'TestSchema'.field == \"field 2\"]", [])

# => ** (MatchError) no match of right hand side value: {:not_ok, [error: {1, :erl_lint, {:undefined_record, :TestSchema}}]}
#  (qlc) lib/qlc.ex:183: Qlc.expr_to_handle/3

Is the syntax right ?

I a possible solution may be to declare Erlang record, Record.defrecord/2 only provide utilities to manipulate such type.

any clues ?

No, it is not correct as Elixir’s structures and Erlang’s records are completely different entities. In your case you want to create record:

require Qlc

defmodule TestSchema do
  Record.defrecord(:foo, id: nil, field: nil)
end

:mnesia.start()

:mnesia.create_table(:test, [
  ram_copies: [node()],
  record_name: :foo,
  attributes: [:id, :field],
  type: :ordered_set
])

However I am not sure if it is possible to make Qlc “aware” of the :foo record in such case. Alternatively if you are using Erlang 21+ then you can do:

Qlc.q("[A || A <- mnesia:table('test'), map_get(field, A) == \"field 2\"]", [])

IIRC there was Elixir library that provided similar features as qlc but was implemented as Elixir macro, but I cannot recall it’s name right now.

1 Like

Tried map_get/2 which don’t work, element/2 neither. It led me to a solution consisting in mnesia record pattern match.

An exemple below :slight_smile:

require Qlc

defmodule TestSchema do
  defstruct id: nil, field: nil
end

:mnesia.start()

:mnesia.create_table(:test, [
  ram_copies: [node()],
  record_name: TestSchema,
  attributes: [:id, :field],
  type: :ordered_set
])

:mnesia.transaction fn ->
  :mnesia.write(:test, {TestSchema, 1, "field 2"}, :write)
end

query = Qlc.q("[[Id, Field] || {Schema, Id, Field} <- mnesia:table('test'), Id == 1]", [])
IO.inspect :mnesia.transaction fn ->
  Qlc.e(query)
end

# => {:atomic, [[1, "field 2"]]}
1 Like

Can also use Erlang records as follow :slight_smile:

require Qlc

defmodule TestSchema do
  defstruct id: nil, field: nil
end

:mnesia.start()

IO.inspect :mnesia.create_table(:test, [
  ram_copies: [node()],
  record_name: :test,
  attributes: [:id, :field],
  type: :ordered_set,
  index: [:field]
])

:mnesia.transaction fn ->
  :mnesia.write(:test, {:test, 1, "field 2"}, :write)
end

defmodule Test do
  require Qlc.Record

  Qlc.Record.defrecord(:test, [:id, :field])

  def query() do
    query = Qlc.q("[X || X <- mnesia:table(test), #{test!(X, :id)} == Field]", [Field: 1])

    IO.puts :qlc.info(query)
    :mnesia.transaction fn ->
      IO.inspect :timer.tc(fn -> Qlc.e(query) end)
    end
  end
end

Test.query()

# {:atomic, :ok}
# mnesia:table(test,
#              [{traverse,
#                {select,
#                 [{'$1',
#                   [{'==', {element, 2, '$1'}, {const, 1000000}}],
#                   ['$1']}]}},
#               {n_objects, 100},
#               {lock, read}])
# {265701, [{:test, 1000000, "field 1000000"}]}
1 Like