Elixir : Mnesia : 更新元素中一组值的最简洁方法是什么?
Elixir : Mnesia : What is the most concise way to update a set of value in an element?
问题
学习将 Mnesia 与 Elixir 结合使用,我创建了一个具有多种功能(例如读取、写入...)的 table。其中之一是在不更改其余数据的情况下更新一组字段(大小从 1 到 count - 1),并将逻辑限制在 mnesia 事务中的最小值。
在搜索过程中,我碰巧发现了这个:Erlang : Mnesia : Updating a single field value in a row 虽然(代码如下)。这是同一个问题,但对于 Erlang,而不是 Elixir。
代码
据我所知,它在 Erlang 中的工作方式是读取 returns 一个直接设置在记录中的元组,这允许我们在写入操作中保存特定数据,因为它们被命名。
update_a(Tab, Key, Value) ->
fun() ->
[P] = mnesia:wread({Tab, Key}),
mnesia:write(P#pixel{a=Value})
end.
其中,对于 Elixir,即使存在记录,它也只是一个元组,您只能在其中更改索引中的数据,并且 return 写入操作的完整元组。
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 中的那样只有 2 行)?
嗯,您可以使用 Record
一些初步步骤(例如在您的应用程序中定义记录。)
defmodule Pixel do
require Record
Record.defrecord(:table_name, id: nil, data1: nil, data2: nil)
end
并在您要更新的模块中 table
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()
另一种可能性是声明一个接受元组的宏,表示 table 行和 {idx, new_value}
元组的列表,并就地更改相应的元素。
问题
学习将 Mnesia 与 Elixir 结合使用,我创建了一个具有多种功能(例如读取、写入...)的 table。其中之一是在不更改其余数据的情况下更新一组字段(大小从 1 到 count - 1),并将逻辑限制在 mnesia 事务中的最小值。
在搜索过程中,我碰巧发现了这个:Erlang : Mnesia : Updating a single field value in a row 虽然(代码如下)。这是同一个问题,但对于 Erlang,而不是 Elixir。
代码
据我所知,它在 Erlang 中的工作方式是读取 returns 一个直接设置在记录中的元组,这允许我们在写入操作中保存特定数据,因为它们被命名。
update_a(Tab, Key, Value) ->
fun() ->
[P] = mnesia:wread({Tab, Key}),
mnesia:write(P#pixel{a=Value})
end.
其中,对于 Elixir,即使存在记录,它也只是一个元组,您只能在其中更改索引中的数据,并且 return 写入操作的完整元组。
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 中的那样只有 2 行)?
嗯,您可以使用 Record
一些初步步骤(例如在您的应用程序中定义记录。)
defmodule Pixel do
require Record
Record.defrecord(:table_name, id: nil, data1: nil, data2: nil)
end
并在您要更新的模块中 table
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()
另一种可能性是声明一个接受元组的宏,表示 table 行和 {idx, new_value}
元组的列表,并就地更改相应的元素。