///|
/// Name-based registry for field indexing and query-time analysis.
pub struct TokenizerManager {
names : Array[String]
tokenizers : Array[&Tokenizer]
versions : Array[String]
}
///|
pub fn TokenizerManager::new() -> TokenizerManager {
{ names: [], tokenizers: [], versions: [] }
}
///|
pub fn TokenizerManager::register(
self : TokenizerManager,
name : String,
tokenizer : &Tokenizer,
) -> Unit raise AnalysisError {
self.register_versioned(name, tokenizer, "")
}
///|
/// Registers an analyzer together with an application-defined immutable
/// resource fingerprint (dictionary hash, synonym revision, pipeline version).
pub fn TokenizerManager::register_versioned(
self : TokenizerManager,
name : String,
tokenizer : &Tokenizer,
version : String,
) -> Unit raise AnalysisError {
guard @schema.valid_tokenizer_name(name) else {
raise AnalysisError::InvalidTokenizerName(name)
}
match self.names.search_by(existing => existing == name) {
Some(index) => {
self.tokenizers[index] = tokenizer
self.versions[index] = version
}
None => {
self.names.push(name)
self.tokenizers.push(tokenizer)
self.versions.push(version)
}
}
}
///|
pub fn TokenizerManager::version(
self : TokenizerManager,
name : String,
) -> String? {
match self.names.search_by(existing => existing == name) {
Some(index) => Some(self.versions[index])
None => None
}
}
///|
/// Compatibility spelling for callers that explicitly register a Tokenizer.
pub fn TokenizerManager::register_tokenizer(
self : TokenizerManager,
name : String,
tokenizer : &Tokenizer,
) -> Unit raise AnalysisError {
self.register(name, tokenizer)
}
///|
pub fn TokenizerManager::contains(
self : TokenizerManager,
name : String,
) -> Bool {
self.names.search_by(existing => existing == name) is Some(_)
}
///|
pub fn TokenizerManager::snapshot(self : TokenizerManager) -> TokenizerManager {
let names : Array[String] = []
let tokenizers : Array[&Tokenizer] = []
let versions : Array[String] = []
for name in self.names {
names.push(name)
}
for tokenizer in self.tokenizers {
tokenizers.push(tokenizer)
}
for version in self.versions {
versions.push(version)
}
{ names, tokenizers, versions }
}
///|
/// Validates every indexed text field against the registered analyzer name and
/// optional resource fingerprint stored in the Schema.
pub fn TokenizerManager::validate_schema(
self : TokenizerManager,
schema : Schema,
) -> Unit raise AnalysisError {
for field_index in 0.. name
None => abort("schema field disappeared during analyzer validation")
}
let expected = match schema.analyzer_version(field_id) {
Some(version) => version
None => ""
}
let actual = match self.version(name) {
Some(version) => version
None => raise AnalysisError::UnknownTokenizer(name)
}
if expected != "" && actual != expected {
raise AnalysisError::AnalyzerVersionMismatch(name, expected, actual)
}
}
}
}
///|
/// Resolves a registered analysis pipeline as a lazy TokenStream.
pub fn TokenizerManager::token_stream(
self : TokenizerManager,
name : String,
text : String,
) -> &TokenStream raise AnalysisError {
match self.names.search_by(existing => existing == name) {
Some(index) => self.tokenizers[index].token_stream(text)
None => raise AnalysisError::UnknownTokenizer(name)
}
}
///|
/// Eager convenience API for query construction, diagnostics, and tests.
pub fn TokenizerManager::analyze(
self : TokenizerManager,
name : String,
text : String,
) -> Array[Token] raise AnalysisError {
collect_token_stream(self.token_stream(name, text))
}
///|
/// Creates the built-in pipelines. NgramTokenizer remains opt-in because its
/// parameters and use case must be chosen by the application.
pub fn TokenizerManager::with_defaults() -> TokenizerManager {
let manager = TokenizerManager::new()
manager.register_versioned("raw", RawTokenizer::new(), "builtin/raw@1") catch {
error => abort(Show::to_string(error))
}
manager.register_versioned(
"whitespace",
WhitespaceTokenizer::new(),
"builtin/whitespace@1",
) catch {
error => abort(Show::to_string(error))
}
let default_analyzer = TextAnalyzer::new(SimpleTokenizer::new())
default_analyzer.add_filter(RemoveLongFilter::new(40))
default_analyzer.add_filter(LowerCaseFilter::new())
manager.register_versioned("default", default_analyzer, "builtin/default@1") catch {
error => abort(Show::to_string(error))
}
manager.register_versioned(
"en_stem",
english_stem_analyzer(),
"builtin/en_stem-porter2@1",
) catch {
error => abort(Show::to_string(error))
}
manager
}