Slick 3.0.0: как запросить отношения "один-ко-многим" и "многие-ко-многим"

В принципе, тот же вопрос задавался примерно год назад для слайков 2.x(scala коллекций "один-ко-многим" ). Мне интересно, есть ли какие-либо улучшения с выпуском реактивного пятна.

Скажем, например, у нас три таблицы. library, book и library_to_book, где в библиотеке много книг. Мне нужен список библиотек с их книгами. В scala это будет что-то вроде Seq[(Library, Seq[Book])]. Запрос, который у меня есть, следующий:

val q = (for {
  l   <- libraries
  ltb <- libraryToBooks if l.id === ltb.libraryId
  b   <- books if ltb.bookId === b.id
} yield (l, b)
db.run(q.result).map( result => ??? )

results в этом случае имеет тип Seq[(Library, Book)]. Как мне изменить свой запрос, чтобы получить результат типа Seq[(Library, Seq[Book])] вместо этого? Что такое "гладкий способ" написания таких запросов?

Ответ 1

IMO ваш код выглядит отлично. Это действительно зависит от того, что вам кажется более читаемым. Кроме того, вы можете использовать join:

val findBooksQuery = libraries
  .join(libraryToBooks).on(_.id === _.libraryId)
  .join(books).on(_.id === _._2.bookId)
  .result

val action = (for {
  booksResult <- findBooksQuery
} yield {
  booksResult.map { row =>
    val (libraryTableRow, libraryToBooksTableRow) = row._1
    val booksTableRow = row._2
    // TODO: Access all data from the rows and construct desired DS
  }
}

db.run(action)

Затем вы можете сделать groupBy на определенном ключе, чтобы получить вид структуры данных, которую вы ищете. В этом случае он будет более развит, поскольку он объединяется через три таблицы. Например, добавьте следующее к вашему запросу:

val findBooksQuery = libraries
  .join(libraryToBooks).on(_.id === _.libraryId)
  .join(books).on(_.id === _._2.bookId)
  // To group by libraries.id
  .groupBy(_._1.id)
  .result

Ответ 2

К тому, что вы хотите сопоставить, db.run возвращает Будущее (чего-то), Будущее [Seq [(Library, Seq [Book])]] в вашем случае. При сопоставлении будущего вы имеете доступ к Seq, и вы можете преобразовать его в нечто другое, чтобы получить новое Будущее.