use std::fmt;
use crate::core::LuciError;
use crate::mapping::quantization::QuantizationType;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum FieldType {
Text,
Keyword,
Integer,
Long,
Float,
Double,
Boolean,
Date,
DenseVector {
dims: usize,
quantization: QuantizationType,
},
GeoPoint,
Nested,
GeoShape,
TokenCount,
Ip,
}
impl FieldType {
pub fn from_es_name(name: &str) -> crate::core::Result<Self> {
match name {
"text" => Ok(Self::Text),
"keyword" => Ok(Self::Keyword),
"integer" => Ok(Self::Integer),
"long" => Ok(Self::Long),
"float" => Ok(Self::Float),
"double" => Ok(Self::Double),
"boolean" => Ok(Self::Boolean),
"date" => Ok(Self::Date),
"dense_vector" => Ok(Self::DenseVector {
dims: 0,
quantization: QuantizationType::DEFAULT,
}),
"geo_point" => Ok(Self::GeoPoint),
"nested" => Ok(Self::Nested),
"geo_shape" => Ok(Self::GeoShape),
"token_count" => Ok(Self::TokenCount),
"ip" => Ok(Self::Ip),
_ => Err(LuciError::InvalidQuery(format!(
"unsupported field type: {name}"
))),
}
}
pub fn es_name(&self) -> &'static str {
match self {
Self::Text => "text",
Self::Keyword => "keyword",
Self::Integer => "integer",
Self::Long => "long",
Self::Float => "float",
Self::Double => "double",
Self::Boolean => "boolean",
Self::Date => "date",
Self::DenseVector { .. } => "dense_vector",
Self::GeoPoint => "geo_point",
Self::Nested => "nested",
Self::GeoShape => "geo_shape",
Self::TokenCount => "token_count",
Self::Ip => "ip",
}
}
pub fn is_numeric(&self) -> bool {
matches!(
self,
Self::Integer | Self::Long | Self::Float | Self::Double | Self::TokenCount
)
}
pub fn is_dense_vector(&self) -> bool {
matches!(self, Self::DenseVector { .. })
}
pub fn vector_dims(&self) -> Option<usize> {
match self {
Self::DenseVector { dims, .. } => Some(*dims),
_ => None,
}
}
pub fn vector_quantization(&self) -> Option<QuantizationType> {
match self {
Self::DenseVector { quantization, .. } => Some(*quantization),
_ => None,
}
}
pub fn dense_vector(dims: usize) -> Self {
Self::DenseVector {
dims,
quantization: QuantizationType::DEFAULT,
}
}
}
impl fmt::Display for FieldType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.es_name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_es_names() {
let types = [
FieldType::Text,
FieldType::Keyword,
FieldType::Integer,
FieldType::Long,
FieldType::Float,
FieldType::Double,
FieldType::Boolean,
FieldType::Date,
];
for ft in &types {
let name = ft.es_name();
let parsed = FieldType::from_es_name(name).unwrap();
assert_eq!(&parsed, ft);
}
}
#[test]
fn unknown_type_is_error() {
assert!(FieldType::from_es_name("percolator").is_err());
assert!(FieldType::from_es_name("").is_err());
}
#[test]
fn is_numeric() {
assert!(FieldType::Integer.is_numeric());
assert!(FieldType::Long.is_numeric());
assert!(FieldType::Float.is_numeric());
assert!(FieldType::Double.is_numeric());
assert!(!FieldType::Text.is_numeric());
assert!(!FieldType::Keyword.is_numeric());
assert!(!FieldType::Boolean.is_numeric());
assert!(!FieldType::Date.is_numeric());
}
#[test]
fn display() {
assert_eq!(format!("{}", FieldType::Text), "text");
assert_eq!(format!("{}", FieldType::Long), "long");
}
}