So I'm trying to build a system that allows users to tailor the content that appears in a feed by creating exclusionary rules. As a quick overview, here's a simple look at the tables I'm working with :
| Table Name | Fields |
|---|---|
| Feed | PRIMARY KEY LONG postId PRIMARY KEY INT feedId STRING date |
| Post | PRIMARY KEY LONG id STRING creatorId STRING title STRING message STRING uploadDate |
| Tag | PRIMARY KEY AUTOGENERATE LONG id FOREIGN KEY LONG parentPostId STRING contents |
| BlacklistRule | PRIMARY KEY AUTOGENERATE LONG id STRING tableName STRING fieldName STRING contents |
The Feed gives you a list of ids to pull from the Posts table. When a Post is rendered, all of its Tags are attached to it.
An earlier version of this system was limited to just blacklisted tags, it was simple to join the Tag table to the BlacklistedTag table and filter the results from there.
@Query("
SELECT DISTINCT Feed.postId FROM Feed
LEFT JOIN (
SELECT DISTINCT Tag.parentPostId as postId FROM Tag
INNER JOIN BlacklistedTag ON Tag.contents = BlacklistedTag.contents
INNER JOIN Feed ON Tag.postId = Feed.postId
WHERE Feed.feedId = :feedId
ORDER BY datetime(Feed.date) DESC
) AS BlockedIds ON Feed.postId = BlockedIds.postId
WHERE Feed.feedId = :feedId
AND BlockedIds.postId IS NULL
ORDER BY datetime(date) DESC
LIMIT :pageSize
OFFSET :offset
")
fun getFilteredPostIdsByPage(
pageSize : Int = 48,
offset : Int = 0,
feedId : Int = ContentFeedIds.Home
) : List<Long>
However, I wanted to make the system more generic. The BlacklistRules are supposed to allow the program to filter out successive subsets of queries, like Tags with contents of FOO (I hate seeing FOO in my content feed) or Posts with BAR in the title.
Each row in the BlacklistRule table represents an aggregated subset of ids that will be used to filter the Feed, but I'm not sure how to perform loops in Android's RoomDB library, or SQLite in general.
So my question is : How would I perform a loop where I continually append unique ids to a set, then use that set as the filter for my content feed? If I were to write this in another language it would work like this :
// create the complete list of blacklisted content
var blacklistedIds : List<Long> = emptyList<Long>()
for (rule : BlacklistRule in BlacklistRules) {
val subquery : String = "SELECT UNIQUE postId FROM $rule.tableName " +
"WHERE $rule.fieldName CONTAINS $rule.contents"
// perform the subquery
// add the ids from the subquery to the blacklistedIds list
}
// fetch and return a page of the feed where the ids are not present in the blacklist
Any help is appreciated. If this is a fundamentally flawed approach, I'd love suggestions on how to improve this process.
The solution could be along the lines of joining every rule with every post/feed/tag permutation which would result in a table from which the rule could be applied.
For example suppose that the tables along with some data where:-
Resulting in:-
and
The the following (SQL) could be the basis of ascertaining what is to be excluded SHOWN TO DEMOSTRATE THE PRINCIPLE(S):-
The above, for the data above produces output as per:-
Note that the above uses CTE's (Common Table Expression) see https://www.sqlite.org/lang_with.html
To use the above to omit blacklisted posts (assumption that is what is required) then the following, which is very similar, can be used:-
i.e.
The above is a little awkward as the rules (CASE WHEN THE ELSE END constructs) have to suit what ever tables/fieldnames should be considered.
Note
The above does not make any reference/indication in regards to incorporating the above into Room. It is simply a matter of cut and past into an
@Queryand then minor editing to eliminate the line feeds.The above also makes many assumptions/interpretations.