blob: 8419b57a2b388460422672cb2fabc943079bc74b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use datafusion_common::Result;
use datafusion_expr::{
scalar_doc_sections::DOC_SECTION_OTHER, ColumnarValue, Documentation, Volatility,
};
use sedona_expr::scalar_udf::{SedonaScalarKernel, SedonaScalarUDF};
use sedona_schema::datatypes::SedonaType;
use std::{fmt::Debug, sync::Arc};
/// SD_Order() scalar UDF implementation
///
/// This function is invoked to obtain a proxy array whose order may be used
/// to sort based on the value. The default implementation returns the value
/// and a utility is provided to order geometry and/or geographies based on
/// the first coordinate. More sophisticated sorting (e.g., XZ2) may be added
/// in the future.
pub fn sd_order_udf() -> SedonaScalarUDF {
SedonaScalarUDF::new(
"sd_order",
vec![Arc::new(SDOrderDefault {})],
Volatility::Immutable,
Some(sd_order_doc()),
)
}
fn sd_order_doc() -> Documentation {
Documentation::builder(
DOC_SECTION_OTHER,
"Return an arbitrary value that may be used to sort the input.",
"SD_Order (value: Any)",
)
.with_argument("value", "Any: An arbitrary value")
.with_sql_example("SELECT SD_Order()")
.build()
}
/// Default implementation that returns its input (i.e., by default, just
/// do whatever DataFusion would have done with the value)
#[derive(Debug)]
struct SDOrderDefault {}
impl SedonaScalarKernel for SDOrderDefault {
fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
if args.len() != 1 {
return Ok(None);
}
Ok(Some(args[0].clone()))
}
fn invoke_batch(
&self,
_arg_types: &[SedonaType],
args: &[ColumnarValue],
) -> Result<ColumnarValue> {
Ok(args[0].clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use arrow_array::{create_array, ArrayRef};
use arrow_schema::DataType;
use datafusion_common::ScalarValue;
use datafusion_expr::ScalarUDF;
use rstest::rstest;
use sedona_schema::datatypes::SedonaType;
use sedona_testing::testers::ScalarUdfTester;
#[test]
fn udf_metadata() {
let udf: ScalarUDF = sd_order_udf().into();
assert_eq!(udf.name(), "sd_order");
assert!(udf.documentation().is_some())
}
#[rstest]
fn order_not_geometry(
#[values(
SedonaType::Arrow(DataType::Utf8),
SedonaType::Arrow(DataType::LargeUtf8)
)]
sedona_type: SedonaType,
) {
let udf = sd_order_udf();
let tester = ScalarUdfTester::new(udf.clone().into(), vec![sedona_type.clone()]);
tester.assert_return_type(sedona_type.clone());
tester.assert_scalar_result_equals("foofy", "foofy");
tester.assert_scalar_result_equals(ScalarValue::Null, ScalarValue::Null);
let array: ArrayRef = create_array!(Utf8, [Some("foofy"), None, Some("other foofy")]);
let array_casted = ColumnarValue::Array(array)
.cast_to(sedona_type.storage_type(), None)
.unwrap()
.to_array(3)
.unwrap();
let result = tester.invoke_array(array_casted.clone()).unwrap();
assert_eq!(&array_casted, &result);
}
}