title: Join multiple datasets in Apache Spark using joinWith
body: <p>I want to join three datasets with <code>joinWith</code> and then process them in a nice way, like <code>case ((t1, t2), t3)) =&gt;</code>, but it fails with an exception. The cause of the error is pretty clear. Result of joining two datasets this way gives:</p>
  
  <pre><code>+-------------+----------+
  |           _1|        _2|
  +-------------+----------+
  |[1, Name1, 1]|[1, Dept1]|
  |[2, Name2, 2]|[2, Dept2]|
  |[3, Name3, 3]|[3, Dept3]|
  +-------------+----------+
  </code></pre>
  
  <p>So I can't join the result table with subsequent table. Maybe there some other ways? Is it even possible to join multiple tables in "typesafe" way (using <code>joinWith</code>)?</p>
  
  <p>Idea:</p>
  
  <pre class="lang-scala prettyprint-override"><code>    object MainApp {
  
        case class Emp(empId: Int, name: String, deptId: Int)
        case class Dept(deptId: Int, name: String)
        case class Addr(empId: Int, name: String)
  
        def main(args: Array[String]): Unit = {
  
          val spark = SparkSession
            .builder()
            .master("local[*]")
            .appName("spark_test")
            .getOrCreate()
  
          import spark.implicits._
  
          val emps = Seq(
            (1, "Name1", 1),
            (2, "Name2", 2),
            (3, "Name3", 3)
          ).toDF("empId", "name", "deptId").as[Emp]
  
          val depts = Seq(
            (1, "Dept1"),
            (2, "Dept2"),
            (3, "Dept3")
          ).toDF("deptId", "name").as[Dept]
  
          val addrs = Seq(
            (1, "Addr1"),
            (2, "Addr2"),
            (3, "Addr3")
          ).toDF("empId", "name").as[Addr]
  
          val result = emps
            .joinWith(depts, emps("deptId") === depts("deptId"), "inner")
            .joinWith(addrs, emps("empId") === addrs("empId"), "inner")         
  
  //        result.map {
  //          case ((emps, depts), addrs) =&gt; ???
  //        }
        }
      }
  </code></pre>
  
  <p>Exception:</p>
  
  <pre><code>Exception in thread "main" org.apache.spark.sql.AnalysisException: Resolved attribute(s) empId#7 missing from _1#41,_2#42,empId#34,name#35 in operator !Join Inner, (empId#7 = empId#34). Attribute(s) with the same name appear in the operation: empId. Please check if the right attribute(s) are used.;;
  !Join Inner, (empId#7 = empId#34)
  :- Join Inner, (_1#41.deptId = _2#42.deptId)
  :  :- Project [named_struct(empId, empId#7, name, name#8, deptId, deptId#9) AS _1#41]
  :  :  +- Project [_1#3 AS empId#7, _2#4 AS name#8, _3#5 AS deptId#9]
  :  :     +- LocalRelation [_1#3, _2#4, _3#5]
  :  +- Project [named_struct(deptId, deptId#22, name, name#23) AS _2#42]
  :     +- Project [_1#19 AS deptId#22, _2#20 AS name#23]
  :        +- LocalRelation [_1#19, _2#20]
  +- Project [_1#31 AS empId#34, _2#32 AS name#35]
     +- LocalRelation [_1#31, _2#32]
  
      at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$class.failAnalysis(CheckAnalysis.scala:42)
      at org.apache.spark.sql.catalyst.analysis.Analyzer.failAnalysis(Analyzer.scala:95)
      at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1.apply(CheckAnalysis.scala:326)
      at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1.apply(CheckAnalysis.scala:85)
      at org.apache.spark.sql.catalyst.trees.TreeNode.foreachUp(TreeNode.scala:127)
      at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$class.checkAnalysis(CheckAnalysis.scala:85)
      at org.apache.spark.sql.catalyst.analysis.Analyzer.checkAnalysis(Analyzer.scala:95)
      at org.apache.spark.sql.catalyst.analysis.Analyzer$$anonfun$executeAndCheck$1.apply(Analyzer.scala:108)
      at org.apache.spark.sql.catalyst.analysis.Analyzer$$anonfun$executeAndCheck$1.apply(Analyzer.scala:105)
      at org.apache.spark.sql.catalyst.plans.logical.AnalysisHelper$.markInAnalyzer(AnalysisHelper.scala:201)
      at org.apache.spark.sql.catalyst.analysis.Analyzer.executeAndCheck(Analyzer.scala:105)
      at org.apache.spark.sql.execution.QueryExecution.analyzed$lzycompute(QueryExecution.scala:57)
      at org.apache.spark.sql.execution.QueryExecution.analyzed(QueryExecution.scala:55)
      at org.apache.spark.sql.Dataset.joinWith(Dataset.scala:1079)
      at sample.sample.MainApp$.main(MainApp.scala:41)
      at sample.sample.MainApp.main(MainApp.scala)
  </code></pre>