Switch to using DatabaseArticle over article dictionaries.
This commit is contained in:
parent
19e65dd076
commit
d9f0e53312
|
@ -158,19 +158,18 @@ private extension ArticlesTable {
|
||||||
|
|
||||||
func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> {
|
func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> {
|
||||||
|
|
||||||
// 1. Create article dictionaries without related objects.
|
// 1. Create DatabaseArticles without related objects.
|
||||||
// 2. Then fetch the related objects, given the set of articleIDs.
|
// 2. Then fetch the related objects, given the set of articleIDs.
|
||||||
// 3. Then fetch statuses.
|
// 3. Then create set of Articles with DatabaseArticles and related objects and return it.
|
||||||
// 4. Then create set of Articles with status and related objects and return it.
|
|
||||||
|
|
||||||
// 1. Create article dictionaries.
|
// 1. Create databaseArticles (intermediate representations).
|
||||||
|
|
||||||
let articleDictionaries = makeArticleDictionaries(with: resultSet)
|
let databaseArticles = makeDatabaseArticles(with: resultSet)
|
||||||
if articleDictionaries.isEmpty {
|
if databaseArticles.isEmpty {
|
||||||
return Set<Article>()
|
return Set<Article>()
|
||||||
}
|
}
|
||||||
|
|
||||||
let articleIDs = Set(articleDictionaries.map { $0[DatabaseKey.articleID] as! String })
|
let articleIDs = databaseArticles.articleIDs()
|
||||||
|
|
||||||
// 2. Fetch related objects.
|
// 2. Fetch related objects.
|
||||||
|
|
||||||
|
@ -178,107 +177,65 @@ private extension ArticlesTable {
|
||||||
let attachmentsMap = attachmentsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
|
let attachmentsMap = attachmentsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
|
||||||
let tagsMap = tagsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
|
let tagsMap = tagsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
|
||||||
|
|
||||||
// 3. All statuses for articleDictionaries were created (when needed) in makeArticleDictionaries.
|
// 3. Create articles with related objects.
|
||||||
|
|
||||||
let statusesDictionary = statusesTable.statusesDictionary(articleIDs)
|
let articles = databaseArticles.map { (databaseArticle) -> Article in
|
||||||
assert(statusesDictionary.count == articleIDs.count)
|
return articleWithDatabaseArticle(databaseArticle, authorsMap, attachmentsMap, tagsMap)
|
||||||
|
|
||||||
// 4. Create articles with related objects.
|
|
||||||
|
|
||||||
let articles = articleDictionaries.flatMap { (articleDictionary) -> Article? in
|
|
||||||
|
|
||||||
guard let articleID = articleDictionary[DatabaseKey.articleID] as? String else {
|
|
||||||
assertionFailure("articleID expected")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let status = statusesDictionary[articleID] else {
|
|
||||||
assertionFailure("status expected")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return articleWithDictionary(articleDictionary, status, authorsMap, attachmentsMap, tagsMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Set(articles)
|
return Set(articles)
|
||||||
}
|
}
|
||||||
|
|
||||||
func articleWithDictionary(_ articleDictionary: [String: Any], _ status: ArticleStatus, _ authorsMap: RelatedObjectsMap?, _ attachmentsMap: RelatedObjectsMap?, _ tagsMap: RelatedObjectsMap?) -> Article? {
|
func articleWithDatabaseArticle(_ databaseArticle: DatabaseArticle, _ authorsMap: RelatedObjectsMap?, _ attachmentsMap: RelatedObjectsMap?, _ tagsMap: RelatedObjectsMap?) -> Article {
|
||||||
|
|
||||||
let articleID = articleDictionary[DatabaseKey.articleID]! as! String
|
let articleID = databaseArticle.articleID
|
||||||
let authors = authorsMap?.authors(for: articleID)
|
let authors = authorsMap?.authors(for: articleID)
|
||||||
let attachments = attachmentsMap?.attachments(for: articleID)
|
let attachments = attachmentsMap?.attachments(for: articleID)
|
||||||
let tags = tagsMap?.tags(for: articleID)
|
let tags = tagsMap?.tags(for: articleID)
|
||||||
|
|
||||||
return Article(dictionary: articleDictionary, accountID: accountID, status: status, authors: authors, attachments: attachments, tags: tags)
|
return Article(databaseArticle: databaseArticle, accountID: accountID, authors: authors, attachments: attachments, tags: tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeArticleDictionaries(with resultSet: FMResultSet) -> [[String: Any]] {
|
func makeDatabaseArticles(with resultSet: FMResultSet) -> Set<DatabaseArticle> {
|
||||||
|
|
||||||
let dictionaries = resultSet.flatMap{ (row) -> [String: Any]? in
|
let articles = resultSet.mapToSet { (row) -> DatabaseArticle? in
|
||||||
|
|
||||||
// The resultSet is a result of a JOIN query with the statuses table,
|
// 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.
|
// so we can get the statuses at the same time and avoid additional database lookups.
|
||||||
|
|
||||||
// Don’t need status locally — we want it to get cached by statusesTable.
|
guard let status = statusesTable.statusWithRow(resultSet) else {
|
||||||
guard let _ = statusesTable.statusWithRow(resultSet) else {
|
|
||||||
assertionFailure("Expected status.")
|
assertionFailure("Expected status.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
|
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
|
||||||
|
assertionFailure("Expected articleID.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
|
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
|
||||||
|
assertionFailure("Expected feedID.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let uniqueID = row.string(forColumn: DatabaseKey.uniqueID) else {
|
guard let uniqueID = row.string(forColumn: DatabaseKey.uniqueID) else {
|
||||||
|
assertionFailure("Expected uniqueID.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var d = [String: Any]()
|
let title = row.string(forColumn: DatabaseKey.title)
|
||||||
|
let contentHTML = row.string(forColumn: DatabaseKey.contentHTML)
|
||||||
|
let contentText = row.string(forColumn: DatabaseKey.contentText)
|
||||||
|
let url = row.string(forColumn: DatabaseKey.url)
|
||||||
|
let externalURL = row.string(forColumn: DatabaseKey.externalURL)
|
||||||
|
let summary = row.string(forColumn: DatabaseKey.summary)
|
||||||
|
let imageURL = row.string(forColumn: DatabaseKey.imageURL)
|
||||||
|
let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL)
|
||||||
|
let datePublished = row.date(forColumn: DatabaseKey.datePublished)
|
||||||
|
let dateModified = row.date(forColumn: DatabaseKey.dateModified)
|
||||||
|
|
||||||
d[DatabaseKey.articleID] = articleID
|
return DatabaseArticle(articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, accountInfo: nil, status: status)
|
||||||
d[DatabaseKey.feedID] = feedID
|
|
||||||
d[DatabaseKey.uniqueID] = uniqueID
|
|
||||||
|
|
||||||
if let title = row.string(forColumn: DatabaseKey.title) {
|
|
||||||
d[DatabaseKey.title] = title
|
|
||||||
}
|
|
||||||
if let contentHTML = row.string(forColumn: DatabaseKey.contentHTML) {
|
|
||||||
d[DatabaseKey.contentHTML] = contentHTML
|
|
||||||
}
|
|
||||||
if let contentText = row.string(forColumn: DatabaseKey.contentText) {
|
|
||||||
d[DatabaseKey.contentText] = contentText
|
|
||||||
}
|
|
||||||
if let url = row.string(forColumn: DatabaseKey.url) {
|
|
||||||
d[DatabaseKey.url] = url
|
|
||||||
}
|
|
||||||
if let externalURL = row.string(forColumn: DatabaseKey.externalURL) {
|
|
||||||
d[DatabaseKey.externalURL] = externalURL
|
|
||||||
}
|
|
||||||
if let summary = row.string(forColumn: DatabaseKey.summary) {
|
|
||||||
d[DatabaseKey.summary] = summary
|
|
||||||
}
|
|
||||||
if let imageURL = row.string(forColumn: DatabaseKey.imageURL) {
|
|
||||||
d[DatabaseKey.imageURL] = imageURL
|
|
||||||
}
|
|
||||||
if let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL) {
|
|
||||||
d[DatabaseKey.bannerImageURL] = bannerImageURL
|
|
||||||
}
|
|
||||||
if let datePublished = row.date(forColumn: DatabaseKey.datePublished) {
|
|
||||||
d[DatabaseKey.datePublished] = datePublished
|
|
||||||
}
|
|
||||||
if let dateModified = row.date(forColumn: DatabaseKey.dateModified) {
|
|
||||||
d[DatabaseKey.dateModified] = dateModified
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: accountInfo
|
return articles
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
resultSet.close()
|
|
||||||
|
|
||||||
return dictionaries
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesWithWhereClause(_ database: FMDatabase, whereClause: String, parameters: [AnyObject], withLimits: Bool) -> Set<Article> {
|
func fetchArticlesWithWhereClause(_ database: FMDatabase, whereClause: String, parameters: [AnyObject], withLimits: Bool) -> Set<Article> {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Data
|
||||||
|
|
||||||
// Intermediate representation of an Article. Doesn’t include related objects.
|
// Intermediate representation of an Article. Doesn’t include related objects.
|
||||||
// Used by ArticlesTable as part of fetching articles.
|
// Used by ArticlesTable as part of fetching articles.
|
||||||
|
@ -32,6 +33,7 @@ struct DatabaseArticle: Hashable {
|
||||||
|
|
||||||
init(articleID: String, feedID: String, uniqueID: String, title: String?, contentHTML: String?, contentText: String?, url: String?, externalURL: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, accountInfo: AccountInfo?, status: ArticleStatus) {
|
init(articleID: String, feedID: String, uniqueID: String, title: String?, contentHTML: String?, contentText: String?, url: String?, externalURL: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, accountInfo: AccountInfo?, status: ArticleStatus) {
|
||||||
|
|
||||||
|
self.articleID = articleID
|
||||||
self.feedID = feedID
|
self.feedID = feedID
|
||||||
self.uniqueID = uniqueID
|
self.uniqueID = uniqueID
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -46,12 +48,20 @@ struct DatabaseArticle: Hashable {
|
||||||
self.dateModified = dateModified
|
self.dateModified = dateModified
|
||||||
self.accountInfo = accountInfo
|
self.accountInfo = accountInfo
|
||||||
self.status = status
|
self.status = status
|
||||||
|
|
||||||
self.hashValue = articleID.hashValue
|
self.hashValue = articleID.hashValue
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: DatabaseArticle, rhs: DatabaseArticle) -> Bool {
|
static func ==(lhs: DatabaseArticle, rhs: DatabaseArticle) -> Bool {
|
||||||
|
|
||||||
return lhs.hashValue == rhs.hashValue && lhs.articleID == rhs.articleID && lhs.feedID == rhs.feedID && lhs.uniqueID == rhs.uniqueID && lhs.title == rhs.title && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.url == rhs.url && lhs.externalURL == rhs.externalURL && lhs.sumary == rhs.summary && lhs.imageURL == rhs.imageURL && lhs.bannerImageURL == rhs.bannerImageURL && lhs.datePublished == rhs.datePublished && lhs.dateModified == rhs.dateModified && lhs.status == rhs.status
|
return lhs.hashValue == rhs.hashValue && lhs.articleID == rhs.articleID && lhs.feedID == rhs.feedID && lhs.uniqueID == rhs.uniqueID && lhs.title == rhs.title && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.url == rhs.url && lhs.externalURL == rhs.externalURL && lhs.summary == rhs.summary && lhs.imageURL == rhs.imageURL && lhs.bannerImageURL == rhs.bannerImageURL && lhs.datePublished == rhs.datePublished && lhs.dateModified == rhs.dateModified && lhs.status == rhs.status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension Set where Element == DatabaseArticle {
|
||||||
|
|
||||||
|
func articleIDs() -> Set<String> {
|
||||||
|
|
||||||
|
return Set<String>(map { $0.articleID })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,50 +13,9 @@ import RSParser
|
||||||
|
|
||||||
extension Article {
|
extension Article {
|
||||||
|
|
||||||
// init?(row: FMResultSet, accountID: String, authors: Set<Author>? = nil, attachments: Set<Attachment>? = nil, tags: Set<String>? = nil, status: ArticleStatus) {
|
init(databaseArticle: DatabaseArticle, accountID: String, authors: Set<Author>?, attachments: Set<Attachment>?, tags: Set<String>?) {
|
||||||
//
|
|
||||||
// guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// guard let uniqueID = row.string(forColumn: DatabaseKey.uniqueID) else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let articleID = row.string(forColumn: DatabaseKey.articleID)!
|
|
||||||
// let title = row.string(forColumn: DatabaseKey.title)
|
|
||||||
// let contentHTML = row.string(forColumn: DatabaseKey.contentHTML)
|
|
||||||
// let contentText = row.string(forColumn: DatabaseKey.contentText)
|
|
||||||
// let url = row.string(forColumn: DatabaseKey.url)
|
|
||||||
// let externalURL = row.string(forColumn: DatabaseKey.externalURL)
|
|
||||||
// let summary = row.string(forColumn: DatabaseKey.summary)
|
|
||||||
// let imageURL = row.string(forColumn: DatabaseKey.imageURL)
|
|
||||||
// let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL)
|
|
||||||
// let datePublished = row.date(forColumn: DatabaseKey.datePublished)
|
|
||||||
// let dateModified = row.date(forColumn: DatabaseKey.dateModified)
|
|
||||||
// let accountInfo: AccountInfo? = nil // TODO
|
|
||||||
//
|
|
||||||
// self.init(accountID: accountID, articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo, status: status)
|
|
||||||
// }
|
|
||||||
|
|
||||||
init?(dictionary: [String: Any], accountID: String, status: ArticleStatus, authors: Set<Author>?, attachments: Set<Attachment>?, tags: Set<String>?) {
|
self.init(accountID: accountID, articleID: databaseArticle.articleID, feedID: databaseArticle.feedID, uniqueID: databaseArticle.uniqueID, title: databaseArticle.title, contentHTML: databaseArticle.contentHTML, contentText: databaseArticle.contentText, url: databaseArticle.url, externalURL: databaseArticle.externalURL, summary: databaseArticle.summary, imageURL: databaseArticle.imageURL, bannerImageURL: databaseArticle.bannerImageURL, datePublished: databaseArticle.datePublished, dateModified: databaseArticle.dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: databaseArticle.accountInfo, status: databaseArticle.status)
|
||||||
|
|
||||||
guard let articleID = dictionary[DatabaseKey.articleID] as? String, let feedID = dictionary[DatabaseKey.feedID] as? String, let uniqueID = dictionary[DatabaseKey.uniqueID] as? String else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let title = dictionary[DatabaseKey.title] as? String
|
|
||||||
let contentHTML = dictionary[DatabaseKey.contentHTML] as? String
|
|
||||||
let contentText = dictionary[DatabaseKey.contentText] as? String
|
|
||||||
let url = dictionary[DatabaseKey.url] as? String
|
|
||||||
let externalURL = dictionary[DatabaseKey.externalURL] as? String
|
|
||||||
let summary = dictionary[DatabaseKey.summary] as? String
|
|
||||||
let imageURL = dictionary[DatabaseKey.imageURL] as? String
|
|
||||||
let bannerImageURL = dictionary[DatabaseKey.bannerImageURL] as? String
|
|
||||||
let datePublished = dictionary[DatabaseKey.datePublished] as? Date
|
|
||||||
let dateModified = dictionary[DatabaseKey.dateModified] as? Date
|
|
||||||
let accountInfo: AccountInfo? = nil // TODO
|
|
||||||
|
|
||||||
self.init(accountID: accountID, articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo, status: status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(parsedItem: ParsedItem, accountID: String, feedID: String, status: ArticleStatus) {
|
init(parsedItem: ParsedItem, accountID: String, feedID: String, status: ArticleStatus) {
|
||||||
|
@ -67,15 +26,6 @@ extension Article {
|
||||||
self.init(accountID: accountID, articleID: parsedItem.syncServiceID, feedID: feedID, uniqueID: parsedItem.uniqueID, title: parsedItem.title, contentHTML: parsedItem.contentHTML, contentText: parsedItem.contentText, url: parsedItem.url, externalURL: parsedItem.externalURL, summary: parsedItem.summary, imageURL: parsedItem.imageURL, bannerImageURL: parsedItem.bannerImageURL, datePublished: parsedItem.datePublished, dateModified: parsedItem.dateModified, authors: authors, tags: parsedItem.tags, attachments: attachments, accountInfo: nil, status: status)
|
self.init(accountID: accountID, articleID: parsedItem.syncServiceID, feedID: feedID, uniqueID: parsedItem.uniqueID, title: parsedItem.title, contentHTML: parsedItem.contentHTML, contentText: parsedItem.contentText, url: parsedItem.url, externalURL: parsedItem.externalURL, summary: parsedItem.summary, imageURL: parsedItem.imageURL, bannerImageURL: parsedItem.bannerImageURL, datePublished: parsedItem.datePublished, dateModified: parsedItem.dateModified, authors: authors, tags: parsedItem.tags, attachments: attachments, accountInfo: nil, status: status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func articleByAttaching(_ authors: Set<Author>?, _ attachments: Set<Attachment>?, _ tags: Set<String>?) -> Article {
|
|
||||||
//
|
|
||||||
// if authors == nil && attachments == nil && tags == nil {
|
|
||||||
// return self
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return Article(accountID: accountID, articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo, status: status)
|
|
||||||
// }
|
|
||||||
|
|
||||||
private func addPossibleStringChangeWithKeyPath(_ comparisonKeyPath: KeyPath<Article,String?>, _ otherArticle: Article, _ key: String, _ dictionary: NSMutableDictionary) {
|
private func addPossibleStringChangeWithKeyPath(_ comparisonKeyPath: KeyPath<Article,String?>, _ otherArticle: Article, _ key: String, _ dictionary: NSMutableDictionary) {
|
||||||
|
|
||||||
if self[keyPath: comparisonKeyPath] != otherArticle[keyPath: comparisonKeyPath] {
|
if self[keyPath: comparisonKeyPath] != otherArticle[keyPath: comparisonKeyPath] {
|
||||||
|
|
Loading…
Reference in New Issue