Say I have a schema (which maps data from a db) with an embeds_many
field:
defmodule Order do
use Ecto.Schema
schema "orders" do
embeds_many :items, Item
end
end
defmodule Item do
use Ecto.Schema
@primary_key {:id, :string, autogenerate: false}
embedded_schema do
field :title
end
end
Periodically, data will come in from an external source where I’d want to add new items to an existing order. The items coming from the external source may or may not already be in the existing order.
Is there a standard way to do something like this? Maybe utilizing the primary key on my embedded schema?
Thanks
1 Like
Deduplication is not hard but you’re going to have to give a bit more detailed example for us to be able to suggest a solution.
Thanks @dimitarvp. So here is a situation that might happen:
- My
orders
table has an entry with order_id = 1
and items = [{"item_id": 1, "title": "a"}]
. Each entry in the items
array is defined by an embedded schema in my app.
- My app receives external data
{"order_id": 1, "item_id": 2, "title": "b"}
. Since my entry for order_id = 1
doesn’t have item_id = 2
yet, I want to add %Item{"item_id": 2, "title": "b"}
to its items
array. Is there any way for me to do this without pulling the existing database entry into my application?
- My app receives external data
{"order_id": 1, "item_id": 1, "title": "a"}
. Since my entry for order_id = 1
already has item_id = 1
, I don’t want to save this to the database. Is there any way for me to check for this without pulling the existing database entry into my application?
AFAIK, it’s not possible without loading the existing record.
Check out Upserts if you haven’t already. It might give you some ideas. It might not work exactly with embedded schemas though. I’m assuming you have items
as maps since they might contain arbitrary keys/values or you’re not able to modify it (working with existing/legacy code, etc). But if that’s not the case, it might be much easier to have 2 separate tables with defined columns. Then you’d do something like this:
Repo.insert!(
%Item{order_id: 1, item_id: 1, title: "a"},
on_conflict: :nothing
)
1 Like
This is not possible with an embedded schema. You can just load the main record + the embedded records, calculate the proper diff and then put the new collection back with Ecto.Changeset.put_assoc
.
3 Likes