蒸汽 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)
              })
          })
      }
    })
}