-
Hello, I was reading through the association documentation and I was wondering if there was way to fetch only a subset in a Let's take the documentation's example: extension Author: TableRecord {
static let books = hasMany(Book.self)
}
struct AuthorInfo: Decodable, FetchableRecord {
struct PartialBook: Decodable {
var title: String
var year: Int
}
var author: Author // The base record
var books: [PartialBook] // A collection of partial associated records
}
let authorInfos = try Author
.including(all: Author.books.select(Column("title"), Column("year")))
.asRequest(of: AuthorInfo.self)
.fetchAll(db) Is there a way to
struct AuthorInfo: Decodable, FetchableRecord {
struct PartialBook: Decodable {
var title: String
var year: Int
}
var author: Author // The base record
var firstBook: PartialBook?
} Thank you. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Hello @adilrc,
Yes, but not trivially, because this is not a built-in GRDB feature. You'll need to do by hand what The easiest technique is to perform as many book requests as there are authors: extension Author {
static let books = hasMany(Book.self)
}
struct AuthorInfo: Decodable, FetchableRecord {
struct PartialBook: Decodable, FetchableRecord {
var title: String
var year: Int
}
var author: Author // The base record
var books: [PartialBook] // A collection of partial associated records
}
// Fetch authors (filtered, ordered, whatever)
let authors = try Author.fetchAll(db)
// Fetch the three most recent partial books for each author
let authorInfos = try authors.map { author in
let books = try author
.request(for: Author.books)
.select(Column("title"), Column("year"))
.order(Column("year").desc, Column("title"))
.limit(3)
.asRequest(of: AuthorInfo.PartialBook.self)
.fetchAll(db)
return AuthorInfo(
author: author,
books: books)
} A more involved technique performs only two SQL requests, but it is much more complex:
Sample codestruct AuthorInfo: Decodable, FetchableRecord {
struct PartialBook: Decodable, FetchableRecord {
var authorId: Int64 // Needed for grouping by author
var title: String
var year: Int
}
var author: Author
var books: [PartialBook]
}
// Fetch authors (filtered, ordered, whatever)
let authors = try Author.fetchAll(db)
// The request for partial books
let authorIds = authors.map(\.id)
let partialBooksRequest = Book.all()
.filter(authorIds.contains(Column("authorId")))
.select(
Column("authorId"), // Needed for grouping by author
Column("title"),
Column("year"))
// The common table expression for the three most recent partial books.
let partialBooksCTE = CommonTableExpression(
named: "rankedBooks",
request: partialBooksRequest.annotated(with: SQL("""
ROW_NUMBER() OVER (PARTITION BY authorId ORDER BY year DESC, title)
""").forKey("rn")))
// Fetch partial books
let partialBooks = try partialBooksCTE.all().with(partialBooksCTE)
.filter(Column("rn") <= 3)
.order(Column("year").desc, Column("title"))
.asRequest(of: AuthorInfo.PartialBook.self)
.fetchAll(db)
// Build the result from fetched authors and partial books
let partialBooksByAuthorId = Dictionary(grouping: partialBooks, by: \.authorId)
let authorInfos = authors.map { author in
AuthorInfo(
author: author,
books: partialBooksByAuthorId[author.id, default: []])
}
I suppose this can be derived from the previous sample codes. |
Beta Was this translation helpful? Give feedback.
Hello @adilrc,
Yes, but not trivially, because this is not a built-in GRDB feature.
You'll need to do by hand what
including(all:)
does for you.The easiest technique is to perform as many book requests as there are authors: