【问题标题】:Building a custom select in Ecto queries在 Ecto 查询中构建自定义选择
【发布时间】:2017-02-02 23:01:35
【问题描述】:

我正在使用 Phoenix 和 Ecto 2.0 构建一个应用程序,它将大量数据存储在多个模型中。

我希望允许用户执行查询并仅选择所有字段的子集。

我已经能够让标准完美运行(在这方面,Ecto 比我预期的更容易使用;对开发人员来说是个大道具!),但是我无法弄清楚选择的东西。

我的初始查询如下所示:

query =
  from e in Event,
    left_join: t in assoc(e, :event_type),
    left_join: r in assoc(e, :event_reason_code),
    left_join: o in assoc(e, :event_outcome_code),
    left_join: i in assoc(e, :item),
    left_join: l in assoc(e, :location),
    left_join: c in assoc(l, :client)

我希望能够动态分配一个选择。

现在,我看起来像:

["event.id", "client.name", ...]

然后我根据第一个句点之前的内容进行模式匹配,然后构建 SQL 以选择正确的字段。

我尝试做这样的事情来让您了解我想要完成的事情:

defp build_selects(query, event_query) do
  select query, [e, t, r, o, i, l, c], Enum.reduce(event_query.select, {}, fn(key, acc) ->
    field =
      case key do
        "event.info." <> rest -> fragment("?->>?", field(e, :info), String.to_atom(rest))
        "event." <> rest -> field(e, String.to_atom(rest))
        "event_type." <> rest -> field(t, String.to_atom(rest))
        "event_reason_code." <> rest -> field(r, String.to_atom(rest))
        "event_outcome_code." <> rest -> field(o, String.to_atom(rest))
        "item.info." <> rest -> fragment("?->>?", field(i, :info), String.to_atom(rest))
        "item." <> rest -> field(i, String.to_atom(rest))
        "location.info." <> rest -> fragment("?->>?", field(l, :info), String.to_atom(rest))
        "location." <> rest -> field(l, String.to_atom(rest))
        "client.info." <> rest -> fragment("?->>?", field(c, :info), String.to_atom(rest))
        "client." <> rest -> field(c, String.to_atom(rest))
      end

    Tuple.append acc, field
  end)
end

不幸的是,在build_selects/2 内的build_select/2 调用中出现错误undefined function c/0 失败。

我已经找到了一种使用原始 SQL 构建查询的方法,该方法运行良好,但我知道这会使自己容易遭受注入攻击等,所以如果可能的话,我想站在 Ecto 的肩膀上.

我希望这是有道理的;如果不是,我会尽力澄清。

谢谢!

更新

我又浏览了 Ecto 的源代码,发现了Ecto.Query.API.take,这似乎符合要求。我尝试创建一个地图(称为s_map),其中包含所有模型和要选择的字段列表(如果有)。然后我在这里设置选择:

from [e, t, r, o, i, l, c] in query,
  select: {take(e, ^Map.get(s_map, :event, [])),
           take(t, ^Map.get(s_map, :event_type, [])),
           take(r, ^Map.get(s_map, :event_reason_code, [])),
           take(o, ^Map.get(s_map, :event_outcome_code, [])),
           take(i, ^Map.get(s_map, :item, [])),
           take(l, ^Map.get(s_map, :location, [])),
           take(c, ^Map.get(s_map, :client, []))}

这似乎也不完全有效,因为必须至少有一个字段,否则会出错。

【问题讨论】:

    标签: elixir ecto


    【解决方案1】:

    对于“至少一个字段,否则会出错”的问题,我会构建一个元组,在一个函数中指定选择。该函数将检查以确保选择了某些列。如果调用者未指定有效列,则应返回错误。然后,您可以决定如何在调用者中处理该错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多