多态嵌入 JSON 值类型?
Polymorphic embedded JSON value types?
我有一个应用程序,用户可以在其中发送自定义数据,这些数据将存储在我的应用程序中。目前我做这样的事情:
defmodule MyApp.CustomField do
use Ecto.Schema
import Ecto.Changeset
alias MyApp.{ Time }
defmodule ValueTypes do
def c_INTEGER, do: "integer"
def c_BOOLEAN, do: "boolean"
def c_STRING, do: "string"
def c_TRUE, do: "true"
def c_FALSE, do: "false"
end
embedded_schema do
field :field
field :value
field :type
end
@required_fields ~w( field value )
@optional_fields ~w( type )
def changeset(model, params \ :empty) do
{:ok, value, type} = cast(model.field, model.value)
params = %{value: value, type: type}
model
|> cast(params, @required_fields, @optional_fields)
|> validate_length(:field, min: 1, max: 20)
|> validate_length(:value, min: 1, max: 255)
|> put_change(:type, type)
end
# date in unix timestamp
def cast(field, value) when is_integer(value) do
value = Integer.to_string(value)
case String.slice(field, -3, 3) do
"_at" -> {:ok, value, ValueTypes.c_DATE}
_ -> {:ok, value, ValueTypes.c_INTEGER}
end
end
def cast(field, value) when is_boolean(value), do: {:ok, (if value, do: ValueTypes.c_TRUE, else: ValueTypes.c_FALSE), ValueTypes.c_BOOLEAN}
def cast(field, value) when is_binary(value), do: {:ok, value, ValueTypes.c_STRING}
def cast(field, value), do: {:error, "Invalid field value"}
end
目前我将所有内容都保存为字符串并保留一个类型字段以在我的应用程序和数据库之间转换数据类型。此外,我必须将所有内容都转换为字符串,因为 Ecto (AFAIK) 不支持嵌入式 JSON.
中的多态类型
这在索引字段和值方面有问题吗?
我考虑过使用自定义 Ecto 类型,但这是不可能的,因为我在转换函数中依赖两个值(转换日期时,我查看字段名称的末尾“_at”)
有没有更好的方法来完成这个?
如果您使用 Postgres 作为数据库,那么您想要的行为已经存在。您需要在架构中使用 :map
数据类型。你可以这样做:
defmodule MyApp.CustomField do
use Ecto.Schema
import Ecto.Changeset
alias MyApp.{ Time }
embedded_schema do
field :data, :map
end
end
然后您可以%CustomField{data: %{"field" => "custom", "value" => 1}} |> Repo.insert!
。 1
将作为整数存储在 data
列的 JSON 中。当您从数据库中获取此记录时,Ecto 将使用像 Poison 这样的 JSON 序列化程序来解码 JSON 数据,并且它将 return 1
作为 Elixir 中的整数。
我有一个应用程序,用户可以在其中发送自定义数据,这些数据将存储在我的应用程序中。目前我做这样的事情:
defmodule MyApp.CustomField do
use Ecto.Schema
import Ecto.Changeset
alias MyApp.{ Time }
defmodule ValueTypes do
def c_INTEGER, do: "integer"
def c_BOOLEAN, do: "boolean"
def c_STRING, do: "string"
def c_TRUE, do: "true"
def c_FALSE, do: "false"
end
embedded_schema do
field :field
field :value
field :type
end
@required_fields ~w( field value )
@optional_fields ~w( type )
def changeset(model, params \ :empty) do
{:ok, value, type} = cast(model.field, model.value)
params = %{value: value, type: type}
model
|> cast(params, @required_fields, @optional_fields)
|> validate_length(:field, min: 1, max: 20)
|> validate_length(:value, min: 1, max: 255)
|> put_change(:type, type)
end
# date in unix timestamp
def cast(field, value) when is_integer(value) do
value = Integer.to_string(value)
case String.slice(field, -3, 3) do
"_at" -> {:ok, value, ValueTypes.c_DATE}
_ -> {:ok, value, ValueTypes.c_INTEGER}
end
end
def cast(field, value) when is_boolean(value), do: {:ok, (if value, do: ValueTypes.c_TRUE, else: ValueTypes.c_FALSE), ValueTypes.c_BOOLEAN}
def cast(field, value) when is_binary(value), do: {:ok, value, ValueTypes.c_STRING}
def cast(field, value), do: {:error, "Invalid field value"}
end
目前我将所有内容都保存为字符串并保留一个类型字段以在我的应用程序和数据库之间转换数据类型。此外,我必须将所有内容都转换为字符串,因为 Ecto (AFAIK) 不支持嵌入式 JSON.
中的多态类型这在索引字段和值方面有问题吗?
我考虑过使用自定义 Ecto 类型,但这是不可能的,因为我在转换函数中依赖两个值(转换日期时,我查看字段名称的末尾“_at”)
有没有更好的方法来完成这个?
如果您使用 Postgres 作为数据库,那么您想要的行为已经存在。您需要在架构中使用 :map
数据类型。你可以这样做:
defmodule MyApp.CustomField do
use Ecto.Schema
import Ecto.Changeset
alias MyApp.{ Time }
embedded_schema do
field :data, :map
end
end
然后您可以%CustomField{data: %{"field" => "custom", "value" => 1}} |> Repo.insert!
。 1
将作为整数存储在 data
列的 JSON 中。当您从数据库中获取此记录时,Ecto 将使用像 Poison 这样的 JSON 序列化程序来解码 JSON 数据,并且它将 return 1
作为 Elixir 中的整数。