Skip to content

Commit 99e2cd4

Browse files
alambizveigor
andauthored
Add Expr::field, Expr::index, and Expr::slice, add docs (#7218)
* Add Expr::field, Expr::index, and Expr::slice`, add docs * tweak * Apply suggestions from code review Co-authored-by: Igor Izvekov <[email protected]> --------- Co-authored-by: Igor Izvekov <[email protected]>
1 parent d1361d5 commit 99e2cd4

File tree

2 files changed

+96
-16
lines changed

2 files changed

+96
-16
lines changed

datafusion/expr/src/expr.rs

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ pub enum Expr {
115115
IsNotUnknown(Box<Expr>),
116116
/// arithmetic negation of an expression, the operand must be of a signed numeric data type
117117
Negative(Box<Expr>),
118-
/// Returns the field of a [`arrow::array::ListArray`] or [`arrow::array::StructArray`] by key
119-
///
118+
/// Returns the field of a [`arrow::array::ListArray`] or
119+
/// [`arrow::array::StructArray`] by index or range
120120
GetIndexedField(GetIndexedField),
121121
/// Whether an expression is between a given range.
122122
Between(Between),
@@ -359,19 +359,20 @@ impl ScalarUDF {
359359
}
360360
}
361361

362+
/// Access a sub field of a nested type, such as `Field` or `List`
362363
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
363364
pub enum GetFieldAccess {
364-
/// returns the field `struct[field]`. For example `struct["name"]`
365+
/// Named field, for example `struct["name"]`
365366
NamedStructField { name: ScalarValue },
366-
/// single list index
367-
// list[i]
367+
/// Single list index, for example: `list[i]`
368368
ListIndex { key: Box<Expr> },
369-
/// list range `list[i:j]`
369+
/// List range, for example `list[i:j]`
370370
ListRange { start: Box<Expr>, stop: Box<Expr> },
371371
}
372372

373-
/// Returns the field of a [`arrow::array::ListArray`] or [`arrow::array::StructArray`] by `key`.
374-
/// If `extra_key` is not `None`, returns the slice of a [`arrow::array::ListArray`] in the range from `key` to `extra_key`.
373+
/// Returns the field of a [`arrow::array::ListArray`] or
374+
/// [`arrow::array::StructArray`] by `key`. See [`GetFieldAccess`] for
375+
/// details.
375376
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
376377
pub struct GetIndexedField {
377378
/// The expression to take the field from
@@ -925,6 +926,90 @@ impl Expr {
925926
))
926927
}
927928

929+
/// Return access to the named field. Example `expr["name"]`
930+
///
931+
/// ## Access field "my_field" from column "c1"
932+
///
933+
/// For example if column "c1" holds documents like this
934+
///
935+
/// ```json
936+
/// {
937+
/// "my_field": 123.34,
938+
/// "other_field": "Boston",
939+
/// }
940+
/// ```
941+
///
942+
/// You can access column "my_field" with
943+
///
944+
/// ```
945+
/// # use datafusion_expr::{col};
946+
/// let expr = col("c1")
947+
/// .field("my_field");
948+
/// assert_eq!(expr.display_name().unwrap(), "c1[my_field]");
949+
/// ```
950+
pub fn field(self, name: impl Into<String>) -> Self {
951+
Expr::GetIndexedField(GetIndexedField {
952+
expr: Box::new(self),
953+
field: GetFieldAccess::NamedStructField {
954+
name: ScalarValue::Utf8(Some(name.into())),
955+
},
956+
})
957+
}
958+
959+
/// Return access to the element field. Example `expr["name"]`
960+
///
961+
/// ## Example Access element 2 from column "c1"
962+
///
963+
/// For example if column "c1" holds documents like this
964+
///
965+
/// ```json
966+
/// [10, 20, 30, 40]
967+
/// ```
968+
///
969+
/// You can access the value "30" with
970+
///
971+
/// ```
972+
/// # use datafusion_expr::{lit, col, Expr};
973+
/// let expr = col("c1")
974+
/// .index(lit(3));
975+
/// assert_eq!(expr.display_name().unwrap(), "c1[Int32(3)]");
976+
/// ```
977+
pub fn index(self, key: Expr) -> Self {
978+
Expr::GetIndexedField(GetIndexedField {
979+
expr: Box::new(self),
980+
field: GetFieldAccess::ListIndex { key: Box::new(key) },
981+
})
982+
}
983+
984+
/// Return elements between `1` based `start` and `stop`, for
985+
/// example `expr[1:3]`
986+
///
987+
/// ## Example: Access element 2, 3, 4 from column "c1"
988+
///
989+
/// For example if column "c1" holds documents like this
990+
///
991+
/// ```json
992+
/// [10, 20, 30, 40]
993+
/// ```
994+
///
995+
/// You can access the value `[20, 30, 40]` with
996+
///
997+
/// ```
998+
/// # use datafusion_expr::{lit, col};
999+
/// let expr = col("c1")
1000+
/// .range(lit(2), lit(4));
1001+
/// assert_eq!(expr.display_name().unwrap(), "c1[Int32(2):Int32(4)]");
1002+
/// ```
1003+
pub fn range(self, start: Expr, stop: Expr) -> Self {
1004+
Expr::GetIndexedField(GetIndexedField {
1005+
expr: Box::new(self),
1006+
field: GetFieldAccess::ListRange {
1007+
start: Box::new(start),
1008+
stop: Box::new(stop),
1009+
},
1010+
})
1011+
}
1012+
9281013
pub fn try_into_col(&self) -> Result<Column> {
9291014
match self {
9301015
Expr::Column(it) => Ok(it.clone()),

datafusion/sql/src/expr/identifier.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717

1818
use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
1919
use datafusion_common::{
20-
Column, DFField, DFSchema, DataFusionError, Result, ScalarValue, TableReference,
20+
Column, DFField, DFSchema, DataFusionError, Result, TableReference,
2121
};
22-
use datafusion_expr::{Case, Expr, GetFieldAccess, GetIndexedField};
22+
use datafusion_expr::{Case, Expr};
2323
use sqlparser::ast::{Expr as SQLExpr, Ident};
2424

2525
impl<'a, S: ContextProvider> SqlToRel<'a, S> {
@@ -136,12 +136,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
136136
)));
137137
}
138138
let nested_name = nested_names[0].to_string();
139-
Ok(Expr::GetIndexedField(GetIndexedField::new(
140-
Box::new(Expr::Column(field.qualified_column())),
141-
GetFieldAccess::NamedStructField {
142-
name: ScalarValue::Utf8(Some(nested_name)),
143-
},
144-
)))
139+
Ok(Expr::Column(field.qualified_column()).field(nested_name))
145140
}
146141
// found matching field with no spare identifier(s)
147142
Some((field, _nested_names)) => {

0 commit comments

Comments
 (0)