moxley

moxley

I’m trying to query members that have all the given tag IDs. The code needs to dynamically build an expression like:

member.member_tags.tag_id = tag_id1
AND member.member_tags.tag_id = tag_id2
AND member.member_tags.tag_id = tag_id3

where tag_id1, tag_id2, and tag_id3 come in as a list.

I tried this:

Enum.reduce(tag_ids, query, fn id, query ->
  Ash.Query.filter(query, exists(member_tags.tag_id == ^id))
end)

That results in:

[warning] `1b6d55b5-7d6a-478d-9872-86153c141ff0`: AshGraphql.Error not implemented for error:

** (Ash.Error.Query.NoSuchFunction) No such function exists for resource GF.Members.Member2
    (elixir 1.16.1) lib/process.ex:860: Process.info/2
    (ash 3.1.3) lib/ash/error/query/no_such_function.ex:5: Ash.Error.Query.NoSuchFunction.exception/1
    (ash 3.1.3) lib/ash/filter/filter.ex:3311: Ash.Filter.resolve_call/2
    (ash 3.1.3) lib/ash/filter/filter.ex:2513: Ash.Filter.add_expression_part/3
    (ash 3.1.3) lib/ash/filter/filter.ex:2451: anonymous fn/3 in Ash.Filter.parse_expression/2

I tried playing with Ash.Expr.expr():

expression = Ash.Expr.expr(true)

Enum.reduce(tag_ids, expression, fn id, expression ->
  Ash.Expr.expr(^expression and member_tags.tag_id == ^id)
end)

query = Ash.Query.filter(query, ^expression)

But the filter part of the query always came back as true.

Showing Posts 1 to 5

sevenseacat

sevenseacat

Author of Ash Framework

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.29.3, I think something like exists(member_tags, query: [filter: tag_id == ^id]) might work?

zachdaniel

zachdaniel

Creator of Ash

Exists is a special case because it was a calculation before the exist aggregate was a thing.


exists(member_tags, tag_id == ^id)

Example here: Expressions — ash v3.29.3

moxley

moxley OP

I tried:

exists(member_tags, query: [filter: tag_id == ^id])

Which produced:

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

I tried:

    Enum.reduce(tag_ids, query, fn id, query ->
      Ash.Query.filter(query, exists(member_tags, query: [filter: tag_id == ^id]))
    end)

It produced:

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.

Is there a solution for this?

zachdaniel

zachdaniel

Creator of Ash

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.

moxley

moxley OP

They both worked!

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.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
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
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews