With group by, you need to use having instead of where. where works on table rows, while having will work on the entire group filtered by group by by means of aggregate functions (count, sum, avg, min, max .etc).
@sfusato gave you an answer, however your query still can be slow. Mostly because COUNT in Postgres can sometimes be slow. Instead it would be better to use EXISTS query (which will remove need for GROUP BY as well). The problem is that Ecto right now do not support such queries in Ecto.Query so you need to use fragment as a workaround:
from e in MyApp.Entity,
where: fragment("NOT EXISTS (SELECT * FROM associations a WHERE a.entity_id = ?", e.id)
Check plan and see which one is more performant (and do not forget about indices).
Trying multiple queries and comparing the execution plans is the best idea in this case (well, in most of the cases that require a non-trivial query).
The result can also be achieved with a left/right join + group by + a where clause on null id as illustrated by the below image (later edit: Venn diagrams are used in conjunction with sets and here it’s a cross product between tables, so keep this distinction in mind…thanks @hauleth for clarifying this).