Say I have a DB with two main entities (Song and Tag) and a many-to-many relationship between them. Using Room, I want to query the Songs that have a series of Tags (all of them) by their names.
So, given this example data in the cross ref table (SongTagCrossRef):
| Song | Tag | 
|---|---|
| song1 | tag1 | 
| song1 | tag2 | 
| song1 | tag3 | 
| song2 | tag2 | 
| song3 | tag2 | 
| song3 | tag3 | 
I want the query to return only song1 if I enter tag1 and tag2, as it's the only song related to both.
I've come up with this @Query in the corresponding Dao:
@Query("""
    SELECT s.* FROM Song s
    JOIN SongTagCrossRef st ON s.song_id = st.song_id
    JOIN Tag t ON st.tag_id = t.tag_id
    WHERE t.name IN (:tagNames)
    GROUP BY s.song_id
    HAVING COUNT(*) = (SELECT COUNT(*) FROM Tag WHERE name IN (:tagNames))
""")
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity>
Since I can't access tagNames.size in the @Query, I've had to use a subquery to artificially get it. This subquery shouldn't be too heavy, but it would always be better to somehow access tagNames.size.
After reading the answers to a slightly related question, I've been toying with creating a @RawQuery and calling it from a function that takes only tagNames, something along these lines:
@RawQuery
fun getSongsWithAllOfTheTagsByName(query: SupportSQLiteQuery): List<SongEntity>
fun getSongsWithAllOfTheTagsByName(vararg tagNames: String): List<SongEntity> {
    val query = SimpleSQLiteQuery("""
        SELECT s.* FROM Song s
        JOIN SongTagCrossRef st ON s.song_id = st.song_id
        JOIN Tag t ON st.tag_id = t.tag_id
        WHERE t.name IN (?)
        GROUP BY s.song_id
        HAVING COUNT(*) = ?
    """, arrayOf(tagNames, tagNames.size))
    return getSongsWithAllOfTheTagsByName(query)
}
(only converting tagNames to something it can actually swallow)
But I've discarded this approach because I don't want to expose a function that takes a query.
Is there a simpler, more elegant way to write this query?
