spark Column 原理用法示例源码分析
文章目录
一、原理
Spark 的 Column
类是 Spark SQL 中用于表示列操作和表达式的核心类之一。它是一个不可变类,封装了对数据集中某一列的操作和转换。
Column
的实现原理主要依赖于 Spark SQL 的逻辑优化器和物理执行引擎。下面是 Column
类的几个关键特点和原理:
-
表达式树:
Column
实际上是一个表达式树,其中每个节点代表一个操作或转换。例如,col("age") + 10
将生成一个二叉表达式树,其中根节点表示加法操作,左子节点表示 “age” 列,右子节点表示常量值 10。 -
逻辑优化器:Spark SQL 的逻辑优化器会对
Column
表达式树进行优化。它可以进行常量折叠、谓词下推、投影消除等优化,以提高查询性能和降低计算开销。 -
Catalyst 编译器:Spark SQL 使用 Catalyst 编译器将
Column
表达式树转换为逻辑计划和物理计划。逻辑计划表示逻辑操作序列,而物理计划则表示如何在分布式环境下执行这些操作。 -
执行引擎:物理计划最终由 Spark 的执行引擎执行。执行引擎会将物理计划转换为一系列的 RDD 转换操作或 Spark SQL 内置的优化器,例如 Tungsten 等。这些操作最终被转换为 Spark 的执行图谱,并在分布式集群上执行。
通过以上的原理,Column
实现了对列的常见操作和转换,例如选择、过滤、聚合、排序、连接等。它提供了丰富的 API,使得用户可以方便地进行表达式计算和数据处理。
需要注意的是,Column
本身并不存储实际的数据,而只是表示对数据的操作和转换。它是一个惰性求值的结构,只有在真正需要结果时才会触发计算。
总结起来,Spark 的 Column
类通过表达式树、逻辑优化器、Catalyst 编译器和执行引擎实现了对列的操作和转换。它是 Spark SQL 中非常重要的组成部分,用于构建复杂的查询和数据处理任务。
二、用法总结
算术运算
*
:乘法运算。/
:除法运算。%
:取模运算。+
:加法运算。-
:减法运算。
关系运算
<
:小于比较。<=
:小于等于比较。>
:大于比较。>=
:大于等于比较。===
:相等比较。=!=
:不等比较。
逻辑运算
&&
:逻辑与运算。||
:逻辑或运算。!
:逻辑非运算。
类型转换
cast
:将列的数据类型转换为指定类型。as[U : Encoder]
:将列转换为具有指定类型的 TypedColumn。
字符串操作
startsWith
:判断字符串是否以指定前缀开头。endsWith
:判断字符串是否以指定后缀结尾。contains
:判断字符串是否包含指定子字符串。like
:使用通配符模式进行匹配。rlike
:使用正则表达式进行匹配。substr
:获取字符串的子串。
集合操作
array
:创建数组类型的字段。map
:创建映射类型的字段。getItem
:获取集合中指定索引位置的元素。isInCollection
:判断是否在给定的集合中。
排序和聚合
asc
:升序排序。desc
:降序排序。isNull
:判断是否为空。isNotNull
:判断是否不为空。over
:用于窗口函数。
其他操作
alias
:给列取别名。name
:给列取别名。otherwise
:当条件不满足时返回指定值。when
:根据条件选择值。
数值比较
eqNullSafe
:安全相等比较,处理空值情况。notEqual
:不等比较。
数据类型操作
binary
:创建二进制类型的字段。boolean
:创建布尔类型的字段。byte
:创建字节类型的字段。date
:创建日期类型的字段。decimal
:创建十进制类型的字段。double
:创建双精度浮点类型的字段。float
:创建单精度浮点类型的字段。int
:创建整型类型的字段。long
:创建长整型类型的字段。short
:创建短整型类型的字段。string
:创建字符串类型的字段。timestamp
:创建时间戳类型的字段。
列操作和别名
alias
:给列取别名。name
:给列取别名。getField
:获取嵌套结构中的字段。
窗口函数和排序
over
:用于窗口函数。asc_nulls_first
:升序排序,空值排在前面。asc_nulls_last
:升序排序,空值排在后面。desc_nulls_first
:降序排序,空值排在前面。desc_nulls_last
:降序排序,空值排在后面。
特殊操作
explain
:解释执行计划。isNaN
:判断是否为NaN。
数值操作
bitwiseAND
:按位与运算。bitwiseOR
:按位或运算。bitwiseXOR
:按位异或运算。unary_-
:取负运算。。
三、示例
package org.example.spark
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
import org.apache.spark.sql.{Column, Encoders, SparkSession, TypedColumn}
object ColumnMethodsDemo {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder()
.appName("ColumnMethodsDemo")
.master("local")
.getOrCreate()
import spark.implicits._
// 创建示例数据集
val data = Seq(
("Alice", 25, "New York"),
("Bob", 30, "San Francisco"),
("Charlie", 35, "London")
)
val df = data.toDF("name", "age", "city")
df.printSchema()
// 算术运算
val column1: Column = col("age") * 2
val column2: Column = col("age") + 10
val column3: Column = col("age") - 5
val column4: Column = col("age") / 3
val column5: Column = col("age") % 4
df.select(column1,column2,column3,column4,column5).show(truncate = false)
// +---------+----------+---------+------------------+---------+
// |(age * 2)|(age + 10)|(age - 5)|(age / 3) |(age % 4)|
// +---------+----------+---------+------------------+---------+
// |50 |35 |20 |8.333333333333334 |1 |
// |60 |40 |25 |10.0 |2 |
// |70 |45 |30 |11.666666666666666|3 |
// +---------+----------+---------+------------------+---------+
// 关系运算
val column6: Column = col("age") < 30
val column7: Column = col("age") <= 30
val column8: Column = col("age") > 30
val column9: Column = col("age") >= 30
val column10: Column = col("age") === 30
val column11: Column = col("age") =!= 30
val column35: Column = col("age").eqNullSafe(30)
val column36: Column = col("age").notEqual(30)
df.select(column6,column7,column8,column9,column10,column11,column35,column36).show(truncate = false)
// +----------+-----------+----------+-----------+----------+----------------+------------+----------------+
// |(age < 30)|(age <= 30)|(age > 30)|(age >= 30)|(age = 30)|(NOT (age = 30))|(age <=> 30)|(NOT (age = 30))|
// +----------+-----------+----------+-----------+----------+----------------+------------+----------------+
// |true |true |false |false |false |true |false |true |
// |false |true |false |true |true |false |true |false |
// |false |false |true |true |false |true |false |true |
// +----------+-----------+----------+-----------+----------+----------------+------------+----------------+
// 逻辑运算
val column12: Column = col("age")===10 && (col("city") === "New York")
val column13: Column = col("age")===10 || (col("city") === "New York")
val column14: Column = !( col("age") === 30)
df.select(column12,column13,column14).show(truncate = false)
// +----------------------------------+---------------------------------+----------------+
// |((age = 10) AND (city = New York))|((age = 10) OR (city = New York))|(NOT (age = 30))|
// +----------------------------------+---------------------------------+----------------+
// |false |true |true |
// |false |false |false |
// |false |false |true |
// +----------------------------------+---------------------------------+----------------+
// 类型转换
val column15: Column = col("age").cast("double")
// TypedColumn 类型操作
val column151: TypedColumn[String, Double] = col("age").as[Double]
// 编码器操作
val encoder = Encoders.DOUBLE
val column152 = col("age").as(encoder).alias("age2")
df.select(column15,column151,column152).printSchema()
df.select(column15,column151,column152).show(truncate = false)
// df.select("age").as[Double].show()
// df.select(col("age").as(encoder)).show()
// |-- age: double (nullable = false)
// |-- age: integer (nullable = false)
// |-- age2: integer (nullable = false)
//
// +----+---+----+
// |age |age|age2|
// +----+---+----+
// |25.0|25 |25 |
// |30.0|30 |30 |
// |35.0|35 |35 |
// +----+---+----+
// 字符串操作
val column16: Column = col("name").startsWith("A")
val column17: Column = col("name").endsWith("e")
val column18: Column = col("name").contains("li")
val column19: Column = col("name").like("A%")
val column20: Column = col("name").rlike("^A.*e$")
val column21: Column = col("name").substr(2, 3)
df.select(column16,column17,column18,column19, column20,column21 ).show(truncate = false)
// +-------------------+-----------------+------------------+------------+-----------------+---------------------+
// |startswith(name, A)|endswith(name, e)|contains(name, li)|name LIKE A%|name RLIKE ^A.*e$|substring(name, 2, 3)|
// +-------------------+-----------------+------------------+------------+-----------------+---------------------+
// |true |true |true |true |true |lic |
// |false |false |false |false |false |ob |
// |false |true |true |false |false |har |
// +-------------------+-----------------+------------------+------------+-----------------+---------------------+
// 集合操作
val column22: Column = array(col("age"), col("city"))
val column23: Column = map(col("name"), col("age"))
val column222: Column = map_from_arrays(column22,column22)
df.select(column22,column23,column222).show(truncate = false)
// +-------------------+---------------+---------------------------------------------------+
// |array(age, city) |map(name, age) |map_from_arrays(array(age, city), array(age, city))|
// +-------------------+---------------+---------------------------------------------------+
// |[25, New York] |[Alice -> 25] |[25 -> 25, New York -> New York] |
// |[30, San Francisco]|[Bob -> 30] |[30 -> 30, San Francisco -> San Francisco] |
// |[35, London] |[Charlie -> 35]|[35 -> 35, London -> London] |
// +-------------------+---------------+---------------------------------------------------+
// 排序
val column24: Column = col("age").asc
val column25: Column = col("age").desc
val column55: Column = col("age").asc_nulls_first
val column56: Column = col("age").asc_nulls_last
val column57: Column = col("age").desc_nulls_first
val column58: Column = col("age").desc_nulls_last
df.orderBy(column24).show()
df.orderBy(column25).show()
df.orderBy(column55).show()
df.orderBy(column56).show()
df.orderBy(column57).show()
df.orderBy(column58).show()
//
// +-------+---+-------------+
// | name|age| city|
// +-------+---+-------------+
// | Alice| 25| New York|
// | Bob| 30|San Francisco|
// |Charlie| 35| London|
// +-------+---+-------------+
//
// +-------+---+-------------+
// | name|age| city|
// +-------+---+-------------+
// |Charlie| 35| London|
// | Bob| 30|San Francisco|
// | Alice| 25| New York|
// +-------+---+-------------+
//
// +-------+---+-------------+
// | name|age| city|
// +-------+---+-------------+
// | Alice| 25| New York|
// | Bob| 30|San Francisco|
// |Charlie| 35| London|
// +-------+---+-------------+
//
// +-------+---+-------------+
// | name|age| city|
// +-------+---+-------------+
// | Alice| 25| New York|
// | Bob| 30|San Francisco|
// |Charlie| 35| London|
// +-------+---+-------------+
//
// +-------+---+-------------+
// | name|age| city|
// +-------+---+-------------+
// |Charlie| 35| London|
// | Bob| 30|San Francisco|
// | Alice| 25| New York|
// +-------+---+-------------+
//
// +-------+---+-------------+
// | name|age| city|
// +-------+---+-------------+
// |Charlie| 35| London|
// | Bob| 30|San Francisco|
// | Alice| 25| New York|
// +-------+---+-------------+
// when otherwise操作
val column30df = df.select( when(col("name") === "jess",0 ).otherwise(2).alias("whencol"))
column30df.show()
// +-------+
// |whencol|
// +-------+
// | 2|
// | 2|
// | 2|
// +-------+
// 数值操作
val column32: Column = col("age").bitwiseAND(5)
val column33: Column = col("age").bitwiseOR(10)
val column34: Column = -col("age")
df.select(column32,column33,column34).show()
// 集合操作
val column37: Column = column22.getItem(0) //column22 array(col("age"), col("city"))
val column38: Column = col("name").isInCollection(Seq("Alice", "Bob"))
val column39: Column = col("name").isin(Seq("Alice", "Bob"):_*)
df.select(column37,column38,column39).show()
// +-----------------------------------+----------------------+----------------------+
// |array(CAST(age AS STRING), city)[0]|(name IN (Alice, Bob))|(name IN (Alice, Bob))|
// +-----------------------------------+----------------------+----------------------+
// | 25| true| true|
// | 30| true| true|
// | 35| false| false|
// +-----------------------------------+----------------------+----------------------+
// 列操作和别名
val column51: Column = col("name").alias("full_name")
val column52: Column = col("name").name("full_name")
val column53: Column = struct("name","age").getField("age")
df.select(column51,column52,column53).show()
// +---------+---------+--------------------------------------+
// |full_name|full_name|named_struct(name, name, age, age).age|
// +---------+---------+--------------------------------------+
// | Alice| Alice| 25|
// | Bob| Bob| 30|
// | Charlie| Charlie| 35|
// +---------+---------+--------------------------------------+
// 窗口函数和排序
val column54: Column = sum(col("age")).over(Window.partitionBy("name").orderBy("city"))
val column28: Column = sum(col("age")).over()
df.select(column54,column28).show()
// +-----------------------------------------------------------------------------------+-----------------------------------+
// |sum(age) OVER (PARTITION BY name ORDER BY city ASC NULLS FIRST unspecifiedframe$())|sum(age) OVER (unspecifiedframe$())|
// +-----------------------------------------------------------------------------------+-----------------------------------+
// | 25| 90|
// | 30| 90|
// | 35| 90|
// +-----------------------------------------------------------------------------------+-----------------------------------+
// 特殊操作
val column60: Column = col("age").isNaN
val column26: Column = col("age").isNull
val column27: Column = col("age").isNotNull
df.select(column60,column26,column27).show()
// +----------+-------------+-----------------+
// |isnan(age)|(age IS NULL)|(age IS NOT NULL)|
// +----------+-------------+-----------------+
// | false| false| true|
// | false| false| true|
// | false| false| true|
// +----------+-------------+-----------------+
}
}
四、中文源码
private[sql] object Column {
// 创建一个Column对象,根据列名
def apply(colName: String): Column = new Column(colName)
// 创建一个Column对象,根据表达式
def apply(expr: Expression): Column = new Column(expr)
// 提取Column对象中的Expression对象
def unapply(col: Column): Option[Expression] = Some(col.expr)
// 生成Expression的别名
private[sql] def generateAlias(e: Expression): String = {
e match {
case a: AggregateExpression if a.aggregateFunction.isInstanceOf[TypedAggregateExpression] =>
a.aggregateFunction.toString
case expr => toPrettySQL(expr)
}
}
}
/**
* Column的泛型版本,用于指定输入类型和返回类型的Encoder。
*
* @tparam T 表达式的输入类型,如果表达式由分析器而不是编译器进行类型检查,则可以为`Any`(例如:`expr("sum(...)")`)
* @tparam U 列的输出类型
*
* @since 1.6.0
*/
@InterfaceStability.Stable
class TypedColumn[-T, U](
expr: Expression,
private[sql] val encoder: ExpressionEncoder[U])
extends Column(expr) {
// 插入特定的输入类型和模式到预期对解码对象操作的任何表达式中
private[sql] def withInputType(
inputEncoder: ExpressionEncoder[_],
inputAttributes: Seq[Attribute]): TypedColumn[T, U] = {
val unresolvedDeserializer = UnresolvedDeserializer(inputEncoder.deserializer, inputAttributes)
val newExpr = expr transform {
case ta: TypedAggregateExpression if ta.inputDeserializer.isEmpty =>
ta.withInputInfo(
deser = unresolvedDeserializer,
cls = inputEncoder.clsTag.runtimeClass,
schema = inputEncoder.schema)
}
new TypedColumn[T, U](newExpr, encoder)
}
// 给TypedColumn指定一个名称(别名)
override def name(alias: String): TypedColumn[T, U] =
new TypedColumn[T, U](super.name(alias).expr, encoder)
}
/**
* 在`DataFrame`中根据数据计算的列。
*
* 可以基于`DataFrame`中的输入列构建新的列:
*
* {{{
* df("columnName") // 在特定的`df` DataFrame 上
* col("columnName") // 一个尚未与任何DataFrame关联的通用列
* col("columnName.field") // 提取结构体字段
* col("`a.column.with.dots`") // 转义列名中的`.`
* $"columnName" // Scala中的命名列的简写方式
* }}}
*
* 可以组合[[Column]]对象来形成复杂的表达式:
*
* {{{
* $"a" + 1
* $"a" === $"b"
* }}}
*
* @note 内部Catalyst表达式可以通过[[expr]]访问,但此方法仅供调试目的,并且可能在任何Spark版本中更改。
*
* @since 1.3.0
*/
@InterfaceStability.Stable
class Column(val expr: Expression) extends Logging {
def this(name: String) = this(name match {
case "*" => UnresolvedStar(None)
case _ if name.endsWith(".*") =>
val parts = UnresolvedAttribute.parseAttributeName(name.substring(0, name.length - 2))
UnresolvedStar(Some(parts))
case _ => UnresolvedAttribute.quotedString(name)
})
// 返回表达式的字符串表示形式
override def toString: String = toPrettySQL(expr)
override def equals(that: Any): Boolean = that match {
case that: Column => that.expr.equals(this.expr)
case _ => false
}
override def hashCode: Int = this.expr.hashCode()
/** 根据给定的表达式创建一个列。 */
private def withExpr(newExpr: Expression): Column = new Column(newExpr)
/**
* 返回此列的表达式,具有现有名称或自动分配的名称。
*/
private[sql] def named: NamedExpression = expr match {
// 使用UnresolvedAlias包装UnresolvedAttribute,因为在解析UnresolvedAttribute时,我们将删除中间的Alias以进行ExtractValue链,并且我们需要再次对其进行别名以使其成为NamedExpression。
case u: UnresolvedAttribute => UnresolvedAlias(u)
case u: UnresolvedExtractValue => UnresolvedAlias(u)
case expr: NamedExpression => expr
// 留下未别名的生成器和空名称列表,因为在解析嵌套表达式类型后,分析器将生成正确的默认值。
case g: Generator => MultiAlias(g, Nil)
case func: UnresolvedFunction => UnresolvedAlias(func, Some(Column.generateAlias))
// 如果有顶级的Cast,有机会给它一个更好的别名,如果在此Cast下有NamedExpression。
case c: Cast =>
c.transformUp {
case c @ Cast(_: NamedExpression, _, _) => UnresolvedAlias(c)
} match {
case ne: NamedExpression => ne
case _ => Alias(expr, toPrettySQL(expr))()
}
case a: AggregateExpression if a.aggregateFunction.isInstanceOf[TypedAggregateExpression] =>
UnresolvedAlias(a, Some(Column.generateAlias))
// 等到结构体被解析。这将生成一个更漂亮的别名。
case struct: CreateNamedStructLike => UnresolvedAlias(struct)
case expr: Expression => Alias(expr, toPrettySQL(expr))()
}
/**
* 提供关于此列预期返回值的类型提示。此信息可用于在[[Dataset]]上执行`select`等操作,以自动将结果转换为正确的JVM类型。
* @since 1.6.0
*/
def as[U : Encoder]: TypedColumn[Any, U] = new TypedColumn[Any, U](expr, encoderFor[U])
/**
* 从复杂类型中提取一个值或多个值。
* 支持以下类型的提取:
* <ul>
* <li>对于Array,可以使用整数序数来检索单个值。</li>
* <li>对于Map,可以使用正确类型的键来检索单个值。</li>
* <li>对于Struct,可以使用字符串字段名来提取该字段。</li>
* <li>对于Array of Structs,可以使用字符串字段名来提取该数组中每个struct的字段,并返回一个字段的数组。</li>
* </ul>
* @group expr_ops
* @since 1.4.0
*/
def apply(extraction: Any): Column = withExpr {
UnresolvedExtractValue(expr, lit(extraction).expr)
}
/**
* 一元负号操作符,即取反表达式。
* {{{
* // Scala:选择amount列并对所有值取反。
* df.select( -df("amount") )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* df.select( negate(col("amount") );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def unary_- : Column = withExpr { UnaryMinus(expr) }
/**
* 取反布尔表达式,即NOT。
* {{{
* // Scala:选择非活动的行(isActive === false)
* df.filter( !df("isActive") )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* df.filter( not(df.col("isActive")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def unary_! : Column = withExpr { Not(expr) }
/**
* 相等性测试。
* {{{
* // Scala:
* df.filter( df("colA") === df("colB") )
*
* // Java
* import static org.apache.spark.sql.functions.*;
* df.filter( col("colA").equalTo(col("colB")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def === (other: Any): Column = withExpr {
val right = lit(other).expr
if (this.expr == right) {
logWarning(
s"Constructing trivially true equals predicate, '${this.expr} = $right'. " +
"Perhaps you need to use aliases.")
}
EqualTo(expr, right)
}
/**
* 相等性测试。
* {{{
* // Scala:
* df.filter( df("colA") === df("colB") )
*
* // Java
* import static org.apache.spark.sql.functions.*;
* df.filter( col("colA").equalTo(col("colB")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def equalTo(other: Any): Column = this === other
/**
* 不等性测试。
* {{{
* // Scala:
* df.select( df("colA") =!= df("colB") )
* df.select( !(df("colA") === df("colB")) )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* df.filter( col("colA").notEqual(col("colB")) );
* }}}
*
* @group expr_ops
* @since 2.0.0
*/
def =!= (other: Any): Column = withExpr{ Not(EqualTo(expr, lit(other).expr)) }
/**
* 不等性测试。
* {{{
* // Scala:
* df.select( df("colA") !== df("colB") )
* df.select( !(df("colA") === df("colB")) )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* df.filter( col("colA").notEqual(col("colB")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
@deprecated("!== does not have the same precedence as ===, use =!= instead", "2.0.0")
def !== (other: Any): Column = this =!= other
/**
* 不等性测试。
* {{{
* // Scala:
* df.select( df("colA") !== df("colB") )
* df.select( !(df("colA") === df("colB")) )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* df.filter( col("colA").notEqual(col("colB")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def notEqual(other: Any): Column = withExpr { Not(EqualTo(expr, lit(other).expr)) }
/**
* 大于。
* {{{
* // Scala:选择年龄大于21的人。
* people.select( people("age") > 21 )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* people.select( people.col("age").gt(21) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def > (other: Any): Column = withExpr { GreaterThan(expr, lit(other).expr) }
/**
* 大于。
* {{{
* // Scala:选择年龄大于21的人。
* people.select( people("age") > lit(21) )
*
* // Java:
* import static org.apache.spark.sql.functions.*;
* people.select( people.col("age").gt(21) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def gt(other: Any): Column = this > other
}
/**
* 小于运算符。
* {{{
* // Scala: 以下选择小于21岁的人。
* people.select( people("age") < 21 )
*
* // Java:
* people.select( people.col("age").lt(21) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def < (other: Any): Column = withExpr { LessThan(expr, lit(other).expr) }
/**
* 小于运算符。
* {{{
* // Scala: 以下选择小于21岁的人。
* people.select( people("age") < 21 )
*
* // Java:
* people.select( people.col("age").lt(21) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def lt(other: Any): Column = this < other
/**
* 小于等于运算符。
* {{{
* // Scala: 以下选择年龄为21或小于21岁的人。
* people.select( people("age") <= 21 )
*
* // Java:
* people.select( people.col("age").leq(21) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def <= (other: Any): Column = withExpr { LessThanOrEqual(expr, lit(other).expr) }
/**
* 小于等于运算符。
* {{{
* // Scala: 以下选择年龄为21或小于21岁的人。
* people.select( people("age") <= 21 )
*
* // Java:
* people.select( people.col("age").leq(21) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def leq(other: Any): Column = this <= other
/**
* 大于等于运算符。
* {{{
* // Scala: 以下选择年龄为21或大于21岁的人。
* people.select( people("age") >= 21 )
*
* // Java:
* people.select( people.col("age").geq(21) )
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def >= (other: Any): Column = withExpr { GreaterThanOrEqual(expr, lit(other).expr) }
/**
* 大于等于运算符。
* {{{
* // Scala: 以下选择年龄为21或大于21岁的人。
* people.select( people("age") >= 21 )
*
* // Java:
* people.select( people.col("age").geq(21) )
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def geq(other: Any): Column = this >= other
/**
* 安全的相等性测试,对空值也有效。
*
* @group expr_ops
* @since 1.3.0
*/
def <=> (other: Any): Column = withExpr {
val right = lit(other).expr
if (this.expr == right) {
logWarning(
s"构造了一个无意义的相等谓词,'${this.expr} <=> $right'。" +
"也许你需要使用别名。")
}
EqualNullSafe(expr, right)
}
/**
* 安全的相等性测试,对空值也有效。
*
* @group java_expr_ops
* @since 1.3.0
*/
def eqNullSafe(other: Any): Column = this <=> other
/**
* 根据条件选择不同的结果表达式。
*
* {{{
* // 示例:将性别字符串编码为整数。
*
* // Scala:
* people.select(when(people("gender") === "male", 0)
* .when(people("gender") === "female", 1)
* .otherwise(2))
*
* // Java:
* people.select(when(col("gender").equalTo("male"), 0)
* .when(col("gender").equalTo("female"), 1)
* .otherwise(2))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def when(condition: Column, value: Any): Column = this.expr match {
case CaseWhen(branches, None) =>
withExpr { CaseWhen(branches :+ ((condition.expr, lit(value).expr))) }
case CaseWhen(branches, Some(_)) =>
throw new IllegalArgumentException(
"当once()应用后,不能再应用when()")
case _ =>
throw new IllegalArgumentException(
"when()只能应用于之前由when()函数生成的Column对象")
}
/**
* 根据条件选择不同的结果表达式。
*
* {{{
* // 示例:将性别字符串编码为整数。
*
* // Scala:
* people.select(when(people("gender") === "male", 0)
* .when(people("gender") === "female", 1)
* .otherwise(2))
*
* // Java:
* people.select(when(col("gender").equalTo("male"), 0)
* .when(col("gender").equalTo("female"), 1)
* .otherwise(2))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def otherwise(value: Any): Column = this.expr match {
case CaseWhen(branches, None) =>
withExpr { CaseWhen(branches, Option(lit(value).expr)) }
case CaseWhen(branches, Some(_)) =>
throw new IllegalArgumentException(
"otherwise()只能应用于之前由when()函数生成的Column对象")
case _ =>
throw new IllegalArgumentException(
"otherwise()只能应用于之前由when()函数生成的Column对象")
}
/**
* 判断当前列是否在给定的上下界之间。
*
* @group java_expr_ops
* @since 1.4.0
*/
def between(lowerBound: Any, upperBound: Any): Column = {
(this >= lowerBound) && (this <= upperBound)
}
/**
* 判断当前表达式是否为NaN。
*
* @group expr_ops
* @since 1.5.0
*/
def isNaN: Column = withExpr { IsNaN(expr) }
/**
* 判断当前表达式是否为null。
*
* @group expr_ops
* @since 1.3.0
*/
def isNull: Column = withExpr { IsNull(expr) }
/**
* 判断当前表达式是否不为null。
*
* @group expr_ops
* @since 1.3.0
*/
def isNotNull: Column = withExpr { IsNotNull(expr) }
/**
* 逻辑或运算符。
* {{{
* // Scala: 以下选择在学校或就业的人。
* people.filter( people("inSchool") || people("isEmployed") )
*
* // Java:
* people.filter( people.col("inSchool").or(people.col("isEmployed")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def || (other: Any): Column = withExpr { Or(expr, lit(other).expr) }
/**
* 逻辑或运算符。
* {{{
* // Scala: 以下选择在学校或就业的人。
* people.filter( people("inSchool") || people("isEmployed") )
*
* // Java:
* people.filter( people.col("inSchool").or(people.col("isEmployed")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def or(other: Column): Column = this || other
/**
* 逻辑与运算符。
* {{{
* // Scala: 以下选择同时在学校和就业的人。
* people.select( people("inSchool") && people("isEmployed") )
*
* // Java:
* people.select( people.col("inSchool").and(people.col("isEmployed")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def && (other: Any): Column = withExpr { And(expr, lit(other).expr) }
/**
* 逻辑与运算符。
* {{{
* // Scala: 以下选择同时在学校和就业的人。
* people.select( people("inSchool") && people("isEmployed") )
*
* // Java:
* people.select( people.col("inSchool").and(people.col("isEmployed")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def and(other: Column): Column = this && other
/**
* 两个表达式的求和。
* {{{
* // Scala: 以下选择一个人身高和体重的总和。
* people.select( people("height") + people("weight") )
*
* // Java:
* people.select( people.col("height").plus(people.col("weight")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def + (other: Any): Column = withExpr { Add(expr, lit(other).expr) }
/**
* 两个表达式的求和。
* {{{
* // Scala: 以下选择一个人身高和体重的总和。
* people.select( people("height") + people("weight") )
*
* // Java:
* people.select( people.col("height").plus(people.col("weight")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def plus(other: Any): Column = this + other
/**
* 减法运算符。从当前表达式中减去另一个表达式。
* {{{
* // Scala: 以下选择人的身高与体重之差。
* people.select( people("height") - people("weight") )
*
* // Java:
* people.select( people.col("height").minus(people.col("weight")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def - (other: Any): Column = withExpr { Subtract(expr, lit(other).expr) }
/**
* 减法运算符。从当前表达式中减去另一个表达式。
* {{{
* // Scala: 以下选择人的身高与体重之差。
* people.select( people("height") - people("weight") )
*
* // Java:
* people.select( people.col("height").minus(people.col("weight")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def minus(other: Any): Column = this - other
/**
* 两个表达式的乘积。
* {{{
* // Scala: 以下选择一个人身高与体重的乘积。
* people.select( people("height") * people("weight") )
*
* // Java:
* people.select( people.col("height").multiply(people.col("weight")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def * (other: Any): Column = withExpr { Multiply(expr, lit(other).expr) }
/**
* 两个表达式的乘积。
* {{{
* // Scala: 以下选择一个人身高与体重的乘积。
* people.select( people("height") * people("weight") )
*
* // Java:
* people.select( people.col("height").multiply(people.col("weight")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def multiply(other: Any): Column = this * other
/**
* 除法运算符。将当前表达式除以另一个表达式。
* {{{
* // Scala: 以下选择一个人身高除以体重。
* people.select( people("height") / people("weight") )
*
* // Java:
* people.select( people.col("height").divide(people.col("weight")) );
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def / (other: Any): Column = withExpr { Divide(expr, lit(other).expr) }
/**
* 除法运算符。将当前表达式除以另一个表达式。
* {{{
* // Scala: 以下选择一个人身高除以体重。
* people.select( people("height") / people("weight") )
*
* // Java:
* people.select( people.col("height").divide(people.col("weight")) );
* }}}
*
* @group java_expr_ops
* @since 1.3.0
*/
def divide(other: Any): Column = this / other
/**
* 取模运算符(即求余数)。
*
* @group expr_ops
* @since 1.3.0
*/
def % (other: Any): Column = withExpr { Remainder(expr, lit(other).expr) }
/**
* 取模运算符(即求余数)。
*
* @group java_expr_ops
* @since 1.3.0
*/
def mod(other: Any): Column = this % other
/**
* 判断当前表达式的值是否在给定列表中。
*
* 注意:由于列表中元素的类型在运行时才能确定,因此元素将被“向上转型”为最常见的类型进行比较。
* 例如:
* 1)对于“Int vs String”情况,将“Int”向上转型为“String”,比较结果为“String vs String”。
* 2)对于“Float vs Double”情况,将“Float”向上转型为“Double”,比较结果为“Double vs Double”。
*
* @group expr_ops
* @since 1.5.0
*/
@scala.annotation.varargs
def isin(list: Any*): Column = withExpr { In(expr, list.map(lit(_).expr)) }
/**
* 判断当前表达式的值是否包含在提供的集合中。
*
* 注意:由于集合中元素的类型在运行时才能确定,因此元素将被“向上转型”为最常见的类型进行比较。
* 例如:
* 1)对于“Int vs String”情况,将“Int”向上转型为“String”,比较结果为“String vs String”。
* 2)对于“Float vs Double”情况,将“Float”向上转型为“Double”,比较结果为“Double vs Double”。
*
* @group expr_ops
* @since 2.4.0
*/
def isInCollection(values: scala.collection.Iterable[_]): Column = isin(values.toSeq: _*)
/**
* 判断当前表达式的值是否包含在提供的集合中。
*
* 注意:由于集合中元素的类型在运行时才能确定,因此元素将被“向上转型”为最常见的类型进行比较。
* 例如:
* 1)对于“Int vs String”情况,将“Int”向上转型为“String”,比较结果为“String vs String”。
* 2)对于“Float vs Double”情况,将“Float”向上转型为“Double”,比较结果为“Double vs Double”。
*
* @group java_expr_ops
* @since 2.4.0
*/
def isInCollection(values: java.lang.Iterable[_]): Column = isInCollection(values.asScala)
/**
* SQL的LIKE表达式。基于SQL LIKE匹配返回一个布尔列。
*
* @group expr_ops
* @since 1.3.0
*/
def like(literal: String): Column = withExpr { Like(expr, lit(literal).expr) }
/**
* SQL的RLIKE表达式(带有正则表达式的LIKE)。基于正则表达式匹配返回一个布尔列。
*
* @group expr_ops
* @since 1.3.0
*/
def rlike(literal: String): Column = withExpr { RLike(expr, lit(literal).expr) }
/**
* 从数组中获取指定位置的元素,或者从MapType中根据键获取值的表达式。
*
* @group expr_ops
* @since 1.3.0
*/
def getItem(key: Any): Column = withExpr { UnresolvedExtractValue(expr, Literal(key)) }
/**
* 从StructType中根据字段名获取字段的表达式。
*
* @group expr_ops
* @since 1.3.0
*/
def getField(fieldName: String): Column = withExpr {
UnresolvedExtractValue(expr, Literal(fieldName))
}
/**
* 返回子字符串的表达式。
* @param startPos 表示起始位置的表达式。
* @param len 表示子字符串长度的表达式。
*
* @group expr_ops
* @since 1.3.0
*/
def substr(startPos: Column, len: Column): Column = withExpr {
Substring(expr, startPos.expr, len.expr)
}
/**
* 返回子字符串的表达式。
* @param startPos 起始位置。
* @param len 子字符串长度。
*
* @group expr_ops
* @since 1.3.0
*/
def substr(startPos: Int, len: Int): Column = withExpr {
Substring(expr, lit(startPos).expr, lit(len).expr)
}
/**
* 判断当前表达式的值是否包含另一个元素。返回一个布尔列,基于字符串匹配。
*
* @group expr_ops
* @since 1.3.0
*/
def contains(other: Any): Column = withExpr { Contains(expr, lit(other).expr) }
/**
* 判断字符串是否以指定的列开始。返回一个布尔列,基于字符串匹配。
*
* @group expr_ops
* @since 1.3.0
*/
def startsWith(other: Column): Column = withExpr { StartsWith(expr, lit(other).expr) }
/**
* 判断字符串是否以指定的字符串字面量开始。返回一个布尔列,基于字符串匹配。
*
* @group expr_ops
* @since 1.3.0
*/
def startsWith(literal: String): Column = this.startsWith(lit(literal))
/**
* 判断字符串是否以指定的列结束。返回一个布尔列,基于字符串匹配。
*
* @group expr_ops
* @since 1.3.0
*/
def endsWith(other: Column): Column = withExpr { EndsWith(expr, lit(other).expr) }
/**
* 判断字符串是否以指定的字符串字面量结束。返回一个布尔列,基于字符串匹配。
*
* @group expr_ops
* @since 1.3.0
*/
def endsWith(literal: String): Column = this.endsWith(lit(literal))
/**
* 为列指定别名。与`as`方法相同。
* {{{
* // 将colA重命名为colB。
* df.select($"colA".alias("colB"))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def alias(alias: String): Column = name(alias)
/**
* 为列指定别名。
* {{{
* // 将colA重命名为colB。
* df.select($"colA".as("colB"))
* }}}
*
* 如果当前列具有元数据关联,则此元数据将传播到新列。如果不需要此功能,请使用具有显式空元数据的`as`方法。
*
* @group expr_ops
* @since 1.3.0
*/
def as(alias: String): Column = name(alias)
/**
* (仅Scala)为表生成函数的结果分配给定的别名。
* {{{
* // 将colA重命名为colB。
* df.select(explode($"myMap").as("key" :: "value" :: Nil))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def as(aliases: Seq[String]): Column = withExpr { MultiAlias(expr, aliases) }
/**
* 为表生成函数的结果分配给定的别名。
* {{{
* // 将colA重命名为colB。
* df.select(explode($"myMap").as("key" :: "value" :: Nil))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def as(aliases: Array[String]): Column = withExpr { MultiAlias(expr, aliases) }
/**
* 为列指定别名。
* {{{
* // 将colA重命名为colB。
* df.select($"colA".as('colB))
* }}}
*
* 如果当前列具有元数据关联,则此元数据将传播到新列。如果不需要此功能,请使用具有显式空元数据的`as`方法。
*
* @group expr_ops
* @since 1.3.0
*/
def as(alias: Symbol): Column = name(alias.name)
/**
* 为列指定带有元数据的别名。
* {{{
* val metadata: Metadata = ...
* df.select($"colA".as("colB", metadata))
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def as(alias: String, metadata: Metadata): Column = withExpr {
Alias(expr, alias)(explicitMetadata = Some(metadata))
}
/**
* 给列一个名称(别名)。
* {{{
* // 将colA重命名为colB。
* df.select($"colA".name("colB"))
* }}}
*
* 如果当前列具有元数据关联,则此元数据将传播到新列。如果不需要此功能,请使用具有显式空元数据的`as`方法。
*
* @group expr_ops
* @since 2.0.0
*/
def name(alias: String): Column = withExpr {
expr match {
case ne: NamedExpression => Alias(expr, alias)(explicitMetadata = Some(ne.metadata))
case other => Alias(other, alias)()
}
}
/**
* 将列转换为不同的数据类型。
* {{{
* // 将colA转换为IntegerType。
* import org.apache.spark.sql.types.IntegerType
* df.select(df("colA").cast(IntegerType))
*
* // 等价于
* df.select(df("colA").cast("int"))
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def cast(to: DataType): Column = withExpr { Cast(expr, to) }
/**
* 将列转换为不同的数据类型,使用类型的规范字符串表示。
* 支持的类型有:"string"、"boolean"、"byte"、"short"、"int"、"long"、"float"、"double"、"decimal"、"date"、"timestamp"。
* {{{
* // 将colA转换为整数。
* df.select(df("colA").cast("int"))
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def cast(to: String): Column = cast(CatalystSqlParser.parseDataType(to))
/**
* 返回一个基于列的降序排序表达式。
* {{{
* // Scala
* df.sort(df("age").desc)
*
* // Java
* df.sort(df.col("age").desc());
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def desc: Column = withExpr { SortOrder(expr, Descending) }
/**
* 返回一个基于列的降序排序表达式,并且null值出现在非null值之前。
* {{{
* // Scala:按照age列降序排序,并且null值出现在最前面。
* df.sort(df("age").desc_nulls_first)
*
* // Java
* df.sort(df.col("age").desc_nulls_first());
* }}}
*
* @group expr_ops
* @since 2.1.0
*/
def desc_nulls_first: Column = withExpr { SortOrder(expr, Descending, NullsFirst, Set.empty) }
/**
* 返回一个基于列的降序排序表达式,并且null值出现在非null值之后。
* {{{
* // Scala:按照age列降序排序,并且null值出现在最后面。
* df.sort(df("age").desc_nulls_last)
*
* // Java
* df.sort(df.col("age").desc_nulls_last());
* }}}
*
* @group expr_ops
* @since 2.1.0
*/
def desc_nulls_last: Column = withExpr { SortOrder(expr, Descending, NullsLast, Set.empty) }
/**
* 返回一个基于列的升序排序表达式。
* {{{
* // Scala:按照age列升序排序。
* df.sort(df("age").asc)
*
* // Java
* df.sort(df.col("age").asc());
* }}}
*
* @group expr_ops
* @since 1.3.0
*/
def asc: Column = withExpr { SortOrder(expr, Ascending) }
/**
* 返回一个基于列的升序排序表达式,并且null值出现在非null值之前。
* {{{
* // Scala:按照age列升序排序,并且null值出现在最前面。
* df.sort(df("age").asc_nulls_first)
*
* // Java
* df.sort(df.col("age").asc_nulls_first());
* }}}
*
* @group expr_ops
* @since 2.1.0
*/
def asc_nulls_first: Column = withExpr { SortOrder(expr, Ascending, NullsFirst, Set.empty) }
/**
* 返回一个基于列的升序排序表达式,并且null值出现在非null值之后。
* {{{
* // Scala:按照age列升序排序,并且null值出现在最后面。
* df.sort(df("age").asc_nulls_last)
*
* // Java
* df.sort(df.col("age").asc_nulls_last());
* }}}
*
* @group expr_ops
* @since 2.1.0
*/
def asc_nulls_last: Column = withExpr { SortOrder(expr, Ascending, NullsLast, Set.empty) }
/**
* 将表达式打印到控制台以进行调试。
*
* @group df_ops
* @since 1.3.0
*/
def explain(extended: Boolean): Unit = {
// scalastyle:off println
if (extended) {
println(expr)
} else {
println(expr.sql)
}
// scalastyle:on println
}
/**
* 计算当前表达式与另一个表达式的按位或。
* {{{
* df.select($"colA".bitwiseOR($"colB"))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def bitwiseOR(other: Any): Column = withExpr { BitwiseOr(expr, lit(other).expr) }
/**
* 计算当前表达式与另一个表达式的按位与。
* {{{
* df.select($"colA".bitwiseAND($"colB"))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def bitwiseAND(other: Any): Column = withExpr { BitwiseAnd(expr, lit(other).expr) }
/**
* 计算当前表达式与另一个表达式的按位异或。
* {{{
* df.select($"colA".bitwiseXOR($"colB"))
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def bitwiseXOR(other: Any): Column = withExpr { BitwiseXor(expr, lit(other).expr) }
/**
* 定义一个窗口列。
*
* {{{
* val w = Window.partitionBy("name").orderBy("id")
* df.select(
* sum("price").over(w.rangeBetween(Window.unboundedPreceding, 2)),
* avg("price").over(w.rowsBetween(Window.currentRow, 4))
* )
* }}}
*
* @group expr_ops
* @since 1.4.0
*/
def over(window: expressions.WindowSpec): Column = window.withAggregate(this)
/**
* 定义一个空的分析子句。在这种情况下,分析函数将应用并呈现给结果集中的所有行。
*
* {{{
* df.select(
* sum("price").over(),
* avg("price").over()
* )
* }}}
*
* @group expr_ops
* @since 2.0.0
*/
def over(): Column = over(Window.spec)
}
/**
* 用于构建模式的方便类。
*
* @since 1.3.0
*/
@InterfaceStability.Stable
class ColumnName(name: String) extends Column(name) {
/**
* 创建一个新的布尔类型的`StructField`。
* @since 1.3.0
*/
def boolean: StructField = StructField(name, BooleanType)
/**
* 创建一个新的字节类型的`StructField`。
* @since 1.3.0
*/
def byte: StructField = StructField(name, ByteType)
/**
* 创建一个新的短整型类型的`StructField`。
* @since 1.3.0
*/
def short: StructField = StructField(name, ShortType)
/**
* 创建一个新的整型类型的`StructField`。
* @since 1.3.0
*/
def int: StructField = StructField(name, IntegerType)
/**
* 创建一个新的长整型类型的`StructField`。
* @since 1.3.0
*/
def long: StructField = StructField(name, LongType)
/**
* 创建一个新的单精度浮点型类型的`StructField`。
* @since 1.3.0
*/
def float: StructField = StructField(name, FloatType)
/**
* 创建一个新的双精度浮点型类型的`StructField`。
* @since 1.3.0
*/
def double: StructField = StructField(name, DoubleType)
/**
* 创建一个新的字符串类型的`StructField`。
* @since 1.3.0
*/
def string: StructField = StructField(name, StringType)
/**
* 创建一个新的日期类型的`StructField`。
* @since 1.3.0
*/
def date: StructField = StructField(name, DateType)
/**
* 创建一个新的十进制类型的`StructField`。
* @since 1.3.0
*/
def decimal: StructField = StructField(name, DecimalType.USER_DEFAULT)
/**
* 创建一个新的指定精度和小数位数的十进制类型的`StructField`。
* @since 1.3.0
*/
def decimal(precision: Int, scale: Int): StructField =
StructField(name, DecimalType(precision, scale))
/**
* 创建一个新的时间戳类型的`StructField`。
* @since 1.3.0
*/
def timestamp: StructField = StructField(name, TimestampType)
/**
* 创建一个新的二进制类型的`StructField`。
* @since 1.3.0
*/
def binary: StructField = StructField(name, BinaryType)
/**
* 创建一个新的数组类型的`StructField`。
* @since 1.3.0
*/
def array(dataType: DataType): StructField = StructField(name, ArrayType(dataType))
/**
* 创建一个新的映射类型的`StructField`。
* @since 1.3.0
*/
def map(keyType: DataType, valueType: DataType): StructField =
map(MapType(keyType, valueType))
def map(mapType: MapType): StructField = StructField(name, mapType)
/**
* 创建一个新的结构体类型的`StructField`。
* @since 1.3.0
*/
def struct(fields: StructField*): StructField = struct(StructType(fields))
/**
* 创建一个新的指定结构体类型的`StructField`。
* @since 1.3.0
*/
def struct(structType: StructType): StructField = StructField(name, structType)
}