diff --git a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj index e2941a807..c00a8c503 100755 --- a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj +++ b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj @@ -565,7 +565,7 @@ INFOPLIST_FILE = RSDatabase/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=25"; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=75"; PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSDatabase; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -586,7 +586,7 @@ INFOPLIST_FILE = RSDatabase/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=25"; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=75"; PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSDatabase; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift b/Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift index ab4f6ef25..8af4cad11 100644 --- a/Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift +++ b/Frameworks/RSDatabase/RSDatabase/DatabaseLookupTable.swift @@ -19,7 +19,7 @@ public final class DatabaseLookupTable { private let foreignKey: String private let relationshipName: String private weak var relatedTable: DatabaseTable? - private var foreignIDsWithNoRelationship = Set() + private let cache: DatabaseLookupTableCache public init(name: String, primaryKey: String, foreignKey: String, relatedTable: DatabaseTable, relationshipName: String) { @@ -28,6 +28,7 @@ public final class DatabaseLookupTable { self.foreignKey = foreignKey self.relatedTable = relatedTable self.relationshipName = relationshipName + self.cache = DatabaseLookupTableCache(relationshipName) } public func attachRelationships(to objects: [DatabaseObject], database: FMDatabase) { @@ -53,6 +54,7 @@ public final class DatabaseLookupTable { } removeRelationships(for: objectsWithNoRelationships, database: database) + updateRelationships(for: objectsWithRelationships, database: database) } } @@ -63,6 +65,32 @@ private extension DatabaseLookupTable { removeLookupsForForeignIDs(objects.databaseIDs(), database) } + func updateRelationships(for objects: [DatabaseObject], database: FMDatabase) { + + let objectsNeedingUpdate = objects.filter { (object) -> Bool in + return !relationshipsMatchCache(object) + } + } + + func relationshipsMatchCache(_ object: DatabaseObject) -> Bool { + + let relationships = object.relatedObjectsWithName(relationshipName) + let cachedRelationshipIDs = cache[object.databaseID] + + if let relationships = relationships { + if let cachedRelationshipIDs = cachedRelationshipIDs { + return relationships.databaseIDs() == cachedRelationshipIDs + } + return false // cachedRelationshipIDs == nil, relationships != nil + } + else { // relationships == nil + if let cachedRelationshipIDs = cachedRelationshipIDs { + return !cachedRelationshipIDs.isEmpty + } + return true // both nil + } + } + func attachRelationshipsUsingLookupTable(to objects: [DatabaseObject], lookupTable: LookupTable, database: FMDatabase) { let primaryIDs = lookupTable.primaryIDs() @@ -99,6 +127,14 @@ private extension DatabaseLookupTable { return LookupTable(lookupValues) } + func cacheForeignIDsWithNoRelationships(_ foreignIDs: Set) { + + foreignIDsWithNoRelationship.formUnion(foreignIDs) + for foreignID in foreignIDs { + cache[foreignID] = nil + } + } + func updateCache(_ lookupValues: Set, _ foreignIDs: Set) { // Maintain foreignIDsWithNoRelationship. @@ -106,9 +142,8 @@ private extension DatabaseLookupTable { // If a relationship does not exist, add the foreignID to foreignIDsWithNoRelationship. let foreignIDsWithRelationship = lookupValues.foreignIDs() - - foreignIDsWithNoRelationship.subtract(foreignIDsWithRelationship) - + + let foreignIDs for foreignID in foreignIDs { if !foreignIDsWithRelationship.contains(foreignID) { foreignIDsWithNoRelationship.insert(foreignID) @@ -210,6 +245,53 @@ struct LookupValue: Hashable { } } +private final class DatabaseLookupTableCache { + + private let relationshipName: String + private var foreignIDsWithNoRelationship = Set() + private var cachedLookups = [String: Set]() // foreignID: Set + + init(_ relationshipName: String) { + + self.relationshipName = relationshipName + } + + func updateCacheWithIDsWithNoRelationship(_ foreignIDs: Set) { + + foreignIDsWithNoRelationship.formUnion(foreignIDs) + for foreignID in foreignIDs { + cachedLookups[foreignID] = nil + } + } + + func updateCacheWithObjects(_ object: [DatabaseObject]) { + + var foreignIDsWithRelationship = Set() + + for object in objects { + + if let relatedObjects = object.relatedObjectsWithName, !relatedObjects.isEmpty { + foreignIDsWithRelationship.insert(object.databaseID) + } + else { + updateCacheWithIDsWithNoRelationship(objects.databaseIDs()) + } + } + + foreignIDsWithNoRelationship.subtract(foreignIDsWithRelationships) + } + + func foreignIDHasNoRelationship(_ foreignID: String) -> Bool { + + return foreignIDsWithNoRelationship.contains(foreignID) + } + + func relationshipIDsForForeignID(_ foreignID: String) -> Set? { + + return cachedLookups[foreignID] + } +} + private extension Set where Element == LookupValue { func primaryIDs() -> Set {