Make more progress on saving/updating articles.

This commit is contained in:
Brent Simmons 2017-09-02 16:08:02 -07:00
parent d33d8a0330
commit fb121f8a8c
10 changed files with 61 additions and 17 deletions

View File

@ -7,6 +7,7 @@
//
import Foundation
import RSCore
// MD5 works because:
// * Its fast

View File

@ -254,7 +254,15 @@ private extension ArticlesTable {
func updateArticles(_ articlesDictionary: [String: Article], _ parsedItemsDictionary: [String: ParsedItem], _ feed: Feed, _ completion: @escaping RSVoidCompletionBlock) {
let parsedItemArticleIDs = Set(parsedItemsDictionary.keys)
queue.update { (database) in
self.statusesTable.ensureStatusesForArticleIDs(parsedItemArticleIDs, database)
}
}
}

View File

@ -8,10 +8,11 @@
import Foundation
import RSParser
import Data
extension ParsedItem {
private func databaseIdentifierWithFeed(_ feed: Feed) -> String {
func databaseIdentifierWithFeed(_ feed: Feed) -> String {
if let identifier = syncServiceID {
return identifier
@ -28,8 +29,8 @@ extension ParsedFeed {
var d = [String: ParsedItem]()
for parsedItem in parsedItems {
let identifier = identifierForParsedItem(parsedItem, feed)
for parsedItem in items {
let identifier = parsedItem.databaseIdentifierWithFeed(feed)
d[identifier] = parsedItem
}

View File

@ -40,7 +40,7 @@ final class StatusesTable: DatabaseTable {
return articleStatus
}
// MARK: Creating
// MARK: Creating/Updating
func ensureStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) {
@ -49,12 +49,32 @@ final class StatusesTable: DatabaseTable {
return
}
createAndSaveStatusesForArticles(articlesNeedingStatuses, database)
let articleIDs = articlesNeedingStatuses.articleIDs()
ensureStatusesForArticleIDs(articleIDs, database)
attachCachedStatuses(articlesNeedingStatuses)
assert(articles.eachHasAStatus())
}
func ensureStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
// Check cache.
let articleIDsMissingCachedStatus = articleIDsWithNoCachedStatus(articleIDs)
if articleIDsMissingCachedStatus.isEmpty {
return
}
// Check database.
fetchAndCacheStatusesForArticleIDs(articleIDsMissingCachedStatus, database)
let articleIDsNeedingStatus = articleIDsWithNoCachedStatus(articleIDs)
if articleIDsNeedingStatus.isEmpty {
return
}
// Create new statuses.
createAndSaveStatusesForArticleIDs(articleIDsNeedingStatus, database)
}
// MARK: Marking
func markArticleIDs(_ articleIDs: Set<String>, _ statusKey: String, _ flag: Bool, _ database: FMDatabase) {
@ -74,6 +94,11 @@ private extension StatusesTable {
}
}
func articleIDsWithNoCachedStatus(_ articleIDs: Set<String>) -> Set<String> {
return Set(articleIDs.filter { cache[$0] == nil })
}
// MARK: Creating
func saveStatuses(_ statuses: Set<ArticleStatus>, _ database: FMDatabase) {
@ -82,7 +107,7 @@ private extension StatusesTable {
insertRows(statusArray, insertType: .orIgnore, in: database)
}
func cacheStatuses(_ statuses: [ArticleStatus]) {
func cacheStatuses(_ statuses: Set<ArticleStatus>) {
let databaseObjects = statuses.map { $0 as DatabaseObject }
cache.addObjectsNotCached(databaseObjects)
@ -97,10 +122,19 @@ private extension StatusesTable {
func createAndSaveStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
let now = Date()
let statuses = articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) }
let statuses = Set(articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) })
cacheStatuses(statuses)
saveStatuses(Set(statuses), database)
saveStatuses(statuses, database)
}
func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else {
return
}
let statuses = resultSet.mapToSet(statusWithRow)
cacheStatuses(statuses)
}
}

View File

@ -23,7 +23,7 @@ NSString *RSStringReplaceAll(NSString *stringToSearch, NSString *searchFor, NSSt
/*The hashed data is a UTF-8 encoded version of the string.*/
- (NSData *)rs_md5Hash;
- (NSData *)rs_md5HashData;
- (NSString *)rs_md5HashString;

View File

@ -45,7 +45,7 @@ NSString *RSStringReplaceAll(NSString *stringToSearch, NSString *searchFor, NSSt
@implementation NSString (RSCore)
- (NSData *)rs_md5Hash {
- (NSData *)rs_md5HashData {
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
return [data rs_md5Hash];
@ -54,7 +54,7 @@ NSString *RSStringReplaceAll(NSString *stringToSearch, NSString *searchFor, NSSt
- (NSString *)rs_md5HashString {
NSData *d = [self rs_md5Hash];
NSData *d = [self rs_md5HashData];
return [d rs_hexadecimalString];
}

View File

@ -114,7 +114,7 @@ private extension JSONFeedParser {
let tags = itemDictionary["tags"] as? [String]
let attachments = parseAttachments(itemDictionary)
return ParsedItem(uniqueID: uniqueID, feedURL: feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments)
return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments)
}
static func parseUniqueID(_ itemDictionary: JSONDictionary) -> String? {

View File

@ -127,7 +127,7 @@ private extension RSSInJSONParser {
}
if let uniqueID = uniqueID {
return ParsedItem(uniqueID: uniqueID, feedURL: feedURL, url: nil, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: nil, authors: authors, tags: tags, attachments: attachments)
return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: feedURL, url: nil, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: nil, authors: authors, tags: tags, attachments: attachments)
}
return nil
}

View File

@ -46,7 +46,7 @@ public struct ParsedItem: Hashable {
self.authors = authors
self.tags = tags
self.attachments = attachments
self.hashValue = articleID.hashValue
self.hashValue = feedURL.hashValue ^ uniqueID.hashValue
}
public static func ==(lhs: ParsedItem, rhs: ParsedItem) -> Bool {

View File

@ -46,7 +46,7 @@ private extension RSParsedFeedTransformer {
let dateModified = parsedArticle.dateModified
let authors = parsedAuthors(parsedArticle.author)
return ParsedItem(uniqueID: uniqueID, feedURL: parsedArticle.feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: nil, attachments: nil)
return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: parsedArticle.feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: nil, attachments: nil)
}
static func parsedAuthors(_ authorEmailAddress: String?) -> [ParsedAuthor]? {