Проблема с построением запроса с sequelize

#node.js #postgresql #express #sequelize.js

#node.js #postgresql #выразить #sequelize.js

Вопрос:

У меня возникли проблемы с воссозданием следующего запроса в Sequelize. Я сидел часами, пытаясь разобраться в этом.

Я использую camelCase вместо snake_case, поэтому я не могу повторно использовать один и тот же запрос.

Запрос не сработает, если я заменю все переменные snake_cased на camelCase в запросе. Я возвращаю «отношение»directmessages «не существует». Я не могу получить доступ к таблице directmessages в psql с помощью ТАБЛИЦЫ directmessages; — но я знаю, что она должна быть там.

Я также не могу понять, как выполнить тот же запрос с sequelize.

В принципе, мне нужно получить каждого пользователя, с которым текущий пользователь уже напрямую связался. Либо в качестве отправителя, либо получателя.

   models.sequelize.query(
    'select distinct on (u.id) u.id, u.username from users as u join direct_messages as dm on (u.id = dm.sender_id) or (u.id = dm.receiver_id) where (:currentUserId = dm.sender_id or :currentUserId = dm.receiver_id) and dm.team_id = :teamId',
    {
      replacements: { currentUserId: user.id, teamId: id },
      model: models.User,
      raw: true,
    },
  

Это соответствующие модели для этого запроса:

Пользовательская модель:

 User.associate = (models) => {
    User.belongsToMany(models.Team, {
        through: models.Member,
        foreignKey: 'userId',
    })
    User.belongsToMany(models.Channel, {
        through: 'channel_member',
        foreignKey: 'userId',
    })

}
  

Модель DirectMessage:

 DirectMessage.associate = (models) => {
    DirectMessage.belongsTo(models.Team, {
        foreignKey: 'teamId',
    })
    DirectMessage.belongsTo(models.User, {
        foreignKey: 'receiverId',
    })
    DirectMessage.belongsTo(models.User, {
        foreignKey: 'senderId',
    })
}
  

Я попытался создать запрос с sequelize следующим образом:

 models.User.findAll({ 
            include:[{
                model: models.DirectMessage,
                where: {                        
                    teamId, 
                    [models.sequelize.Op.or]: [{senderId: user.id}, {receiverId: user.id}]
                }
            }]
            }, { raw: true })
  

Я получаю обратно «сообщение»: «directMessage не связан с пользователем!», что, я полагаю, связано с тем, что DirectMessage связан с пользователем, но не наоборот.

Есть ли у кого-нибудь какие-либо советы о том, как я могу восстановить этот запрос?

Комментарии:

1. Пытался удалить базу данных, воссоздав все данные. При попытке извлечь directMessageMembers выдается следующая ошибка: «сообщение»: «отношение «directmessages» не существует»

Ответ №1:

я полагаю, это потому, что DirectMessage связан с User, но не наоборот.

Я бы тоже так предположил. Достаточно просто связать пользователя с DirectMessage (дважды):

 User.hasMany(models.DirectMessage, {foreignKey: 'receiverId', as: 'receivers'});        
User.hasMany(models.DirectMessage, {foreignKey: 'senderId', as: 'senders'});        
  

Тогда запрос не так уж плох. Обратите внимание, что значение «as» указывает Sequelizer, какой FK использовать, поэтому убедитесь, что hasMany соответствует модели включения запросов

     models.Users.findAll({
        include: [
            {
                model: models.DirectMessage, as: 'receivers',
                attributes:  [[Sequelize.fn('count', 'id'), 'number_received']]
            },
            {
                model: models.DirectMessage, as: 'senders',
                attributes: [[Sequelize.fn('count', 'id'), 'number_sent']]
            }
            ],
        where : {id : user.id}, // parameter
        group : ['id']
    })        
  

Затем вам нужно будет убедиться, что либо «senders.number_sent», либо «receivers.number_received» не равны нулю и > 0. Теоретически, вы могли бы сделать это с помощью предложения HAVING , но ИМХО это не слишком хорошо реализовано в Sequelize….

Ответ №2:

Здесь есть пара моментов. Во-первых, когда вы используете belongsToMany , вам нужно выполнить ассоциацию для обеих моделей, потому что это отношение M: N, например: Также у вас неверный foreignKey параметр для ассоциации. Должно быть что-то вроде этого:

 User.associate = (models) => {
  User.belongsToMany(models.Team, {
      through: models.Member,
      foreignKey: 'user_id',
  })

  models.Team.belongsToMany(User, {
      through: models.Member,
      foreignKey: 'team_id',
  })

  // ----------------------------------
  User.belongsToMany(models.Channel, {
      through: 'channel_member',
      foreignKey: 'user_id',
  })

  models.Channel.belongsToMany(user, {
      through: 'channel_member',
      foreignKey: 'channel_id',
  })
  

}

Теперь, чтобы иметь возможность использовать имена camelCase в javascript и sequelize и сохранить snake_case в sql, вы можете сделать это:

 module.exports = (sequelize, DataTypes) => {
  const Member = sequelize.define('Member', {
    // here goes your regular fields
    userId: { //now you can use this property on sequelize
      field: 'user_id', //and is linked yo the foreignKey
      type: DataTypes.INTEGER
    },
    teamId: {
      field: 'team_id',
      type: DataTypes.INTEGER
    }
  }
}
  

И последняя проблема, которая у вас возникает с включением, заключается в том, что вам нужно указать, какую ассоциацию вы вызываете, и, как вы сказали, вам нужно
объявить ассоциацию другим способом, от User к DirectMessage :

 User.hasMany(models.DirectMessage, { //there is also hasOne()
  as: 'Sender', // this is important because you have two association from User to  DirectMessage
  foreignKey: 'sender_id',
})

User.hasMany(models.DirectMessage, { //there is also hasOne()
  as: 'Receiver', // this is important because you have two association from User to  DirectMessage
  foreignKey: 'user_id',
})
  

Теперь вы можете выполнить свой запрос

 models.User.findAll({ 
  include:[{
      model: models.DirectMessage,
      as: 'Sender'
      where: {                        
          teamId,
      }
  }]
}, { raw: true })
  

У меня есть сомнения по поводу этого запроса, потому что на самом деле вы вызываете все, DirectMessages поэтому фильтр senderId и receiverId не имеет особого смысла.

В любом случае, надеюсь, что с этим есть лучшая идея, что делать.