11import Foundation
22import USearch
3+ import USearchObjective
4+
5+ @globalActor
6+ private actor TemporaryUSearchActor {
7+ static let shared = TemporaryUSearchActor ( )
8+ }
39
410/// A temporary USearch index for small and temporary documents.
5- public class TemporaryUSearch {
11+ public actor TemporaryUSearch : VectorStore {
612 public let identifier : String
713 let index : USearchIndex
814 var documents : [ UInt32 : Document ] = [ : ]
15+ var isViewOnly : Bool = false
916
1017 public init ( identifier: String ) {
1118 self . identifier = identifier
@@ -18,21 +25,21 @@ public class TemporaryUSearch {
1825 }
1926
2027 /// Load a USearch index if found.
21- public static func load( identifier: String ) -> TemporaryUSearch ? {
28+ public static func load( identifier: String ) async -> TemporaryUSearch ? {
2229 let it = TemporaryUSearch ( identifier: identifier)
2330 do {
24- try it. load ( )
31+ try await it. load ( )
2532 return it
2633 } catch {
2734 return nil
2835 }
2936 }
3037
3138 /// Create a readonly USearch instance if the index is found.
32- public static func view( identifier: String ) -> TemporaryUSearch ? {
39+ public static func view( identifier: String ) async -> TemporaryUSearch ? {
3340 let it = TemporaryUSearch ( identifier: identifier)
3441 do {
35- try it. view ( )
42+ try await it. view ( )
3643 return it
3744 } catch {
3845 return nil
@@ -50,24 +57,41 @@ public class TemporaryUSearch {
5057 }
5158 }
5259
53- public func search( embeddings: [ Float ] , count: Int , threshold: Float = 0.1 ) -> [ Document ] {
60+ public func searchWithDistance(
61+ embeddings: [ Float ] ,
62+ count: Int
63+ ) async throws -> [ ( document: Document , distance: Float ) ] {
5464 let embeddings = embeddings. map { Float32 ( $0) } [ ... ]
5565 let result = index. search ( vector: embeddings, count: count)
56- var matches = [ Document] ( )
66+ var matches = [ ( document : Document, distance : Float ) ] ( )
5767 for (index, distance) in zip ( result. 0 , result. 1 ) {
58- if let document = documents [ index] , distance < threshold {
59- matches. append ( document)
68+ if let document = documents [ index] {
69+ matches. append ( ( document, distance ) )
6070 }
6171 }
6272 return matches
6373 }
6474
6575 public func clear( ) {
76+ guard !isViewOnly else { return }
6677 index. clear ( )
6778 documents = [ : ]
6879 }
6980
70- public func set( _ documents: [ EmbeddedDocument ] ) {
81+ public func add( _ documents: [ EmbeddedDocument ] ) async throws {
82+ guard !isViewOnly else { return }
83+ let lastIndex = self . documents. keys. max ( ) ?? 0
84+ for (i, document) in documents. enumerated ( ) {
85+ let key = lastIndex + UInt32( i) + 1
86+ let embeddings = document. embeddings. map { Float32 ( $0) } [ ... ]
87+ index. add ( label: key, vector: embeddings)
88+ self . documents [ key] = document. document
89+ }
90+ save ( )
91+ }
92+
93+ public func set( _ documents: [ EmbeddedDocument ] ) async throws {
94+ guard !isViewOnly else { return }
7195 clear ( )
7296 for (i, document) in documents. enumerated ( ) {
7397 let embeddings = document. embeddings. map { Float32 ( $0) } [ ... ]
@@ -110,6 +134,7 @@ public class TemporaryUSearch {
110134 }
111135 let docs = try JSONDecoder ( ) . decode ( [ UInt32 : Document ] . self, from: documentsData)
112136 documents = docs
137+ isViewOnly = true
113138 }
114139}
115140
0 commit comments