Я хочу иметь возможность создать запрос с помощью 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"
}
}
Это сложный вопрос, если что-то непонятно, дайте мне знать, и я постараюсь прояснить это.





Есть несколько вопросов:
underscore, используемый в предложении ON, не будет работать должным образом._.level = something это задание, а не условиеПредполагая, что PlayerTable.playerQuery — это TableQuery[PlayerTable], а PlayerGameTable.playerGameQuery — это TableQuery[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 для необходимых join и создавать поверх них фильтрующие запросы, как показано ниже:
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, g1 и g2 относятся к типу Option, поэтому isDefined применяется для операции or.
Отдельное примечание, учитывая, что ваши условия фильтрации только на PlayerGameTable, вероятно, было бы более эффективно выполнять filtering перед joins.
Я думаю, что проблема в joinLeft, я не уверен, но думаю, что именно поэтому я не могу фильтровать условия. Slick не может разрешить === или like. Я получаю value === is not a member of slick.lifted.Rep[Option[Option[String]]]
Большое спасибо за Вашу помощь. Я думаю, что, возможно, проблема в том, что внутри filter у меня нет Option[PlayerGame] у меня есть Rep[Option[PlayerGame]], может быть, поэтому === не распознается, вам это понятно?
@agusgambina, извините, я упростил запросы фильтрации. Ответ обновлен.
@agusgambina, просто к вашему сведению, фильтрующие запросы были упрощены/переработаны.
Спасибо за ваш ответ. Я изменил ошибку запроса, которую я сделал, и ваш ответ действительно имеет для меня смысл. Я пытаюсь заставить это работать. Я не уверен, какую ошибку я делаю, например, в
query1===не может быть решен.