///|
/// Indexing and storage behavior for one text field.
pub struct TextOptions {
indexed : Bool
stored : Bool
fast : Bool
multi_valued : Bool
tokenizer_name : String
analyzer_version : String
}
///|
pub fn TextOptions::new(indexed : Bool, stored : Bool) -> TextOptions {
{
indexed,
stored,
fast: false,
multi_valued: false,
tokenizer_name: "whitespace",
analyzer_version: "",
}
}
///|
pub fn valid_tokenizer_name(name : String) -> Bool {
if name.length() == 0 {
return false
}
for ch in name {
if ch.is_whitespace() {
return false
}
}
true
}
///|
/// Selects the named Tokenizer pipeline used for this field at index and query
/// time.
pub fn TextOptions::with_tokenizer(
self : TextOptions,
tokenizer_name : String,
) -> TextOptions {
guard valid_tokenizer_name(tokenizer_name) else {
abort("invalid tokenizer name: \{tokenizer_name}")
}
{
indexed: self.indexed,
stored: self.stored,
fast: self.fast,
multi_valued: self.multi_valued,
tokenizer_name,
analyzer_version: self.analyzer_version,
}
}
///|
pub fn TextOptions::with_fast(self : TextOptions, fast : Bool) -> TextOptions {
{
indexed: self.indexed,
stored: self.stored,
fast,
multi_valued: self.multi_valued,
tokenizer_name: self.tokenizer_name,
analyzer_version: self.analyzer_version,
}
}
///|
pub fn TextOptions::with_multi_valued(
self : TextOptions,
multi_valued : Bool,
) -> TextOptions {
{
indexed: self.indexed,
stored: self.stored,
fast: self.fast,
multi_valued,
tokenizer_name: self.tokenizer_name,
analyzer_version: self.analyzer_version,
}
}
///|
/// Pins the analyzer resource fingerprint expected when indexing or reopening
/// this field. Empty keeps backward-compatible name-only resolution.
pub fn TextOptions::with_analyzer_version(
self : TextOptions,
analyzer_version : String,
) -> TextOptions {
guard !analyzer_version.contains("\n") && !analyzer_version.contains("\r") else {
abort("analyzer version must be a single line")
}
{
indexed: self.indexed,
stored: self.stored,
fast: self.fast,
multi_valued: self.multi_valued,
tokenizer_name: self.tokenizer_name,
analyzer_version,
}
}
///|
pub fn TextOptions::is_indexed(self : TextOptions) -> Bool {
self.indexed
}
///|
pub fn TextOptions::is_stored(self : TextOptions) -> Bool {
self.stored
}
///|
pub fn TextOptions::is_fast(self : TextOptions) -> Bool {
self.fast
}
///|
pub fn TextOptions::is_multi_valued(self : TextOptions) -> Bool {
self.multi_valued
}
///|
pub fn TextOptions::tokenizer_name(self : TextOptions) -> String {
self.tokenizer_name
}
///|
pub fn TextOptions::analyzer_version(self : TextOptions) -> String {
self.analyzer_version
}
///|
pub(all) enum FieldType {
Text
Keyword
I64
U64
F64
Bool
Date
Bytes
} derive(Eq, @debug.Debug)
///|
/// Index/storage/column flags for non-analyzed fields.
pub struct FieldOptions {
indexed : Bool
stored : Bool
fast : Bool
multi_valued : Bool
}
///|
pub fn FieldOptions::new(
indexed : Bool,
stored : Bool,
fast : Bool,
) -> FieldOptions {
{ indexed, stored, fast, multi_valued: false }
}
///|
pub fn FieldOptions::with_multi_valued(
self : FieldOptions,
multi_valued : Bool,
) -> FieldOptions {
{ indexed: self.indexed, stored: self.stored, fast: self.fast, multi_valued }
}
///|
pub fn FieldOptions::is_indexed(self : FieldOptions) -> Bool {
self.indexed
}
///|
pub fn FieldOptions::is_stored(self : FieldOptions) -> Bool {
self.stored
}
///|
pub fn FieldOptions::is_fast(self : FieldOptions) -> Bool {
self.fast
}
///|
pub fn FieldOptions::is_multi_valued(self : FieldOptions) -> Bool {
self.multi_valued
}
///|
struct FieldEntry {
name : String
field_type : FieldType
indexed : Bool
stored : Bool
fast : Bool
multi_valued : Bool
tokenizer_name : String
analyzer_version : String
}
///|
/// Immutable registry of field names, identifiers, and text options.
pub struct Schema {
fields : ReadOnlyArray[FieldEntry]
}
///|
pub fn Schema::field_count(self : Schema) -> Int {
self.fields.length()
}
///|
pub fn Schema::field_name(self : Schema, field_id : FieldId) -> String? {
if field_id.value < 0 || field_id.value >= self.fields.length() {
None
} else {
Some(self.fields[field_id.value].name)
}
}
///|
pub fn Schema::field(self : Schema, name : String) -> FieldId? {
match self.fields.search_by(entry => entry.name == name) {
Some(index) => Some(FieldId::new(index))
None => None
}
}
///|
pub fn Schema::is_indexed(self : Schema, field_id : FieldId) -> Bool {
match self.entry(field_id) {
Some(entry) => entry.indexed
None => false
}
}
///|
pub fn Schema::is_stored(self : Schema, field_id : FieldId) -> Bool {
match self.entry(field_id) {
Some(entry) => entry.stored
None => false
}
}
///|
pub fn Schema::is_fast(self : Schema, field_id : FieldId) -> Bool {
match self.entry(field_id) {
Some(entry) => entry.fast
None => false
}
}
///|
pub fn Schema::is_multi_valued(self : Schema, field_id : FieldId) -> Bool {
match self.entry(field_id) {
Some(entry) => entry.multi_valued
None => false
}
}
///|
pub fn Schema::field_type(self : Schema, field_id : FieldId) -> FieldType? {
match self.entry(field_id) {
Some(entry) => Some(entry.field_type)
None => None
}
}
///|
/// Returns the Tokenizer registry name configured for one field.
pub fn Schema::tokenizer_name(self : Schema, field_id : FieldId) -> String? {
match self.entry(field_id) {
Some(entry) if entry.field_type == Text => Some(entry.tokenizer_name)
None => None
_ => None
}
}
///|
pub fn Schema::analyzer_version(self : Schema, field_id : FieldId) -> String? {
match self.entry(field_id) {
Some(entry) if entry.field_type == Text => Some(entry.analyzer_version)
_ => None
}
}
///|
pub fn Schema::options(self : Schema, field_id : FieldId) -> TextOptions? {
match self.entry(field_id) {
Some(entry) if entry.field_type == Text =>
Some({
indexed: entry.indexed,
stored: entry.stored,
fast: entry.fast,
multi_valued: entry.multi_valued,
tokenizer_name: entry.tokenizer_name,
analyzer_version: entry.analyzer_version,
})
_ => None
}
}
///|
fn Schema::entry(self : Schema, field_id : FieldId) -> FieldEntry? {
if field_id.value < 0 || field_id.value >= self.fields.length() {
None
} else {
Some(self.fields[field_id.value])
}
}
///|
/// Mutable builder that assigns sequential, stable FieldId values.
pub struct SchemaBuilder {
fields : Array[FieldEntry]
}
///|
pub fn SchemaBuilder::new() -> SchemaBuilder {
{ fields: [] }
}
///|
pub fn SchemaBuilder::add_text_field(
self : SchemaBuilder,
name : String,
options : TextOptions,
) -> FieldId {
if self.fields.search_by(entry => entry.name == name) is Some(_) {
abort("duplicate field name: \{name}")
}
let field_id = FieldId::new(self.fields.length())
self.fields.push({
name,
field_type: Text,
indexed: options.indexed,
stored: options.stored,
fast: options.fast,
multi_valued: options.multi_valued,
tokenizer_name: options.tokenizer_name,
analyzer_version: options.analyzer_version,
})
field_id
}
///|
fn SchemaBuilder::add_typed_field(
self : SchemaBuilder,
name : String,
field_type : FieldType,
options : FieldOptions,
) -> FieldId {
if self.fields.search_by(entry => entry.name == name) is Some(_) {
abort("duplicate field name: \{name}")
}
let field_id = FieldId::new(self.fields.length())
self.fields.push({
name,
field_type,
indexed: options.indexed,
stored: options.stored,
fast: options.fast,
multi_valued: options.multi_valued,
tokenizer_name: "",
analyzer_version: "",
})
field_id
}
///|
pub fn SchemaBuilder::add_keyword_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, Keyword, options)
}
///|
pub fn SchemaBuilder::add_i64_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, I64, options)
}
///|
pub fn SchemaBuilder::add_u64_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, U64, options)
}
///|
pub fn SchemaBuilder::add_f64_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, F64, options)
}
///|
pub fn SchemaBuilder::add_bool_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, Bool, options)
}
///|
pub fn SchemaBuilder::add_date_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, Date, options)
}
///|
pub fn SchemaBuilder::add_bytes_field(
self : SchemaBuilder,
name : String,
options : FieldOptions,
) -> FieldId {
self.add_typed_field(name, Bytes, options)
}
///|
pub fn SchemaBuilder::build(self : SchemaBuilder) -> Schema {
let fields : Array[FieldEntry] = []
for entry in self.fields {
fields.push(entry)
}
{ fields: ReadOnlyArray::from_array(fields) }
}
///|
pub fn schemas_equal(left : Schema?, right : Schema?) -> Bool {
match (left, right) {
(None, None) => true
(Some(left), Some(right)) => {
if left.fields.length() != right.fields.length() {
return false
}
for index in 0.. false
}
}