Cache statuses when fetching articles — since articles are fetched using a join statement with the statuses table.

This commit is contained in:
Brent Simmons 2017-09-14 13:32:06 -07:00
parent 80c8a848e9
commit 72cfc84001
3 changed files with 68 additions and 41 deletions

View File

@ -166,26 +166,15 @@ private extension ArticlesTable {
// MARK: Fetching
func articleWithRow(_ row: FMResultSet) -> Article? {
guard let article = Article(row: row, accountID: accountID) else {
return nil
}
// Note: the row is a result of a JOIN query with the statuses table,
// so we can get the status at the same time and avoid additional database lookups.
// article.status = statusesTable.statusWithRow(row)
return article
}
func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> {
// Create set of stub Articles without related objects.
// Then fetch the related objects, given the set of articleIDs.
// Then create set of Articles *with* related objects and return it.
let (stubArticles, statuses) = stubArticlesAndStatuses(with: resultSet)
let stubArticles = resultSet.mapToSet(articleWithRow)
statusesTable.addIfNotCached(statuses)
if stubArticles.isEmpty {
return stubArticles
}
@ -202,23 +191,44 @@ private extension ArticlesTable {
}
// Create articles with related objects.
var articles = Set<Article>()
for stubArticle in articles {
let articleID = stubArticle.articleID
let authors = authorsMap?.authors(for: articleID)
let attachments = attachmentsMap?.attachments(for: articleID)
let tags = tagsMap?.tags(for: articleID)
let realArticle = stubArticle.articleByAttaching(authors, attachments, tags)
articles.insert(realArticle)
}
let articles = Set(stubArticles.map { articleWithAttachedRelatedObjects($0, authorsMap, attachmentsMap, tagsMap) })
return articles
}
func stubArticlesAndStatuses(with resultSet: FMResultSet) -> (Set<Article>, Set<ArticleStatus>) {
var stubArticles = Set<Article>()
var statuses = Set<ArticleStatus>()
// Note: the resultSet is a result of a JOIN query with the statuses table,
// so we can get the statuses at the same time and avoid additional database lookups.
while resultSet.next() {
if let stubArticle = Article(row: resultSet, accountID: accountID) {
stubArticles.insert(stubArticle)
}
if let status = statusesTable.statusWithRow(resultSet) {
statuses.insert(status)
}
}
resultSet.close()
return (stubArticles, statuses)
}
func articleWithAttachedRelatedObjects(_ stubArticle: Article, _ authorsMap: RelatedObjectsMap?, _ attachmentsMap: RelatedObjectsMap?, _ tagsMap: RelatedObjectsMap?) -> Article {
let articleID = stubArticle.articleID
let authors = authorsMap?.authors(for: articleID)
let attachments = attachmentsMap?.attachments(for: articleID)
let tags = tagsMap?.tags(for: articleID)
let realArticle = stubArticle.articleByAttaching(authors, attachments, tags)
return realArticle
}
func fetchArticlesWithWhereClause(_ database: FMDatabase, whereClause: String, parameters: [AnyObject], withLimits: Bool) -> Set<Article> {
// Dont fetch articles that shouldnt appear in the UI. The rules:

View File

@ -27,7 +27,9 @@ final class StatusesTable: DatabaseTable {
self.queue = queue
}
// MARK: Cache
func cachedStatus(for articleID: String) -> ArticleStatus? {
assert(Thread.isMainThread)
@ -35,6 +37,22 @@ final class StatusesTable: DatabaseTable {
return cache[articleID]
}
func addIfNotCached(_ statuses: Set<ArticleStatus>) {
if statuses.isEmpty {
return
}
if Thread.isMainThread {
self.cache.addIfNotCached(statuses)
}
else {
DispatchQueue.main.async {
self.cache.addIfNotCached(statuses)
}
}
}
// MARK: Creating/Updating
func ensureStatusesForArticleIDs(_ articleIDs: Set<String>, _ completion: @escaping StatusesCompletionBlock) {
@ -71,11 +89,6 @@ final class StatusesTable: DatabaseTable {
updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs), database: database)
}
}
// MARK: - Private
private extension StatusesTable {
// MARK: Fetching
@ -91,6 +104,11 @@ private extension StatusesTable {
let articleStatus = ArticleStatus(articleID: articleID, dateArrived: dateArrived, row: row)
return articleStatus
}
}
// MARK: - Private
private extension StatusesTable {
// MARK: Cache

View File

@ -6,12 +6,12 @@
</editor> -->
<title>ToDo</title>
<dateCreated>Tue, 12 Sep 2017 20:15:17 GMT</dateCreated>
<expansionState>11,16,17,20,24,29,31,34,37,39,40,44,49,52,54,56,58,67,72,78,83</expansionState>
<vertScrollState>59</vertScrollState>
<windowTop>248</windowTop>
<windowLeft>30</windowLeft>
<windowRight>762</windowRight>
<windowBottom>1007</windowBottom>
<expansionState>11,16,17,20,24,29,31,34,37,39,40,44,48,51,53,55,57,66,71,77,82</expansionState>
<vertScrollState>0</vertScrollState>
<windowTop>523</windowTop>
<windowLeft>40</windowLeft>
<windowRight>772</windowRight>
<windowBottom>1282</windowBottom>
</head>
<body>
<outline text="App">
@ -72,7 +72,6 @@
<outline text="mark articles"/>
<outline text="Delete old articles"/>
<outline text="Update cutoff date periodically"/>
<outline text="Do something with statuses on fetching articles"/>
</outline>
<outline text="StatusesTable">
<outline text="Update cached statuses on marking"/>