Skip to content

Commit

Permalink
Added the async manager
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-ludwig committed Nov 6, 2023
1 parent 6982d39 commit 189394d
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 18 deletions.
8 changes: 8 additions & 0 deletions SearchKitDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
617658A32AF161AD000C5197 /* SearchKitDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617658A22AF161AD000C5197 /* SearchKitDemoUITests.swift */; };
617658A52AF161AD000C5197 /* SearchKitDemoUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617658A42AF161AD000C5197 /* SearchKitDemoUITestsLaunchTests.swift */; };
617658B42AF16B04000C5197 /* SearchManger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617658B32AF16B04000C5197 /* SearchManger.swift */; };
617D04BF2AF9769300CD2CDA /* AsyncIndexSearchingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617D04BE2AF9769300CD2CDA /* AsyncIndexSearchingTests.swift */; };
619DD8012AF962A000A9364E /* SearchIndexer+AsyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619DD8002AF962A000A9364E /* SearchIndexer+AsyncManager.swift */; };
61E2E7802AF3EDDC00E3AA4A /* SearchIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E2E77F2AF3EDDC00E3AA4A /* SearchIndexer.swift */; };
61E2E7822AF3F7F800E3AA4A /* SearchIndexer+Add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E2E7812AF3F7F800E3AA4A /* SearchIndexer+Add.swift */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -74,6 +76,8 @@
617658A22AF161AD000C5197 /* SearchKitDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchKitDemoUITests.swift; sourceTree = "<group>"; };
617658A42AF161AD000C5197 /* SearchKitDemoUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchKitDemoUITestsLaunchTests.swift; sourceTree = "<group>"; };
617658B32AF16B04000C5197 /* SearchManger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchManger.swift; sourceTree = "<group>"; };
617D04BE2AF9769300CD2CDA /* AsyncIndexSearchingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncIndexSearchingTests.swift; sourceTree = "<group>"; };
619DD8002AF962A000A9364E /* SearchIndexer+AsyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchIndexer+AsyncManager.swift"; sourceTree = "<group>"; };
61E2E77F2AF3EDDC00E3AA4A /* SearchIndexer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchIndexer.swift; sourceTree = "<group>"; };
61E2E7812AF3F7F800E3AA4A /* SearchIndexer+Add.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchIndexer+Add.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -154,6 +158,7 @@
617658982AF161AD000C5197 /* SearchKitDemoTests.swift */,
6129F87F2AF555E800A10F67 /* InMemoryIndexing.swift */,
6129F8862AF6BD5900A10F67 /* FileIndexingTests.swift */,
617D04BE2AF9769300CD2CDA /* AsyncIndexSearchingTests.swift */,
6129F8882AF6BDD700A10F67 /* FileHelper.swift */,
);
path = SearchKitDemoTests;
Expand All @@ -180,6 +185,7 @@
isa = PBXGroup;
children = (
61E2E77F2AF3EDDC00E3AA4A /* SearchIndexer.swift */,
619DD8002AF962A000A9364E /* SearchIndexer+AsyncManager.swift */,
6129F8842AF6BAAF00A10F67 /* SearchIndexer+File.swift */,
6129F8822AF556B000A10F67 /* SearchIndexer+Memory.swift */,
6129F8772AF50CE600A10F67 /* SearchIndexer+Search.swift */,
Expand Down Expand Up @@ -332,6 +338,7 @@
6129F87C2AF5522E00A10F67 /* SearchIndexer+Indexer.swift in Sources */,
6129F87A2AF5211B00A10F67 /* SearchIndexer+ProgressivSearch.swift in Sources */,
617658842AF161AB000C5197 /* SearchKitDemoApp.swift in Sources */,
619DD8012AF962A000A9364E /* SearchIndexer+AsyncManager.swift in Sources */,
617658882AF161AB000C5197 /* ContentView.swift in Sources */,
6129F8782AF50CE600A10F67 /* SearchIndexer+Search.swift in Sources */,
6129F8762AF502DE00A10F67 /* SearchIndexer+Terms.swift in Sources */,
Expand All @@ -347,6 +354,7 @@
6129F8802AF555E800A10F67 /* InMemoryIndexing.swift in Sources */,
617658992AF161AD000C5197 /* SearchKitDemoTests.swift in Sources */,
6129F8892AF6BDD700A10F67 /* FileHelper.swift in Sources */,
617D04BF2AF9769300CD2CDA /* AsyncIndexSearchingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
172 changes: 172 additions & 0 deletions SearchKitDemo/Indexer/SearchIndexer+AsyncManager.swift
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
}
}

12 changes: 3 additions & 9 deletions SearchKitDemo/Indexer/SearchIndexer+ProgressivSearch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

extension SearchIndexer {
/// Object representaitng the search results
public class SearchResult: NSObject {
public class SearchResult {
/// The identifying url for the document
public let url: URL

Expand All @@ -19,11 +19,6 @@ extension SearchIndexer {
init(url: URL, score: Float) {
self.url = url
self.score = score
super.init()
}

public override var debugDescription: String {
return "Score: \(self.score), URL: \(self.url)"
}
}

Expand All @@ -35,8 +30,8 @@ extension SearchIndexer {
return ProgressivSearch(options: options, index: self, query: query)
}

public class ProgressivSearch: NSObject {
public class Results: NSObject {
public class ProgressivSearch {
public class Results {
/// Create a search result
///
/// - Parameters:
Expand All @@ -45,7 +40,6 @@ extension SearchIndexer {
public init(moreResultsAvailable: Bool, results: [SearchResult]) {
self.moreResultsAvailable = moreResultsAvailable
self.results = results
super.init()
}

/// A boolean indicating whether more search results are available
Expand Down
7 changes: 1 addition & 6 deletions SearchKitDemo/Indexer/SearchIndexer+Terms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

extension SearchIndexer {
/// A class to contain a term and the count of times it appears
public class TermCount: NSObject {
public class TermCount {
/// A term within the document
public let term: String

Expand All @@ -19,11 +19,6 @@ extension SearchIndexer {
init(term: String, count: Int) {
self.term = term
self.count = count
super.init()
}

public override var debugDescription: String {
return "Term: '\(self.term)', Count: \(self.count)'"
}
}

Expand Down
5 changes: 2 additions & 3 deletions SearchKitDemo/Indexer/SearchIndexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class Synchronised {
}

/// Indexer using SKIndex
public class SearchIndexer: NSObject {
public class SearchIndexer {
let queue = DispatchQueue(label: "com.activcoding.SearchKitDemo")

public enum IndexType: UInt32 {
Expand All @@ -42,7 +42,7 @@ public class SearchIndexer: NSObject {
case invertedVector = 3
}

public class CreateProperties: NSObject {
public class CreateProperties {
/// The type of the index to be created
private(set) var indexType: SKIndexType = kSKIndexInverted
/// Whether the index should use proximity indexing
Expand Down Expand Up @@ -112,7 +112,6 @@ public class SearchIndexer: NSObject {

init(index: SKIndex) {
self.index = index
super.init()
}

deinit {
Expand Down
37 changes: 37 additions & 0 deletions SearchKitDemoTests/AsyncIndexSearchingTests.swift
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
}
}

0 comments on commit 189394d

Please sign in to comment.