mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-19 13:03:14 +12:00
improvement: support access syntax
This commit is contained in:
parent
079eb2e08d
commit
e2bd3c5aec
11 changed files with 176 additions and 9 deletions
2
.github/workflows/elixir.yml
vendored
2
.github/workflows/elixir.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
|||
matrix:
|
||||
otp: ["23"]
|
||||
elixir: ["1.11.0"]
|
||||
ash: ["master", "1.50.16"]
|
||||
ash: ["master", "1.50.19"]
|
||||
pg_version: ["9.6", "11"]
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
33
lib/expr.ex
33
lib/expr.ex
|
@ -4,7 +4,7 @@ defmodule AshPostgres.Expr do
|
|||
alias Ash.Filter
|
||||
alias Ash.Query.{BooleanExpression, Not, Ref}
|
||||
alias Ash.Query.Operator.IsNil
|
||||
alias Ash.Query.Function.{Ago, Contains, If}
|
||||
alias Ash.Query.Function.{Ago, Contains, GetPath, If}
|
||||
alias AshPostgres.Functions.{Fragment, TrigramSimilarity, Type}
|
||||
|
||||
require Ecto.Query
|
||||
|
@ -88,6 +88,33 @@ defmodule AshPostgres.Expr do
|
|||
Ecto.Query.dynamic(datetime_add(^DateTime.utc_now(), ^left * -1, ^to_string(right)))
|
||||
end
|
||||
|
||||
defp do_dynamic_expr(
|
||||
query,
|
||||
%GetPath{arguments: [left, right], embedded?: pred_embedded?},
|
||||
bindings,
|
||||
embedded?,
|
||||
type
|
||||
) do
|
||||
path = Enum.map(right, &to_string/1)
|
||||
|
||||
do_dynamic_expr(
|
||||
query,
|
||||
%Fragment{
|
||||
embedded?: pred_embedded?,
|
||||
arguments: [
|
||||
raw: "(",
|
||||
expr: left,
|
||||
raw: " #> ",
|
||||
expr: path,
|
||||
raw: ")"
|
||||
]
|
||||
},
|
||||
bindings,
|
||||
embedded?,
|
||||
type
|
||||
)
|
||||
end
|
||||
|
||||
defp do_dynamic_expr(
|
||||
query,
|
||||
%Contains{arguments: [left, %Ash.CiString{} = right], embedded?: pred_embedded?},
|
||||
|
@ -125,9 +152,9 @@ defmodule AshPostgres.Expr do
|
|||
%Fragment{
|
||||
embedded?: pred_embedded?,
|
||||
arguments: [
|
||||
raw: "strpos(",
|
||||
raw: "strpos((",
|
||||
expr: left,
|
||||
raw: ", ",
|
||||
raw: "), ",
|
||||
expr: right,
|
||||
raw: ") > 0"
|
||||
]
|
||||
|
|
|
@ -1521,7 +1521,7 @@ defmodule AshPostgres.MigrationGenerator do
|
|||
|
||||
def get_snapshots(resource, all_resources) do
|
||||
Code.ensure_compiled!(AshPostgres.repo(resource))
|
||||
|
||||
|
||||
if AshPostgres.polymorphic?(resource) do
|
||||
all_resources
|
||||
|> Enum.flat_map(&Ash.Resource.Info.relationships/1)
|
||||
|
|
2
mix.exs
2
mix.exs
|
@ -97,7 +97,7 @@ defmodule AshPostgres.MixProject do
|
|||
{:ecto, github: "elixir-ecto/ecto", branch: "master", override: true},
|
||||
{:jason, "~> 1.0"},
|
||||
{:postgrex, ">= 0.0.0"},
|
||||
{:ash, ash_version("~> 1.50 and >= 1.50.16")},
|
||||
{:ash, ash_version("~> 1.50 and >= 1.50.19")},
|
||||
{:git_ops, "~> 2.4.5", only: :dev},
|
||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||
{:ex_check, "~> 0.11.0", only: :dev},
|
||||
|
|
5
mix.lock
5
mix.lock
|
@ -1,5 +1,5 @@
|
|||
%{
|
||||
"ash": {:hex, :ash, "1.50.16", "11fb1d4f761b9e9cf3e6d5f5910a330381cb0c5e35b574d207f20f06afbb9faa", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:timex, ">= 3.0.0", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "47813d5da2f209fc98f55a793777ec9f24a02dcd386898d3716ed919ac212818"},
|
||||
"ash": {:hex, :ash, "1.50.19", "83d7c9ad840602bb0b02aa06545cdac7fa4cc6b09ad0510d2cbc2454466125ae", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.9", [hex: :sourceror, repo: "hexpm", optional: false]}, {:timex, ">= 3.0.0", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "04a0c442a98efc4774e55fbfa46cde5769ad22efb56e288e712edde9c9b7a90a"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
|
@ -21,7 +21,7 @@
|
|||
"excoveralls": {:hex, :excoveralls, "0.13.0", "4e1b7cc4e0351d8d16e9be21b0345a7e165798ee5319c7800b9138ce17e0b38e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "fe2a56c8909564e2e6764765878d7d5e141f2af3bc8ff3b018a68ee2a218fced"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"getopt": {:hex, :getopt, "1.0.1", "c73a9fa687b217f2ff79f68a3b637711bb1936e712b521d8ce466b29cbf7808a", [:rebar3], [], "hexpm", "53e1ab83b9ceb65c9672d3e7a35b8092e9bdc9b3ee80721471a161c10c59959c"},
|
||||
"gettext": {:hex, :gettext, "0.19.0", "6909d61b38bb33339558f128f8af5913d5d5fe304a770217bf352b1620fb7ec4", [:mix], [], "hexpm", "3f7a274f52ebda9bb6655dfeda3d6b0dc4537ae51ce41dcccc7f73ca7379ad5e"},
|
||||
"gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"},
|
||||
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
|
||||
"git_ops": {:hex, :git_ops, "2.4.5", "185a724dfde3745edd22f7571d59c47a835cf54ded67e9ccbc951920b7eec4c2", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e323a5b01ad53bc8c19c3a444be3e61ed7803ecd2e95530446ae9327d0143ecc"},
|
||||
"hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
|
||||
|
@ -38,6 +38,7 @@
|
|||
"postgrex": {:hex, :postgrex, "0.16.1", "f94628a32c571266f53cd1e5fca705e626e2417bf1eee6f868985d14e874160a", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "6b225df32c857b9430619dbe30200a7ae664e23415a771ae9209396ee8eeee64"},
|
||||
"providers": {:hex, :providers, "1.8.1", "70b4197869514344a8a60e2b2a4ef41ca03def43cfb1712ecf076a0f3c62f083", [:rebar3], [{:getopt, "1.0.1", [hex: :getopt, repo: "hexpm", optional: false]}], "hexpm", "e45745ade9c476a9a469ea0840e418ab19360dc44f01a233304e118a44486ba0"},
|
||||
"sobelow": {:hex, :sobelow, "0.10.2", "00e91208046d3b434f9f08779fe0ca7c6d6595b7fa33b289e792dffa6dde8081", [:mix], [], "hexpm", "e30fc994330cf6f485c1c4f2fb7c4b2d403557d0e101c6e5329fd17a58e55a7e"},
|
||||
"sourceror": {:hex, :sourceror, "0.10.0", "a6a85d8acc874df59bedb7497374f8f7517e4d54a5805da903ff31aaee14a1a7", [:mix], [], "hexpm", "ddd94d23d96ac949696616eb89cecac977dfed7ecdfac6bbd3f83617e4daea28"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
|
||||
"timex": {:hex, :timex, "3.7.6", "502d2347ec550e77fdf419bc12d15bdccd31266bb7d925b30bf478268098282f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a296327f79cb1ec795b896698c56e662ed7210cc9eb31f0ab365eb3a62e2c589"},
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"uuid_generate_v4()\")",
|
||||
"generated?": false,
|
||||
"name": "id",
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"name": "first_name",
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"name": "last_name",
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"name": "bio",
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"type": "map"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"has_create_action": true,
|
||||
"hash": "19AEE20A027B64FBA25A86974080E72C7B5B4FBB85005325F2F36BA580E32346",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.AshPostgres.TestRepo",
|
||||
"table": "authors"
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
defmodule AshPostgres.TestRepo.Migrations.MigrateResources2 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(:authors) do
|
||||
add :bio, :map
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:authors) do
|
||||
remove :bio
|
||||
end
|
||||
end
|
||||
end
|
|
@ -59,6 +59,19 @@ defmodule AshPostgres.CalculationTest do
|
|||
|> Api.read!()
|
||||
end
|
||||
|
||||
test "calculations can refer to embedded attributes" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
||||
|> Api.create!()
|
||||
|
||||
assert %{title: "Mr."} =
|
||||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:title)
|
||||
|> Api.read_one!()
|
||||
end
|
||||
|
||||
test "calculations can be used in related filters" do
|
||||
post =
|
||||
Post
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.FilterTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Comment, Post}
|
||||
alias AshPostgres.Test.{Api, Author, Comment, Post}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
@ -263,6 +263,43 @@ defmodule AshPostgres.FilterTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "accessing embeds" do
|
||||
setup do
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, bio: %{title: "Dr.", bio: "Strange"})
|
||||
|> Api.create!()
|
||||
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create,
|
||||
bio: %{title: "Highlander", bio: "There can be only one."}
|
||||
)
|
||||
|> Api.create!()
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "works using simple equality" do
|
||||
assert [%{bio: %{title: "Dr."}}] =
|
||||
Author
|
||||
|> Ash.Query.filter(bio[:title] == "Dr.")
|
||||
|> Api.read!()
|
||||
end
|
||||
|
||||
test "works using an expression" do
|
||||
assert [%{bio: %{title: "Highlander"}}] =
|
||||
Author
|
||||
|> Ash.Query.filter(contains(type(bio[:bio], :string), "only one."))
|
||||
|> Api.read!()
|
||||
end
|
||||
|
||||
test "calculations that use embeds can be filtered on" do
|
||||
assert [%{bio: %{title: "Dr."}}] =
|
||||
Author
|
||||
|> Ash.Query.filter(title == "Dr.")
|
||||
|> Api.read!()
|
||||
end
|
||||
end
|
||||
|
||||
describe "basic expressions" do
|
||||
test "basic expressions work" do
|
||||
Post
|
||||
|
|
|
@ -12,9 +12,11 @@ defmodule AshPostgres.Test.Author do
|
|||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:first_name, :string)
|
||||
attribute(:last_name, :string)
|
||||
attribute(:bio, AshPostgres.Test.Bio)
|
||||
end
|
||||
|
||||
calculations do
|
||||
calculate(:title, :string, expr(bio[:title]))
|
||||
calculate(:full_name, :string, expr(first_name <> " " <> last_name))
|
||||
|
||||
calculate(
|
||||
|
|
9
test/support/resources/bio.ex
Normal file
9
test/support/resources/bio.ex
Normal file
|
@ -0,0 +1,9 @@
|
|||
defmodule AshPostgres.Test.Bio do
|
||||
@moduledoc false
|
||||
use Ash.Resource, data_layer: :embedded
|
||||
|
||||
attributes do
|
||||
attribute(:title, :string)
|
||||
attribute(:bio, :string)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue