вход в Скала

Вы получаете эти ошибки, потому что схема, определенная sqlType, никогда не отображается и не предназначена для прямого доступа. Он просто предоставляет способ выражения сложных типов данных с использованием собственных типов Spark SQL.

Вы можете получить доступ к отдельным атрибутам с помощью UDF, но сначала покажите, что внутренняя структура действительно не отображается:

dataFrame.printSchema
// root
//  |-- person_id: integer (nullable = true)
//  |-- person: mockperso (nullable = true)

Для создания UDF нужны функции, которые принимают в качестве аргумента объект типа, представленного данным UDT:

import org.apache.spark.sql.functions.udf

val getFirstName = (person: MockPerson) => person.getFirstName
val getLastName = (person: MockPerson) => person.getLastName
val getAge = (person: MockPerson) => person.getAge

, который можно обернуть с помощью функции udf:

val getFirstNameUDF = udf(getFirstName)
val getLastNameUDF = udf(getLastName)
val getAgeUDF = udf(getAge)

dataFrame.select(
  getFirstNameUDF($"person").alias("first_name"),
  getLastNameUDF($"person").alias("last_name"),
  getAgeUDF($"person").alias("age")
).show()

// +----------+---------+---+
// |first_name|last_name|age|
// +----------+---------+---+
// |    First1|    Last1|  1|
// |    First2|    Last2|  2|
// +----------+---------+---+

Чтобы использовать их с необработанным SQL, у вас есть функции регистрации через SQLContext:

sqlContext.udf.register("first_name", getFirstName)
sqlContext.udf.register("last_name", getLastName)
sqlContext.udf.register("age", getAge)

sqlContext.sql("""
  SELECT first_name(person) AS first_name, last_name(person) AS last_name
  FROM person
  WHERE age(person) < 100""").show

// +----------+---------+
// |first_name|last_name|
// +----------+---------+
// |    First1|    Last1|
// |    First2|    Last2|
// +----------+---------+

К сожалению, он поставляется с прикрепленной ценой. Прежде всего, для каждой операции требуется десериализация. Это также существенно ограничивает способы оптимизации запроса. В частности, для любой операции join в одном из этих полей требуется декартово произведение.

На практике, если вы хотите кодировать сложную структуру, которая содержит атрибуты, которые могут быть выражены с помощью встроенных типов, лучше использовать StructType:

case class Person(first_name: String, last_name: String, age: Int)

val df = sc.parallelize(
  (1 to 2).map(i => (i, Person(s"First$i", s"Last$i", i)))).toDF("id", "person")

df.printSchema

// root
//  |-- id: integer (nullable = false)
//  |-- person: struct (nullable = true)
//  |    |-- first_name: string (nullable = true)
//  |    |-- last_name: string (nullable = true)
//  |    |-- age: integer (nullable = false)

df
  .where($"person.age" < 100)
  .select($"person.first_name", $"person.last_name")
  .show

// +----------+---------+
// |first_name|last_name|
// +----------+---------+
// |    First1|    Last1|
// |    First2|    Last2|
// +----------+---------+

и зарезервировать UDT для фактических расширений типа, подобных встроенным VectorUDT, или вещам, которые могут извлечь выгоду из определенного представления , как перечисления .

30
задан IttayD 7 January 2010 в 14:12
поделиться

7 ответов

Я бы просто придерживался подхода "с лесозаготовками". Чистый дизайн выигрывает каждый раз - если вы вытащите шаблон из шаблона, то есть шансы, что вы сможете найти гораздо более полезные результаты, достижимые в других областях.

Имейте в виду, что фреймворк логгеров будет кэшировать логгеры, так что у вас все равно есть по одному на каждый класс, даже если каждый экземпляр этого класса случайно содержит (недорогую) ссылку.

Без доказательств того, что ссылки на логгеры наносят вред вашей куче, это пахнет как преждевременная оптимизация.... Просто расслабьтесь и не волнуйтесь об этом, если только профилировщик не скажет вам обратное.

На несвязанной ноте, возможно, вы захотите рассмотреть использование slf4j и logback вместо log4j. slf4j имеет более чистый дизайн, который лучше подходит к идиоматической скале.

.
20
ответ дан 28 November 2019 в 00:07
поделиться
object Log {
    def log(message: String) = {
        .....
    }
}

No?

3
ответ дан 28 November 2019 в 00:07
поделиться

Одним из способов является расширение логгера на сопутствующий объект:

object A extends LoggerSupport

class A {
    import A._
    log("hi")
}

trait LoggerSupport{
    val logger = LoggerFactory.getLogger(this.getClass)
    def log(msg : String)= logger.log(msg)
}

//classes of the logging framework
trait Logger{
    def log(msg : String) : Unit
}

object LoggerFactory{
    def getLogger(classOfLogger : Class[_]) : Logger = ...
}

Или же вы можете кэшировать экземпляры логгера:

import collection.mutable.Map
object LoggerCache{
    var cache : Map[Class[_], Logger] = Map()
    def logger(c : Class[_]) = cache.getOrElseUpdate(c, LoggerFactory.getLogger(c))
}

trait LoggerSupport{
    def log(msg : String) = LoggerCache.logger(this.getClass).log(msg)
}

class A extends LoggerSupport{
    log("hi")
}

Это проще в использовании, но будет иметь худшую производительность. Производительность будет действительно плохой, когда вы собираетесь отбрасывать большинство сообщений журнала (из-за настройки уровня журнала).

0
ответ дан 28 November 2019 в 00:07
поделиться

Вот быстрый взлом (который я на самом деле не использовал, честно ;@)

object LogLevel extends Enumeration {
  val Error   = Value(" ERROR   ")
  val Warning = Value(" WARNING ")                                                                                                      
  val Info    = Value(" INFO    ")
  val Debug   = Value(" DEBUG   ")
}

trait Identity {
  val id: String
}

trait Logging extends Identity {
  import LogLevel._

  abstract class LogWriter {
    protected val writer: Actor
    protected val tstampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ")

    def tstamp = tstampFormat.format(new Date)

    def log(id: String, logLevel: LogLevel.Value, msg: String) {
      writer ! (tstamp + id + logLevel + msg)
    }
  }

  object NullLogWriter extends LogWriter {
    protected val writer = actor{loop {react{case msg: String =>}}}
  }

  object ConsoleWriter extends LogWriter {
    protected val writer = actor{loop {react {case msg: String => Console.out.println(msg); Console.flush case _ =>}}}
  }

  class FileWriter(val file: File) extends LogWriter {
    require(file != null)
    require(file.canWrite)

    protected val writer = actor{loop {react {case msg: String => destFile.println(msg); destFile.flush case _ =>}}}

    private val destFile = {
      try {new PrintStream(new FileOutputStream(file))}
      catch {case e => ConsoleWriter.log("FileWriter", LogLevel.Error, "Unable to create FileWriter for file " + file +
                                         " exception was: " + e); Console.out}
    }
  }

  protected var logWriter: LogWriter = ConsoleWriter
  protected var logLevel             = Info

  def setLoggingLevel(level: LogLevel.Value) {logLevel = level}

  def setLogWriter(lw: LogWriter) {if (lw != null) logWriter = lw}

  def logError(msg: => String) {if (logLevel <= Error) logWriter.log(id, Error, msg)}

  def logWarning(msg: => String) {if (logLevel <= Warning) logWriter.log(id, Warning, msg)}

  def logInfo(msg: => String) {if (logLevel <= Info) logWriter.log(id, Info, msg)}

  def logDebug(msg: => String) {if (logLevel <= Debug) logWriter.log(id, Debug, msg)}
}

Надеюсь, он принесет пользу.

1
ответ дан 28 November 2019 в 00:07
поделиться

Я использовал Log4j со Scala, создавая черта и имея регистрацию по экземплярам, ​​а не в классе. С некоторыми магией Scala и проявляются, вы можете изменить регистратор для статического (внутреннего объекта), но я не уверен на 100%. Лично я согласен с @kevinwright, что сделав статический отель, представляет собой преждевременную оптимизацию.

также обратите внимание, что код ниже имеет сообщения журнала как по имени, что означает, что ваши вызовы регистратора не должны быть завернуты в `If (log.isdebugenabled ()); Комплексные журналы Сообщений, созданные через Concatenation String, не оцениваются, если уровень журнала не подходит. Смотрите эту ссылку для получения дополнительной информации: http://www.naildrivin5.com/scalatour/wiki_pages/typeedepentedClosures

http://github.com/davetron5000/shorty/blob/master/src/main/cala/ Более коротко / logs.scala

package shorty

import org.apache.log4j._

trait Logs {
  private[this] val logger = Logger.getLogger(getClass().getName());

  import org.apache.log4j.Level._

  def debug(message: => String) = if (logger.isEnabledFor(DEBUG)) logger.debug(message)
  def debug(message: => String, ex:Throwable) = if (logger.isEnabledFor(DEBUG)) logger.debug(message,ex)
  def debugValue[T](valueName: String, value: => T):T = {
    val result:T = value
    debug(valueName + " == " + result.toString)
    result
  }

  def info(message: => String) = if (logger.isEnabledFor(INFO)) logger.info(message)
  def info(message: => String, ex:Throwable) = if (logger.isEnabledFor(INFO)) logger.info(message,ex)

  def warn(message: => String) = if (logger.isEnabledFor(WARN)) logger.warn(message)
  def warn(message: => String, ex:Throwable) = if (logger.isEnabledFor(WARN)) logger.warn(message,ex)

  def error(ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(ex.toString,ex)
  def error(message: => String) = if (logger.isEnabledFor(ERROR)) logger.error(message)
  def error(message: => String, ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(message,ex)

  def fatal(ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(ex.toString,ex)
  def fatal(message: => String) = if (logger.isEnabledFor(FATAL)) logger.fatal(message)
  def fatal(message: => String, ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(message,ex)
}

class Foo extends SomeBaseClass with Logs {
  def doit(s:Option[String]) = {
    debug("Got param " + s)
    s match {
      case Some(string) => info(string)
      case None => error("Expected something!")
    } 
  }
}
12
ответ дан 28 November 2019 в 00:07
поделиться

Если вы действительно обеспокоены космическими расходами и / или дополнительным временем В инициализаторах объекта хорошая стратегия может быть, чтобы иметь вещественную черту, которая оставляет реферат регистратора, как в


trait Logging {
  def logger: Logger
  def debug(message: String) { logger.debug(message) }
  def warn(message: String) { logger.warn(message) }
}

для классов, которые должны быть максимально легкими, тогда вы можете сделать


object MustBeLightweight {
  val logger = Logger.getLog(classOf[MustBeLightweight])
}
class MustBeLightWeight extends Logging {
  final def logger = MustBeLightweight.logger
}

JIT может даже встроен Debug Уорн и в этом случае в этом случае.

Вы также можете иметь черту, чтобы смешать для классов, где накладные расходы дополнительного поля не являются проблемой


trait PerInstanceLog {
  val logger = Logger.getLog(this.getClass())
}

, еще одна вариант - оставить выходить из класса и полностью положить его в объект, как в


object Foo {
  object log extends Logging {
    override val logger = Logger.getLogger(classOf[Foo])
  } 
}

class Foo {
  import Foo.log._
  def someMethod() = { warn("xyz") }
}

Я согласен с Кевином, хотя, не добавляйте сложность, если вам это не нужно.

5
ответ дан 28 November 2019 в 00:07
поделиться

Иногда правильным ответом является ведение журнала на уровне пакета. Scala делает это проще, чем java, потому что объекты могут быть определены непосредственно в пакете. Если вы определили журнал следующим образом:

package example 
object Log extends au.com.langdale.util.PackageLogger 

Этот журнал доступен везде в примере пакета. Чтобы получить более детальное ведение журнала, вы можете разбросать похожие определения по иерархии пакетов. Или вы можете определить все регистраторы пакетов вместе следующим образом:

package example {
  import au.com.langdale.util.PackageLogger

  object Log extends PackageLogger 

  package frobber {
    object Log extends PackageLogger 

    package undulater {
      object Log extends PackageLogger
    } 
  }
}

Класс PackageLogger может быть определен следующим образом (при условии SLF4J):

package au.com.langdale.util
import org.slf4j.LoggerFactory

class PackageLogger {
  val name = { val c = getClass.getName; c.substring(0, c.lastIndexOf('.')) }
  val inner = LoggerFactory.getLogger(name)

  // various convenient logging methods follow....
  def apply( mesg: => Any ) = inner.info(mesg.toString)
  def info( mesg: String ) = inner.info(mesg)
  def warn( mesg: String ) = inner.warn(mesg)
  def error( mesg: String ) = inner.error(mesg)
}
2
ответ дан 28 November 2019 в 00:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: