mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-20 06:30:31 +01:00
LookupTable.
This commit is contained in:
parent
67cb2c57d7
commit
2fe6b0e2a6
@ -44,13 +44,13 @@ public extension DatabaseTable {
|
|||||||
|
|
||||||
// MARK: Updating
|
// MARK: Updating
|
||||||
|
|
||||||
public func updateRowsWithValue(_ value: Any, valueKey: String, whereKey: String, matches: [Any]) {
|
// public func updateRowsWithValue(_ value: Any, valueKey: String, whereKey: String, matches: [Any]) {
|
||||||
|
//
|
||||||
queue.update { (database: FMDatabase!) in
|
// queue.update { (database: FMDatabase!) in
|
||||||
|
//
|
||||||
let _ = database.rs_updateRows(withValue: value, valueKey: valueKey, whereKey: whereKey, inValues: matches, tableName: self.name)
|
// let _ = database.rs_updateRows(withValue: value, valueKey: valueKey, whereKey: whereKey, inValues: matches, tableName: self.name)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// MARK: Saving
|
// MARK: Saving
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
84419B061B5ABFF700C26BB2 /* FMResultSet+RSExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 84419B041B5ABFF700C26BB2 /* FMResultSet+RSExtras.m */; };
|
84419B061B5ABFF700C26BB2 /* FMResultSet+RSExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 84419B041B5ABFF700C26BB2 /* FMResultSet+RSExtras.m */; };
|
||||||
844D97411F2D32F300CEDDEA /* ObjectCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844D97401F2D32F300CEDDEA /* ObjectCache.swift */; };
|
844D97411F2D32F300CEDDEA /* ObjectCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844D97401F2D32F300CEDDEA /* ObjectCache.swift */; };
|
||||||
849BF8C61C94FB8E0071D1DA /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 849BF8C51C94FB8E0071D1DA /* libsqlite3.tbd */; };
|
849BF8C61C94FB8E0071D1DA /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 849BF8C51C94FB8E0071D1DA /* libsqlite3.tbd */; };
|
||||||
84ABC1D11F364B07000DCC55 /* LookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* LookupTable.swift */; };
|
84ABC1D11F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */; };
|
||||||
84ABC1D21F364B07000DCC55 /* LookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* LookupTable.swift */; };
|
84ABC1D21F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */; };
|
||||||
84C6DD011F395C13009AFB47 /* DatabaseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C6DD001F395C13009AFB47 /* DatabaseObject.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, ); }; };
|
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 */; };
|
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 = "<group>"; };
|
84419B041B5ABFF700C26BB2 /* FMResultSet+RSExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "FMResultSet+RSExtras.m"; path = "RSDatabase/FMResultSet+RSExtras.m"; sourceTree = "<group>"; };
|
||||||
844D97401F2D32F300CEDDEA /* ObjectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ObjectCache.swift; path = RSDatabase/ObjectCache.swift; sourceTree = "<group>"; };
|
844D97401F2D32F300CEDDEA /* ObjectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ObjectCache.swift; path = RSDatabase/ObjectCache.swift; sourceTree = "<group>"; };
|
||||||
849BF8C51C94FB8E0071D1DA /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
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 = "<group>"; };
|
84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseLookupTable.swift; path = RSDatabase/DatabaseLookupTable.swift; sourceTree = "<group>"; };
|
||||||
84C6DD001F395C13009AFB47 /* DatabaseObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseObject.swift; path = RSDatabase/DatabaseObject.swift; sourceTree = "<group>"; };
|
84C6DD001F395C13009AFB47 /* DatabaseObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseObject.swift; path = RSDatabase/DatabaseObject.swift; sourceTree = "<group>"; };
|
||||||
84DDF18B1C94FC45005E6CF5 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = "<group>"; };
|
84DDF18B1C94FC45005E6CF5 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = "<group>"; };
|
||||||
84DDF18C1C94FC45005E6CF5 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = "<group>"; };
|
84DDF18C1C94FC45005E6CF5 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = "<group>"; };
|
||||||
@ -162,7 +162,7 @@
|
|||||||
84419AD91B5ABD7400C26BB2 /* NSString+RSDatabase.m */,
|
84419AD91B5ABD7400C26BB2 /* NSString+RSDatabase.m */,
|
||||||
840405DA1F1C158C00DF0296 /* DatabaseTable.swift */,
|
840405DA1F1C158C00DF0296 /* DatabaseTable.swift */,
|
||||||
84C6DD001F395C13009AFB47 /* DatabaseObject.swift */,
|
84C6DD001F395C13009AFB47 /* DatabaseObject.swift */,
|
||||||
84ABC1D01F364B07000DCC55 /* LookupTable.swift */,
|
84ABC1D01F364B07000DCC55 /* DatabaseLookupTable.swift */,
|
||||||
844D97401F2D32F300CEDDEA /* ObjectCache.swift */,
|
844D97401F2D32F300CEDDEA /* ObjectCache.swift */,
|
||||||
84DDF18A1C94FC45005E6CF5 /* FMDB */,
|
84DDF18A1C94FC45005E6CF5 /* FMDB */,
|
||||||
84F22C5A1B52E0D9000060CE /* Info.plist */,
|
84F22C5A1B52E0D9000060CE /* Info.plist */,
|
||||||
@ -357,7 +357,7 @@
|
|||||||
files = (
|
files = (
|
||||||
8400AC001E0CFC0700AA7C57 /* RSDatabaseQueue.m in Sources */,
|
8400AC001E0CFC0700AA7C57 /* RSDatabaseQueue.m in Sources */,
|
||||||
8400AC061E0CFC0700AA7C57 /* NSString+RSDatabase.m in Sources */,
|
8400AC061E0CFC0700AA7C57 /* NSString+RSDatabase.m in Sources */,
|
||||||
84ABC1D21F364B07000DCC55 /* LookupTable.swift in Sources */,
|
84ABC1D21F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */,
|
||||||
8400AC0C1E0CFC3100AA7C57 /* FMResultSet.m in Sources */,
|
8400AC0C1E0CFC3100AA7C57 /* FMResultSet.m in Sources */,
|
||||||
840405DC1F1C15EA00DF0296 /* DatabaseTable.swift in Sources */,
|
840405DC1F1C15EA00DF0296 /* DatabaseTable.swift in Sources */,
|
||||||
8400AC021E0CFC0700AA7C57 /* FMDatabase+RSExtras.m in Sources */,
|
8400AC021E0CFC0700AA7C57 /* FMDatabase+RSExtras.m in Sources */,
|
||||||
@ -376,7 +376,7 @@
|
|||||||
84C6DD011F395C13009AFB47 /* DatabaseObject.swift in Sources */,
|
84C6DD011F395C13009AFB47 /* DatabaseObject.swift in Sources */,
|
||||||
84419ADB1B5ABD7400C26BB2 /* NSString+RSDatabase.m in Sources */,
|
84419ADB1B5ABD7400C26BB2 /* NSString+RSDatabase.m in Sources */,
|
||||||
840405DB1F1C158C00DF0296 /* DatabaseTable.swift in Sources */,
|
840405DB1F1C158C00DF0296 /* DatabaseTable.swift in Sources */,
|
||||||
84ABC1D11F364B07000DCC55 /* LookupTable.swift in Sources */,
|
84ABC1D11F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */,
|
||||||
84DDF1971C94FC45005E6CF5 /* FMDatabase.m in Sources */,
|
84DDF1971C94FC45005E6CF5 /* FMDatabase.m in Sources */,
|
||||||
84DDF1A01C94FC45005E6CF5 /* FMResultSet.m in Sources */,
|
84DDF1A01C94FC45005E6CF5 /* FMResultSet.m in Sources */,
|
||||||
84419B061B5ABFF700C26BB2 /* FMResultSet+RSExtras.m in Sources */,
|
84419B061B5ABFF700C26BB2 /* FMResultSet+RSExtras.m in Sources */,
|
||||||
|
@ -11,19 +11,15 @@ import Foundation
|
|||||||
// Implement a lookup table for a many-to-many relationship.
|
// 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));
|
// 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.
|
// 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<LookupValue>] // key is foreignID
|
|
||||||
|
|
||||||
public final class LookupTable {
|
public final class DatabaseLookupTable {
|
||||||
|
|
||||||
public let name: String
|
public let name: String
|
||||||
let primaryKey: String
|
let primaryKey: String
|
||||||
let foreignKey: String
|
let foreignKey: String
|
||||||
private var foreignIDsWithNoRelationship = Set<String>()
|
private var foreignIDsWithNoRelationship = Set<String>()
|
||||||
private var lookupsByForeignID = LookupTableDictionary()
|
private var cache = LookupTable(Set<LookupValue>())
|
||||||
|
|
||||||
public init(name: String, primaryKey: String, foreignKey: String) {
|
public init(name: String, primaryKey: String, foreignKey: String) {
|
||||||
|
|
||||||
@ -32,7 +28,7 @@ public final class LookupTable {
|
|||||||
self.foreignKey = foreignKey
|
self.foreignKey = foreignKey
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchLookupTableDictionary(_ foreignIDs: Set<String>, _ database: FMDatabase) -> LookupTableDictionary? {
|
public func fetchLookupTable(_ foreignIDs: Set<String>, _ database: FMDatabase) -> LookupTable? {
|
||||||
|
|
||||||
let foreignIDsToLookup = foreignIDs.subtracting(foreignIDsWithNoRelationship)
|
let foreignIDsToLookup = foreignIDs.subtracting(foreignIDsWithNoRelationship)
|
||||||
if foreignIDsToLookup.isEmpty {
|
if foreignIDsToLookup.isEmpty {
|
||||||
@ -43,12 +39,12 @@ public final class LookupTable {
|
|||||||
var foreignIDsToFetchFromDatabase = Set<String>()
|
var foreignIDsToFetchFromDatabase = Set<String>()
|
||||||
|
|
||||||
// Pull from cache.
|
// Pull from cache.
|
||||||
for oneForeignID in foreignIDsToLookup {
|
for foreignID in foreignIDsToLookup {
|
||||||
if let cachedLookups = lookupsByForeignID[oneForeignID] {
|
if let cachedLookups = cache[foreignID] {
|
||||||
lookupValues.formUnion(cachedLookups)
|
lookupValues.formUnion(cachedLookups)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
foreignIDsToFetchFromDatabase.insert(oneForeignID)
|
foreignIDsToFetchFromDatabase.insert(foreignID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +55,7 @@ public final class LookupTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cacheNotFoundForeignIDs(lookupValues, foreignIDsToFetchFromDatabase)
|
cacheNotFoundForeignIDs(lookupValues, foreignIDsToFetchFromDatabase)
|
||||||
cacheLookupValues(lookupValues)
|
cache.addLookupValues(lookupValues)
|
||||||
|
|
||||||
return lookupTableDictionary(with: lookupValues)
|
return lookupTableDictionary(with: lookupValues)
|
||||||
}
|
}
|
||||||
@ -109,7 +105,7 @@ public final class LookupTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for oneForeignID in foreignIDsToRemove {
|
for oneForeignID in foreignIDsToRemove {
|
||||||
lookupsByForeignID[oneForeignID] = nil
|
cache[oneForeignID] = nil
|
||||||
}
|
}
|
||||||
foreignIDsWithNoRelationship.formUnion(foreignIDsToRemove)
|
foreignIDsWithNoRelationship.formUnion(foreignIDsToRemove)
|
||||||
|
|
||||||
@ -117,18 +113,17 @@ public final class LookupTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension LookupTable {
|
private extension DatabaseLookupTable {
|
||||||
|
|
||||||
func addToLookupTableDictionary(_ lookupValues: Set<LookupValue>, _ table: inout LookupTableDictionary) {
|
func addToLookupTableDictionary(_ lookupValues: Set<LookupValue>, _ table: inout LookupTableDictionary) {
|
||||||
|
|
||||||
for lookupValue in lookupValues {
|
for lookupValue in lookupValues {
|
||||||
let foreignID = lookupValue.foreignID
|
let foreignID = lookupValue.foreignID
|
||||||
let primaryID = lookupValue.primaryID
|
|
||||||
if table[foreignID] == nil {
|
if table[foreignID] == nil {
|
||||||
table[foreignID] = Set([primaryID])
|
table[foreignID] = Set([lookupValue])
|
||||||
}
|
}
|
||||||
else {
|
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.
|
// 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<String>()
|
||||||
for foreignID in foreignIDs {
|
for foreignID in foreignIDs {
|
||||||
if !foundForeignIDs.contains(foreignID) {
|
if !foundForeignIDs.contains(foreignID) {
|
||||||
foreignIDsWithNoRelationship.insert(foreignID)
|
foreignIDsWithNoRelationship.insert(foreignID)
|
||||||
|
foreignIDsToRemove.insert(foreignID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache.removeLookupValuesWithForeignIDs(foreignIDsToRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupValuesWithResultSet(_ resultSet: FMResultSet) -> Set<LookupValue> {
|
func lookupValuesWithResultSet(_ resultSet: FMResultSet) -> Set<LookupValue> {
|
||||||
@ -175,11 +174,68 @@ private extension LookupTable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct LookupValue: Hashable {
|
final class LookupTable {
|
||||||
|
|
||||||
|
var lookupValues: Set<LookupValue>
|
||||||
|
var dictionary = [String: Set<LookupValues>]()
|
||||||
|
|
||||||
|
init(lookupValues: Set<LookupValue>) {
|
||||||
|
|
||||||
|
self.lookupValues = lookupValues
|
||||||
|
addLookupValuesToDictionary()
|
||||||
|
}
|
||||||
|
|
||||||
|
func primaryIDs() -> Set<String> {
|
||||||
|
|
||||||
|
return lookupValues.primaryIDs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLookupValues(_ values: Set<LookupValue>) {
|
||||||
|
|
||||||
|
lookupValues.formUnion(values)
|
||||||
|
addLookupValuesToDictionary(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeLookupValuesWithForeignIDs(_ foreignIDs: Set<String>) {
|
||||||
|
|
||||||
|
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<LookupValue>) {
|
||||||
|
|
||||||
public let primaryID: String
|
for lookupValue in values {
|
||||||
public let foreignID: String
|
let foreignID = lookupValue.foreignID
|
||||||
public let hashValue: Int
|
if self[foreignID] == nil {
|
||||||
|
self[foreignID] = Set([lookupValue])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self[foreignID]!.insert(lookupValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(_ foreignID: String) -> Set<LookupValue>? {
|
||||||
|
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) {
|
init(primaryID: String, foreignID: String) {
|
||||||
|
|
||||||
@ -195,9 +251,15 @@ public struct LookupValue: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private extension Set where Element == LookupValue {
|
private extension Set where Element == LookupValue {
|
||||||
|
|
||||||
func primaryIDs() -> Set<String> {
|
func primaryIDs() -> Set<String> {
|
||||||
|
|
||||||
|
return Set<String>(self.map { $0.primaryID })
|
||||||
|
}
|
||||||
|
|
||||||
|
func foreignIDs() -> Set<String> {
|
||||||
|
|
||||||
return Set(map { $0.primaryID })
|
return Set<String>(self.map { $0.foreignID })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user