2016-05-21 3 views
1

У меня есть приложение, в котором пользователи могут отправлять пользовательские данные, и они будут сохранены в моем приложении. В настоящее время я делаю что-то вроде этого:Полиморфные встроенные типы значений JSON?

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 

В настоящее время я сохранить все как строку и сохранить поле Type для преобразования типов данных между моим приложением и БД. Кроме того, я должен преобразовать все в строку, потому что Ecto (AFAIK) не поддерживает полиморфные типы во встроенном JSON.

Является ли это проблематичным в отношении индексации поля и стоимости?

Я думал использовать пользовательские типы экто, но это не представляется возможным из-за того, как я завишу от двух значений в моей функции литом (при заливке даты, я смотрю в конце имени поля для «_at»)

Есть ли лучший способ достичь этого?

ответ

1

Если вы используете 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 здесь будет храниться как целое число в JSON в столбце data. Когда вы получите эту запись из БД, Ecto будет использовать JSON-сериализатор, например Poison, для декодирования данных JSON, и он вернет 1 как целое число в Elixir.

+0

Но таким образом невозможно провести какие-либо проверки, да? – Tarlen

+0

С этим можно сделать валидации. Какие проверки вы имеете в виду? Вы не работали? – Gjaldon

+0

Например, длина значений – Tarlen

Смежные вопросы