Основная проблема объединения перед группировкой и сбором списков заключается в том, что объединение приведет к большому количеству записей для свертывания группы, в вашем примере это 12 записей после объединения и перед группировкой, также вам нужно беспокоиться о выбор "firstName", "address" out detailsDf из 12 дубликатов. Чтобы избежать обеих проблем, вы можете предварительно обработать продовольственные, электронные и игровые фреймы данных, используя struct и groupBy, и присоединить их к detailsDf без риска взрыва ваших данных из-за нескольких записей с одним и тем же userId в объединенных таблицах.
val detailsDf = Seq((123,"first123","xyz"))
.toDF("userId","firstName","address")
val emailDf = Seq((123,"abc@gmail.com"),
(123,"def@gmail.com"))
.toDF("userId","email")
val foodDf = Seq((123,"food2",false,"Italian",2),
(123,"food3",true,"American",3),
(123,"food1",true,"Mediterranean",1))
.toDF("userId","foodName","isFavFood","cuisine","score")
val gameDf = Seq((123,"chess",false,2),
(123,"football",true,1))
.toDF("userId","gameName","isOutdoor","score")
val emailGrp = emailDf.groupBy("userId").agg(collect_list("email").as("UserEmail"))
val foodGrp = foodDf
.select($"userId", struct("score", "foodName","isFavFood","cuisine").as("UserFoodFavourites"))
.groupBy("userId").agg(sort_array(collect_list("UserFoodFavourites")).as("UserFoodFavourites"))
val gameGrp = gameDf
.select($"userId", struct("gameName","isOutdoor","score").as("UserGameFavourites"))
.groupBy("userId").agg(collect_list("UserGameFavourites").as("UserGameFavourites"))
val result = detailsDf.join(emailGrp, Seq("userId"))
.join(foodGrp, Seq("userId"))
.join(gameGrp, Seq("userId"))
result.show(100, false)
Выход:
+------+---------+-------+------------------------------+-----------------------------------------------------------------------------------------+----------------------------------------+
|userId|firstName|address|UserEmail |UserFoodFavourites |UserGameFavourites |
+------+---------+-------+------------------------------+-----------------------------------------------------------------------------------------+----------------------------------------+
|123 |first123 |xyz |[abc@gmail.com, def@gmail.com]|[[1, food1, true, Mediterranean], [2, food2, false, Italian], [3, food3, true, American]]|[[chess, false, 2], [football, true, 1]]|
+------+---------+-------+------------------------------+-----------------------------------------------------------------------------------------+----------------------------------------+
Поскольку все groupBy выполняются на userId и также включаются, спарк оптимизирует его достаточно хорошо.
ОБНОВЛЕНИЕ 1 : После того, как @ user238607 указал, что я пропустил исходное требование сортировки предпочтений в продуктах питания, быстро исправил и поместил столбец оценка в качестве первого элемента структуры UserFoodFavourites и использует функцию sort_array для упорядочения данных в нужном порядке без принудительного выполнения дополнительной операции перемешивания. Обновлен код и его вывод.