Scala Slick joinLeft和组合条件

问题描述 投票:0回答:1

我希望能够使用Slick创建一个查询,让我以动态的方式过滤左连接

case class Player(
  id: Long,
  createdAt: DateTime,
  lastModificationDate: DateTime,
  name: String
)

class PlayerTable(tag: Tag)  extends Table[Player](tag, "players") {
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def createdAt = column[DateTime]("createdAt")
  def lastModificationDate = column[DateTime]("lastModificationDate")
  def name = column[String]("name")
  override def * : ProvenShape[Player] = (
  id,
  createdAt,
  lastModificationDate,
  updatedAt,
  name
  ) <> (Player.tupled, Player.unapply)
}

case class PlayerGame(
  id: Long,
  createdAt: DateTime,
  lastModificationDate: DateTime,
  playerId: Long,
  level: Int,
  status: String
)

class PlayerGameTable(tag: Tag)  extends Table[PlayerGame](tag, "player_games") {
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def createdAt = column[DateTime]("createdAt")
  def lastModificationDate = column[DateTime]("lastModificationDate")
  def playerId = column[Long]("playerId")
  def level = column[Int]("level")
  def status = column[String]("status")
  override def * : ProvenShape[PlayerGame] = (
  id,
  createdAt,
  lastModificationDate,
  playerId,
  level,
  status
  ) <> (PlayerGame.tupled, PlayerGame.unapply)
}

我想用Slick编写这样的查询,其中WHERE CLAUSE是动态的。我写了两个例子

SELECT *
FROM players
LEFT JOIN player_games AS playerGamesOne ON players.id = playerGamesOne.playerId AND playerGamesOne.level = 1
LEFT JOIN player_games AS playerGamesTwo ON players.id = playerGamesTwo.playerId AND playerGamesTwo.level = 2
WHERE playerGamesOne.status LIKE 'gameOver'
OR playerGamesTWO.status LIKE 'gameOver'


SELECT *
FROM players
LEFT JOIN player_games AS playerGamesOne ON players.id = playerGamesOne.playerId AND playerGamesOne.level = 1
LEFT JOIN player_games AS playerGamesTwo ON players.id = playerGamesTwo.playerId AND playerGamesTwo.level = 2
WHERE playerGamesOne.status LIKE 'playing'
OR playerGamesTwo.status NOT LIKE 'gameOver'

我正在尝试这样的东西,但我得到Rep[Option[PlayerGameTable]]作为参数。也许有不同的方式来做这样的事情

val baseQuery = for {
  ((p, g1), g2) <- PlayerTable.playerQuery joinLeft 
    PlayerGameTable.playerGameQuery ON ((x, y) => x.id === y.playerId && y.level === 1) joinLeft
    PlayerGameTable.playerGameQuery ON ((x, y) => x._1.id === y.playerId && y.level === 2)
} yield (p, g1, g2)

private def filterPlayerGames(gameStatus: String, playerGamesOneOpt: Option[PlayerGameTable], playerGamesTwoOpt: Option[PlayerGameTable]) = {
  (gameStatus, playerGamesOneOpt, playerGamesOneOpt) match {
    case (gameStatus: String, Some(playerGamesOne: PlayerGameTable), Some(playerGamesOne: PlayerGameTable)) if gameStatus == "gameOver"  => playerGamesOne.status === "gameOver" || playerGamesTwo.status === "gameOver"
 }
}

这是一个复杂的问题,如果问题不明确请告诉我,我会尽力澄清

scala playframework slick
1个回答
2
投票

有几个问题:

  1. 在多种条件下,underscore子句中使用的ON占位符不会按照预期的方式工作
  2. _.level = something是一项任务,而非条件

假设PlayerTable.playerQueryTableQuery[PlayerTable]PlayerGameTable.playerGameQueryTableQuery[PlayerGameTable],你的baseQuery应该是这样的:

val baseQuery = for {
  ((p, g1), g2) <- PlayerTable.playerQuery joinLeft 
    PlayerGameTable.playerGameQuery on ((x, y) => x.id === y.playerId && y.level === 1) joinLeft
    PlayerGameTable.playerGameQuery on ((x, y) => x._1.id === y.playerId && y.level === 2)
} yield (p, g1, g2)

我并不完全清楚你的filterPlayerGames方法将如何处理动态条件。我也不认为任何过滤包装方法都足够灵活,可以用任意and/or/negation算子覆盖多个条件。我建议您使用baseQuery作为必要的joins并在其上构建过滤查询,类似于下面的内容:

val query1 = baseQuery.filter{ case (_, g1, g2) =>
  g1.filter(_.status === "gameOver").isDefined || g2.filter(_.status === "gameOver").isDefined
}

val query2 = baseQuery.filter{ case (_, g1, g2) =>
  g1.filter(_.status === "playing").isDefined || g2.filter(_.status =!= "gameOver").isDefined
}

请注意,使用left joins,g1g2属于Option类型,因此isDefined适用于or操作。

另外注意,鉴于您的过滤条件仅在PlayerGameTable上,在filterings之前执行join可能更有效。

© www.soinside.com 2019 - 2024. All rights reserved.