Implement Spark-compatible `map_zip_with` (higher-order function over two maps)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 35/100
調査の方向性
Start in crates/spark/src/function/lambda/mod.rs, currently an empty ScalarUDF placeholder, and see how other functions register with register_higher_order_function / SessionStateBuilder::higher_order_functions. Read DataFusion's HigherOrderUDFImpl in datafusion/expr/src/higher_order_function.rs and LambdaArgument::evaluate/spread_captures, then model the behavior on Spark's MapZipWith in higherOrderFunctions.scala. Reuse the helpers in crates/spark/src/function/map/utils.rs for MapArray rebuilding, and add crates/sqllogictest/test_files/lambda/map_zip_with.slt; done means the examples and edge cases in the issue pass.
索引モデルが issue の本文から書いたものです。
説明
Part of #176 and apache/datafusion-comet#7300.
What the function does
map_zip_with(map1, map2, func) merges two maps into one by calling func(k, v1, v2) for every key in the union of
their keys. For a key present in only one map, NULL is passed for the missing side. If an input map contains
duplicated keys, only the first entry of each key is used. The result type is
MAP<keyType, func.dataType> with valueContainsNull = func.nullable. The result is NULL if either map is NULL.
Spark evaluates map1 first and skips map2 if map1 is NULL. The lambda parameters are k (non-null) and v1,
v2 (always nullable). Non-lambda func arguments are wrapped in a hidden lambda. Available since Spark 3.0.0. No
aliases.
Key types. If the two key types differ, the MapZipWithCoercion rule casts both maps to the wider key type
(findWiderTypeForTwo), unless the cast would force nullability. If no common type exists, analysis fails with
DATATYPE_MISMATCH.MAP_ZIP_WITH_DIFF_TYPES, for example INT against MAP<INT, INT>. The key type must be
orderable.
Output order. Keys from map1 come first in their original order, followed by keys that appear only in map2, in
map2 order. Spark builds a LinkedHashMap for key types with proper equality, and does a linear search with
interpreted ordering otherwise (binary, struct and array keys). No dedup policy is applied to the result.
Examples
SELECT map_zip_with(map(1, 'a', 2, 'b'), map(1, 'x', 2, 'y'), (k, v1, v2) -> concat(v1, v2));
-- {1:"ax",2:"by"}
SELECT map_zip_with(map('a', 1, 'b', 2), map('b', 3, 'c', 4), (k, v1, v2) -> coalesce(v1, 0) + coalesce(v2, 0));
-- {"a":1,"b":5,"c":4}
SELECT map_zip_with(map('a', 1), map('a', 10), 100);
-- {"a":100}
SELECT map_zip_with(map('a', 1), map('a', 10), struct('left', 1, 'right', 10));
-- {"a":{"col1":"left","col2":1,"col3":"right","col4":10}}
These come from the ExpressionDescription and higher-order-functions.sql.out. HigherOrderFunctionsSuite, with
(k, v1, v2) -> k * v1 * v2, adds:
| map1 | map2 | result |
|---|---|---|
{1:10, 2:20, 3:30} |
{1:-1, 2:-2, 4:-4} |
{1:-10, 2:-80, 3:null, 4:null} |
{1:10, 2:20, 3:30} |
{1:null, 2:-2, 3:null} |
{1:null, 2:-80, 3:null} |
{1:10, 2:20, 3:30} |
{} |
{1:null, 2:null, 3:null} |
{1:10, 2:20, 3:30} |
NULL | NULL |
With concat(k, v1, v2) on strings, {"d":"b","b":"d"} and {"c":null,"b":"t","a":null} give
{"d":null,"b":"bdt","c":null,"a":null}. The same test also covers binary keys, which use the linear-search path.
Edge cases to test
- Either map NULL gives NULL. Both maps empty gives
{}. One map empty gives every key of the other, with NULL for
the missing side. - Key-only-in-one-side handling, NULL values that are actually present (indistinguishable from missing values inside
the lambda), and the output order described above. - Duplicate keys inside an input map. Only the first occurrence is used, and later occurrences are ignored rather
than raisingDUPLICATED_MAP_KEY. Normal map construction forbids duplicates, but Arrow input can contain them. - Floating-point keys. With
spark.sql.mapZipWithUsesJavaCollections=true(internal, default true, 4.1+), keys are
normalized withNormalizeFloatingNumbers, soNaNmatchesNaNand-0.0matches0.0. 3.4 through 4.0 use a
ScalaLinkedHashMapwith no normalization, and the config's doc says the Java path exists to avoid "NaN equality
issues". TestNaNand-0.0keys and expect the older versions to differ, at least forNaN. - Key type coercion:
map(1, ...)(INT) againstmap(1L, ...)(BIGINT) gives BIGINT keys. Also test incompatible key
types (MAP_ZIP_WITH_DIFF_TYPES) and non-orderable keys. - Different value types on the two sides, for example
MAP<STRING, INT>andMAP<STRING, STRING>. - Lambda arity: exactly 3 parameters.
(x, y) -> ...fails withINVALID_LAMBDA_FUNCTION_CALL.NUM_ARGS_MISMATCH. - Captured outer columns, nondeterministic bodies (once per output key), and ANSI errors inside the body.
- Very large maps:
mapSizeExceedArraySizeWhenZipMapErroron the brute-force path.
Differences across Spark versions
- 4.1.0 adds
spark.sql.mapZipWithUsesJavaCollections(internal, defaulttrue). The fast path then uses a Java
LinkedHashMapwith float/double key normalization, which fixesNaNequality. 3.4, 3.5 and 4.0 use a Scala
LinkedHashMapwithout normalization. - 4.0 changed the key-type comparison to
DataTypeUtils.sameType. That is an internal refactor.
Implementation notes
- Spark:
MapZipWithinsql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/higherOrderFunctions.scala
(getKeysWithIndexesFastAsJava,getKeysWithIndexesFastUsingScala,getKeysWithIndexesBruteForce). Coercion
isMapZipWithTypeCoercioninsql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercionHelper.scala. - Infrastructure. DataFusion's higher-order function framework (
HigherOrderUDFImplin
datafusion/expr/src/higher_order_function.rs, andLambdaArgument::evaluatewithspread_capturesfor captured
columns) is onbranch-56, which this crate uses.lambda_parametersshould offer
[key (non-null), v1 (nullable), v2 (nullable)]. Key coercion has to happen incoerce_value_types. - DataFusion core has no equivalent. Implementation outline:
- For each row, compute the key union and two index vectors into the value arrays, with NULL for a missing key.
Usearrow_rowor a hash on normalized keys. takethe keys and both value arrays to get three aligned flattened columns.- Evaluate the lambda once over those columns.
- Rebuild the
MapArray.
- For each row, compute the key union and two index vectors into the value arrays, with NULL for a missing key.
- This crate's
crates/spark/src/function/lambda/mod.rsis an emptyScalarUDFplaceholder. It needs a
HigherOrderUDFlist and registration throughregister_higher_order_function/
SessionStateBuilder::higher_order_functions.crates/spark/src/function/map/utils.rshasMapArrayhelpers. - Add
crates/sqllogictest/test_files/lambda/map_zip_with.sltand document the function with#[user_doc].
This issue was generated by an LLM (Claude) from the Apache Spark source code and tests. Examples come from Spark's ExpressionDescription examples, test suites and .sql.out golden files, but please verify them against Spark before relying on them.
- 主要言語
- Rust
- スター
- 7
- フォーク
- 15
- 平均マージ
- 16時間 43分
- マージ済み PR(30日)
- 26
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
apache/datafusion-spark のほかの issue
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
apache/datafusion-spark#287 ·
メンテナーはふだん 1 日以内に返信
-
enhancement good first issue
難易度 2/5 1〜3時間 初心者へのやさしさ 80/100
apache/datafusion-spark#266 ·
メンテナーはふだん 1 日以内に返信
-
[Bug] make_dt_interval returns NULL for more than about 1.49 million days, Spark returns the interval対応中かも @Subodh-17 が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
apache/datafusion-spark#109 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 5/5 1週間以上 初心者へのやさしさ 18/100
apache/datafusion-spark#299 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 4/5 3〜5日 初心者へのやさしさ 35/100
apache/datafusion-spark#298 ·
メンテナーはふだん 1 日以内に返信
apache/datafusion-spark の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
storytold/photocraft#15232 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
suhina-daw/suhina-daw#1477 ·
メンテナーはふだん 1 日以内に返信
-
parity priority: low ui
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
storytold/pdfcraft#7483 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
IngvarConsulting/unica#3097 ·
メンテナーはふだん 1 日以内に返信
-
enhancement in-progress
難易度 1/5 1時間未満 初心者へのやさしさ 64/100