Эликсир: Mnesia: Какой самый краткий способ обновить набор значений в элементе?

Проблема

Научившись использовать Mnesia с Elixir, я создал таблицу с несколькими функциями (такими как чтение, запись ...). Один из них - обновить набор полей (размер от 1 до count - 1) без изменения остальных данных и ограничения логики до минимума внутри транзакции mnesia.

Во время поиска я случайно нашел это: Erlang: Mnesia: обновление одного значения поля в строке (код ниже). Это тот же вопрос, но для Erlang, а не для Elixir.

Код

Насколько я понимаю, он работает в Erlang, поскольку чтение возвращает кортеж, который непосредственно устанавливается внутри записи, что позволяет нам сохранять определенные данные в действии записи в том виде, в каком они названы.

update_a(Tab, Key, Value) ->
  fun() ->
    [P] = mnesia:wread({Tab, Key}),
    mnesia:write(P#pixel{a=Value})
  end.

Где для Elixir, несмотря на то, что записи существуют, это будет просто кортеж, в котором вы можете только изменять данные в индексе и возвращать полный кортеж для действия записи.

Table: {table_name, id, data1, data2, data3, data4}
changes = [{2, new_val_for_data1}, {4, new_val_for_data3}]

def handle_call({:update_and_read, table, {id, changes}}, _, state) do
  {:atomic, new_object} =
    :mnesia.transaction(fn ->
      object =
        :mnesia.wread({table, id})
        |> List.first()

      ret =
        Enum.reduce(changes, object, fn {index, value}, acc ->
          acc |> Tuple.delete_at(index) |> Tuple.insert_at(index, value)
        end)

      :mnesia.write(object)
      ret
    end)

  {:reply, {:ok, new_object}, state}
end

Вопрос

Можно ли иметь более короткую функцию в Elixir (в идеале, две строки, как в Erlang)?


person Aridjar    schedule 23.07.2020    source источник


Ответы (1)


Что ж, вы можете использовать Record с некоторыми предварительными шагами (например, определение записи внутри вашего приложения).

defmodule Pixel do
  require Record
  Record.defrecord(:table_name, id: nil, data1: nil, data2: nil)
end

и в модуле, в котором вы хотите обновить таблицу

import Pixel

def handle_call({:update_and_read, table, {id, changes}}, _, state) do
  # changes = [data2: new_val_for_data1, ...]
  {:atomic, result} =
    :mnesia.transaction(fn ->
      case :mnesia.wread({table, id}) do
        [object] -> 
          :mnesia.write(pixel(object, changes))
          {:ok, object}
        [] -> :error
      end
    end)

  {:reply, result, state}
end

Другая возможность - пройти

changes = %{2 => new_val_for_data1, 4 => new_val_for_data3}
object
|> Tuple.to_list()
|> Enum.with_index()
|> Enum.map(fn {value, idx} -> Map.get(changes, idx, value) end)
|> List.to_tuple()

Другой возможностью было бы объявить макрос, который принимает кортеж, представляющий строку таблицы и список из {idx, new_value} кортежей, и изменяет соответствующие элементы на месте.

person Aleksei Matiushkin    schedule 23.07.2020