diff --git a/Frameworks/RSDatabase/DatabaseTable.swift b/Frameworks/RSDatabase/DatabaseTable.swift index 978382b36..e49053c1d 100644 --- a/Frameworks/RSDatabase/DatabaseTable.swift +++ b/Frameworks/RSDatabase/DatabaseTable.swift @@ -44,13 +44,13 @@ public extension DatabaseTable { // MARK: Updating - public func updateRowsWithValue(_ value: Any, valueKey: String, whereKey: String, matches: [Any]) { - - queue.update { (database: FMDatabase!) in - - let _ = database.rs_updateRows(withValue: value, valueKey: valueKey, whereKey: whereKey, inValues: matches, tableName: self.name) - } - } +// public func updateRowsWithValue(_ value: Any, valueKey: String, whereKey: String, matches: [Any]) { +// +// queue.update { (database: FMDatabase!) in +// +// let _ = database.rs_updateRows(withValue: value, valueKey: valueKey, whereKey: whereKey, inValues: matches, tableName: self.name) +// } +// } // MARK: Saving diff --git a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj index 7bf030d71..ab2506259 100755 --- a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj +++ b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj @@ -35,8 +35,8 @@ 84419B061B5ABFF700C26BB2 /* FMResultSet+RSExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 84419B041B5ABFF700C26BB2 /* FMResultSet+RSExtras.m */; }; 844D97411F2D32F300CEDDEA /* ObjectCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844D97401F2D32F300CEDDEA /* ObjectCache.swift */; }; 849BF8C61C94FB8E0071D1DA /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 849BF8C51C94FB8E0071D1DA /* libsqlite3.tbd */; }; - 84ABC1D11F364B07000DCC55 /* LookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* LookupTable.swift */; }; - 84ABC1D21F364B07000DCC55 /* LookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* LookupTable.swift */; }; + 84ABC1D11F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */; }; + 84ABC1D21F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */; }; 84C6DD011F395C13009AFB47 /* DatabaseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C6DD001F395C13009AFB47 /* DatabaseObject.swift */; }; 84DDF1961C94FC45005E6CF5 /* FMDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DDF18B1C94FC45005E6CF5 /* FMDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; }; 84DDF1971C94FC45005E6CF5 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 84DDF18C1C94FC45005E6CF5 /* FMDatabase.m */; }; @@ -74,7 +74,7 @@ 84419B041B5ABFF700C26BB2 /* FMResultSet+RSExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "FMResultSet+RSExtras.m"; path = "RSDatabase/FMResultSet+RSExtras.m"; sourceTree = ""; }; 844D97401F2D32F300CEDDEA /* ObjectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ObjectCache.swift; path = RSDatabase/ObjectCache.swift; sourceTree = ""; }; 849BF8C51C94FB8E0071D1DA /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; - 84ABC1D01F364B07000DCC55 /* LookupTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LookupTable.swift; path = RSDatabase/LookupTable.swift; sourceTree = ""; }; + 84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseLookupTable.swift; path = RSDatabase/DatabaseLookupTable.swift; sourceTree = ""; }; 84C6DD001F395C13009AFB47 /* DatabaseObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseObject.swift; path = RSDatabase/DatabaseObject.swift; sourceTree = ""; }; 84DDF18B1C94FC45005E6CF5 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; 84DDF18C1C94FC45005E6CF5 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; @@ -162,7 +162,7 @@ 84419AD91B5ABD7400C26BB2 /* NSString+RSDatabase.m */, 840405DA1F1C158C00DF0296 /* DatabaseTable.swift */, 84C6DD001F395C13009AFB47 /* DatabaseObject.swift */, - 84ABC1D01F364B07000DCC55 /* LookupTable.swift */, + 84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */, 844D97401F2D32F300CEDDEA /* ObjectCache.swift */, 84DDF18A1C94FC45005E6CF5 /* FMDB */, 84F22C5A1B52E0D9000060CE /* Info.plist */, @@ -357,7 +357,7 @@ files = ( 8400AC001E0CFC0700AA7C57 /* RSDatabaseQueue.m in Sources */, 8400AC061E0CFC0700AA7C57 /* NSString+RSDatabase.m in Sources */, - 84ABC1D21F364B07000DCC55 /* LookupTable.swift in Sources */, + 84ABC1D21F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */, 8400AC0C1E0CFC3100AA7C57 /* FMResultSet.m in Sources */, 840405DC1F1C15EA00DF0296 /* DatabaseTable.swift in Sources */, 8400AC021E0CFC0700AA7C57 /* FMDatabase+RSExtras.m in Sources */, @@ -376,7 +376,7 @@ 84C6DD011F395C13009AFB47 /* DatabaseObject.swift in Sources */, 84419ADB1B5ABD7400C26BB2 /* NSString+RSDatabase.m in Sources */, 840405DB1F1C158C00DF0296 /* DatabaseTable.swift in Sources */, - 84ABC1D11F364B07000DCC55 /* LookupTable.swift in Sources */, + 84ABC1D11F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */, 84DDF1971C94FC45005E6CF5 /* FMDatabase.m in Sources */, 84DDF1A01C94FC45005E6CF5 /* FMResultSet.m in Sources */, 84419B061B5ABFF700C26BB2 /* FMResultSet+RSExtras.m in Sources */, diff --git a/Frameworks/RSDatabase/RSDatabase/LookupTable.swift b/Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift similarity index 69% rename from Frameworks/RSDatabase/RSDatabase/LookupTable.swift rename to Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift index cf8992028..3e98cbd12 100644 --- a/Frameworks/RSDatabase/RSDatabase/LookupTable.swift +++ b/Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift @@ -11,19 +11,15 @@ import Foundation // Implement a lookup table for a many-to-many relationship. // Example: CREATE TABLE if not EXISTS authorLookup (authorID TEXT NOT NULL, articleID TEXT NOT NULL, PRIMARY KEY(authorID, articleID)); // authorID is primaryKey; articleID is foreignKey. -// -// foreignIDsWithNoRelationship: caches the foreignIDs where it’s known that there’s no relationship. -// lookupsByForeignID: caches the LookupValues for a foreignID. -public typealias LookupTableDictionary = [String: Set] // key is foreignID -public final class LookupTable { +public final class DatabaseLookupTable { public let name: String let primaryKey: String let foreignKey: String private var foreignIDsWithNoRelationship = Set() - private var lookupsByForeignID = LookupTableDictionary() + private var cache = LookupTable(Set()) public init(name: String, primaryKey: String, foreignKey: String) { @@ -32,7 +28,7 @@ public final class LookupTable { self.foreignKey = foreignKey } - public func fetchLookupTableDictionary(_ foreignIDs: Set, _ database: FMDatabase) -> LookupTableDictionary? { + public func fetchLookupTable(_ foreignIDs: Set, _ database: FMDatabase) -> LookupTable? { let foreignIDsToLookup = foreignIDs.subtracting(foreignIDsWithNoRelationship) if foreignIDsToLookup.isEmpty { @@ -43,12 +39,12 @@ public final class LookupTable { var foreignIDsToFetchFromDatabase = Set() // Pull from cache. - for oneForeignID in foreignIDsToLookup { - if let cachedLookups = lookupsByForeignID[oneForeignID] { + for foreignID in foreignIDsToLookup { + if let cachedLookups = cache[foreignID] { lookupValues.formUnion(cachedLookups) } else { - foreignIDsToFetchFromDatabase.insert(oneForeignID) + foreignIDsToFetchFromDatabase.insert(foreignID) } } @@ -59,7 +55,7 @@ public final class LookupTable { } cacheNotFoundForeignIDs(lookupValues, foreignIDsToFetchFromDatabase) - cacheLookupValues(lookupValues) + cache.addLookupValues(lookupValues) return lookupTableDictionary(with: lookupValues) } @@ -109,7 +105,7 @@ public final class LookupTable { } for oneForeignID in foreignIDsToRemove { - lookupsByForeignID[oneForeignID] = nil + cache[oneForeignID] = nil } foreignIDsWithNoRelationship.formUnion(foreignIDsToRemove) @@ -117,18 +113,17 @@ public final class LookupTable { } } -private extension LookupTable { +private extension DatabaseLookupTable { func addToLookupTableDictionary(_ lookupValues: Set, _ table: inout LookupTableDictionary) { for lookupValue in lookupValues { let foreignID = lookupValue.foreignID - let primaryID = lookupValue.primaryID if table[foreignID] == nil { - table[foreignID] = Set([primaryID]) + table[foreignID] = Set([lookupValue]) } else { - table[foreignID]!.insert(primaryID) + table[foreignID]!.insert(lookupValue) } } } @@ -149,12 +144,16 @@ private extension LookupTable { // Note where nothing was found, and cache the foreignID in foreignIDsWithNoRelationship. - let foundForeignIDs = Set(lookupValues.map { $0.foreignID }) + let foundForeignIDs = lookupValues.foreignIDs() + var foreignIDsToRemove = Set() for foreignID in foreignIDs { if !foundForeignIDs.contains(foreignID) { foreignIDsWithNoRelationship.insert(foreignID) + foreignIDsToRemove.insert(foreignID) } } + + cache.removeLookupValuesWithForeignIDs(foreignIDsToRemove) } func lookupValuesWithResultSet(_ resultSet: FMResultSet) -> Set { @@ -175,11 +174,68 @@ private extension LookupTable { } -public struct LookupValue: Hashable { +final class LookupTable { + + var lookupValues: Set + var dictionary = [String: Set]() + + init(lookupValues: Set) { + + self.lookupValues = lookupValues + addLookupValuesToDictionary() + } + + func primaryIDs() -> Set { + + return lookupValues.primaryIDs() + } + + func addLookupValues(_ values: Set) { + + lookupValues.formUnion(values) + addLookupValuesToDictionary(values) + } + + func removeLookupValuesWithForeignIDs(_ foreignIDs: Set) { + + for foreignID in foreignIDs { + self[foreignID] = nil + } + + let lookupValuesToRemove = lookupValues.filter { (lookupValue) -> Bool in + foreignIDs.contains(lookupValue.foreignID) + } + lookupValues.subtract(lookupValuesToRemove) + } + + func addLookupValuesToDictionary(_ values: Set) { - public let primaryID: String - public let foreignID: String - public let hashValue: Int + for lookupValue in values { + let foreignID = lookupValue.foreignID + if self[foreignID] == nil { + self[foreignID] = Set([lookupValue]) + } + else { + self[foreignID]!.insert(lookupValue) + } + } + } + + subscript(_ foreignID: String) -> Set? { + get { + return dictionary[foreignID] + } + set { + dictionary[foreignID] = newValue + } + } +} + +struct LookupValue: Hashable { + + let primaryID: String + let foreignID: String + let hashValue: Int init(primaryID: String, foreignID: String) { @@ -195,9 +251,15 @@ public struct LookupValue: Hashable { } private extension Set where Element == LookupValue { - + func primaryIDs() -> Set { + + return Set(self.map { $0.primaryID }) + } + + func foreignIDs() -> Set { - return Set(map { $0.primaryID }) + return Set(self.map { $0.foreignID }) } } +