我需要允许用户定义不同的命名集合,他们可以在稍后的Spark DataFrame SQL构建中使用它们。
我计划为此目的使用Spark广播变量,但基于以下SO问题How to refer broadcast variable in Spark DataFrameSQL,看来这是不可能的
比方说,作为用户,我已经通过应用程序用户界面创建了以下集合:
name: countries_dict
values: Seq("Italy", "France", "United States", "Poland", "Spain")
在另一个应用程序用户界面(今天与其他页面不同)中,我创建了以下Spark SQL查询:
SELECT name, phone, country FROM users
,我想按SELECT name, phone, country FROM users WHERE countries in countries_dict
例如,现在,我可以通过以下方式创建类似的内容:
val countriesDict = Seq("Italy", "France", "United States", "Poland", "Spain")
val inDict = (s: String) => {
countriesDict.contains(s)
}
spark.udf.register("in_dict", inDict)
然后:
SELECT name, phone, country FROM users WHERE in_dict(country)
但是这种方法的最大问题是countriesDict
被硬编码在代码中,而不是根据UI上的用户输入动态创建的。
是否可以通过某种方式扩展此方法,以通过应用程序UI支持由用户动态创建的具有名称和元素的集合?
答案 0 :(得分:1)
我当然不知道您的应用程序的UI等,但是有什么反对将集合转换为数据帧的说法吗?当然,您不能使用WHERE countries in countries_dict
语法,但必须使用联接。
但是当联接的数据帧低于某个阈值时,Spark将自动以广播形式执行联接。如Mastering Apache Spark
您只需要一些存储空间,用户就可以在其中存储这些小型数据帧的内容,例如作为CSV文件。
答案 1 :(得分:1)
在这里使用广播变量实际上没有任何意义。即使不理会结构问题,调用udf的成本也可能会超过广播的收益(尤其是在结构如此小的情况下)。
如果数据很小(请使用您喜欢的SQL处理库以避免SQL注入的风险),则可以内联查询:
SELECT name, phone, country FROM users
WHERE country IN ('Italy', 'France', 'United States', 'Poland', 'Spain')
或仅将输入转换为DataFrame
:
countriesDict.toDF("country").createOrReplaceTempView("countries")
并使用ANTI JOIN
(如果数据足够小,则根据广播阈值自动将其升级为广播连接)
SELECT *
FROM users LEFT ANTI JOIN countries
ON users.country = countries.country
或带有明确的广播提示
SELECT /*+ MAPJOIN(countries) */ *
FROM users LEFT ANTI JOIN countries
ON users.country = countries.country
最后,您可以跳过SQL部分,并通过DataFrame
使用isin
API:
spark.table("users").where($"country" isin (countriesDict: _*))
或者如果您确实有需要UDF的逻辑:
import org.apache.spark.sql.functions.typedLit
val f = udf((x: String, xs: Seq[String]) => { xs.contains(x) })
spark.table("users").where(f($"country", typedLit(countriesDict)))