spark: как объединить строки в массив jsons - PullRequest
0 голосов
/ 01 марта 2019

Ввод:

id1   id2    name   value           epid
"xxx" "yyy"  "EAN"  "5057723043"    "1299"
"xxx" "yyy"  "MPN"  "EVBD"          "1299"

Я хочу:

{         "id1": "xxx",
          "id2": "yyy",
          "item_specifics": [
            {
              "name": "EAN",
              "value": "5057723043"
            },
            {
              "name": "MPN",
              "value": "EVBD"
            },
            {
              "name": "EPID",
              "value": "1299"
            }
          ]
}

Я попробовал следующие два решения из Как объединить столбцы в массив json? и Как объединить строки в столбец искрового фрейма данных как vaild json, чтобы записать его в mysql :

pi_df.groupBy(col("id1"), col("id2"))
  //.agg(collect_list(to_json(struct(col("name"), col("value"))).alias("item_specifics"))) // => not working
  .agg(collect_list(struct(col("name"),col("value"))).alias("item_specifics"))

Но я получил:

{ "name":"EAN","value":"5057723043", "EPID": "1299", "id1": "xxx", "id2": "yyy" }

Как это исправить?Спасибо

Ответы [ 3 ]

0 голосов
/ 01 марта 2019

Вот что вам нужно сделать

    import scala.util.parsing.json.JSONObject
    import scala.collection.mutable.WrappedArray

    //Define udf
    val jsonFun = udf((id1 : String, id2 : String, item_specifics: WrappedArray[Map[String, String]], epid: String)=> {
 //Add epid to item_specifics json
val item_withEPID = item_specifics :+ Map("epid" -> epid)

val item_specificsArray = item_withEPID.map(m => ( Array(Map("name" -> m.keys.toSeq(0), "value" -> m.values.toSeq(0))))).map(m => m.map( mi => JSONObject(mi).toString().replace("\\",""))).flatten.mkString("[",",","]")

 //Add id1 and id2 to output json
val m = Map("id1"-> id1, "id2"-> id2, "item_specifics" -> item_specificsArray.toSeq )
JSONObject(m).toString().replace("\\","")
})

val pi_df = Seq( ("xxx","yyy","EAN","5057723043","1299"), ("xxx","yyy","MPN","EVBD","1299")).toDF("id1","id2","name","value","epid")

//Add epid as part of group by column else the column will not be available after group by and aggregation
val df = pi_df.groupBy(col("id1"), col("id2"), col("epid")).agg(collect_list(map(col("name"), col("value")) as "map").as("item_specifics")).withColumn("item_specifics",jsonFun($"id1",$"id2",$"item_specifics",$"epid"))

df.show(false)

scala> df.show(false)
+---+---+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|id1|id2|epid|item_specifics                                                                                                                                                      |
+---+---+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|xxx|yyy|1299|{"id1" : "xxx", "id2" : "yyy", "item_specifics" : [{"name" : "MPN", "value" : "EVBD"},{"name" : "EAN", "value" : "5057723043"},{"name" : "epid", "value" : "1299"}]}|
+---+---+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Содержимое столбца item_specifics / output

{
    "id1": "xxx",
    "id2": "yyy",
    "item_specifics": [{
        "name": "MPN",
        "value": "EVBD"
    }, {
        "name": "EAN",
        "value": "5057723043"
    }, {
        "name": "epid",
        "value": "1299"
    }]
}
0 голосов
/ 01 марта 2019

Для Spark <2.4 </h2> Вы можете создать 2 кадра данных, один с именем и значением, а другой с эпическим как имя и эпическим значением как значение, и объединить их вместе.Затем объедините их как collect_set и создайте json.Код должен выглядеть следующим образом. //Creating Test Data val df = Seq(("xxx","yyy" ,"EAN" ,"5057723043","1299"), ("xxx","yyy" ,"MPN" ,"EVBD", "1299") ) .toDF("id1", "id2", "name", "value", "epid") df.show(false) +---+---+----+----------+----+ |id1|id2|name|value |epid| +---+---+----+----------+----+ |xxx|yyy|EAN |5057723043|1299| |xxx|yyy|MPN |EVBD |1299| +---+---+----+----------+----+ val df1 = df.withColumn("map", struct(col("name"), col("value"))) .select("id1", "id2", "map") val df2 = df.withColumn("map", struct(lit("EPID").as("name"), col("epid").as("value"))) .select("id1", "id2", "map") val jsonDF = df1.union(df2).groupBy("id1", "id2") .agg(collect_set("map").as("item_specifics")) .withColumn("json", to_json(struct("id1", "id2", "item_specifics"))) jsonDF.select("json").show(false) +---------------------------------------------------------------------------------------------------------------------------------------------+ |json | +---------------------------------------------------------------------------------------------------------------------------------------------+ |{"id1":"xxx","id2":"yyy","item_specifics":[{"name":"MPN","value":"EVBD"},{"name":"EAN","value":"5057723043"},{"name":"EPID","value":"1299"}]}| +---------------------------------------------------------------------------------------------------------------------------------------------+ Для Spark = 2,4

Он предоставляет метод array_union.Может быть полезно сделать это без объединения.Я еще не пробовал это.

val jsonDF = df.withColumn("map1", struct(col("name"), col("value")))
  .withColumn("map2", struct(lit("epid").as("name"), col("epid").as("value")))
  .groupBy("id1", "id2")
    .agg(collect_set("map1").as("item_specifics1"),
      collect_set("map2").as("item_specifics2"))
  .withColumn("item_specifics", array_union(col("item_specifics1"), col("item_specifics2")))
  .withColumn("json", to_json(struct("id1", "id2", "item_specifics2")))
0 голосов
/ 01 марта 2019

Ты довольно близко.Я считаю, что вы ищете что-то вроде этого:

val pi_df2 = pi_df.withColumn("name", lit("EPID")).
withColumnRenamed("epid", "value").
select("id1", "id2", "name","value")

pi_df.select("id1", "id2", "name","value").
union(pi_df2).withColumn("item_specific", struct(col("name"), col("value"))).
groupBy(col("id1"), col("id2")).
agg(collect_list(col("item_specific")).alias("item_specifics")).
write.json(...)

Союз должен вернуть epid в item_specifics

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...