extension Member {
static let services = hasMany(Service.self)
}
struct MemberInfo: Decodable, FetchableRecord {
var member: Member
var services: Bool
}
// << Solution 1 >>
// SELECT member.*, COUNT(DISTINCT service.id) > 0 AS services
// FROM member
// LEFT JOIN service ON service.memberId = member.id
// GROUP BY member.id
let memberInfos = try Member
.annotated(with: (Member.services.isEmpty == false).forKey("services"))
.asRequest(of: MemberInfo.self)
.fetchAll(db)
You may prefer a more straightforward SQL solution:
// << Solution 2 >>
// SELECT *, EXISTS (
// SELECT * FROM service
// WHERE service.memberId = member.id
// ) AS services
// FROM "member"
let memberInfos = try Member
.annotated(with: [SQL("""
EXISTS (
SELECT * FROM service
WHERE service.memberId = member.id
) AS services
""")])
.asRequest(of: MemberInfo.self)
.fetchAll(db)
The SQL version above can also be generated with Swift apis, as below:
// << Solution 3 >>
let memberAlias = TableAlias()
let services = Service.filter(Column("memberId") == memberAlias[Column("id")])
let memberInfos = try Member
.aliased(memberAlias)
.annotated(with: services.exists().forKey("services"))
.asRequest(of: MemberInfo.self)
.fetchAll(db)
You'll pick your favorite solution. If your database is big, maybe make benchmarks and choose the most efficient one. Otherwise, pick the one that looks the most clear and easy to maintain for you. My personal choice would be the raw SQL one.