How to create a relationship which filters by comparing the value of a nested resource of the target resource against the value of the source resource?

I’m trying to create a relationship which filters by comparing the value of a nested resource of the target resource against the value of the source resource.

Source Resource:

defmodule App.Alerts.AlertRule do

  attributes do
    uuid_primary_key :id

    attribute :key, :string do
      allow_nil? false
      public? true
      default "Env"
    end

    attribute :value, :string do
      allow_nil? false
      public? true
      default "Prod"
    end
end

Target Resource

defmodule App.Deployments.Deployment do

  relationships do
    has_many :deployment_tags, App.Deployments.DeploymentTag do
      destination_attribute :deployment_id
    end
  end
end

defmodule App.Deployments.DeploymentTag do
  attributes do
    uuid_primary_key :id

    attribute :key, :string do
      allow_nil? false
      constraints max_length: 20
      public? true
    end

    attribute :value, :string do
      allow_nil? false
      constraints max_length: 20
      public? true
    end

  end

end

Inside the source resource I’ve the following relationship defined:

    has_many :monitored_deployments, App.Deployments.Deployment do
      no_attributes? true
      filter expr(workspace_id == parent(workspace_id))
      filter expr(exists(deployment_tags, key == parent(key) and value == parent(value)))
    end

The first filter expression with the workspace_id seems to work fine.
In the second filter the key and value fields of the parent (which I expect to point to the AlertRule) can’t be referenced.

 ** (Ash.Error.Unknown)
     Bread Crumbs:
       > Error returned from: App.Alerts.AlertRule.read

     Unknown Error

     * Invalid reference key
         at monitored_deployments, filter
       (ash 3.5.26) ....../deps/splode/lib/splode.ex:322: Ash.Error.to_error/2
       (ash 3.5.26) lib/ash/query/query.ex:4359: Ash.Query.add_error/3
       (ash 3.5.26) lib/ash/actions/read/relationships.ex:329: Ash.Actions.Read.Relationships.related_query/4
       (elixir 1.18.4) lib/enum.ex:1714: Enum."-map/2-lists^map/1-1-"/2
       (ash 3.5.26) lib/ash/actions/read/relationships.ex:25: Ash.Actions.Read.Relationships.load/4
       (ash 3.5.26) lib/ash/actions/read/read.ex:358: Ash.Actions.Read.do_run/3
       (ash 3.5.26) lib/ash/actions/read/read.ex:86: anonymous fn/3 in Ash.Actions.Read.run/3
       (ash 3.5.26) lib/ash/actions/read/read.ex:85: Ash.Actions.Read.run/3
       (ash 3.5.26) lib/ash.ex:3002: Ash.do_read_one/3
       (ash 3.5.26) lib/ash.ex:2910: Ash.read_one/2
       (ash 3.5.26) lib/ash.ex:2863: Ash.read_one!/2

The desired behavior is to select all instances of Deployment which have at least one DeploymentTag matching the key and value field of the AlertRule.

Every help is appreciated. Thanks in advance!

Directly after posting it I figured it out. I’ve to “escape” twice to the parent resource.

    has_many :monitored_deployments, App.Deployments.Deployment do
      no_attributes? true
      filter expr(workspace_id == parent(workspace_id))

      filter expr(
               exists(
                 deployment_tags,
                 key == parent(parent(key)) and value == parent(parent(value))
               )
             )
    end
1 Like

Could you not model this with a many_to_many relationship? You would have to add belongs_to on your deployment tags.

I’m not 100% sure. I haven’t tried it myself.

edit: This isn’t gonna work because you would need some kind of key that you could use for the join that you can specify as the source_attribute_on_join_resource.

Your implementation might become a future performance issue I think unless you manually create a composite index on key,value I think.