如何使用 Fluent 在 Vapor 3 中从同一个 table 进行两个 JOIN 查询?
How to do two JOIN query from the same table in Vapor 3 using Fluent?
这就是我想要做的(使用 postgresql):
select H."name", A."name"
来自"Matches" M
加入"Teams" H ON M."homeTeamID" = H.id
加入"Teams" M."awayTeamID" = A.id
//This will give me an error
return Matches.query(on: request)
.join(\Teams.id, to: \Matches.homeTeamID)
.alsoDecode(Teams.self)
.join(\Teams.id, to: \Matches.awayTeamID)
.alsoDecode(Teams.self)
这里是错误:
{
错误:正确,
原因:“table 名称 "Teams" 指定了不止一次”
}
感谢任何帮助!谢谢!
@arema,我尝试重现您的用例,并且在 Fluent 中遇到了类似的问题。
我在 Fluent 的 github 上报告了这个问题:
https://github.com/vapor/fluent/issues/563
这是一个解决方法,但它远非优雅。
// Requires conforming `Match` to hashable, Equatable.
func getMatches2Handler(_ req: Request) throws -> Future<[MatchObjects]> {
return map(
to: [MatchObjects].self,
Match.query(on: req).join(\Team.id, to: \Match.homeTeamID).alsoDecode(Team.self).all(),
Match.query(on: req).join(\Team.id, to: \Match.awayTeamID).alsoDecode(Team.self).all()
) { homeTuples, awayTuples in
let homeDictionary = homeTuples.toDictionary()
let awayDictionary = awayTuples.toDictionary()
var matchObjectsArray: [MatchObjects] = []
matchObjectsArray.reserveCapacity(homeDictionary.count)
for (match, homeTeam) in homeDictionary {
let awayTeam = awayDictionary[match]!
matchObjectsArray.append(MatchObjects(match: match, homeTeam: homeTeam, awayTeam: awayTeam))
}
return matchObjectsArray
}
}
//...
extension Array {
func toDictionary<K,V>() -> [K:V] where Iterator.Element == (K,V) {
return self.reduce([:]) {
var dict:[K:V] = [=10=]
dict[.0] = .1
return dict
}
}
}
我在这里创建了一个测试项目:
https://github.com/mixio/multi-join-test
现在意识到这是一个老问题,但我有一个类似的问题,我通过使用原始 SQL 查询的替代方法解决了它。
下面将为主队和客队名称添加额外的列。您需要创建一个 MatchObject 来解码结果并根据您的情况建立连接。
func matchObjects(_ req: Request) throws -> Future<[MatchObject]> {
return req.withPooledConnection(to: .psql, closure: { conn in
return conn.raw("""
SELECT "Matches".*, h.name as home_team_name, a.name as away_team_name
FROM "Matches"
INNER JOIN "Teams" as h ON "Matches"."homeTeamID" = h.id
INNER JOIN "Teams" as a ON "Matches"."awayTeamID" = a.id
""").all(decoding: MatchObject.self)
})
}
这就是我想要做的(使用 postgresql):
select H."name", A."name"
来自"Matches" M
加入"Teams" H ON M."homeTeamID" = H.id
加入"Teams" M."awayTeamID" = A.id
//This will give me an error
return Matches.query(on: request)
.join(\Teams.id, to: \Matches.homeTeamID)
.alsoDecode(Teams.self)
.join(\Teams.id, to: \Matches.awayTeamID)
.alsoDecode(Teams.self)
这里是错误:
{
错误:正确,
原因:“table 名称 "Teams" 指定了不止一次”
}
感谢任何帮助!谢谢!
@arema,我尝试重现您的用例,并且在 Fluent 中遇到了类似的问题。 我在 Fluent 的 github 上报告了这个问题: https://github.com/vapor/fluent/issues/563
这是一个解决方法,但它远非优雅。
// Requires conforming `Match` to hashable, Equatable.
func getMatches2Handler(_ req: Request) throws -> Future<[MatchObjects]> {
return map(
to: [MatchObjects].self,
Match.query(on: req).join(\Team.id, to: \Match.homeTeamID).alsoDecode(Team.self).all(),
Match.query(on: req).join(\Team.id, to: \Match.awayTeamID).alsoDecode(Team.self).all()
) { homeTuples, awayTuples in
let homeDictionary = homeTuples.toDictionary()
let awayDictionary = awayTuples.toDictionary()
var matchObjectsArray: [MatchObjects] = []
matchObjectsArray.reserveCapacity(homeDictionary.count)
for (match, homeTeam) in homeDictionary {
let awayTeam = awayDictionary[match]!
matchObjectsArray.append(MatchObjects(match: match, homeTeam: homeTeam, awayTeam: awayTeam))
}
return matchObjectsArray
}
}
//...
extension Array {
func toDictionary<K,V>() -> [K:V] where Iterator.Element == (K,V) {
return self.reduce([:]) {
var dict:[K:V] = [=10=]
dict[.0] = .1
return dict
}
}
}
我在这里创建了一个测试项目: https://github.com/mixio/multi-join-test
现在意识到这是一个老问题,但我有一个类似的问题,我通过使用原始 SQL 查询的替代方法解决了它。
下面将为主队和客队名称添加额外的列。您需要创建一个 MatchObject 来解码结果并根据您的情况建立连接。
func matchObjects(_ req: Request) throws -> Future<[MatchObject]> {
return req.withPooledConnection(to: .psql, closure: { conn in
return conn.raw("""
SELECT "Matches".*, h.name as home_team_name, a.name as away_team_name
FROM "Matches"
INNER JOIN "Teams" as h ON "Matches"."homeTeamID" = h.id
INNER JOIN "Teams" as a ON "Matches"."awayTeamID" = a.id
""").all(decoding: MatchObject.self)
})
}