lardcanoe

lardcanoe

I am trying to parse a user generated filter that will apply to jsonb field (that is just a map, not an embedded resource).

The field is data, and if I include it in the statement sent to parse_input I get an Ash.Error.Query.NoSuchFilterPredicate error.

If I exclude that field, and instead manually apply it, |> Ash.Query.filter(data[:size] == ^"Big") then things works.

Is there a syntax to allow passing fields of a map into the parse function? If not, how can I pass it to Ash.Query.filter? To make it slightly more complicated, I would also like to be able to filter on the data field when it is on a reference as well. e.g. `%{person: %{data: %{size: “Big”}}}

    statement = %{
      person: %{parent: %{name: "Daniels"}},
      data: %{size: "Big"}
    }

    # This will blow up with the above
    {:ok, filter} = Ash.Filter.parse_input(resource, statement)

    # If I remove "data" and instead do it manually, then this works:
    resource
    |> Ash.Query.for_read(read_action, %{}, actor: actor, tenant: tenant)
    |> Ash.Query.filter_input(filter)
    |> Ash.Query.filter(data[:size] == ^"King")

Filtering on embedded resource? - #6 by zachdaniel was where I saw the Ash.Query.filter approach on arbitrary fields of a jsonb attribute; I just couldn’t figure out how to pass my user generated filter since that is a macro.

Thank you!

Showing Posts 1 to 10

zachdaniel

zachdaniel

Creator of Ash

I think this is actually something missing from Ash.Filter, specifically when given a nested map like that we should check for embedded resources and allow you to apply that kind of filter. Could you open an issue for it in Ash?

Another way to achieve this kind of thing is to add calculations to the root resource, i.e


```elixir
calculate :size, :string, expr(data[:size])

and then allow filtering on :size. Not a feasible option for every case though.

lardcanoe

lardcanoe OP

Created Filter a map attribute using user input · Issue #862 · ash-project/ash · GitHub

The size filter was just an example, it could be any field, and deeply nested. Basically, that data field maps to a JSON schema that is defined at runtime. I hacked together an approach using fragments that works fine until you join another table that also has a data field, and postgres has the ambiguous error. I saw in another post using an as(0) notation, but that seems hokey for my case and also wasn’t sure how to in the fragment.

There is no “hidden” method I can call that replicates what Ash.Query.filter(data[:size] == ^"King") is doing? Because I am fine breaking out the data filters into another approach while keeping parse_input for the pre-defined attributes.

zachdaniel

zachdaniel

Creator of Ash

There are definitely ways to build the filter yourself and to dynamically build references that won’t bring on the ambiguous references that you mentioned, i.e a simplified thing

require Ash.Expr

...
map
|> Enum.reduce(true, fn {key, value}, expr -> 
  Ash.Expr.expr(ref(^key) == ^value) and ^expr)
end)

This can be adapted to support nested keys and to do things like get_path(ref(^key), ^list_of_keys) == ^value).

However, I’ve just pushed support for doing this automatically with non-array embedded types in input filters to ash main. Keep in mind there is still some experimental stuff in main, and if you want to use it you should also upgrade ash_postgres to latest main as well.

lardcanoe

lardcanoe OP

You’re a machine! :joy: What cup of coffee are you on?

I pulled main for ash and ash_postgres and it had the same error.

I think your new code worked because the attribute is defined as attribute :embedded_bio, EmbeddedBio and not attribute :embedded_bio, :map

I hacked up my own embedded resource and attached it to my data field and the code works. But I need it for a generic :map

Sorry for the bad news :frowning:

I don’t want to abuse you, so I’ll reopen the github issue and get to it when you can. Thanks!

zachdaniel

zachdaniel

Creator of Ash

Ah :thinking: so, this is a bit harder. The reason for that is that you can use a predicate %{data: %{eq: 10}}, and we can’t tell if you mean data.eq == 10 or data == 10. The format is, unfortunately, a bit ambiguous. We’d have to figure something else out to for this. :thinking:

EDIT: to make it clear, you can use all kinds of predicates, like %{data: %{less_than: 10}}

zachdaniel

zachdaniel

Creator of Ash

I’m thinking what we need to do actually is walk back the previous change, and introduce a new syntax for including paths in input. Perhaps something like this:

%{data: %{at_path: ["foo"], eq: 10}} 
zachdaniel

zachdaniel

Creator of Ash

So, I made that change and realized that it just kind of shuffles the problem around, because if you have an attribute called at_path it would be problematic…but actually I think it’s fine. The reason its fine is that at_path expects a list, and if you need to match on it you can say at_path: %{eq: ["foo"]}, so there is an escape hatch (albeit a bit of a convoluted one)

zachdaniel

zachdaniel

Creator of Ash

Alright, I’ve pushed this change to main (and removed the previous change I added)

lardcanoe

lardcanoe OP

(edit: replied before seeing your new change. let me do that first)

Can you see in the filter logic that data is an Ash.Type.Map so any condition being applied to it would actually be for fields in the map?

For your example, I wasn’t sure how to use it.

    resource
    |> Ash.Query.for_read(read_action, %{}, actor: actor, tenant: tenant)
    |> Ash.Query.filter_input(filter)
    |> Ash.Query.filter(
      Enum.reduce(%{size: "King"}, true, fn {key, value}, expr ->
        Ash.Expr.expr(get_path(ref(:data), ^key) == ^value and ^expr)
      end)
    )

Do you have a more complete example of using Ash.Expr.expr because elixir ain’t happy with that. Can’t find key, value, expr.

zachdaniel

zachdaniel

Creator of Ash

Can you see in the filter logic that data is an Ash.Type.Map so any condition being applied to it would actually be for fields in the map?

This covers the case a little bit, but there can also be custom types that are backed by maps, and we can’t detect them all.

Do you have a more complete example of using Ash.Expr.expr because elixir ain’t happy with that. Can’t find key, value, expr.

You need to require Ash.Expr because Ash.Expr.expr is a macro.

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews