use anyhow::{bail, Context};
use quickwit_proto::SearchRequest;
use serde::{Deserialize, Serialize};
use tantivy::schema::{FieldType, Schema};
use tantivy::Order as TantivyOrder;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SortOrder {
Asc,
Desc,
}
impl Default for SortOrder {
fn default() -> Self {
Self::Desc
}
}
impl From<SortOrder> for TantivyOrder {
fn from(order: SortOrder) -> Self {
match order {
SortOrder::Asc => TantivyOrder::Asc,
SortOrder::Desc => TantivyOrder::Desc,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SortByField {
pub field_name: String,
pub order: SortOrder,
}
impl From<String> for SortByField {
fn from(string: String) -> Self {
let (field_name, order) = if let Some(rest) = string.strip_prefix('+') {
(rest.trim().to_string(), SortOrder::Asc)
} else if let Some(rest) = string.strip_prefix('-') {
(rest.trim().to_string(), SortOrder::Desc)
} else {
(string.trim().to_string(), SortOrder::Asc)
};
SortByField { field_name, order }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SortBy {
DocId,
FastField {
field_name: String,
order: SortOrder,
},
}
pub(crate) fn validate_sort_by_field_name(field_name: &str, schema: &Schema) -> anyhow::Result<()> {
let sort_by_field = schema
.get_field(field_name)
.with_context(|| format!("Unknown sort by field: `{}`", field_name))?;
let sort_by_field_entry = schema.get_field_entry(sort_by_field);
if matches!(sort_by_field_entry.field_type(), FieldType::Str(_)) {
bail!(
"Sort by field on type text is currently not supported `{}`.",
field_name
)
}
if !sort_by_field_entry.is_fast() {
bail!(
"Sort by field must be a fast field, please add the fast property to your field `{}`.",
field_name
)
}
Ok(())
}
impl Default for SortBy {
fn default() -> Self {
Self::DocId
}
}
impl From<&SearchRequest> for SortBy {
fn from(req: &SearchRequest) -> Self {
if let Some(ref sort_by_field) = req.sort_by_field {
SortBy::FastField {
field_name: sort_by_field.to_string(),
order: req
.sort_order
.map(|sort_order| sort_order.into())
.unwrap_or_default(),
}
} else {
SortBy::DocId
}
}
}
impl From<i32> for SortOrder {
fn from(sort: i32) -> Self {
match sort {
0 => Self::Asc,
1 => Self::Desc,
_ => panic!("unexpected value {}", sort),
}
}
}