@@ -29,14 +29,12 @@ extension Module {
29
29
}
30
30
31
31
@warn_unused_result public static func FTS4( columns: [ Expressible ] = [ ] , tokenize tokenizer: Tokenizer ? = nil ) -> Module {
32
- var columns = columns
33
-
34
- if let tokenizer = tokenizer {
35
- columns. append ( " = " . join ( [ Expression < Void > ( literal: " tokenize " ) , Expression < Void > ( literal: tokenizer. description) ] ) )
36
- }
37
- return Module ( name: " fts4 " , arguments: columns)
32
+ return FTS4 ( FTS4Config ( ) . columns ( columns) . tokenizer ( tokenizer) )
38
33
}
39
34
35
+ @warn_unused_result public static func FTS4( config: FTS4Config ) -> Module {
36
+ return Module ( name: " fts4 " , arguments: config. arguments ( ) )
37
+ }
40
38
}
41
39
42
40
extension VirtualTable {
@@ -156,3 +154,189 @@ extension Connection {
156
154
}
157
155
158
156
}
157
+
158
+ /// Configuration options shared between the [FTS4](https://www.sqlite.org/fts3.html) and
159
+ /// [FTS5](https://www.sqlite.org/fts5.html) extensions.
160
+ public class FTSConfig {
161
+ public enum ColumnOption {
162
+ /// [The notindexed= option](https://www.sqlite.org/fts3.html#section_6_5)
163
+ case unindexed
164
+ }
165
+
166
+ typealias ColumnDefinition = ( Expressible , options: [ ColumnOption ] )
167
+ var columnDefinitions = [ ColumnDefinition] ( )
168
+ var tokenizer : Tokenizer ?
169
+ var prefixes = [ Int] ( )
170
+ var externalContentSchema : SchemaType ?
171
+ var isContentless : Bool = false
172
+
173
+ /// Adds a column definition
174
+ public func column( column: Expressible , _ options: [ ColumnOption ] = [ ] ) -> Self {
175
+ self . columnDefinitions. append ( ( column, options) )
176
+ return self
177
+ }
178
+
179
+ public func columns( columns: [ Expressible ] ) -> Self {
180
+ for column in columns {
181
+ self . column ( column)
182
+ }
183
+ return self
184
+ }
185
+
186
+ /// [Tokenizers](https://www.sqlite.org/fts3.html#tokenizer)
187
+ public func tokenizer( tokenizer: Tokenizer ? ) -> Self {
188
+ self . tokenizer = tokenizer
189
+ return self
190
+ }
191
+
192
+ /// [The prefix= option](https://www.sqlite.org/fts3.html#section_6_6)
193
+ public func prefix( prefix: [ Int ] ) -> Self {
194
+ self . prefixes += prefix
195
+ return self
196
+ }
197
+
198
+ /// [The content= option](https://www.sqlite.org/fts3.html#section_6_2)
199
+ public func externalContent( schema: SchemaType ) -> Self {
200
+ self . externalContentSchema = schema
201
+ return self
202
+ }
203
+
204
+ /// [Contentless FTS4 Tables](https://www.sqlite.org/fts3.html#section_6_2_1)
205
+ public func contentless( ) -> Self {
206
+ self . isContentless = true
207
+ return self
208
+ }
209
+
210
+ func formatColumnDefinitions( ) -> [ Expressible ] {
211
+ return columnDefinitions. map { $0. 0 }
212
+ }
213
+
214
+ func arguments( ) -> [ Expressible ] {
215
+ return options ( ) . arguments
216
+ }
217
+
218
+ func options( ) -> Options {
219
+ var options = Options ( )
220
+ options. append ( formatColumnDefinitions ( ) )
221
+ if let tokenizer = tokenizer {
222
+ options. append ( " tokenize " , value: Expression < Void > ( literal: tokenizer. description) )
223
+ }
224
+ options. appendCommaSeparated ( " prefix " , values: prefixes. sort ( ) . map { String ( $0) } )
225
+ if isContentless {
226
+ options. append ( " content " , value: " " )
227
+ } else if let externalContentSchema = externalContentSchema {
228
+ options. append ( " content " , value: externalContentSchema. tableName ( ) )
229
+ }
230
+ return options
231
+ }
232
+
233
+ struct Options {
234
+ var arguments = [ Expressible] ( )
235
+
236
+ mutating func append( columns: [ Expressible ] ) -> Options {
237
+ arguments. appendContentsOf ( columns)
238
+ return self
239
+ }
240
+
241
+ mutating func appendCommaSeparated( key: String , values: [ String ] ) -> Options {
242
+ if values. isEmpty {
243
+ return self
244
+ } else {
245
+ return append ( key, value: values. joinWithSeparator ( " , " ) )
246
+ }
247
+ }
248
+
249
+ mutating func append( key: String , value: CustomStringConvertible ? ) -> Options {
250
+ return append ( key, value: value? . description)
251
+ }
252
+
253
+ mutating func append( key: String , value: String ? ) -> Options {
254
+ return append ( key, value: value. map { Expression < String > ( $0) } )
255
+ }
256
+
257
+ mutating func append( key: String , value: Expressible ? ) -> Options {
258
+ if let value = value {
259
+ arguments. append ( " = " . join ( [ Expression < Void > ( literal: key) , value] ) )
260
+ }
261
+ return self
262
+ }
263
+ }
264
+ }
265
+
266
+ /// Configuration for the [FTS4](https://www.sqlite.org/fts3.html) extension.
267
+ public class FTS4Config : FTSConfig {
268
+ /// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4)
269
+ public enum MatchInfo : CustomStringConvertible {
270
+ case FTS3
271
+ public var description : String {
272
+ return " fts3 "
273
+ }
274
+ }
275
+
276
+ /// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options)
277
+ public enum Order : CustomStringConvertible {
278
+ /// Data structures are optimized for returning results in ascending order by docid (default)
279
+ case Asc
280
+ /// FTS4 stores its data in such a way as to optimize returning results in descending order by docid.
281
+ case Desc
282
+
283
+ public var description : String {
284
+ switch self {
285
+ case Asc: return " asc "
286
+ case Desc: return " desc "
287
+ }
288
+ }
289
+ }
290
+
291
+ var compressFunction : String ?
292
+ var uncompressFunction : String ?
293
+ var languageId : String ?
294
+ var matchInfo : MatchInfo ?
295
+ var order : Order ?
296
+
297
+ override public init ( ) {
298
+ }
299
+
300
+ /// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1)
301
+ public func compress( functionName: String ) -> Self {
302
+ self . compressFunction = functionName
303
+ return self
304
+ }
305
+
306
+ /// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1)
307
+ public func uncompress( functionName: String ) -> Self {
308
+ self . uncompressFunction = functionName
309
+ return self
310
+ }
311
+
312
+ /// [The languageid= option](https://www.sqlite.org/fts3.html#section_6_3)
313
+ public func languageId( columnName: String ) -> Self {
314
+ self . languageId = columnName
315
+ return self
316
+ }
317
+
318
+ /// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4)
319
+ public func matchInfo( matchInfo: MatchInfo ) -> Self {
320
+ self . matchInfo = matchInfo
321
+ return self
322
+ }
323
+
324
+ /// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options)
325
+ public func order( order: Order ) -> Self {
326
+ self . order = order
327
+ return self
328
+ }
329
+
330
+ override func options( ) -> Options {
331
+ var options = super. options ( )
332
+ for (column, _) in ( columnDefinitions. filter { $0. options. contains ( . unindexed) } ) {
333
+ options. append ( " notindexed " , value: column)
334
+ }
335
+ options. append ( " languageid " , value: languageId)
336
+ options. append ( " compress " , value: compressFunction)
337
+ options. append ( " uncompress " , value: uncompressFunction)
338
+ options. append ( " matchinfo " , value: matchInfo)
339
+ options. append ( " order " , value: order)
340
+ return options
341
+ }
342
+ }
0 commit comments