2016-07-24 17 views
0

這個問題與以前的question有關。但由於這個問題已經結束,我必須另外創建一個。 用例如下:我有一個5列的表。創建記錄時,只需要2列。稍後用戶將在同一記錄中添加更多信息。例如具有以下結構的用戶表:id |電話|電子郵件|信貸|水平。當用戶註冊時,我只需要他們的電子郵件地址。用戶將被創建一個ID和電子郵件。稍後,用戶想要添加電話號碼,學分,系統也會在該用戶擁有足夠的學分時更新級別。 我創建了一個如何使用幻影dsl的可選列?

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表,並建立與現有的列值,並增加新的值的用戶對象,然後更新記錄這種方法不順利的更新方法。在更新方法中編寫很多條件也是不現實的。如果我有很多列並且每列可以單獨更新,那麼我將不得不編寫一個可能的值組合列表。下面的方法可能工作:

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 
    ...... 

但我不知道這是一個很好的做法,因爲這將是一場噩夢,如果你要處理的每次更新更新失敗。我應該如何使用可選列來實現我所需要的?

回答

1

幻影中的查詢是類,直到你通過調用諸如.future.one之類的東西來實現它們。在最底層,你可以通過使用matchif語句建立一個查詢...

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() 
} 

這是一種醜陋和繁瑣,但它的工作。

我碰到類似的情況,當我有一個模型,它有Either類型的屬性。我最終爲PhantomDSL寫了一個擴展名,我應該正式確定並重新提交,但它很好地清理了上面的混亂。這是爲INSERT,但這應該給你一個想法,如果你想爲UPDATE做同樣的事。棘手的部分將是處理modifyand,但我確定在源代碼中挖掘它們正在進行相同的操作,其差別僅在於審美。

在使用中:

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)column.parse(row)。你會得到一個Try[A],從那裏你可以做column.parse(row).toOption。我不記得了,但我認爲可能有一個函數用於檢查列是否在您嘗試讀取之前進行了定義,因此您不必爲流量控制™使用異常。

+1

的'column.parse(行).toOption'自動添加爲您在選擇使用'Optional'列。類似集合的東西也會自動默認爲空而不需要'Optional'。 – flavian

2

自幻像1.28.5開始,您可以使用新的setIfDefined運算符實現您想要的結果,該運算符會給出您想要的結果,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() 
}