Skip to content

Commit 23cc10a

Browse files
authored
fix: do not build query filter with or: [] when all records are successfully inserted, for return_skipped_upsert?: true (#659)
1 parent 586b6ce commit 23cc10a

File tree

2 files changed

+65
-30
lines changed

2 files changed

+65
-30
lines changed

lib/data_layer.ex

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,39 +2059,46 @@ defmodule AshPostgres.DataLayer do
20592059
{Map.take(r, keys), r}
20602060
end)
20612061

2062-
ash_query =
2063-
resource
2064-
|> Ash.Query.do_filter(
2065-
or:
2066-
changesets
2067-
|> Enum.filter(fn changeset ->
2068-
not Map.has_key?(
2069-
results_by_identity,
2070-
Map.take(changeset.attributes, keys)
2071-
)
2072-
end)
2073-
|> Enum.map(fn changeset ->
2074-
changeset.attributes
2075-
|> Map.take(keys)
2076-
|> Keyword.new()
2077-
end)
2078-
)
2079-
|> then(fn
2080-
query when is_nil(identity) or is_nil(identity.where) -> query
2081-
query -> Ash.Query.do_filter(query, identity.where)
2062+
skipped_filter =
2063+
changesets
2064+
|> Enum.filter(fn changeset ->
2065+
not Map.has_key?(
2066+
results_by_identity,
2067+
Map.take(changeset.attributes, keys)
2068+
)
2069+
end)
2070+
|> Enum.map(fn changeset ->
2071+
changeset.attributes
2072+
|> Map.take(keys)
2073+
|> Keyword.new()
20822074
end)
2083-
|> Ash.Query.set_tenant(changeset.tenant)
20842075

20852076
skipped_upserts =
2086-
with {:ok, ecto_query} <- Ash.Query.data_layer_query(ash_query),
2087-
{:ok, results} <- run_query(ecto_query, resource) do
2088-
results
2089-
|> Enum.map(fn result ->
2090-
Ash.Resource.put_metadata(result, :upsert_skipped, true)
2091-
end)
2092-
|> Enum.reduce(%{}, fn r, acc ->
2093-
Map.put(acc, Map.take(r, keys), r)
2094-
end)
2077+
case skipped_filter do
2078+
[] ->
2079+
# No skipped records to query for
2080+
%{}
2081+
2082+
skipped_filter ->
2083+
ash_query =
2084+
resource
2085+
|> Ash.Query.do_filter(or: skipped_filter)
2086+
|> then(fn
2087+
query when is_nil(identity) or is_nil(identity.where) -> query
2088+
query -> Ash.Query.do_filter(query, identity.where)
2089+
end)
2090+
|> Ash.Query.set_tenant(changeset.tenant)
2091+
2092+
with {:ok, ecto_query} <- Ash.Query.data_layer_query(ash_query),
2093+
{:ok, results} <- run_query(ecto_query, resource) do
2094+
results
2095+
|> Enum.map(fn result ->
2096+
Ash.Resource.put_metadata(result, :upsert_skipped, true)
2097+
end)
2098+
|> Enum.reduce(%{}, fn r, acc ->
2099+
Map.put(acc, Map.take(r, keys), r)
2100+
end)
2101+
end
20952102
end
20962103

20972104
results =

test/bulk_create_test.exs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,34 @@ defmodule AshPostgres.BulkCreateTest do
237237
refute Ash.Resource.get_metadata(no_conflict, :upsert_skipped)
238238
end
239239

240+
test "bulk upsert with return_skipped_upsert? when all records are not skipped" do
241+
%Ash.BulkResult{records: records, errors: errors} =
242+
Ash.bulk_create!(
243+
[
244+
%{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 100},
245+
%{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 200}
246+
],
247+
Post,
248+
:upsert_with_no_filter,
249+
upsert_condition: expr(price != upsert_conflict(:price)),
250+
return_errors?: true,
251+
return_records?: true,
252+
return_skipped_upsert?: true
253+
)
254+
255+
assert errors == []
256+
257+
assert [row1, row2] = records
258+
259+
assert row1.title == "fredfoo"
260+
assert row1.price == 100
261+
refute Ash.Resource.get_metadata(row1, :upsert_skipped)
262+
263+
assert row2.title == "georgefoo"
264+
assert row2.price == 200
265+
refute Ash.Resource.get_metadata(row2, :upsert_skipped)
266+
end
267+
240268
# confirmed that this doesn't work because it can't. An upsert must map to a potentially successful insert.
241269
# leaving this test here for posterity
242270
# test "bulk creates can upsert with id" do

0 commit comments

Comments
 (0)