1use serde_json::{Map, Value};
12
13use super::{Geo, GeoPoint, NumericType, ScriptSortType};
14use crate::query::AsQuery;
15use crate::{FlussoDocument, nested_boundaries};
16
17#[derive(Debug, Clone, Copy)]
19pub enum SortOrder {
20 Asc,
22 Desc,
24}
25
26impl SortOrder {
27 pub(crate) fn as_str(self) -> &'static str {
28 match self {
29 SortOrder::Asc => "asc",
30 SortOrder::Desc => "desc",
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy)]
37pub enum SortMode {
38 Min,
40 Max,
42 Avg,
44 Sum,
46 Median,
48}
49
50impl SortMode {
51 fn as_str(self) -> &'static str {
52 match self {
53 SortMode::Min => "min",
54 SortMode::Max => "max",
55 SortMode::Avg => "avg",
56 SortMode::Sum => "sum",
57 SortMode::Median => "median",
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
66pub struct Sort {
67 key: String,
68 body: Map<String, Value>,
69}
70
71impl Sort {
72 pub(crate) fn new(field: &str, order: SortOrder) -> Self {
74 let mut body = Map::new();
75 body.insert(
76 "order".to_string(),
77 Value::String(order.as_str().to_string()),
78 );
79 Self {
80 key: field.to_string(),
81 body,
82 }
83 }
84
85 #[must_use]
87 pub fn score() -> Self {
88 let mut sort = Self {
89 key: "_score".to_string(),
90 body: Map::new(),
91 };
92 sort.body
93 .insert("order".to_string(), Value::String("desc".to_string()));
94 sort
95 }
96
97 #[must_use]
101 pub fn script(
102 script_type: ScriptSortType,
103 source: impl Into<String>,
104 order: SortOrder,
105 ) -> Self {
106 let mut script = Map::new();
107 script.insert("source".to_string(), Value::String(source.into()));
108 let mut body = Map::new();
109 body.insert(
110 "type".to_string(),
111 Value::String(script_type.as_str().to_string()),
112 );
113 body.insert("script".to_string(), Value::Object(script));
114 body.insert(
115 "order".to_string(),
116 Value::String(order.as_str().to_string()),
117 );
118 Self {
119 key: "_script".to_string(),
120 body,
121 }
122 }
123
124 pub(crate) fn from_parts(key: String, body: Map<String, Value>) -> Self {
126 Self { key, body }
127 }
128
129 pub(crate) fn field<S: FlussoDocument>(path: &str, order: SortOrder) -> Self {
135 let mut sort = Sort::new(path, order);
136 let boundaries = nested_boundaries(S::PATH);
137 if let Some(nested) = nested_clause(&boundaries) {
138 sort.body.insert("nested".to_string(), nested);
139 sort.body.insert(
140 "mode".to_string(),
141 Value::String(default_mode(order).to_string()),
142 );
143 }
144 sort
145 }
146
147 #[must_use]
149 pub fn asc(mut self) -> Self {
150 self.body
151 .insert("order".to_string(), Value::String("asc".to_string()));
152 self
153 }
154
155 #[must_use]
157 pub fn desc(mut self) -> Self {
158 self.body
159 .insert("order".to_string(), Value::String("desc".to_string()));
160 self
161 }
162
163 #[must_use]
165 pub fn missing_first(mut self) -> Self {
166 self.body
167 .insert("missing".to_string(), Value::String("_first".to_string()));
168 self
169 }
170
171 #[must_use]
173 pub fn missing_last(mut self) -> Self {
174 self.body
175 .insert("missing".to_string(), Value::String("_last".to_string()));
176 self
177 }
178
179 #[must_use]
181 pub fn missing(mut self, value: impl Into<Value>) -> Self {
182 self.body.insert("missing".to_string(), value.into());
183 self
184 }
185
186 #[must_use]
188 pub fn mode(mut self, mode: SortMode) -> Self {
189 self.body
190 .insert("mode".to_string(), Value::String(mode.as_str().to_string()));
191 self
192 }
193
194 #[must_use]
197 pub fn unmapped_type(mut self, unmapped_type: impl Into<String>) -> Self {
198 self.body.insert(
199 "unmapped_type".to_string(),
200 Value::String(unmapped_type.into()),
201 );
202 self
203 }
204
205 #[must_use]
207 pub fn numeric_type(mut self, numeric_type: NumericType) -> Self {
208 self.body.insert(
209 "numeric_type".to_string(),
210 Value::String(numeric_type.as_str().to_string()),
211 );
212 self
213 }
214
215 #[must_use]
217 pub fn format(mut self, format: impl Into<String>) -> Self {
218 self.body
219 .insert("format".to_string(), Value::String(format.into()));
220 self
221 }
222
223 #[must_use]
229 pub fn nested_filtered<S>(mut self, path: impl Into<String>, filter: impl AsQuery<S>) -> Self {
230 let mut nested = Map::new();
231 nested.insert("path".to_string(), Value::String(path.into()));
232 if let Some(query) = filter.into_query() {
233 nested.insert("filter".to_string(), query.to_value());
234 }
235 self.body
236 .insert("nested".to_string(), Value::Object(nested));
237 self
238 }
239
240 pub(crate) fn to_value(&self) -> Value {
241 let mut outer = Map::new();
242 outer.insert(self.key.clone(), Value::Object(self.body.clone()));
243 Value::Object(outer)
244 }
245
246 pub(crate) fn key(&self) -> &str {
249 &self.key
250 }
251
252 pub(crate) fn without_nested_context(mut self) -> Self {
257 if self.body.remove("nested").is_some() {
258 self.body.remove("mode");
259 }
260 self
261 }
262}
263
264fn nested_clause(boundaries: &[String]) -> Option<Value> {
267 let (path, rest) = boundaries.split_first()?;
268 let mut clause = Map::new();
269 clause.insert("path".to_string(), Value::String(path.clone()));
270 if let Some(inner) = nested_clause(rest) {
271 clause.insert("nested".to_string(), inner);
272 }
273 Some(Value::Object(clause))
274}
275
276fn default_mode(order: SortOrder) -> &'static str {
280 match order {
281 SortOrder::Asc => "min",
282 SortOrder::Desc => "max",
283 }
284}
285
286pub trait Sortable {
296 fn asc(&self) -> Sort;
298 fn desc(&self) -> Sort;
300}
301
302#[derive(Debug, Clone)]
304pub enum Missing {
305 First,
307 Last,
309 Value(Value),
311}
312
313#[derive(Debug, Clone)]
320pub struct OrderBy {
321 order: SortOrder,
322 missing: Option<Missing>,
323 mode: Option<SortMode>,
324 numeric_type: Option<NumericType>,
325 unmapped_type: Option<String>,
326 format: Option<String>,
327}
328
329impl OrderBy {
330 #[must_use]
332 pub fn asc() -> Self {
333 Self::new(SortOrder::Asc)
334 }
335
336 #[must_use]
338 pub fn desc() -> Self {
339 Self::new(SortOrder::Desc)
340 }
341
342 fn new(order: SortOrder) -> Self {
343 Self {
344 order,
345 missing: None,
346 mode: None,
347 numeric_type: None,
348 unmapped_type: None,
349 format: None,
350 }
351 }
352
353 #[must_use]
355 pub fn missing_first(mut self) -> Self {
356 self.missing = Some(Missing::First);
357 self
358 }
359
360 #[must_use]
362 pub fn missing_last(mut self) -> Self {
363 self.missing = Some(Missing::Last);
364 self
365 }
366
367 #[must_use]
369 pub fn missing(mut self, value: impl Into<Value>) -> Self {
370 self.missing = Some(Missing::Value(value.into()));
371 self
372 }
373
374 #[must_use]
376 pub fn mode(mut self, mode: SortMode) -> Self {
377 self.mode = Some(mode);
378 self
379 }
380
381 #[must_use]
383 pub fn numeric_type(mut self, numeric_type: NumericType) -> Self {
384 self.numeric_type = Some(numeric_type);
385 self
386 }
387
388 #[must_use]
390 pub fn unmapped_type(mut self, unmapped_type: impl Into<String>) -> Self {
391 self.unmapped_type = Some(unmapped_type.into());
392 self
393 }
394
395 #[must_use]
397 pub fn format(mut self, format: impl Into<String>) -> Self {
398 self.format = Some(format.into());
399 self
400 }
401
402 fn into_sort<H: Sortable>(self, handle: &H) -> Sort {
404 let mut sort = match self.order {
405 SortOrder::Asc => handle.asc(),
406 SortOrder::Desc => handle.desc(),
407 };
408 sort = match self.missing {
409 Some(Missing::First) => sort.missing_first(),
410 Some(Missing::Last) => sort.missing_last(),
411 Some(Missing::Value(value)) => sort.missing(value),
412 None => sort,
413 };
414 if let Some(mode) = self.mode {
415 sort = sort.mode(mode);
416 }
417 if let Some(numeric_type) = self.numeric_type {
418 sort = sort.numeric_type(numeric_type);
419 }
420 if let Some(unmapped_type) = self.unmapped_type {
421 sort = sort.unmapped_type(unmapped_type);
422 }
423 if let Some(format) = self.format {
424 sort = sort.format(format);
425 }
426 sort
427 }
428}
429
430impl From<SortOrder> for OrderBy {
431 fn from(order: SortOrder) -> Self {
432 Self::new(order)
433 }
434}
435
436#[derive(Debug, Clone)]
443pub struct MaybeOrderBy(Option<OrderBy>);
444
445impl From<OrderBy> for MaybeOrderBy {
446 fn from(order: OrderBy) -> Self {
447 Self(Some(order))
448 }
449}
450
451impl From<SortOrder> for MaybeOrderBy {
452 fn from(order: SortOrder) -> Self {
453 Self(Some(order.into()))
454 }
455}
456
457impl<T: Into<OrderBy>> From<Option<T>> for MaybeOrderBy {
458 fn from(order: Option<T>) -> Self {
459 Self(order.map(Into::into))
460 }
461}
462
463#[derive(Debug, Default)]
485pub struct SortBuilder {
486 sorts: Vec<Sort>,
487 fallback: Option<Sort>,
488}
489
490impl SortBuilder {
491 #[must_use]
493 pub fn new() -> Self {
494 Self::default()
495 }
496
497 fn push_unique(&mut self, sort: Sort) {
499 if !self
500 .sorts
501 .iter()
502 .any(|existing| existing.key() == sort.key())
503 {
504 self.sorts.push(sort);
505 }
506 }
507
508 #[must_use]
513 pub fn by<H: Sortable>(mut self, handle: H, dir: impl Into<MaybeOrderBy>) -> Self {
514 if let Some(order) = dir.into().0 {
515 self.push_unique(order.into_sort(&handle));
516 }
517 self
518 }
519
520 #[must_use]
523 pub fn near<S>(mut self, handle: Geo<S>, center: impl Into<Option<GeoPoint>>) -> Self {
524 if let Some(center) = center.into() {
525 self.push_unique(handle.distance_from(center));
526 }
527 self
528 }
529
530 #[must_use]
532 pub fn score(mut self) -> Self {
533 self.push_unique(Sort::score());
534 self
535 }
536
537 #[must_use]
539 pub fn score_if(self, cond: bool) -> Self {
540 if cond { self.score() } else { self }
541 }
542
543 #[must_use]
547 pub fn raw(mut self, sort: impl Into<Option<Sort>>) -> Self {
548 if let Some(sort) = sort.into() {
549 self.sorts.push(sort);
550 }
551 self
552 }
553
554 #[must_use]
557 pub fn tiebreak<H: Sortable>(mut self, handle: H) -> Self {
558 self.push_unique(handle.asc());
559 self
560 }
561
562 #[must_use]
564 pub fn or_default(mut self, sort: impl Into<Sort>) -> Self {
565 if self.fallback.is_none() {
566 self.fallback = Some(sort.into());
567 }
568 self
569 }
570
571 #[must_use]
573 pub fn build(mut self) -> Vec<Sort> {
574 if self.sorts.is_empty()
575 && let Some(fallback) = self.fallback
576 {
577 self.sorts.push(fallback);
578 }
579 self.sorts
580 }
581}
582
583impl IntoIterator for SortBuilder {
584 type Item = Sort;
585 type IntoIter = std::vec::IntoIter<Sort>;
586
587 fn into_iter(self) -> Self::IntoIter {
588 self.build().into_iter()
589 }
590}