在scala

时间:2016-08-25 17:17:12

标签: scala apache-spark

我正在尝试导入spark.implicits._ 显然,这是scala中类中的一个对象。 当我用这样的方法导入它时:

def f() = {
  val spark = SparkSession()....
  import spark.implicits._
}

它工作正常,但我正在编写一个测试类,我想让这个导入可用于所有测试 我试过了:

class SomeSpec extends FlatSpec with BeforeAndAfter {
  var spark:SparkSession = _

  //This won't compile
  import spark.implicits._

  before {
    spark = SparkSession()....
    //This won't either
    import spark.implicits._
  }

  "a test" should "run" in {
    //Even this won't compile (although it already looks bad here)
    import spark.implicits._

    //This was the only way i could make it work
    val spark = this.spark
    import spark.implicits._
  }
}

这不仅看起来很糟糕,我也不想为每次测试做这件事 什么是"正确"这样做的方式?

8 个答案:

答案 0 :(得分:18)

您可以执行类似于Spark测试套件中所做的操作。例如,这将起作用(受SQLTestData启发):

class SomeSpec extends FlatSpec with BeforeAndAfter { self =>

  var spark: SparkSession = _

  private object testImplicits extends SQLImplicits {
    protected override def _sqlContext: SQLContext = self.spark.sqlContext
  }
  import testImplicits._

  before {
    spark = SparkSession.builder().master("local").getOrCreate()
  }

  "a test" should "run" in {
    // implicits are working
    val df = spark.sparkContext.parallelize(List(1,2,3)).toDF()
  }
}

或者你可以直接使用SharedSQLContext之类的东西,它提供testImplicits: SQLImplicits,即:

class SomeSpec extends FlatSpec with SharedSQLContext {
  import testImplicits._

  // ...

}

答案 1 :(得分:9)

我认为SparkSession.scala文件中的GitHub代码可以给你一个很好的提示:

      /**
       * :: Experimental ::
       * (Scala-specific) Implicit methods available in Scala for converting
       * common Scala objects into [[DataFrame]]s.
       *
       * {{{
       *   val sparkSession = SparkSession.builder.getOrCreate()
       *   import sparkSession.implicits._
       * }}}
       *
       * @since 2.0.0
       */
      @Experimental
      object implicits extends SQLImplicits with Serializable {
        protected override def _sqlContext: SQLContext = SparkSession.this.sqlContext
      }

这里" spark" in" spark.implicits ._"就是我们创建的sparkSession对象。

Here是另一个参考!

答案 2 :(得分:5)

我只是实例化SparkSession并在使用之前,"导入含义"。

    @transient lazy val spark = SparkSession
    .builder()
    .master("spark://master:7777")
    .getOrCreate()

    import spark.implicits._

答案 3 :(得分:0)

要在将任何rdd转换为数据集之前,创建一个sparksession对象并使用spark.implicit ._。

赞:

val spark = SparkSession
      .builder
      .appName("SparkSQL")
      .master("local[*]")
      .getOrCreate()

import spark.implicits._
val someDataset = someRdd.toDS

答案 4 :(得分:0)

它必须在scala中使用val vs var做一些事情。

例如以下操作不起作用

var sparkSession = new SparkSession.Builder().appName("my-app").config(sparkConf).getOrCreate
import sparkSession.implicits._

但是遵循

sparkSession = new SparkSession.Builder().appName("my-app").config(sparkConf).getOrCreate
val sparkSessionConst = sparkSession
import sparkSessionConst.implicits._

我对scala非常熟悉,所以我只能猜测其原因与为什么我们只能在java的闭包内部使用声明为final的外部变量相同。

答案 5 :(得分:0)

感谢@ bluenote10提供有用的答案,我们可以再次简化它,例如,没有辅助对象testImplicits

private object testImplicits extends SQLImplicits {
  protected override def _sqlContext: SQLContext = self.spark.sqlContext
}

通过以下方式:

trait SharedSparkSession extends BeforeAndAfterAll { self: Suite =>

  /**
   * The SparkSession instance to use for all tests in one suite.
   */
  private var spark: SparkSession = _

  /**
   * Returns local running SparkSession instance.
   * @return SparkSession instance `spark`
   */
  protected def sparkSession: SparkSession = spark

  /**
   * A helper implicit value that allows us to import SQL implicits.
   */
  protected lazy val sqlImplicits: SQLImplicits = self.sparkSession.implicits

  /**
   * Starts a new local spark session for tests.
   */
  protected def startSparkSession(): Unit = {
    if (spark == null) {
      spark = SparkSession
        .builder()
        .master("local[2]")
        .appName("Testing Spark Session")
        .getOrCreate()
    }
  }

  /**
   * Stops existing local spark session.
   */
  protected def stopSparkSession(): Unit = {
    if (spark != null) {
      spark.stop()
      spark = null
    }
  }

  /**
   * Runs before all tests and starts spark session.
   */
  override def beforeAll(): Unit = {
    startSparkSession()
    super.beforeAll()
  }

  /**
   * Runs after all tests and stops existing spark session.
   */
  override def afterAll(): Unit = {
    super.afterAll()
    stopSparkSession()
  }
}

最后我们可以使用SharedSparkSession进行单元测试并导入sqlImplicits

class SomeSuite extends FunSuite with SharedSparkSession {
  // We can import sql implicits 
  import sqlImplicits._

  // We can use method sparkSession which returns locally running spark session
  test("some test") {
    val df = sparkSession.sparkContext.parallelize(List(1,2,3)).toDF()
    //...
  }
}

答案 6 :(得分:0)

好吧,我一直在每个被调用的方法中重用现有的SparkSession。通过在方法内部创建本地val-

val spark: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession.active

然后

import spark.implicits._

答案 7 :(得分:0)

我知道这是旧文章,但是只是想分享我的观点,我认为这是您声明sparkSession的方式的问题。当您将sparkSession声明为var时,它不会使其不可变,这可以在以后更改时间。因此它不允许导入隐式内容,因为它可能导致歧义,因为在以后的阶段它可以更改,因为在val情况下它可能不相同