1use core::fmt;
22use std::fmt::Formatter;
23use std::sync::Arc;
24
25use serde::{Deserialize, Serialize};
26use typed_builder::TypedBuilder;
27
28use super::transform::Transform;
29use crate::error::Result;
30use crate::spec::Schema;
31use crate::{Error, ErrorKind};
32
33pub type SortOrderRef = Arc<SortOrder>;
35#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Copy, Clone)]
36pub enum SortDirection {
38 #[serde(rename = "asc")]
40 Ascending,
41 #[serde(rename = "desc")]
43 Descending,
44}
45
46impl fmt::Display for SortDirection {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 match *self {
49 SortDirection::Ascending => write!(f, "ascending"),
50 SortDirection::Descending => write!(f, "descending"),
51 }
52 }
53}
54
55#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Copy, Clone)]
56pub enum NullOrder {
58 #[serde(rename = "nulls-first")]
59 First,
61 #[serde(rename = "nulls-last")]
62 Last,
64}
65
66impl fmt::Display for NullOrder {
67 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
68 match *self {
69 NullOrder::First => write!(f, "first"),
70 NullOrder::Last => write!(f, "last"),
71 }
72 }
73}
74
75#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, TypedBuilder)]
76#[serde(rename_all = "kebab-case")]
77pub struct SortField {
79 pub source_id: i32,
81 pub transform: Transform,
83 pub direction: SortDirection,
85 pub null_order: NullOrder,
87}
88
89impl fmt::Display for SortField {
90 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91 write!(
92 f,
93 "SortField {{ source_id: {}, transform: {}, direction: {}, null_order: {} }}",
94 self.source_id, self.transform, self.direction, self.null_order
95 )
96 }
97}
98
99#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Builder, Default)]
100#[serde(rename_all = "kebab-case")]
101#[builder(setter(prefix = "with"))]
102#[builder(build_fn(skip))]
103pub struct SortOrder {
106 #[builder(default)]
108 pub order_id: i64,
109 #[builder(setter(each(name = "with_sort_field")), default)]
111 pub fields: Vec<SortField>,
112}
113
114impl SortOrder {
115 pub(crate) const UNSORTED_ORDER_ID: i64 = 0;
116
117 pub fn builder() -> SortOrderBuilder {
119 SortOrderBuilder::default()
120 }
121
122 pub fn unsorted_order() -> SortOrder {
124 SortOrder {
125 order_id: SortOrder::UNSORTED_ORDER_ID,
126 fields: Vec::new(),
127 }
128 }
129
130 pub fn is_unsorted(&self) -> bool {
134 self.fields.is_empty()
135 }
136
137 pub fn with_order_id(self, order_id: i64) -> SortOrder {
139 SortOrder {
140 order_id,
141 fields: self.fields,
142 }
143 }
144}
145
146impl SortOrderBuilder {
147 pub fn build_unbound(&self) -> Result<SortOrder> {
149 let fields = self.fields.clone().unwrap_or_default();
150 match (self.order_id, fields.as_slice()) {
151 (Some(SortOrder::UNSORTED_ORDER_ID) | None, []) => Ok(SortOrder::unsorted_order()),
152 (_, []) => Err(Error::new(
153 ErrorKind::Unexpected,
154 format!("Unsorted order ID must be {}", SortOrder::UNSORTED_ORDER_ID),
155 )),
156 (Some(SortOrder::UNSORTED_ORDER_ID), [..]) => Err(Error::new(
157 ErrorKind::Unexpected,
158 format!(
159 "Sort order ID {} is reserved for unsorted order",
160 SortOrder::UNSORTED_ORDER_ID
161 ),
162 )),
163 (maybe_order_id, [..]) => Ok(SortOrder {
164 order_id: maybe_order_id.unwrap_or(1),
165 fields: fields.to_vec(),
166 }),
167 }
168 }
169
170 pub fn build(&self, schema: &Schema) -> Result<SortOrder> {
172 let unbound_sort_order = self.build_unbound()?;
173 SortOrderBuilder::check_compatibility(unbound_sort_order, schema)
174 }
175
176 fn check_compatibility(sort_order: SortOrder, schema: &Schema) -> Result<SortOrder> {
178 let sort_fields = &sort_order.fields;
179 for sort_field in sort_fields {
180 match schema.field_by_id(sort_field.source_id) {
181 None => {
182 return Err(Error::new(
183 ErrorKind::DataInvalid,
184 format!("Cannot find source column for sort field: {sort_field}"),
185 ));
186 }
187 Some(source_field) => {
188 let source_type = source_field.field_type.as_ref();
189
190 if !source_type.is_primitive() {
191 return Err(Error::new(
192 ErrorKind::DataInvalid,
193 format!("Cannot sort by non-primitive source field: {source_type}"),
194 ));
195 }
196
197 let field_transform = sort_field.transform;
198 field_transform.result_type(source_type)?;
199 }
200 }
201 }
202
203 Ok(sort_order)
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::spec::{ListType, NestedField, PrimitiveType, Type};
211
212 #[test]
213 fn test_sort_field() {
214 let spec = r#"
215 {
216 "transform": "bucket[4]",
217 "source-id": 3,
218 "direction": "desc",
219 "null-order": "nulls-last"
220 }
221 "#;
222
223 let field: SortField = serde_json::from_str(spec).unwrap();
224 assert_eq!(Transform::Bucket(4), field.transform);
225 assert_eq!(3, field.source_id);
226 assert_eq!(SortDirection::Descending, field.direction);
227 assert_eq!(NullOrder::Last, field.null_order);
228 }
229
230 #[test]
231 fn test_sort_order() {
232 let spec = r#"
233 {
234 "order-id": 1,
235 "fields": [ {
236 "transform": "identity",
237 "source-id": 2,
238 "direction": "asc",
239 "null-order": "nulls-first"
240 }, {
241 "transform": "bucket[4]",
242 "source-id": 3,
243 "direction": "desc",
244 "null-order": "nulls-last"
245 } ]
246 }
247 "#;
248
249 let order: SortOrder = serde_json::from_str(spec).unwrap();
250 assert_eq!(Transform::Identity, order.fields[0].transform);
251 assert_eq!(2, order.fields[0].source_id);
252 assert_eq!(SortDirection::Ascending, order.fields[0].direction);
253 assert_eq!(NullOrder::First, order.fields[0].null_order);
254
255 assert_eq!(Transform::Bucket(4), order.fields[1].transform);
256 assert_eq!(3, order.fields[1].source_id);
257 assert_eq!(SortDirection::Descending, order.fields[1].direction);
258 assert_eq!(NullOrder::Last, order.fields[1].null_order);
259 }
260
261 #[test]
262 fn test_build_unbound_should_return_err_if_unsorted_order_does_not_have_an_order_id_of_zero() {
263 assert_eq!(
264 SortOrder::builder()
265 .with_order_id(1)
266 .build_unbound()
267 .expect_err("Expected an Err value")
268 .message(),
269 "Unsorted order ID must be 0"
270 )
271 }
272
273 #[test]
274 fn test_build_unbound_should_return_err_if_order_id_equals_zero_is_used_for_anything_other_than_unsorted_order()
275 {
276 assert_eq!(
277 SortOrder::builder()
278 .with_order_id(SortOrder::UNSORTED_ORDER_ID)
279 .with_sort_field(
280 SortField::builder()
281 .source_id(2)
282 .direction(SortDirection::Ascending)
283 .null_order(NullOrder::First)
284 .transform(Transform::Identity)
285 .build()
286 )
287 .build_unbound()
288 .expect_err("Expected an Err value")
289 .message(),
290 "Sort order ID 0 is reserved for unsorted order"
291 )
292 }
293
294 #[test]
295 fn test_build_unbound_returns_correct_default_order_id_for_no_fields() {
296 assert_eq!(
297 SortOrder::builder()
298 .build_unbound()
299 .expect("Expected an Ok value")
300 .order_id,
301 SortOrder::UNSORTED_ORDER_ID
302 )
303 }
304
305 #[test]
306 fn test_build_unbound_returns_correct_default_order_id_for_fields() {
307 let sort_field = SortField::builder()
308 .source_id(2)
309 .direction(SortDirection::Ascending)
310 .null_order(NullOrder::First)
311 .transform(Transform::Identity)
312 .build();
313 assert_ne!(
314 SortOrder::builder()
315 .with_sort_field(sort_field.clone())
316 .build_unbound()
317 .expect("Expected an Ok value")
318 .order_id,
319 SortOrder::UNSORTED_ORDER_ID
320 )
321 }
322
323 #[test]
324 fn test_build_unbound_should_return_unsorted_sort_order() {
325 assert_eq!(
326 SortOrder::builder()
327 .with_order_id(SortOrder::UNSORTED_ORDER_ID)
328 .build_unbound()
329 .expect("Expected an Ok value"),
330 SortOrder::unsorted_order()
331 )
332 }
333
334 #[test]
335 fn test_build_unbound_should_return_sort_order_with_given_order_id_and_sort_fields() {
336 let sort_field = SortField::builder()
337 .source_id(2)
338 .direction(SortDirection::Ascending)
339 .null_order(NullOrder::First)
340 .transform(Transform::Identity)
341 .build();
342
343 assert_eq!(
344 SortOrder::builder()
345 .with_order_id(2)
346 .with_sort_field(sort_field.clone())
347 .build_unbound()
348 .expect("Expected an Ok value"),
349 SortOrder {
350 order_id: 2,
351 fields: vec![sort_field]
352 }
353 )
354 }
355
356 #[test]
357 fn test_build_unbound_should_return_sort_order_with_given_sort_fields_and_defaults_to_1_if_missing_an_order_id()
358 {
359 let sort_field = SortField::builder()
360 .source_id(2)
361 .direction(SortDirection::Ascending)
362 .null_order(NullOrder::First)
363 .transform(Transform::Identity)
364 .build();
365
366 assert_eq!(
367 SortOrder::builder()
368 .with_sort_field(sort_field.clone())
369 .build_unbound()
370 .expect("Expected an Ok value"),
371 SortOrder {
372 order_id: 1,
373 fields: vec![sort_field]
374 }
375 )
376 }
377
378 #[test]
379 fn test_build_should_return_err_if_sort_order_field_is_not_present_in_schema() {
380 let schema = Schema::builder()
381 .with_schema_id(1)
382 .with_fields(vec![
383 NestedField::required(1, "foo", Type::Primitive(PrimitiveType::Int)).into(),
384 ])
385 .build()
386 .unwrap();
387
388 let sort_order_builder_result = SortOrder::builder()
389 .with_sort_field(
390 SortField::builder()
391 .source_id(2)
392 .direction(SortDirection::Ascending)
393 .null_order(NullOrder::First)
394 .transform(Transform::Identity)
395 .build(),
396 )
397 .build(&schema);
398
399 let err = sort_order_builder_result.expect_err("Expected an Err value");
400 assert_eq!(err.kind(), ErrorKind::DataInvalid);
401 assert_eq!(
402 err.message(),
403 "Cannot find source column for sort field: SortField { source_id: 2, transform: identity, direction: ascending, null_order: first }"
404 );
405 }
406
407 #[test]
408 fn test_build_should_return_err_if_source_field_is_not_a_primitive_type() {
409 let schema = Schema::builder()
410 .with_schema_id(1)
411 .with_fields(vec![
412 NestedField::required(
413 1,
414 "foo",
415 Type::List(ListType {
416 element_field: NestedField::list_element(
417 2,
418 Type::Primitive(PrimitiveType::String),
419 true,
420 )
421 .into(),
422 }),
423 )
424 .into(),
425 ])
426 .build()
427 .unwrap();
428
429 let sort_order_builder_result = SortOrder::builder()
430 .with_sort_field(
431 SortField::builder()
432 .source_id(1)
433 .direction(SortDirection::Ascending)
434 .null_order(NullOrder::First)
435 .transform(Transform::Identity)
436 .build(),
437 )
438 .build(&schema);
439
440 let err = sort_order_builder_result.expect_err("Expected an Err value");
441 assert_eq!(err.kind(), ErrorKind::DataInvalid);
442 assert_eq!(
443 err.message(),
444 "Cannot sort by non-primitive source field: list"
445 );
446 }
447
448 #[test]
449 fn test_build_should_return_err_if_source_field_is_variant() {
450 let schema = Schema::builder()
451 .with_schema_id(1)
452 .with_fields(vec![
453 NestedField::optional(1, "v", Type::Variant(crate::spec::VariantType)).into(),
454 ])
455 .build()
456 .unwrap();
457
458 let sort_order_builder_result = SortOrder::builder()
459 .with_sort_field(
460 SortField::builder()
461 .source_id(1)
462 .direction(SortDirection::Ascending)
463 .null_order(NullOrder::First)
464 .transform(Transform::Identity)
465 .build(),
466 )
467 .build(&schema);
468
469 let err = sort_order_builder_result.expect_err("Expected an Err value");
470 assert_eq!(err.kind(), ErrorKind::DataInvalid);
471 assert_eq!(
472 err.message(),
473 "Cannot sort by non-primitive source field: variant"
474 );
475 }
476
477 #[test]
478 fn test_build_should_return_err_if_source_field_type_is_not_supported_by_transform() {
479 let schema = Schema::builder()
480 .with_schema_id(1)
481 .with_fields(vec![
482 NestedField::required(1, "foo", Type::Primitive(PrimitiveType::Int)).into(),
483 ])
484 .build()
485 .unwrap();
486
487 let sort_order_builder_result = SortOrder::builder()
488 .with_sort_field(
489 SortField::builder()
490 .source_id(1)
491 .direction(SortDirection::Ascending)
492 .null_order(NullOrder::First)
493 .transform(Transform::Year)
494 .build(),
495 )
496 .build(&schema);
497
498 let err = sort_order_builder_result.expect_err("Expected an Err value");
499 assert_eq!(err.kind(), ErrorKind::DataInvalid);
500 assert_eq!(
501 err.message(),
502 "int is not a valid input type of year transform"
503 );
504 }
505
506 #[test]
507 fn test_build_should_return_valid_sort_order() {
508 let schema = Schema::builder()
509 .with_schema_id(1)
510 .with_fields(vec![
511 NestedField::required(1, "foo", Type::Primitive(PrimitiveType::String)).into(),
512 NestedField::required(2, "bar", Type::Primitive(PrimitiveType::Int)).into(),
513 ])
514 .build()
515 .unwrap();
516
517 let sort_field = SortField::builder()
518 .source_id(2)
519 .direction(SortDirection::Ascending)
520 .null_order(NullOrder::First)
521 .transform(Transform::Identity)
522 .build();
523
524 let sort_order_builder_result = SortOrder::builder()
525 .with_sort_field(sort_field.clone())
526 .build(&schema);
527
528 assert_eq!(
529 sort_order_builder_result.expect("Expected an Ok value"),
530 SortOrder {
531 order_id: 1,
532 fields: vec![sort_field],
533 }
534 )
535 }
536}