【问题标题】:Scala Slick joinLeft and combined conditionsScala Slick joinLeft 和组合条件
【发布时间】:2019-03-08 01:29:09
【问题描述】:

我希望能够使用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】:

    有几个问题:

    1. 在多个条件下,ON 子句中使用的 underscore 占位符将无法按预期方式工作
    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 上,在joins 之前执行filtering 可能会更有效。

    【讨论】:

    • 感谢您的回答。我改变了我犯的查询错误,你的回答对我来说真的很有意义。我正试图让这个工作。我不确定我在做什么错误,例如,query1 中的=== 无法解决。
    • 我认为问题出在joinLeft,我不确定,但我认为这就是我无法过滤条件的原因。 Slick 无法解析 ===like。我得到value === is not a member of slick.lifted.Rep[Option[Option[String]]]
    • 非常感谢您的帮助。我认为问题可能在于filter 内部我没有Option[PlayerGame] 我有Rep[Option[PlayerGame]] 也许这就是=== 未被识别的原因,这对你有意义吗?
    • @agusgambina,对不起,我过度简化了过滤查询。答案已更新。
    • @agusgambina,仅供参考,过滤查询已被简化/重构。
    猜你喜欢
    • 1970-01-01
    • 2021-04-24
    • 2016-02-15
    • 2015-02-21
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    • 2020-08-24
    • 1970-01-01
    相关资源
    最近更新 更多