Node.js Sequelize(PostgresSQL)多对多聊天实现

Node.js Sequelize(PostgresSQL)多对多聊天实现,node.js,postgresql,sequelize.js,Node.js,Postgresql,Sequelize.js,我一直在尝试创建一个带有Node JS和Sequelize的聊天应用程序。现在,我遇到了一个问题:创建一个查询来查找一个有我的id和用户id(我正试图发送文本的id)的对话。因此,我要做的是发送一个post请求,其中包含我要发送消息的用户的id,然后我查看我的对话模型,检查该对话是否包含我的id和我要发送短信的用户的id 我的模型通过多对多关系进行关联。因此,主要目标是只找到与我的ID和使用相同的会话ID发短信给的用户的ID的对话 以下是我的模型: 使用者 谈话 module.exports =

我一直在尝试创建一个带有Node JS和Sequelize的聊天应用程序。现在,我遇到了一个问题:创建一个查询来查找一个有我的id和用户id(我正试图发送文本的id)的对话。因此,我要做的是发送一个post请求,其中包含我要发送消息的用户的id,然后我查看我的对话模型,检查该对话是否包含我的id和我要发送短信的用户的id

我的模型通过多对多关系进行关联。因此,主要目标是只找到与我的ID和使用相同的会话ID发短信给的用户的ID的对话

以下是我的模型: 使用者

谈话

module.exports = (sequelize, DataTypes) => {
  const Conversation = sequelize.define(
    "Conversation",
    {
      lastMessage: DataTypes.STRING,
      recipients: DataTypes.ARRAY(DataTypes.INTEGER),
    },
    {
      sequelize,
      modelName: "Conversation",
    }
  );

  Conversation.associate = (models) => {
    Conversation.belongsToMany(models.User, {
      as: "participants",
      foreignKey: "conversation_id",
      through: models.ConversationUsers,
    });
    Conversation.hasMany(models.Message, {
      as: "messages",
    });
  };

  return Conversation;
};


对话用户通过模型多对多

"use strict";

module.exports = (sequelize, DataTypes) => {
  const ConversationUsers = sequelize.define(
    "ConversationUsers",
    {
      user_id: DataTypes.INTEGER,
      conversation_id: DataTypes.INTEGER,
    },
    {
      sequelize,
      modelName: "ConversationUsers",
    }
  );

  return ConversationUsers;
};
信息


module.exports = (sequelize, DataTypes) => {
  const Message = sequelize.define(
    "Message",
    {
      conversationId: { type: DataTypes.INTEGER, allowNull: false },
      sentTo: DataTypes.INTEGER,
      sentFrom: DataTypes.INTEGER,
      body: { type: DataTypes.STRING, allowNull: false },
    },
    {
      sequelize,
      modelName: "Message",
    }
  );

  Message.associate = (models) => {
    Message.belongsTo(models.User, {
      as: "messageTo",
      foreignKey: "sentTo",
    });
    Message.belongsTo(models.User, {
      as: "messageFrom",
      foreignKey: "sentFrom",
    });
    Message.belongsTo(models.Conversation, {
      as: "messages",
    });
  };
  return Message;
};


我想你可以从你的模型上去掉一些碎片,再重新做一点。 消息不需要
sentTo
,它们只需要
sentFrom
。您可以使用
ConversationUsers
表了解收件人。这也为您提供了与两个以上成员进行对话的灵活性,因为您当前的模型本质上强制一条消息只能发送给一个用户

所以,让我们先来看看模型的变化

module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define(
    "User",
    {
      name: { type: DataTypes.STRING },
      password: { type: DataTypes.STRING, allowNull: false },
      username: { type: DataTypes.STRING, allowNull: false },
      email: { type: DataTypes.STRING, allowNull: false },
    },
    {
    // I think moving the associations to other files might make this more clear
    }

  );
  };
  return User;
};
好的,现在谈谈你的问题,现在可能会更简单。如果您已经知道
会话ID
,则只需检查发送消息的人是否是会话的成员。如果是,则在
Messages
表中写入一行。不管信息“发给”谁——你是在给对话的成员写信,而不是给任何个人

async function canMessageHelper({conversationId, userId }) {
    const convo = await models.Conversation.findOne({
        attributes: ["id"], // whatever attrs you need, probably not many if any
        where: {
            id: conversationId,
        },
        include: [{
            model: models.ConversationMember,
            attributes: ["ConversationId"], // whatever you need if anything
            where: { // this where is critical, it creates an inner join so convo only returns if myUserId is a member of the Conversation
                 UserId: userId 
            }
        }]
    });
    
    if (!convo) {
       return false;
    }
    return convo;

}

async function sendMessage({conversationId, authorUserId, messageText}) {
   const allowMessage = await canMessageHelper({conversationId, userId: authorUserId}); 
   if (!allowMessage) {
      return false; 
   }
   await models.Message.create({sentFromUserId: authorUserId, body: messageText});
}

如果要尝试此操作,请确保在同步之前从数据库中删除已使用这些名称创建的所有表


我没有为我提到的钩子提供任何代码,但您将有基础来开发这些想法。

嗨,如果我真的只想将消息发送给一个用户怎么办?您仍然可以使用它,只要有一些逻辑来确保每个对话最多有两个成员。
module.exports = (sequelize, DataTypes) => {
  const Conversation = sequelize.define(
    "Conversation",
    {
        // perhaps something like a subject could go here e.g.
        subject: DataTypes.STRING(500),
    },
    {
      sequelize,
      modelName: "Conversation",
    }
  );

  Conversation.associate = (models) => {
    Conversation.hasMany(models.Message, {
      as: "ConversationMessages",
    }); // adds ConversationId onto Message, gives us Conversation.getConversationMessages() etc
    models.Message.belongsTo(Conversation); // create association both ways for convenience methods to find convo from a message 
    models.Message.hasOne(Conversation, {
        as: 'LastMessage', 
        constraints: false, 
        allowNull:true, 
        defaultValue:null
    }); // adds LastMessageId onto Conversation model (you'll have to write code to maintain this value, probably through an afterCreate hook on Message model)

  };

  return Conversation;
};
module.exports = (sequelize, DataTypes) => {
  const Message = sequelize.define(
    "Message",
    {
     id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true, // if you want to do the hook thing i talked about to set LastMessageId, you need to put this in 
    },
      body: { type: DataTypes.STRING, allowNull: false },
    },
    {
      sequelize,
      modelName: "Message",
    }
  );

  Message.associate = (models) => {
     Message.belongsTo(models.User, {as: "sentFromUser"});
  };
  return Message;
};
   // I'm going to rename your many-to-many table "ConversationMembers"
   module.exports = (sequelize, DataTypes) => {
   const ConversationMembers = sequelize.define(
    "ConversationMembers",
    {
    // again, the associations will build these fields for you
    },
    {
      sequelize,
      modelName: "ConversationMembers",
    }
    );

    models.Conversation.belongsToMany(models.User, {
      through: "ConversationMember",
      as: "Members",
    }); // gives us Conversation.getMembers()
    models.User.belongsToMany(models.Conversation, {
      through: "ConversationMember",
      as: "MemberConversations",
    }); // gives us User.getMemberConversations()
    ConversationMember.belongsTo(models.Message, { as: "LastReadMessage" }); // gives us the potential ability to track the last read message for each convo member as ConversationMember.LastReadMessageId, you'll need to set this value manually on read for each user if you care about having it
    models.Conversation.hasMany(ConversationMember);
    models.User.hasMany(ConversationMember);
  
    return ConversationMember;

async function canMessageHelper({conversationId, userId }) {
    const convo = await models.Conversation.findOne({
        attributes: ["id"], // whatever attrs you need, probably not many if any
        where: {
            id: conversationId,
        },
        include: [{
            model: models.ConversationMember,
            attributes: ["ConversationId"], // whatever you need if anything
            where: { // this where is critical, it creates an inner join so convo only returns if myUserId is a member of the Conversation
                 UserId: userId 
            }
        }]
    });
    
    if (!convo) {
       return false;
    }
    return convo;

}

async function sendMessage({conversationId, authorUserId, messageText}) {
   const allowMessage = await canMessageHelper({conversationId, userId: authorUserId}); 
   if (!allowMessage) {
      return false; 
   }
   await models.Message.create({sentFromUserId: authorUserId, body: messageText});
}