Save and fetch attachments. Use a cache.
This commit is contained in:
parent
29c8badc34
commit
9341515926
|
@ -23,8 +23,18 @@ public struct Attachment: Hashable {
|
|||
self.url = url
|
||||
self.mimeType = mimeType
|
||||
self.title = title
|
||||
self.sizeInBytes = sizeInBytes
|
||||
self.durationInSeconds = durationInSeconds
|
||||
if let sizeInBytes = sizeInBytes, sizeInBytes > 0 {
|
||||
self.sizeInBytes = sizeInBytes
|
||||
}
|
||||
else {
|
||||
self.sizeInBytes = nil
|
||||
}
|
||||
if let durationInSeconds = durationInSeconds, durationInSeconds > 0 {
|
||||
self.durationInSeconds = durationInSeconds
|
||||
}
|
||||
else {
|
||||
self.durationInSeconds = nil
|
||||
}
|
||||
|
||||
var s = url
|
||||
s += mimeType ?? ""
|
||||
|
|
|
@ -14,14 +14,48 @@ final class AttachmentsTable: DatabaseRelatedObjectsTable {
|
|||
|
||||
let name: String
|
||||
let databaseIDKey = DatabaseKey.attachmentID
|
||||
var cache = [String: Attachment]()
|
||||
|
||||
init(name: String) {
|
||||
|
||||
self.name = name
|
||||
}
|
||||
|
||||
// MARK: DatabaseTable Methods
|
||||
|
||||
// MARK: DatabaseRelatedObjectsTable
|
||||
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject]? {
|
||||
|
||||
if databaseIDs.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cachedAttachments = Set<Attachment>()
|
||||
var databaseIDsToFetch = Set<String>()
|
||||
|
||||
for attachmentID in databaseIDs {
|
||||
if let cachedAttachment = cache[attachmentID] {
|
||||
cachedAttachments.insert(cachedAttachment)
|
||||
}
|
||||
else {
|
||||
databaseIDsToFetch.insert(attachmentID)
|
||||
}
|
||||
}
|
||||
|
||||
if databaseIDsToFetch.isEmpty {
|
||||
return cachedAttachments.databaseObjects()
|
||||
}
|
||||
|
||||
guard let resultSet = selectRowsWhere(key: databaseIDKey, inValues: Array(databaseIDsToFetch), in: database) else {
|
||||
return cachedAttachments.databaseObjects()
|
||||
}
|
||||
let fetchedDatabaseObjects = objectsWithResultSet(resultSet)
|
||||
let fetchedAttachments = Set(fetchedDatabaseObjects.map { $0 as Attachment })
|
||||
cacheAttachments(fetchedAttachments)
|
||||
|
||||
let allAttachments = cachedAttachments.union(fetchedAttachments)
|
||||
return allAttachments.databaseObjects()
|
||||
}
|
||||
|
||||
func objectWithRow(_ row: FMResultSet) -> DatabaseObject? {
|
||||
|
||||
if let attachment = attachmentWithRow(row) {
|
||||
|
@ -31,17 +65,37 @@ final class AttachmentsTable: DatabaseRelatedObjectsTable {
|
|||
}
|
||||
|
||||
func save(_ objects: [DatabaseObject], in database: FMDatabase) {
|
||||
// TODO
|
||||
|
||||
let attachments = objects.map { $0 as! Attachment }
|
||||
|
||||
// Attachments in cache must already exist in database. Filter them out.
|
||||
let attachmentsToSave = Set(attachments.filter { (attachment) -> Bool in
|
||||
if let _ = cache[attachment.attachmentID] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
cacheAttachments(attachmentsToSave)
|
||||
|
||||
insertRows(attachmentsToSave.databaseDictionaries(), insertType: .orIgnore, in: database)
|
||||
}
|
||||
}
|
||||
|
||||
private extension AttachmentsTable {
|
||||
|
||||
func cacheAttachments(_ attachments: Set<Attachment>) {
|
||||
|
||||
for attachment in attachments {
|
||||
cache[attachment.attachmentID] = attachment
|
||||
}
|
||||
}
|
||||
|
||||
func attachmentWithRow(_ row: FMResultSet) -> Attachment? {
|
||||
|
||||
guard let attachmentID = row.string(forColumn: DatabaseKey.attachmentID) else {
|
||||
return nil
|
||||
}
|
||||
// attachmentID is non-null in database schema.
|
||||
let attachmentID = row.string(forColumn: DatabaseKey.attachmentID)!
|
||||
return Attachment(attachmentID: attachmentID, row: row)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,29 @@ extension Attachment {
|
|||
let attachments = parsedAttachments.flatMap{ Attachment(parsedAttachment: $0) }
|
||||
return attachments.isEmpty ? nil : Set(attachments)
|
||||
}
|
||||
|
||||
func databaseDictionary() -> NSDictionary {
|
||||
|
||||
var d = NSMutableDictionary()
|
||||
|
||||
d[DatabaseKey.attachmentID] = attachmentID
|
||||
d[DatabaseKey.url] = url
|
||||
|
||||
if let mimeType = mimeType {
|
||||
d[DatabaseKey.mimeType] = mimeType
|
||||
}
|
||||
if let title = title {
|
||||
d[DatabaseKey.title] = title
|
||||
}
|
||||
if let sizeInBytes = sizeInBytes {
|
||||
d[DatabaseKey.sizeInBytes] = NSNumber(sizeInBytes)
|
||||
}
|
||||
if let durationInSeconds = durationInSeconds {
|
||||
d[DatabaseKey.durationInSeconds] = NSNumber(durationInSeconds)
|
||||
}
|
||||
|
||||
return d.copy() as! NSDictionary
|
||||
}
|
||||
}
|
||||
|
||||
private func optionalIntForColumn(_ row: FMResultSet, _ columnName: String) -> Int? {
|
||||
|
@ -60,3 +83,16 @@ extension Attachment: DatabaseObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Set where Element == Attachment {
|
||||
|
||||
func databaseDictionaries() -> [NSDictionary] {
|
||||
|
||||
return self.map<NSDictionary> { $0.databaseDictionary() }
|
||||
}
|
||||
|
||||
func databaseObjects() -> [DatabaseObject] {
|
||||
|
||||
return self.map<DatabaseObject> { $0 as DatabaseObject }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ final class TagsTable: DatabaseRelatedObjectsTable {
|
|||
|
||||
// MARK: DatabaseTable Methods
|
||||
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject] {
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject]? {
|
||||
|
||||
// A tag is a string, and it is its own databaseID.
|
||||
return databaseIDs.map{ $0 as DatabaseObject }
|
||||
|
@ -43,6 +43,5 @@ final class TagsTable: DatabaseRelatedObjectsTable {
|
|||
|
||||
// Nothing to do, since tags are saved in the lookup table, not in a separate table.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -12,16 +12,11 @@ public protocol DatabaseObject {
|
|||
|
||||
var databaseID: String { get }
|
||||
|
||||
func setRelatedObjects(_ objects: [DatabaseObject], name: String)
|
||||
func relatedObjectsWithName(_ name: String) -> [DatabaseObject]?
|
||||
}
|
||||
|
||||
public extension DatabaseObject {
|
||||
|
||||
func setRelatedObjects(_ objects: [DatabaseObject], name: String) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
func relatedObjectsWithName(_ name: String) -> [DatabaseObject]? {
|
||||
|
||||
return nil
|
||||
|
|
|
@ -183,8 +183,7 @@ private extension DatabaseLookupTable {
|
|||
|
||||
func fetchRelatedObjectsWithIDs(_ relatedObjectIDs: Set<String>, _ database: FMDatabase) -> [DatabaseObject]? {
|
||||
|
||||
let relatedObjects = relatedTable.fetchObjectsWithIDs(relatedObjectIDs, in: database)
|
||||
if relatedObjects.isEmpty {
|
||||
guard let relatedObjects = relatedTable.fetchObjectsWithIDs(relatedObjectIDs, in: database), !relatedObjects.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return relatedObjects
|
||||
|
|
|
@ -14,7 +14,7 @@ public protocol DatabaseRelatedObjectsTable: DatabaseTable {
|
|||
|
||||
var databaseIDKey: String { get}
|
||||
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject]
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject]?
|
||||
func objectsWithResultSet(_ resultSet: FMResultSet) -> [DatabaseObject]
|
||||
func objectWithRow(_ row: FMResultSet) -> DatabaseObject?
|
||||
|
||||
|
@ -25,10 +25,10 @@ public extension DatabaseRelatedObjectsTable {
|
|||
|
||||
// MARK: Default implementations
|
||||
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject] {
|
||||
func fetchObjectsWithIDs(_ databaseIDs: Set<String>, in database: FMDatabase) -> [DatabaseObject]? {
|
||||
|
||||
guard let resultSet = selectRowsWhere(key: databaseIDKey, inValues: Array(databaseIDs), in: database) else {
|
||||
return [DatabaseObject]()
|
||||
return nil
|
||||
}
|
||||
return objectsWithResultSet(resultSet)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue