I have two models, Owner
and Property
, where the schema for Owner
has an embeds_many
declaration, like this:
defmodule MyApp.Owner do
use MyApp.Web, :model
alias MyApp.Property
schema "owners" do
field name, :string
embeds_many :properties, Property
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [])
|> validate_required([])
end
end
and this:
defmodule MyApp.Property do
use MyApp.Web, :model
embedded_schema do
field :name, :string
field :value, :float, default: 0
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name, :value])
|> validate_required([:name])
end
end
The migration I'm using is:
defmodule MyApp.Repo.Migrations.CreateOwner do
use Ecto.Migration
def down do
drop table(:owners)
end
def change do
drop_if_exists table(:owners)
create table(:owners) do
add :name, :string
add :properties, :map
timestamps()
end
end
end
And a possible seed is:
alias MyApp.{Repo, Owner, Property}
Repo.insert!(%Owner{
name: "John Doe",
properties: [
%Property{
name: "Property A"
},
%Property{
name: "Property B",
value: 100000
},
%Property{
name: "Property C",
value: 200000
}
]
})
Finally, my questions are: how can I update John Doe
's Property C
's value from 200000
to 300000
? And if John Doe
buys a Property D
:
%Property{
name: "Property D"
value: 400000
}
How do I add that to his properties
in the database? (I'm using Postgres).
The simplest way would be to fetch the record, update the properties
list and save the changes:
owner = Repo.get!(Owner, 1)
properties = owner.properties
# update all properties with name "Property C"'s value to 400000
properties = for %Property{name: name} = property <- properties do
if name == "Property C" do
%{property | value: 400000}
else
property
end
end
# add a property to the start
properties = [%Property{name: "Property D", value: 400000} | properties]
# or to the end
# properties = properties ++ [%Property{name: "Property D", value: 400000}]
# update
Owner.changeset(owner, %{properties: properties})
|> Repo.update!
You can do some operations (at least inserting a property) using the JSON functions
provided by PostgreSQL using
fragment
but I don't think you can search and conditionally update an item
of an array using them.