Node JS 和 Sequelize:显示当前用户的所有对话

Posted

技术标签:

【中文标题】Node JS 和 Sequelize:显示当前用户的所有对话【英文标题】:NodeJS & Sequelize: showing all the conversation the current user has 【发布时间】:2020-12-14 06:51:07 【问题描述】:

我目前正在尝试从我的所有对话屏幕中实现我的后端。我正在查找所有对话并包括与每个对话相关的所有消息。

在我的 React Native 前端,在我的所有对话屏幕中,我将所有对话显示为一个平面列表,并通过从消息列表中挑选出来显示两个用户之间发送的最后一条消息。

我应该继续使用这种方法,还是在性能方面更好,在会话表中包含指向最后一条消息的链接并在每次在会话中发送新消息时更新它?如果是这样,我该如何更改我的代码?

对话模型:

const Conversation = db.define("Conversation", ,

paranoid: true, 
timestamps: true, 
deletedAt: "deletedAt",

);

module.exports = Conversation;

User.hasMany(Conversation, 
foreignKey: 'user1'
);

User.hasMany(Conversation, 
foreignKey: 'user2'
);

Conversation.belongsTo(User,  as: 'Creator', foreignKey: 'user1', allowNull: false )
Conversation.belongsTo(User,  as: 'Recipient', foreignKey: 'user2', allowNull: false )

消息模型:

const Message = db.define("Message", 
senderId: 
type: Sequelize.STRING,
,
receiverId: 
type: Sequelize.STRING,
,
message: 
type: Sequelize.STRING,
,
conversationId:
type: Sequelize.INTEGER,

);

module.exports = Message;

User.hasMany(Message, 
foreignKey: 'senderId',
);

User.hasMany(Message, 
foreignKey: 'receiverId',
);

  Message.associate = (models) => 
  Message.belongsTo(models.Conversations,
  foreignKey: "conversationId",
  
  ); 
  ;

Conversation.hasMany(Message,
foreignKey: "conversationId",

);

Message.belongsTo(User, 
as:"Sender",
foreignKey: "senderId",
allowNull: false 
);

Message.belongsTo(User, 
as:"Receiver",
foreignKey: "receiverId",
allowNull: false 
);

获取所有对话查询:

router.get('/', auth, async (req, res) => 
const conversations = await Conversation.findAll(
  order: [[Message,"createdAt", "DESC"]],
  include: [
    
    model: Message,
    where: 
      [Op.or]: [
         senderId: req.user.id , 
         receiverId: req.user.id ,
      ],
    ,
    required: true, // RIGHT  JOIN
  ]
  )
 

if (!conversations) return res.status(404).send();

    res.status(200).send();
);

【问题讨论】:

【参考方案1】:

您可以尝试将每个对话中包含的消息限制为仅最后一条消息

const conversations = await Conversation.findAll(
  order: [[Message,"createdAt", "DESC"]],
  include: [
    
    model: Message,
    order: [["createdAt", "DESC"]],
    limit: 1,
    where: 
      [Op.or]: [
         senderId: req.user.id , 
         receiverId: req.user.id ,
      ],
    ,
    required: true, // RIGHT  JOIN
  ]
  )

如果上述查询不起作用,您可以随时尝试存储对话中的最后一条消息 ID,但如果您存储一条消息并忘记更新指向对话中最后一条消息的链接,或者您删除最后一条消息并再次忘记更新对话中的链接。

【讨论】:

嗨 Anatoly,我尝试了上面的查询并得到了这个错误 (node:35418) UnhandledPromiseRejectionWarning: SequelizeDatabaseError: missing FROM-clause entry for table "Messages" 订单语句只能在查询模型的顶层。为此,请删除内部订单语句 ant limit,将 limit statemet 移到 inlcude 之外 将发送者和接收者的条件移动到Conversation,关闭required,将separate: true添加到include选项 @hellikiam 将 limit 移动到选项将限制对话而不是消息! @Anatoly 确实如此。但是仍然不能在这一点中使用命令吗?我的意思是,在包含【参考方案2】:

您可以将对话和消息缓存到本地数据库中 使用移动设备的sqlite。

为此,您不必每次登录都查询整个对话。 元数据表甚至可以帮助缓解服务器压力

我建议你在RN中使用sqlite并在远程数据库中添加元表。


图案。

    登录时从远程数据库加载会话并计算消息数。 (如果元数据表存在,这部分会更快更轻)

    显示转换时,使用本地数据库中的最新数据。

    比较消息的数量,如果数量不同,您可以假设此设备中的用户没有查看最新消息。所以渲染徽章组件以通知用户他或她还没有阅读它。

    当用户进入会话时,获取消息并将其存储到设备的本地数据库中。


Socket io部分 简要概念是这样的。

参考。 https://socket.io/docs/v3/server-api/#socket-on-eventName-callback

客户

    const [ conversations, setConversations ] = useState([])
    const [ syncState, setSyncState ] = useState(false)
    const [ socket, setSocket ] = useState(null)

    useEffect(() =>
    ...
    // you might wanna set conversation from local database here
    ...

    io.on('connection', soc => 
        soc.join("your room")
        ...
        socket.on('synced', data =>
           //mapNewDataToConversationState
           ...
           setSynced(true)
        )
        setSocket(soc)
      )
    
    ...
    , [ ... ])
     

    useEffect(() =>//when loaded and socked established
      if(!socket) return
      if(syncState) return
      socket.emit('sync',  data : conversation )
    , [ socket ])

服务器


//let say you already set app.io as socket io connection
//add a event listener for client sync

socket.on('sync', async conversation =>
   const responseData = 

   for(const c of conversation)
      const lastestMsg = c.Message[c.Message.length - 1]
      
      const messages = await Message.findAll(
        where : 
        ...//Maybe greater than createdAt, any cursor attribute in here
        
      )

      responseData[c.id] = messages
   

   socket.emit('synced', responseData )

)

【讨论】:

您好,感谢您回复我。我目前在后端使用 Postgres。如何使用本地数据库 sqlite? 我建议你使用 sqlite 不是为了后端存储兄弟。您可以启动 RN 的 sqlite。签出,github.com/andpor/react-native-sqlite-storage 和asyncStorage是同一个概念吗? 我不知道异步存储。所以我查了一下,reactnative.dev/docs/asyncstorage。我认为你是对的,因为 android 使用 asyncStorage 作为 RocksDB 和 Sqlite。 如果你使用的是socket io,我猜这个解决方案会更容易

以上是关于Node JS 和 Sequelize:显示当前用户的所有对话的主要内容,如果未能解决你的问题,请参考以下文章

Node.js,ORM框架,Sequelize,入门及增、删、改、查代码案例

多张图片 + Node.js + Sequelize + Mysql

Node.js 和 sequelize-typescript - 数据访问对象和业务对象

如何使用 Sequelize 和 node.js 进行批量插入

Node.js - 使用 'async' 和 'await' 和 sequelize ORM

如何在 Node.js 中使用 Sequelize 运行 AND 和 Or 运算符