-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6982d39
commit 189394d
Showing
6 changed files
with
223 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
// | ||
// SearchIndexer+AsyncManager.swift | ||
// SearchKitDemo | ||
// | ||
// Created by Tommy Ludwig on 06.11.23. | ||
// | ||
|
||
import Foundation | ||
|
||
extension SearchIndexer { | ||
/// Manager for SearchIndexer objct that supports async calls to the index | ||
public class AsyncManager { | ||
let index: SearchIndexer | ||
|
||
|
||
/// Queue for handling async modifications to the index | ||
// fileprivate let modifyQueue = DispatchQueue(label: "com.SearchkitDemo.modifyQueue", attributes: .concurrent) | ||
|
||
init(index: SearchIndexer) { | ||
self.index = index | ||
} | ||
|
||
|
||
class TextTask { | ||
let url: URL | ||
let text: String | ||
|
||
/// Create a text async task | ||
/// | ||
/// - Parameters: | ||
/// - url: the identifying document URL | ||
/// - text: The text to add to the index | ||
init(url: URL, text: String) { | ||
self.url = url | ||
self.text = text | ||
} | ||
} | ||
|
||
/// A task nor handling searches | ||
class SearchTask: AsyncManager { | ||
private var search: SearchIndexer.ProgressivSearch | ||
|
||
let query: String | ||
|
||
private let searchQueue = DispatchQueue(label: "com.SearchkitDemo.searchQueue", attributes: .concurrent) | ||
|
||
init(_ index: SearchIndexer, query: String) { | ||
self.query = query | ||
self.search = index.progressiveSearch(query: query) | ||
super.init(index: index) | ||
} | ||
|
||
deinit { | ||
self.search.cancel() | ||
} | ||
|
||
func next( | ||
_ maxResults: Int, | ||
timeout: TimeInterval = 1.0, | ||
complete: @escaping (SearchTask, SearchIndexer.ProgressivSearch.Results) -> Void | ||
) { | ||
searchQueue.async { | ||
let results = self.search.next(maxResults, timeout: timeout) | ||
let searchResults = SearchIndexer.ProgressivSearch.Results(moreResultsAvailable: results.moreResultsAvailable, results: results.results) | ||
|
||
DispatchQueue.main.async { | ||
complete(self, searchResults) | ||
} | ||
} | ||
} | ||
} | ||
|
||
class AddTask: AsyncManager { | ||
private let addQueue = DispatchQueue(label: "com.SearchkitDemo.addQueue", attributes: .concurrent) | ||
|
||
func addText( | ||
async textTask: [TextTask], | ||
flushWhenComplete: Bool = false, | ||
complete: @escaping ([TextTask]) -> Void | ||
) { | ||
let dispatchGroup = DispatchGroup() | ||
|
||
for task in textTask { | ||
dispatchGroup.enter() | ||
addQueue.async { [weak self] in | ||
guard let self = self else { return } | ||
let _ = self.index.add(task.url, text: task.text) | ||
dispatchGroup.leave() | ||
} | ||
} | ||
|
||
dispatchGroup.notify(queue: .main) { | ||
if flushWhenComplete { | ||
self.index.flush() | ||
} | ||
complete(textTask) | ||
} | ||
} | ||
|
||
func addFiles( | ||
urls: [URL], | ||
flushWhenComplete: Bool = false | ||
) { | ||
let dispatchGroup = DispatchGroup() | ||
|
||
for url in urls { | ||
dispatchGroup.enter() | ||
addQueue.async { [weak self] in | ||
guard let self = self else { return } | ||
let _ = self.index.add(fileURL: url, canReplace: false) | ||
dispatchGroup.leave() | ||
} | ||
} | ||
|
||
dispatchGroup.notify(queue: .main) { | ||
if flushWhenComplete { | ||
self.index.flush() | ||
} | ||
} | ||
} | ||
|
||
func addFolder( | ||
url: URL, | ||
flushWhenComplete: Bool = false | ||
) { | ||
let dispatchGroup = DispatchGroup() | ||
|
||
let fileManager = FileManager.default | ||
let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles], errorHandler: nil)! | ||
|
||
for case let fileURL as URL in enumerator { | ||
dispatchGroup.enter() | ||
|
||
if FileHelper.urlIsFolder(url) { | ||
addQueue.async { [weak self] in | ||
guard let self = self else { return } | ||
self.addFolder(url: url) | ||
dispatchGroup.leave() | ||
} | ||
} else { | ||
addQueue.async { [weak self] in | ||
guard let self = self else { return } | ||
let _ = self.index.add(fileURL: fileURL, canReplace: false) | ||
dispatchGroup.leave() | ||
} | ||
} | ||
} | ||
|
||
dispatchGroup.notify(queue: .main) { | ||
if flushWhenComplete { | ||
self.index.flush() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
class FileHelper { | ||
static func urlIsFolder(_ url: URL) -> Bool { | ||
var isDirectory: ObjCBool = false | ||
let exists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) | ||
return exists && isDirectory.boolValue | ||
} | ||
|
||
static func urlIsFile(_ url: URL) -> Bool { | ||
var isDirectory: ObjCBool = false | ||
let exists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) | ||
return exists && !isDirectory.boolValue | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// | ||
// AsyncIndexSearchingTests.swift | ||
// SearchKitDemoTests | ||
// | ||
// Created by Tommy Ludwig on 06.11.23. | ||
// | ||
|
||
@testable import SearchKitDemo | ||
import XCTest | ||
|
||
final class AsyncIndexSearchingTests: XCTestCase { | ||
fileprivate func bundleResourceURL(forResource name: String, withExtension ext: String) -> URL { | ||
let thisSourceFile = URL(fileURLWithPath: #file) | ||
var thisDirectory = thisSourceFile.deletingLastPathComponent() | ||
thisDirectory = thisDirectory.appendingPathComponent("Resources") | ||
thisDirectory = thisDirectory.appendingPathComponent(name + "." + ext) | ||
return thisDirectory | ||
} | ||
fileprivate func bundleResourceFolderURL() -> URL { | ||
let thisSourceFile = URL(fileURLWithPath: #file) | ||
var thisDirectory = thisSourceFile.deletingLastPathComponent() | ||
thisDirectory = thisDirectory.appendingPathComponent("Resources") | ||
return thisDirectory | ||
} | ||
|
||
func testAddDocuments() { | ||
guard let indexer = SearchIndexer.Memory.Create() else { | ||
XCTFail() | ||
return | ||
} | ||
|
||
let filePath = bundleResourceURL(forResource: "APACHE_LICENSE", withExtension: "pdf") | ||
let txtPath = bundleResourceURL(forResource: "the_school_short_story", withExtension: "txt") | ||
|
||
let asyncManager = SearchIndexer.AsyncManager.SearchTask | ||
} | ||
} |