在Echo查询中通过该结构的其他值动态替换结构的某些值的最佳方法

时间:2017-07-22 14:03:11

标签: elixir

我有一个功能:

def listAll(dataSchema, fields) do                                 
    dataSchema |> order() |> Repo.all()
end

返回如下所示的查询结果:

[%Skeleton.Data.Evento{__meta__: #Ecto.Schema.Metadata<:loaded, "eventos">,
  date: "dewfwef", date_de: nil, date_es: nil, date_fr: nil, date_pt: nil,
  id: nil, inserted_at: nil, text: "hello", text_de: nil,
  text_es: nil, text_fr: "Bonjour", text_pt: "Olá", title: "Good morning", title_de: nil, title_es: nil, title_fr: nil, title_pt: "Bom dia"},
 %Skeleton.Data.Evento{__meta__: #Ecto.Schema.Metadata<:loaded, "eventos">,
  date: "ds", date_de: nil, date_es: nil, date_fr: nil, date_pt: nil, id: nil,
  inserted_at: nil, text: "Bye", text_de: nil, text_es: nil,
  text_fr: nil, text_pt: "Adeus", title: "Good evening", title_de: nil, title_es: nil, title_fr: nil, title_pt: "Boa noite"}]

fields是一个包含任意数量键的列表:

fields = [:date_pt, :text_pt, :title_pt]

如何将date, text, title的所有值(相同的键名但没有后缀)替换为text_pt的值(或fields参数中传递的任何键/键) ? 请注意,我们不知道我们拥有哪些密钥,因为它们作为参数在个案情况下传递,并且我希望在Ecto查询中执行此操作,而不是在结果的结构列表中执行此操作。

1 个答案:

答案 0 :(得分:1)

以下是我如何执行此操作,假设字段包含带有字符串的locale键,例如"pt"

def listAll(dataSchema, %{locale: locale}) do                                 
    text_field = :"text_#{locale}"
    dataSchema
    |> select([m], %{m | text: field(m, ^text_field)})
    |> order()
    |> Repo.all()
end

这会将text的值替换为动态计算字段text_field的值。

注意:您应确保使用白名单在某个时刻验证locale。如果用户可以发送任意区域设置,则创建该原子可能会导致VM崩溃。那:插值相当于String.to_atom("text_#{locale}")

对于编辑过的问题:函数的API看起来很奇怪。有可能采用更好的方法来实现您想要实现的目标。但如果你真的需要这样做,这是我现在能想到的最简单的方法:

def listAll(dataSchema, fields) do                                 
  dataSchema
  |> select([m], %{m | text: field(m, ^text_field)})
  |> order()
  |> Repo.all()
  |> Enum.map(fn record ->
    Enum.reduce(fields, record, fn field, record ->
      # You might want to replace the Regex with something faster like `:binary.match` + `:binary.part` since you only need to find a '_' and remove everything after that.
      without_suffix = field |> Atom.to_string |> String.replace(~r/_.*/, "") |> String.to_existing_atom
      Map.put(record, without_suffix, Map.get(record, field))
    end)
  end)
end