Как в Slick 3.0 упростить вложенный `db.run`?

Я использую Slick 3.0, и вот мои коды:

def registerMember(newMember: TeamMember): Future[Long] = {

  db.run(
    teamProfileTable.filter(u => u.ID === newMember.ID).result.headOption
  ).flatMap {
    case None => Future(-1)
    case _ => db.run(
      (teamProfileTable returning teamProfileTable.map(_.staffID)) += newMember.toTeamRecord
    )
  }
}

Это может выглядеть нормально. Но когда есть больше уровней обратного вызова, коды могут стать трудными для чтения. Я пытался упростить коды, используя for-expression или andThen.. Но из-за части сопоставления с образцом я могу использовать только flatMap для реализации этого..

У кого-нибудь есть идеи о том, как реорганизовать это?


person Community    schedule 12.05.2015    source источник
comment
Вы должны объединить запросы, а затем только в качестве последнего шага запустить композицию в БД. Вот как я думаю, это должно работать. Если вы добавите еще немного кода, чтобы сделать работающий пример, я мог бы попробовать с ответом.   -  person pagoda_5b    schedule 12.05.2015
comment
@pagoda_5b Спасибо! Но как объединить два запроса, если второй запрос зависит от результата первого запроса? Я думаю, что andThen здесь не подходит..   -  person Hanfei Sun    schedule 12.05.2015
comment
@hanfeisun Вам, вероятно, следует использовать для понимания, но я не могу вам больше помочь, если вы не можете предоставить некоторый самосогласованный код для тестирования. Я могу только догадываться о намерении и результате вашего кода, если вы не будете более явным. :)   -  person pagoda_5b    schedule 18.05.2015


Ответы (2)


Я думаю, что для понимания здесь должно быть все в порядке, вам просто нужна условная обработка Option в результате первого Future. Что-то вроде этого должно работать (обратите внимание, что я не компилировал это):

def registerMember(newMember: TeamMember): Future[Long] = {

  for{
    r1Opt <- db.run(teamProfileTable.filter(u => u.ID === newMember.ID).result.headOption
    r2 <- r1Opt.fold(Future.successful(-1L))(r1 => db.run((teamProfileTable returning teamProfileTable.map(_.staffID)) += newMember.toTeamRecord)
  } yield r2

}

Вы можете видеть в правой части fold, что у меня есть доступ к результату первого Future, если это был Some (как r1).

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

def registerMember(newMember: TeamMember): Future[Long] = {
  def findMember = 
    db.run(teamProfileTable.filter(u => u.ID === newMember.ID).result.headOption

  def addMember(r1Opt:Option[TeamMember]) = {
    r1Opt.fold(Future.successful(-1L)){r1 =>
      db.run((teamProfileTable returning teamProfileTable.map(_.staffID)) += 
        newMember.toTeamRecord)
    }
  }

  for{
    r1Opt <- findMember
    r2 <- addMember(r1Opt)
  } yield r2

}
person cmbaxter    schedule 12.05.2015

Другой подход к упрощению вложенных db.runs в Slick 3.0, когда запрос охватывает две таблицы, может заключаться в объединении запросов в один запрос. Соединение и архивирование. Однако у OP, похоже, несколько более редкий случай вложенных запросов к одной и той же таблице, поэтому этот подход может быть бесполезен в этом конкретном случае.

val query = slickLoginInfos join slickUserLoginInfos on 
   ((l,ul) => l.id === ul.loginInfoId) 
db.run((for { (l, ul) <- query } yield (ul)).result.headOption)
person Sully    schedule 19.06.2015