I think your first attempt was closer to the mark, but the syntax isnβt quite right.
The docs are a bit sparse, but looking at Aggregates β ash v3.2.2, I think something like exists(member_tags, query: [filter: tag_id == ^id]) might work?
Compiling 95 files (.ex)
warning: variable "query" is unused (if the variable is not meant to be used, prefix it with an underscore)
β
124 β def members_query(query, {:tag_ids, tag_ids}) do
β ~~~~~
β
ββ lib/gf/members/members2_query.ex:124:21: GF.Members.Members2Query.members_query/2
warning: variable "tag_ids" is unused (if the variable is not meant to be used, prefix it with an underscore)
β
124 β def members_query(query, {:tag_ids, tag_ids}) do
β ~~~~~~~
β
ββ lib/gf/members/members2_query.ex:124:39: GF.Members.Members2Query.members_query/2
error: undefined function exists/2 (expected GF.Members.Members2Query to define such a function or for it to be imported, but none are available)
β
140 β exists(member_tags, query: [filter: tag_id == ^id])
β ^^^^^^
β
ββ lib/gf/members/members2_query.ex:140:5: GF.Members.Members2Query.members_query/2
error: misplaced operator ^id
The pin operator ^ is supported only inside matches or inside custom macros. Make sure you are inside a match or all necessary macros have been required
β
140 β exists(member_tags, query: [filter: tag_id == ^id])
β ^
β
ββ lib/gf/members/members2_query.ex:140:51: GF.Members.Members2Query.members_query/2
error: undefined variable "tag_id"
β
140 β exists(member_tags, query: [filter: tag_id == ^id])
β ^^^^^^
β
ββ lib/gf/members/members2_query.ex:140:41: GF.Members.Members2Query.members_query/2
error: undefined variable "member_tags"
β
140 β exists(member_tags, query: [filter: tag_id == ^id])
β ^^^^^^^^^^^
β
ββ lib/gf/members/members2_query.ex:140:12: GF.Members.Members2Query.members_query/2
05:47:15.598 request_id=F-NPcmVbW3yUlOAAAd4B [warning] `87826e8d-913e-4847-88c9-d26d287f24ce`: AshGraphql.Error not implemented for error:
** (Ash.Error.Query.NoSuchField) No such field query for resource GF.Members.MemberTag2
(elixir 1.16.1) lib/process.ex:860: Process.info/2
(ash 3.1.3) lib/ash/error/query/no_such_field.ex:5: Ash.Error.Query.NoSuchField.exception/1
(ash 3.1.3) lib/ash/filter/filter.ex:2857: Ash.Filter.add_expression_part/3
The documentation that @zachdaniel pointed to doesnβt cover the case where the recordβs has_many relationship needs to match every value in a list.
The query: [filter: ....] variation wonβt work. That is the syntax for inline aggregates, whereas exists is special. The snippet I posed should work.
exists(member_tags, tag_id == ^id)
The kind of query you are writing is, in general, not a very easy query to write. There are a few ways you could do it. Your first example was very close to correct
Using exists
# where a member_tag exists for each tag_id
Enum.reduce(tag_ids, query, fn id, query ->
Ash.Query.filter(query, exists(member_tags, tag_id == ^id))
end)
Would do what you want. This will result in a rather large SQL query w/ an EXISTS per tag_id youβre searching for (and would probably be fine in general).
Using count/2
However, you could also do something like this which would result in joining once. Only works if your join resource is unique on member_id and tag_id.
# where the number of tags that exist with these tag_ids
# equals the number of tag_ids I asked for
Ash.Query.filter(
count(member_tags, query: [filter: tag_id in ^tag_ids]) == ^Enum.count(tag_ids)
)
And here you can see the syntax @sevenseacat was getting at in her first response. That syntax just happens to not apply to exists/2
You may want to profile/try each out to see how it performs in your use case.
I had to modify the second one, to add the query first argument
# where the number of tags that exist with these tag_ids
# equals the number of tag_ids I asked for
Ash.Query.filter(
query,
count(member_tags, query: [filter: tag_id in ^tag_ids]) == ^Enum.count(tag_ids)
)
Indeed, the join table should be unique on Member and Tag, so this solution is probably better.