蒸汽 4 流利。复杂的查询,如果不存在则过滤和创建
Vapor 4 fluent. Complex query, filter and create if doesn't exist
我正在尝试查询与两个用户的聊天。如果它存在,我想 return 它。如果不是,我想与这两个用户创建新的聊天并 return 它。
如果存在聊天,评论会显示失败的地方。
如果你能展示如何在这种情况下与这两个用户建立新的聊天,也会很有帮助。
func create(req: Request) throws -> EventLoopFuture<Chat> {
let currentUser = try req.auth.require(User.self)
guard
let userID = req.parameters.get("userID"),
let uuid = UUID(userID)
else { throw Abort(.badRequest) }
return User
.query(on: req.db)
.filter(\.$id == uuid)
.with(\.$chats)
.first()
.unwrap(or: Abort(.internalServerError))
.flatMap({ chatUser -> EventLoopFuture<Chat> in
let chat = chatUser
.chats
.filter({
[=10=].users.contains(where: { [=10=].id == currentUser.id }) // FAILS HERE: Fatal error: Siblings relation not eager loaded, use $ prefix to access: Siblings<Chat, User, ChatUser>(from: [chat_id], to: [user_id]): file
})
.first
if let chat = chat {
return req
.eventLoop
.makeSucceededFuture(chat)
} else {
// create new chat with those two users and return
}
})
}
简化模型:
final class Chat: Model, Content {
@ID(key: .id)
var id: UUID?
@Siblings(through: ChatUser.self, from: \.$chat, to: \.$user)
var users: [User]
}
final class User: Model, Content {
@ID(key: .id)
var id: UUID?
@Siblings(through: ChatUser.self, from: \.$user, to: \.$chat)
var chats: [Chat]
}
final class ChatUser: Model {
@ID(key: .id)
var id: UUID?
@Parent(key: Keys.chatId)
var chat: Chat
@Parent(key: Keys.userId)
var user: User
}
您的问题是,本质上您需要对 ChatUser
模型中使用的 User
模型进行进一步的预加载。如果可能的话,在您的第一个查询中,以及 .with(\.$chats)
您会做类似 .with(\.$chats.$chats)
的事情,但这是不可能的。
在这种情况下,与其直接回答您的问题,我能否建议您考虑从 ChatUser
上的查询开始?如果您在此 Model
中查找匹配的记录,然后相应地进行操作。类似于:
ChatUser.query(on:req.db).filter(\.$id == currentUser.id).filter(\.$chat == userId).first().map { mayExist in
if let doesExist = mayExist {
// work with existing chat
} else {
// does not exist so create a new one
// how, depends on your init for the model
let newChat = ChatUser( user:currentUser.id!, chat:userId)
newChat.create(on:req.db).map{
// do something here
}
}
}
PS - 赶着上班,所以这里可能漏洞百出!
0xTim 在 Discord 上帮助了我。我在阅读 This doc
后设法让它工作
这是可行的解决方案:
func create(req: Request) throws -> EventLoopFuture<Chat> {
let currentUser = try req.auth.require(User.self)
guard
let userID = req.parameters.get("userID"),
let userUUID = UUID(userID)
else { throw Abort(.badRequest) }
return User
.query(on: req.db)
.filter(\.$id == userUUID)
.with(\.$chats, { chats in
chats.with(\.$users)
})
.first()
.unwrap(or: Abort(.internalServerError))
.flatMap({ chatUser -> EventLoopFuture<Chat> in
let chat = chatUser
.chats
.filter({
[=10=].users.contains(where: { [=10=].id! == currentUser.id! })
})
.first
if let chat = chat {
return req.eventLoop.makeSucceededFuture(chat)
} else {
let newChat = Chat()
return newChat
.save(on: req.db)
.flatMap({
newChat
.$users
.attach([chatUser, currentUser], on: req.db)
.flatMap({
return req.eventLoop.makeSucceededFuture(newChat)
})
})
}
})
}
我正在尝试查询与两个用户的聊天。如果它存在,我想 return 它。如果不是,我想与这两个用户创建新的聊天并 return 它。
如果存在聊天,评论会显示失败的地方。
如果你能展示如何在这种情况下与这两个用户建立新的聊天,也会很有帮助。
func create(req: Request) throws -> EventLoopFuture<Chat> {
let currentUser = try req.auth.require(User.self)
guard
let userID = req.parameters.get("userID"),
let uuid = UUID(userID)
else { throw Abort(.badRequest) }
return User
.query(on: req.db)
.filter(\.$id == uuid)
.with(\.$chats)
.first()
.unwrap(or: Abort(.internalServerError))
.flatMap({ chatUser -> EventLoopFuture<Chat> in
let chat = chatUser
.chats
.filter({
[=10=].users.contains(where: { [=10=].id == currentUser.id }) // FAILS HERE: Fatal error: Siblings relation not eager loaded, use $ prefix to access: Siblings<Chat, User, ChatUser>(from: [chat_id], to: [user_id]): file
})
.first
if let chat = chat {
return req
.eventLoop
.makeSucceededFuture(chat)
} else {
// create new chat with those two users and return
}
})
}
简化模型:
final class Chat: Model, Content {
@ID(key: .id)
var id: UUID?
@Siblings(through: ChatUser.self, from: \.$chat, to: \.$user)
var users: [User]
}
final class User: Model, Content {
@ID(key: .id)
var id: UUID?
@Siblings(through: ChatUser.self, from: \.$user, to: \.$chat)
var chats: [Chat]
}
final class ChatUser: Model {
@ID(key: .id)
var id: UUID?
@Parent(key: Keys.chatId)
var chat: Chat
@Parent(key: Keys.userId)
var user: User
}
您的问题是,本质上您需要对 ChatUser
模型中使用的 User
模型进行进一步的预加载。如果可能的话,在您的第一个查询中,以及 .with(\.$chats)
您会做类似 .with(\.$chats.$chats)
的事情,但这是不可能的。
在这种情况下,与其直接回答您的问题,我能否建议您考虑从 ChatUser
上的查询开始?如果您在此 Model
中查找匹配的记录,然后相应地进行操作。类似于:
ChatUser.query(on:req.db).filter(\.$id == currentUser.id).filter(\.$chat == userId).first().map { mayExist in
if let doesExist = mayExist {
// work with existing chat
} else {
// does not exist so create a new one
// how, depends on your init for the model
let newChat = ChatUser( user:currentUser.id!, chat:userId)
newChat.create(on:req.db).map{
// do something here
}
}
}
PS - 赶着上班,所以这里可能漏洞百出!
0xTim 在 Discord 上帮助了我。我在阅读 This doc
后设法让它工作这是可行的解决方案:
func create(req: Request) throws -> EventLoopFuture<Chat> {
let currentUser = try req.auth.require(User.self)
guard
let userID = req.parameters.get("userID"),
let userUUID = UUID(userID)
else { throw Abort(.badRequest) }
return User
.query(on: req.db)
.filter(\.$id == userUUID)
.with(\.$chats, { chats in
chats.with(\.$users)
})
.first()
.unwrap(or: Abort(.internalServerError))
.flatMap({ chatUser -> EventLoopFuture<Chat> in
let chat = chatUser
.chats
.filter({
[=10=].users.contains(where: { [=10=].id! == currentUser.id! })
})
.first
if let chat = chat {
return req.eventLoop.makeSucceededFuture(chat)
} else {
let newChat = Chat()
return newChat
.save(on: req.db)
.flatMap({
newChat
.$users
.attach([chatUser, currentUser], on: req.db)
.flatMap({
return req.eventLoop.makeSucceededFuture(newChat)
})
})
}
})
}