use serde_json::{Map, Value};
use super::{Geo, GeoPoint, NumericType, ScriptSortType};
use crate::query::AsQuery;
use crate::{FlussoDocument, nested_boundaries};
#[derive(Debug, Clone, Copy)]
pub enum SortOrder {
Asc,
Desc,
}
impl SortOrder {
pub(crate) fn as_str(self) -> &'static str {
match self {
SortOrder::Asc => "asc",
SortOrder::Desc => "desc",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SortMode {
Min,
Max,
Avg,
Sum,
Median,
}
impl SortMode {
fn as_str(self) -> &'static str {
match self {
SortMode::Min => "min",
SortMode::Max => "max",
SortMode::Avg => "avg",
SortMode::Sum => "sum",
SortMode::Median => "median",
}
}
}
#[derive(Debug, Clone)]
pub struct Sort {
key: String,
body: Map<String, Value>,
}
impl Sort {
pub(crate) fn new(field: &str, order: SortOrder) -> Self {
let mut body = Map::new();
body.insert(
"order".to_string(),
Value::String(order.as_str().to_string()),
);
Self {
key: field.to_string(),
body,
}
}
#[must_use]
pub fn score() -> Self {
let mut sort = Self {
key: "_score".to_string(),
body: Map::new(),
};
sort.body
.insert("order".to_string(), Value::String("desc".to_string()));
sort
}
#[must_use]
pub fn script(
script_type: ScriptSortType,
source: impl Into<String>,
order: SortOrder,
) -> Self {
let mut script = Map::new();
script.insert("source".to_string(), Value::String(source.into()));
let mut body = Map::new();
body.insert(
"type".to_string(),
Value::String(script_type.as_str().to_string()),
);
body.insert("script".to_string(), Value::Object(script));
body.insert(
"order".to_string(),
Value::String(order.as_str().to_string()),
);
Self {
key: "_script".to_string(),
body,
}
}
pub(crate) fn from_parts(key: String, body: Map<String, Value>) -> Self {
Self { key, body }
}
pub(crate) fn field<S: FlussoDocument>(path: &str, order: SortOrder) -> Self {
let mut sort = Sort::new(path, order);
let boundaries = nested_boundaries(S::PATH);
if let Some(nested) = nested_clause(&boundaries) {
sort.body.insert("nested".to_string(), nested);
sort.body.insert(
"mode".to_string(),
Value::String(default_mode(order).to_string()),
);
}
sort
}
#[must_use]
pub fn asc(mut self) -> Self {
self.body
.insert("order".to_string(), Value::String("asc".to_string()));
self
}
#[must_use]
pub fn desc(mut self) -> Self {
self.body
.insert("order".to_string(), Value::String("desc".to_string()));
self
}
#[must_use]
pub fn missing_first(mut self) -> Self {
self.body
.insert("missing".to_string(), Value::String("_first".to_string()));
self
}
#[must_use]
pub fn missing_last(mut self) -> Self {
self.body
.insert("missing".to_string(), Value::String("_last".to_string()));
self
}
#[must_use]
pub fn missing(mut self, value: impl Into<Value>) -> Self {
self.body.insert("missing".to_string(), value.into());
self
}
#[must_use]
pub fn mode(mut self, mode: SortMode) -> Self {
self.body
.insert("mode".to_string(), Value::String(mode.as_str().to_string()));
self
}
#[must_use]
pub fn unmapped_type(mut self, unmapped_type: impl Into<String>) -> Self {
self.body.insert(
"unmapped_type".to_string(),
Value::String(unmapped_type.into()),
);
self
}
#[must_use]
pub fn numeric_type(mut self, numeric_type: NumericType) -> Self {
self.body.insert(
"numeric_type".to_string(),
Value::String(numeric_type.as_str().to_string()),
);
self
}
#[must_use]
pub fn format(mut self, format: impl Into<String>) -> Self {
self.body
.insert("format".to_string(), Value::String(format.into()));
self
}
#[must_use]
pub fn nested_filtered<S>(mut self, path: impl Into<String>, filter: impl AsQuery<S>) -> Self {
let mut nested = Map::new();
nested.insert("path".to_string(), Value::String(path.into()));
if let Some(query) = filter.into_query() {
nested.insert("filter".to_string(), query.to_value());
}
self.body
.insert("nested".to_string(), Value::Object(nested));
self
}
pub(crate) fn to_value(&self) -> Value {
let mut outer = Map::new();
outer.insert(self.key.clone(), Value::Object(self.body.clone()));
Value::Object(outer)
}
pub(crate) fn key(&self) -> &str {
&self.key
}
pub(crate) fn without_nested_context(mut self) -> Self {
if self.body.remove("nested").is_some() {
self.body.remove("mode");
}
self
}
}
fn nested_clause(boundaries: &[String]) -> Option<Value> {
let (path, rest) = boundaries.split_first()?;
let mut clause = Map::new();
clause.insert("path".to_string(), Value::String(path.clone()));
if let Some(inner) = nested_clause(rest) {
clause.insert("nested".to_string(), inner);
}
Some(Value::Object(clause))
}
fn default_mode(order: SortOrder) -> &'static str {
match order {
SortOrder::Asc => "min",
SortOrder::Desc => "max",
}
}
pub trait Sortable {
fn asc(&self) -> Sort;
fn desc(&self) -> Sort;
}
#[derive(Debug, Clone)]
pub enum Missing {
First,
Last,
Value(Value),
}
#[derive(Debug, Clone)]
pub struct OrderBy {
order: SortOrder,
missing: Option<Missing>,
mode: Option<SortMode>,
numeric_type: Option<NumericType>,
unmapped_type: Option<String>,
format: Option<String>,
}
impl OrderBy {
#[must_use]
pub fn asc() -> Self {
Self::new(SortOrder::Asc)
}
#[must_use]
pub fn desc() -> Self {
Self::new(SortOrder::Desc)
}
fn new(order: SortOrder) -> Self {
Self {
order,
missing: None,
mode: None,
numeric_type: None,
unmapped_type: None,
format: None,
}
}
#[must_use]
pub fn missing_first(mut self) -> Self {
self.missing = Some(Missing::First);
self
}
#[must_use]
pub fn missing_last(mut self) -> Self {
self.missing = Some(Missing::Last);
self
}
#[must_use]
pub fn missing(mut self, value: impl Into<Value>) -> Self {
self.missing = Some(Missing::Value(value.into()));
self
}
#[must_use]
pub fn mode(mut self, mode: SortMode) -> Self {
self.mode = Some(mode);
self
}
#[must_use]
pub fn numeric_type(mut self, numeric_type: NumericType) -> Self {
self.numeric_type = Some(numeric_type);
self
}
#[must_use]
pub fn unmapped_type(mut self, unmapped_type: impl Into<String>) -> Self {
self.unmapped_type = Some(unmapped_type.into());
self
}
#[must_use]
pub fn format(mut self, format: impl Into<String>) -> Self {
self.format = Some(format.into());
self
}
fn into_sort<H: Sortable>(self, handle: &H) -> Sort {
let mut sort = match self.order {
SortOrder::Asc => handle.asc(),
SortOrder::Desc => handle.desc(),
};
sort = match self.missing {
Some(Missing::First) => sort.missing_first(),
Some(Missing::Last) => sort.missing_last(),
Some(Missing::Value(value)) => sort.missing(value),
None => sort,
};
if let Some(mode) = self.mode {
sort = sort.mode(mode);
}
if let Some(numeric_type) = self.numeric_type {
sort = sort.numeric_type(numeric_type);
}
if let Some(unmapped_type) = self.unmapped_type {
sort = sort.unmapped_type(unmapped_type);
}
if let Some(format) = self.format {
sort = sort.format(format);
}
sort
}
}
impl From<SortOrder> for OrderBy {
fn from(order: SortOrder) -> Self {
Self::new(order)
}
}
#[derive(Debug, Clone)]
pub struct MaybeOrderBy(Option<OrderBy>);
impl From<OrderBy> for MaybeOrderBy {
fn from(order: OrderBy) -> Self {
Self(Some(order))
}
}
impl From<SortOrder> for MaybeOrderBy {
fn from(order: SortOrder) -> Self {
Self(Some(order.into()))
}
}
impl<T: Into<OrderBy>> From<Option<T>> for MaybeOrderBy {
fn from(order: Option<T>) -> Self {
Self(order.map(Into::into))
}
}
#[derive(Debug, Default)]
pub struct SortBuilder {
sorts: Vec<Sort>,
fallback: Option<Sort>,
}
impl SortBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
fn push_unique(&mut self, sort: Sort) {
if !self
.sorts
.iter()
.any(|existing| existing.key() == sort.key())
{
self.sorts.push(sort);
}
}
#[must_use]
pub fn by<H: Sortable>(mut self, handle: H, dir: impl Into<MaybeOrderBy>) -> Self {
if let Some(order) = dir.into().0 {
self.push_unique(order.into_sort(&handle));
}
self
}
#[must_use]
pub fn near<S>(mut self, handle: Geo<S>, center: impl Into<Option<GeoPoint>>) -> Self {
if let Some(center) = center.into() {
self.push_unique(handle.distance_from(center));
}
self
}
#[must_use]
pub fn score(mut self) -> Self {
self.push_unique(Sort::score());
self
}
#[must_use]
pub fn score_if(self, cond: bool) -> Self {
if cond { self.score() } else { self }
}
#[must_use]
pub fn raw(mut self, sort: impl Into<Option<Sort>>) -> Self {
if let Some(sort) = sort.into() {
self.sorts.push(sort);
}
self
}
#[must_use]
pub fn tiebreak<H: Sortable>(mut self, handle: H) -> Self {
self.push_unique(handle.asc());
self
}
#[must_use]
pub fn or_default(mut self, sort: impl Into<Sort>) -> Self {
if self.fallback.is_none() {
self.fallback = Some(sort.into());
}
self
}
#[must_use]
pub fn build(mut self) -> Vec<Sort> {
if self.sorts.is_empty()
&& let Some(fallback) = self.fallback
{
self.sorts.push(fallback);
}
self.sorts
}
}
impl IntoIterator for SortBuilder {
type Item = Sort;
type IntoIter = std::vec::IntoIter<Sort>;
fn into_iter(self) -> Self::IntoIter {
self.build().into_iter()
}
}