From 3a1294029ea931209224ecd17bd1c384c80cd6e5 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sun, 15 Jan 2023 09:48:20 +0000 Subject: [PATCH] Add SQL `array::all()`, `array::any()`, `array::min()`, and `array::max()` functions Closes #1588 --- lib/src/fnc/array.rs | 28 +++++++++++ lib/src/fnc/mod.rs | 4 ++ lib/src/sql/function.rs | 4 ++ lib/tests/function.rs | 108 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) diff --git a/lib/src/fnc/array.rs b/lib/src/fnc/array.rs index 533827d7..5831a871 100644 --- a/lib/src/fnc/array.rs +++ b/lib/src/fnc/array.rs @@ -9,6 +9,20 @@ use crate::sql::array::Union; use crate::sql::array::Uniq; use crate::sql::value::Value; +pub fn all((arg,): (Value,)) -> Result { + match arg { + Value::Array(v) => Ok(v.iter().all(Value::is_truthy).into()), + _ => Ok(Value::False), + } +} + +pub fn any((arg,): (Value,)) -> Result { + match arg { + Value::Array(v) => Ok(v.iter().any(Value::is_truthy).into()), + _ => Ok(Value::False), + } +} + pub fn combine(arrays: (Value, Value)) -> Result { Ok(match arrays { (Value::Array(v), Value::Array(w)) => v.combine(w).into(), @@ -96,6 +110,20 @@ pub fn len((arg,): (Value,)) -> Result { } } +pub fn max((arg,): (Value,)) -> Result { + match arg { + Value::Array(v) => Ok(v.into_iter().max().unwrap_or(Value::None)), + _ => Ok(Value::None), + } +} + +pub fn min((arg,): (Value,)) -> Result { + match arg { + Value::Array(v) => Ok(v.into_iter().min().unwrap_or(Value::None)), + _ => Ok(Value::None), + } +} + pub fn sort((array, order): (Value, Option)) -> Result { match array { Value::Array(mut v) => match order { diff --git a/lib/src/fnc/mod.rs b/lib/src/fnc/mod.rs index b7cf3481..aceb261f 100644 --- a/lib/src/fnc/mod.rs +++ b/lib/src/fnc/mod.rs @@ -60,6 +60,8 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec) -> Result array::all, + "array::any" => array::any, "array::combine" => array::combine, "array::complement" => array::complement, "array::concat" => array::concat, @@ -70,6 +72,8 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec) -> Result array::insert, "array::intersect" => array::intersect, "array::len" => array::len, + "array::max" => array::max, + "array::min" => array::min, "array::sort" => array::sort, "array::union" => array::union, "array::sort::asc" => array::sort::asc, diff --git a/lib/src/sql/function.rs b/lib/src/sql/function.rs index f5f84326..d79d8deb 100644 --- a/lib/src/sql/function.rs +++ b/lib/src/sql/function.rs @@ -234,6 +234,8 @@ fn function_names(i: &str) -> IResult<&str, &str> { fn function_array(i: &str) -> IResult<&str, &str> { alt(( + tag("array::all"), + tag("array::any"), tag("array::combine"), tag("array::complement"), tag("array::concat"), @@ -244,6 +246,8 @@ fn function_array(i: &str) -> IResult<&str, &str> { tag("array::insert"), tag("array::intersect"), tag("array::len"), + tag("array::max"), + tag("array::min"), tag("array::sort::asc"), tag("array::sort::desc"), tag("array::sort"), diff --git a/lib/tests/function.rs b/lib/tests/function.rs index b70e2c29..a3db52a5 100644 --- a/lib/tests/function.rs +++ b/lib/tests/function.rs @@ -5,6 +5,60 @@ use surrealdb::err::Error; use surrealdb::kvs::Datastore; use surrealdb::sql::Value; +#[tokio::test] +async fn function_array_all() -> Result<(), Error> { + let sql = r#" + RETURN array::all([]); + RETURN array::all("some text"); + RETURN array::all([1,2,"text",3,NONE,3,4]); + "#; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result?; + let val = Value::True; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::None; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::False; + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn function_array_any() -> Result<(), Error> { + let sql = r#" + RETURN array::any([]); + RETURN array::any("some text"); + RETURN array::any([1,2,"text",3,NONE,3,4]); + "#; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result?; + let val = Value::True; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::None; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::True; + assert_eq!(tmp, val); + // + Ok(()) +} + #[tokio::test] async fn function_array_combine() -> Result<(), Error> { let sql = r#" @@ -253,6 +307,60 @@ async fn function_array_intersect() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn function_array_max() -> Result<(), Error> { + let sql = r#" + RETURN array::max([]); + RETURN array::max("some text"); + RETURN array::max([1,2,"text",3,3,4]); + "#; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result?; + let val = Value::None; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::None; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("4"); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn function_array_min() -> Result<(), Error> { + let sql = r#" + RETURN array::min([]); + RETURN array::min("some text"); + RETURN array::min([1,2,"text",3,3,4]); + "#; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result?; + let val = Value::None; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::None; + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("1"); + assert_eq!(tmp, val); + // + Ok(()) +} + #[tokio::test] async fn function_array_union() -> Result<(), Error> { let sql = r#"