1

这个问题与上一个问题有关。但由于这个问题已经结束,我必须创建一个单独的问题。用例是这样的:我有一个有 5 列的表。创建记录时,只需要 2 列。稍后用户将向同一记录添加更多信息。例如,具有以下结构的用户表:id | 电话 | 电子邮件 | 信用 | 等级。当用户注册时,我只需要他们的电子邮件地址。将使用 ID 和电子邮件创建用户。稍后,用户想要添加电话号码,积分,当该用户有足够的积分时,系统也会更新级别。我创建了一个

case class User(id:UUID, phone:Option[String], email:Option[String], 
    credit:Option[Double], level:Option[String]

我也有

sealed class Users extends CassandraTable[Users, User] {
    object id extends UUIDColumn(this) with PartitionKey[UUID]

    object phone extends OptionalStringColumn(this)

    object email extends OptionalStringColumn(this)

    object credit extends OptionalDoubleColumn(this)

    object level extends OptionalStringColumn(this)

    def fromRow(row: Row): User = {
        User(id(row), phone(row), email(row), credit(row), level(row))
    }
}

我在使用可选列吗?我应该如何处理用户更新一个或多个特定列的用例?我尝试了这样的更新方法

def updateUser(u: User): Future[ResultSet] = {
    update.where(_.id eqs u.id).modify(_.phone setTo u.phone)
      .and(_.email setTo u.email)
      .and(_.credit setTo u.credit)
      .and(_.level setTo u.level)
      .consistencyLevel_=(ConsistencyLevel.QUORUM)
      .future()
  }

此方法效果不佳,因为您必须从 id 读取表并使用现有列值创建一个 User 对象并添加新值,然后更新记录。在更新方法中写很多条件也是不现实的。如果我有很多列并且每一列都可以单独更新,我将不得不编写一个巨大的可能值组合列表。以下方法可能有效:

    if(u.phone != None) update.where(_.id eqs u.id).modify(_.phone setTo u.phone).future
    if(u.email != None) update.where(_.id eqs u.id).modify(_.email setTo u.email).future
    if(u.credit != None) update.where(_.id eqs u.id).modify(_.credit setTo u.credit).future
    ......

但我不确定这是一个好习惯,因为如果您想在每次更新时处理更新失败,这将是一场噩梦。我应该如何使用可选列来实现我所需要的?

4

2 回答 2

3

从 Phantom 1.28.5 开始,您可以使用 newsetIfDefined运算符实现您想要的,这将为您提供您想要的,该Update子句仅考虑已定义的选项。

您的更新方法现在变为:

def updateUser(u: User): Future[ResultSet] = {
  update.where(_.id eqs u.id).modify(_.phone setTo u.phone)
    .and(_.email setIfDefined u.email)
    .and(_.credit setIfDefined u.credit)
    .and(_.level setIfDefined u.level)
    .consistencyLevel_=(ConsistencyLevel.QUORUM)
    .future()
}
于 2016-08-14T11:15:36.737 回答
1

Phantom 中的查询是类,直到您通过调用类似.futureor来实现它们.onematch在最低级别,您可以使用or语句构建查询if......

def updateUser(u: User): Future[ResultSet] = {
  val baseQuery = update.where(_.id eqs u.id).modify(_.phone setTo u.phone)

  val query2 = u.email match {
    case Some(email) => baseQuery.and(_.email setTo email)
    case None => baseQuery // don't do anything if none!
  }

  // ...

  query5.future()
}

这有点丑陋和乏味,但它确实有效。

当我有一个具有 type 属性的模型时,我遇到了类似的情况Either。我最终为 PhantomDSL 编写了一个小扩展,我可能应该将其形式化并提交回来,但它很好地清理了上面的混乱。这是为了INSERT,但这应该让您了解如果您想为UPDATE. 棘手的部分是处理modifyvs. and,但我确信在源代码中挖掘他们在幕后进行相同的操作,不同之处只是美观。

正在使用:

table.insert
  .value(_.foo, "foo")
  .valueOpt(_.bar, Some("bar"))
  .valueIf(_.buz, x, x.nonEmpty)
  .matchOn(someEither) {
    case (Left(Person(name, age), insert) => insert
      .value(_.personName, name) 
      .value(_.personAge, age)
    case (Right(Company(name, employeeCount), insert) => insert
      .value(_.companyName, name)
      .value(_.companyAge, age)
  }
  .future() 

资源:

class RichInsert[T <: CassandraTable[T, _], R, S <: ConsistencyBound, P <: HList](val insert: InsertQuery[T,R,S,P]) {

  def value[RR](col: T => AbstractColumn[RR], value: RR): RichInsert[T,R,S,P] =
    new RichInsert(insert.value(col, value))

  def valueIf[RR](col: T => AbstractColumn[RR], value: RR, clause: => Boolean): RichInsert[T,R,S,P] = {
    if (clause) RichInsert(insert.value(col, value))
    else this
  }

  def valueOpt[RR](col: T => AbstractColumn[RR], value: Option[RR]): RichInsert[T,R,S,P] = {
    value match {
      case Some(rr) => RichInsert(insert.value(col, rr))
      case None => this
    }
  }

  def matchOn[A](value: A)(pf: PartialFunction[(A, RichInsert[T,R,S,P]), RichInsert[T,R,S,P]]) =
    if (pf.isDefinedAt((value, this))) pf.apply((value, this))
    else this
}

object RichInsert {
  def apply[T <: CassandraTable[T, _], R, S <: ConsistencyBound, P <: HList](insert: InsertQuery[T,R,S,P]): RichInsert[T,R,S,P] =
    new RichInsert(insert)
}

implicit def insertQueryToRichInsert[T <: CassandraTable[T, _], R, S <: ConsistencyBound, P <: HList](insert: InsertQuery[T,R,S,P]): RichInsert[T,R,S,P] =
  RichInsert(insert)

编辑 -

我忘记提到的一件事是,当你读回数据而不是做column(row)do 时column.parse(row)。你会得到一个Try[A]并且从那里你可以做column.parse(row).toOption。我不记得了,但我认为那里可能有一个函数可以在您尝试阅读之前检查该列是否已定义,这样您就不必为 flow control™ 使用异常。

于 2016-07-27T16:58:06.180 回答