mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-20 05:23:18 +12:00
improvement: support count_nils
expression
This commit is contained in:
parent
b1d10c32cc
commit
4930a69b95
5 changed files with 397 additions and 0 deletions
26
lib/expr.ex
26
lib/expr.ex
|
@ -10,6 +10,7 @@ defmodule AshPostgres.Expr do
|
||||||
At,
|
At,
|
||||||
CompositeType,
|
CompositeType,
|
||||||
Contains,
|
Contains,
|
||||||
|
CountNils,
|
||||||
DateAdd,
|
DateAdd,
|
||||||
DateTimeAdd,
|
DateTimeAdd,
|
||||||
Error,
|
Error,
|
||||||
|
@ -373,6 +374,31 @@ defmodule AshPostgres.Expr do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_dynamic_expr(
|
||||||
|
query,
|
||||||
|
%CountNils{arguments: [list], embedded?: pred_embedded?},
|
||||||
|
bindings,
|
||||||
|
embedded?,
|
||||||
|
acc,
|
||||||
|
type
|
||||||
|
) do
|
||||||
|
do_dynamic_expr(
|
||||||
|
query,
|
||||||
|
%Fragment{
|
||||||
|
embedded?: pred_embedded?,
|
||||||
|
arguments: [
|
||||||
|
raw: "(SELECT COUNT(*) FROM unnest(",
|
||||||
|
expr: list,
|
||||||
|
raw: ") AS item WHERE item IS NULL)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
bindings,
|
||||||
|
embedded?,
|
||||||
|
acc,
|
||||||
|
type
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp do_dynamic_expr(
|
defp do_dynamic_expr(
|
||||||
query,
|
query,
|
||||||
%Contains{arguments: [left, right], embedded?: pred_embedded?},
|
%Contains{arguments: [left, right], embedded?: pred_embedded?},
|
||||||
|
|
328
priv/resource_snapshots/test_repo/posts/20240129221511.json
Normal file
328
priv/resource_snapshots/test_repo/posts/20240129221511.json
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"default": "fragment(\"uuid_generate_v4()\")",
|
||||||
|
"size": null,
|
||||||
|
"type": "uuid",
|
||||||
|
"source": "id",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": false,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "title_column",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "bigint",
|
||||||
|
"source": "score",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "boolean",
|
||||||
|
"source": "public",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "citext",
|
||||||
|
"source": "category",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "\"sponsored\"",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "type",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "bigint",
|
||||||
|
"source": "price",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "\"0\"",
|
||||||
|
"size": null,
|
||||||
|
"type": "decimal",
|
||||||
|
"source": "decimal",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "status",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "status",
|
||||||
|
"source": "status_enum",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"float"
|
||||||
|
],
|
||||||
|
"source": "point",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "custom_point",
|
||||||
|
"source": "composite_point",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "map",
|
||||||
|
"source": "stuff",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "uniq_one",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "uniq_two",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "uniq_custom_one",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "text",
|
||||||
|
"source": "uniq_custom_two",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"text"
|
||||||
|
],
|
||||||
|
"source": "list_containing_nils",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "fragment(\"now()\")",
|
||||||
|
"size": null,
|
||||||
|
"type": "utc_datetime_usec",
|
||||||
|
"source": "created_at",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": false,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "fragment(\"now()\")",
|
||||||
|
"size": null,
|
||||||
|
"type": "utc_datetime_usec",
|
||||||
|
"source": "updated_at",
|
||||||
|
"references": null,
|
||||||
|
"allow_nil?": false,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "uuid",
|
||||||
|
"source": "organization_id",
|
||||||
|
"references": {
|
||||||
|
"name": "posts_organization_id_fkey",
|
||||||
|
"table": "orgs",
|
||||||
|
"schema": "public",
|
||||||
|
"on_delete": null,
|
||||||
|
"multitenancy": {
|
||||||
|
"global": null,
|
||||||
|
"strategy": null,
|
||||||
|
"attribute": null
|
||||||
|
},
|
||||||
|
"primary_key?": true,
|
||||||
|
"destination_attribute": "id",
|
||||||
|
"deferrable": false,
|
||||||
|
"match_type": null,
|
||||||
|
"match_with": null,
|
||||||
|
"on_update": null,
|
||||||
|
"destination_attribute_default": null,
|
||||||
|
"destination_attribute_generated": null
|
||||||
|
},
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "nil",
|
||||||
|
"size": null,
|
||||||
|
"type": "uuid",
|
||||||
|
"source": "author_id",
|
||||||
|
"references": {
|
||||||
|
"name": "posts_author_id_fkey",
|
||||||
|
"table": "authors",
|
||||||
|
"schema": "public",
|
||||||
|
"on_delete": null,
|
||||||
|
"multitenancy": {
|
||||||
|
"global": null,
|
||||||
|
"strategy": null,
|
||||||
|
"attribute": null
|
||||||
|
},
|
||||||
|
"primary_key?": true,
|
||||||
|
"destination_attribute": "id",
|
||||||
|
"deferrable": false,
|
||||||
|
"match_type": null,
|
||||||
|
"match_with": null,
|
||||||
|
"on_update": null,
|
||||||
|
"destination_attribute_default": null,
|
||||||
|
"destination_attribute_generated": null
|
||||||
|
},
|
||||||
|
"allow_nil?": true,
|
||||||
|
"generated?": false,
|
||||||
|
"primary_key?": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "posts",
|
||||||
|
"hash": "750AEDC8C41D03DB043C207300801B6D4543AA1E17F98A14E89FED6610EA2967",
|
||||||
|
"repo": "Elixir.AshPostgres.TestRepo",
|
||||||
|
"identities": [
|
||||||
|
{
|
||||||
|
"name": "uniq_one_and_two",
|
||||||
|
"keys": [
|
||||||
|
"uniq_one",
|
||||||
|
"uniq_two"
|
||||||
|
],
|
||||||
|
"base_filter": "type = 'sponsored'",
|
||||||
|
"all_tenants?": false,
|
||||||
|
"index_name": "posts_uniq_one_and_two_index"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schema": null,
|
||||||
|
"check_constraints": [
|
||||||
|
{
|
||||||
|
"name": "price_must_be_positive",
|
||||||
|
"check": "price > 0",
|
||||||
|
"attribute": [
|
||||||
|
"price"
|
||||||
|
],
|
||||||
|
"base_filter": "type = 'sponsored'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"custom_indexes": [
|
||||||
|
{
|
||||||
|
"message": "dude what the heck",
|
||||||
|
"name": null,
|
||||||
|
"table": null,
|
||||||
|
"include": null,
|
||||||
|
"prefix": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"type": "atom",
|
||||||
|
"value": "uniq_custom_one"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "atom",
|
||||||
|
"value": "uniq_custom_two"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"where": null,
|
||||||
|
"unique": true,
|
||||||
|
"all_tenants?": false,
|
||||||
|
"concurrently": true,
|
||||||
|
"error_fields": [
|
||||||
|
"uniq_custom_one",
|
||||||
|
"uniq_custom_two"
|
||||||
|
],
|
||||||
|
"using": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"base_filter": "type = 'sponsored'",
|
||||||
|
"multitenancy": {
|
||||||
|
"global": null,
|
||||||
|
"strategy": null,
|
||||||
|
"attribute": null
|
||||||
|
},
|
||||||
|
"custom_statements": [],
|
||||||
|
"has_create_action": true
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
defmodule AshPostgres.TestRepo.Migrations.MigrateResources15 do
|
||||||
|
@moduledoc """
|
||||||
|
Updates resources based on their most recent snapshots.
|
||||||
|
|
||||||
|
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:posts) do
|
||||||
|
add :list_containing_nils, {:array, :text}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:posts) do
|
||||||
|
remove :list_containing_nils
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -534,6 +534,23 @@ defmodule AshPostgres.CalculationTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "count_nils/1" do
|
||||||
|
test "counts nil values" do
|
||||||
|
Post
|
||||||
|
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", nil, "c"]})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
Post
|
||||||
|
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", "c"]})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
assert [_] =
|
||||||
|
Post
|
||||||
|
|> Ash.Query.filter(count_nils(list_containing_nils) == 2)
|
||||||
|
|> Api.read!()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "-/1" do
|
describe "-/1" do
|
||||||
test "makes numbers negative" do
|
test "makes numbers negative" do
|
||||||
Post
|
Post
|
||||||
|
|
|
@ -101,6 +101,11 @@ defmodule AshPostgres.Test.Post do
|
||||||
attribute(:uniq_two, :string)
|
attribute(:uniq_two, :string)
|
||||||
attribute(:uniq_custom_one, :string)
|
attribute(:uniq_custom_one, :string)
|
||||||
attribute(:uniq_custom_two, :string)
|
attribute(:uniq_custom_two, :string)
|
||||||
|
|
||||||
|
attribute :list_containing_nils, {:array, :string} do
|
||||||
|
constraints(nil_items?: true)
|
||||||
|
end
|
||||||
|
|
||||||
create_timestamp(:created_at)
|
create_timestamp(:created_at)
|
||||||
update_timestamp(:updated_at)
|
update_timestamp(:updated_at)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue