pknoth
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 ?
Marked As Solved
pknoth
Can also use Erlang records as follow 
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"}]}
Also Liked
hauleth
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.
pknoth
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 
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"]]}







