mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-26 21:39:37 -04:00
SearchOperation Optimization
This commit is contained in:
parent
6a2ff68936
commit
12587cf642
@ -121,17 +121,17 @@ class SearchResultTBVC: UIViewController, UITableViewDataSource, UITableViewDele
|
||||
tableView.reloadData()
|
||||
return
|
||||
}
|
||||
// let operation = SearchOperation(searchTerm: searchText) { [unowned self] (results) in
|
||||
// guard let results = results else {return}
|
||||
// self.searchResults = results
|
||||
// self.tableView.reloadData()
|
||||
// if results.count > 0 {
|
||||
// self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: true)
|
||||
// }
|
||||
// }
|
||||
|
||||
let operation = SearchOperation(searchTerm: searchText)
|
||||
operation.addObserver(DidFinishObserver {(operation, errors) in
|
||||
print("search op did finish, result injection")
|
||||
guard let operation = operation as? SearchOperation else {return}
|
||||
NSOperationQueue.mainQueue().addOperationWithBlock({
|
||||
self.searchResults = operation.results
|
||||
self.tableView.reloadData()
|
||||
|
||||
guard operation.results.count > 0 else {return}
|
||||
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: true)
|
||||
})
|
||||
})
|
||||
GlobalQueue.shared.add(search: operation)
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.8.279</string>
|
||||
<string>1.8.323</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.8.282</string>
|
||||
<string>1.8.326</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
|
@ -355,7 +355,6 @@
|
||||
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalQueue.swift; sourceTree = "<group>"; };
|
||||
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshLibraryOperation.swift; sourceTree = "<group>"; };
|
||||
97D6811E1D6F70AC00E5FA99 /* ScanLocalBookOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanLocalBookOperation.swift; sourceTree = "<group>"; };
|
||||
97D6811F1D6F70AC00E5FA99 /* SearchOperation-old.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SearchOperation-old.swift"; sourceTree = "<group>"; };
|
||||
97D681201D6F70AC00E5FA99 /* UIOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIOperations.swift; sourceTree = "<group>"; };
|
||||
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateWidgetDataSourceOperation.swift; sourceTree = "<group>"; };
|
||||
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionDownloadTaskOperation.swift; sourceTree = "<group>"; };
|
||||
@ -990,7 +989,6 @@
|
||||
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */,
|
||||
97D6811E1D6F70AC00E5FA99 /* ScanLocalBookOperation.swift */,
|
||||
97D4D64E1D874E6E00C1B065 /* SearchOperation.swift */,
|
||||
97D6811F1D6F70AC00E5FA99 /* SearchOperation-old.swift */,
|
||||
97D681201D6F70AC00E5FA99 /* UIOperations.swift */,
|
||||
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */,
|
||||
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */,
|
||||
|
@ -1,110 +0,0 @@
|
||||
//
|
||||
// SearchOperation.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 4/9/16.
|
||||
// Copyright © 2016 Chris. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Operations
|
||||
|
||||
class SearchOperation: GroupOperation {
|
||||
let completionHandler: ([SearchResult]?) -> Void
|
||||
private(set) var results = [SearchResult]()
|
||||
//private let startTime = NSDate()
|
||||
|
||||
init(searchTerm: String, completionHandler: ([SearchResult]?) -> Void) {
|
||||
self.completionHandler = completionHandler
|
||||
super.init(operations: [NSOperation]())
|
||||
|
||||
let sortOperation = SortSearchResultsOperation { (results) in
|
||||
self.results = results
|
||||
}
|
||||
|
||||
for (id, zimReader) in ZimMultiReader.shared.readers {
|
||||
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
|
||||
guard let book = Book.fetch(id, context: managedObjectContext) else {continue}
|
||||
guard book.includeInSearch else {continue}
|
||||
let operation = SingleBookSearchOperation(zimReader: zimReader,
|
||||
lowerCaseSearchTerm: searchTerm.lowercaseString,
|
||||
completionHandler: { [unowned sortOperation] (results) in
|
||||
sortOperation.results += results
|
||||
})
|
||||
|
||||
addOperation(operation)
|
||||
sortOperation.addDependency(operation)
|
||||
}
|
||||
|
||||
addOperation(sortOperation)
|
||||
|
||||
addCondition(MutuallyExclusive<ZimMultiReader>())
|
||||
}
|
||||
|
||||
override func operationDidFinish(errors: [ErrorType]) {
|
||||
NSOperationQueue.mainQueue().addOperationWithBlock {
|
||||
self.completionHandler(self.cancelled ? nil : self.results)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SingleBookSearchOperation: Operation {
|
||||
let zimReader: ZimReader
|
||||
let lowerCaseSearchTerm: String
|
||||
let completionHandler: ([SearchResult]) -> Void
|
||||
|
||||
init(zimReader: ZimReader, lowerCaseSearchTerm: String, completionHandler: ([SearchResult]) -> Void) {
|
||||
self.zimReader = zimReader
|
||||
self.lowerCaseSearchTerm = lowerCaseSearchTerm
|
||||
self.completionHandler = completionHandler
|
||||
super.init()
|
||||
}
|
||||
|
||||
override private func execute() {
|
||||
var results = [String: SearchResult]()
|
||||
let indexedDics = zimReader.searchUsingIndex(lowerCaseSearchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
|
||||
let titleDics = zimReader.searchSuggestionsSmart(lowerCaseSearchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
|
||||
let mixedDics = titleDics + indexedDics // It is important we process the title search result first, so that we always keep the indexed search result
|
||||
for dic in mixedDics {
|
||||
guard let result = SearchResult (rawResult: dic, lowerCaseSearchTerm: lowerCaseSearchTerm) else {continue}
|
||||
results[result.path] = result
|
||||
}
|
||||
completionHandler(Array(results.values))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private class SortSearchResultsOperation: Operation {
|
||||
let completionHandler: ([SearchResult]) -> Void
|
||||
var results = [SearchResult]()
|
||||
|
||||
init(completionHandler: ([SearchResult]) -> Void) {
|
||||
self.completionHandler = completionHandler
|
||||
super.init()
|
||||
}
|
||||
|
||||
override private func execute() {
|
||||
sort()
|
||||
completionHandler(results)
|
||||
finish()
|
||||
}
|
||||
|
||||
private func sort() {
|
||||
results.sortInPlace { (result0, result1) -> Bool in
|
||||
if result0.score != result1.score {
|
||||
return result0.score < result1.score
|
||||
} else {
|
||||
if result0.snippet != nil {return true}
|
||||
if result1.snippet != nil {return false}
|
||||
return titleCaseInsensitiveCompare(result0, result1: result1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Utilities
|
||||
|
||||
private func titleCaseInsensitiveCompare(result0: SearchResult, result1: SearchResult) -> Bool {
|
||||
return result0.title.caseInsensitiveCompare(result1.title) == NSComparisonResult.OrderedAscending
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,72 @@
|
||||
import Operations
|
||||
|
||||
class SearchOperation: GroupOperation {
|
||||
private(set) var results = [SearchResult]()
|
||||
|
||||
init(searchTerm: String) {
|
||||
super.init(operations: [])
|
||||
let searches: [BookSearch] = ZimMultiReader.shared.readers.keys.map({ BookSearch(zimID: $0, searchTerm: searchTerm) })
|
||||
let sort = Sort()
|
||||
searches.forEach { (search) in
|
||||
sort.injectResultFromDependency(search, block: { (operation, dependency, errors) in
|
||||
operation.requirement += dependency.results
|
||||
})
|
||||
}
|
||||
super.init(operations: searches + [sort])
|
||||
|
||||
sort.addObserver(DidFinishObserver { [unowned self] (operation, errors) in
|
||||
guard let operation = operation as? Sort else {return}
|
||||
self.results = operation.requirement
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class BookSearch: Operation {
|
||||
let zimID: String
|
||||
let searchTerm: String
|
||||
private var results = [SearchResult]()
|
||||
|
||||
init(zimID: String, searchTerm: String) {
|
||||
self.zimID = zimID
|
||||
self.searchTerm = searchTerm
|
||||
super.init()
|
||||
}
|
||||
|
||||
override private func execute() {
|
||||
guard let reader = ZimMultiReader.shared.readers[zimID] else {return}
|
||||
|
||||
let indexedDics = reader.searchUsingIndex(searchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
|
||||
let titleDics = reader.searchSuggestionsSmart(searchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
|
||||
let mixedDics = titleDics + indexedDics // It is important we process the title search result first, so that we always keep the indexed search result
|
||||
|
||||
for dic in mixedDics {
|
||||
guard let result = SearchResult (rawResult: dic, lowerCaseSearchTerm: searchTerm) else {continue}
|
||||
self.results.append(result)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private class Sort: Operation, AutomaticInjectionOperationType {
|
||||
var requirement = [SearchResult]()
|
||||
|
||||
private override func execute() {
|
||||
sort()
|
||||
finish()
|
||||
}
|
||||
|
||||
private func sort() {
|
||||
requirement.sortInPlace { (result0, result1) -> Bool in
|
||||
if result0.score != result1.score {
|
||||
return result0.score < result1.score
|
||||
} else {
|
||||
if result0.snippet != nil {return true}
|
||||
if result1.snippet != nil {return false}
|
||||
return titleCaseInsensitiveCompare(result0, result1: result1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func titleCaseInsensitiveCompare(result0: SearchResult, result1: SearchResult) -> Bool {
|
||||
return result0.title.caseInsensitiveCompare(result1.title) == NSComparisonResult.OrderedAscending
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user