Scala Slick joinLeft и комбинированные условия

Я хочу иметь возможность создать запрос с помощью 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"
 }
}

Это сложный вопрос, если что-то непонятно, дайте мне знать, и я постараюсь прояснить это.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
1 049
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Есть несколько вопросов:

  1. При наличии нескольких условий заполнитель underscore, используемый в предложении ON, не будет работать должным образом.
  2. _.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.

Спасибо за ваш ответ. Я изменил ошибку запроса, которую я сделал, и ваш ответ действительно имеет для меня смысл. Я пытаюсь заставить это работать. Я не уверен, какую ошибку я делаю, например, в query1=== не может быть решен.

agusgambina 08.03.2019 14:45

Я думаю, что проблема в joinLeft, я не уверен, но думаю, что именно поэтому я не могу фильтровать условия. Slick не может разрешить === или like. Я получаю value === is not a member of slick.lifted.Rep[Option[Option[String]]]

agusgambina 08.03.2019 16:19

Большое спасибо за Вашу помощь. Я думаю, что, возможно, проблема в том, что внутри filter у меня нет Option[PlayerGame] у меня есть Rep[Option[PlayerGame]], может быть, поэтому === не распознается, вам это понятно?

agusgambina 08.03.2019 18:03

@agusgambina, извините, я упростил запросы фильтрации. Ответ обновлен.

Leo C 08.03.2019 20:37

@agusgambina, просто к вашему сведению, фильтрующие запросы были упрощены/переработаны.

Leo C 11.03.2019 21:49

Другие вопросы по теме