【问题标题】:Polymorphic embedded structs多态嵌入式结构
【发布时间】:2016-10-23 21:52:40
【问题描述】:

我想在 Postgres 中存储一个树结构,我希望在树的每个节点上嵌入一个任意 Elixir 结构,如下所示:

defmodule Node do
  use Ecto.Schema

  schema "nodes" do
    belongs_to :parent_node, Node
    embeds_one :struct, ArbitraryDataType
  end
end

但是,我相信embeds_one 需要指定特定的结构数据类型,这不适用于我的情况。有没有办法解决这个问题?


我的备份计划是使用两个字段,一个用于结构type,一个用于结构fields,如下所示:

defmodule Node do
  use Ecto.Schema

  schema "nodes" do
    belongs_to :parent_node, Node
    field :struct_type, :string
    field :fields, :map
  end
end

为了首先保存记录,我需要使用__struct__ 字段来确定结构类型。然后,在从数据库中检索节点后,我将使用类似以下的逻辑来重建原始结构:

Enum.reduce(
  retrieved_node.fields,
  String.to_atom("Elixir.#{retrieved_node.struct_type}") |> struct,
  fn {k,v}, s -> Map.put(s, String.to_atom(k), v) end
)

【问题讨论】:

  • 为什么要存储struct_type?结构基本上只是一个带有__struct__ 字段的映射。我会按原样存储它(作为一张普通的旧地图),你会得到ArbitraryDataType开箱即用。我错过了什么吗?
  • 我同意@mudasobwa。也许您想重新考虑首先存储struct_type 的设计决策。
  • 这正是我希望做的,而且很可能有办法做到这一点。但是,当我将结构保存到 Postgres 中的 json 列时,Ecto 不会保留 __struct__ 键/值信息。因此,当我从数据库中检索该行时,我得到了一个“常规”旧地图,而不是我希望的结构 - type 信息丢失了。
  • 您找到解决方案了吗?我也对使用任意结构以及能够持久化和查询并取回相同的结构感兴趣。我在此线程上发表了与您类似的问题的评论:elixirforum.com/t/…
  • 还没有,虽然我太忙了,不能再做那个项目了。它可能会在架子上放一段时间……:/

标签: elixir ecto


【解决方案1】:

我最近解决了一个类似的问题,在我看来,您有两个选择。要么你...

使用自定义Ecto.Type

这使您可以准确控制要编码到字段中的数据类型。通过这样做,您可以相对轻松地保留结构的模块和字段。

可能的实现可能如下所示:

defmodule EctoStruct do
  use Ecto.Type

  def type, do: :map

  def cast(%_{} = struct), do: {:ok, struct}
  def cast(_), do: :error

  def dump(%module{} = struct) do
    data = %{
      "module" => Atom.to_string(module),
      "fields" => Map.from_struct(struct)
    }

    {:ok, data}
  end

  def load(%{"module" => module, "fields" => fields}) do
    module = String.to_existing_atom(module)
    fields = Enum.map(fields, fn {k, v} -> {String.to_existing_atom(k), v} end)

    {:ok, struct!(module, fields)}
  rescue
    _ -> :error
  end
end

有了这个,你可以“简单地”在你的架构中使用field :my_struct, EctoStruct

或者你...

重新考虑您选择的数据库

树是一种内在连接的数据结构。根据您的具体要求和树的深度,使用 Postgres 遍历所述树可能会变得非常缓慢。

虽然我解决了前面提到的问题,但我很早就遇到了性能问题,不得不使用递归连接和物化视图来保持接近可用的响应时间。

从那时起,我切换到图形数据库 (Neo4j),我的性能问题完全消失了。这也将允许您通过使用Labels 轻松地将各种不同的结构类型编码到您的树中。

根据您的特定要求,这可能值得考虑。

【讨论】:

  • 感谢您的这个想法,到目前为止,我可能已经编写了大约 5 种不同的变体,但没有一个感觉很好,因为他们总是需要在之后将字符串键转换为原子键的额外步骤从数据库加载一些东西。实际上,通过使用新的 Ecto.Type.embedded_dump/embedded_load 函数来转储/加载值,同时将键转换为原子,我实际上将这个想法更进一步。我还将编写一个通用的cast_polymorphic_embed/4 函数,而不是cast_embed
【解决方案2】:

以下库支持多态嵌入:

https://github.com/mathieuprog/polymorphic_embed

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多