Использование И работа со связью «многие ко многим» в Sequelize

#javascript #sql #node.js #postgresql #sequelize.js

#javascript #sql #node.js #postgresql #sequelize.js

Вопрос:

Моя проблема в двух словах: я хочу использовать операцию И в объединенной таблице. Я думаю, что это очень общий вариант использования в реальной жизни, но я пока не нашел ни одной связанной статьи, блога или проблемы. (Я думаю, это плохо: D)

Позвольте мне описать пример того, что я имею в виду: я хотел бы создать веб-магазин, и у меня есть мобильная и функциональная модели, и между ними существует отношение «многие ко многим». Для этой функции есть фильтр с несколькими вариантами выбора (на моем веб-сайте), и я хочу перечислить те мобильные телефоны, у которых есть выбранные функции. (например.: A, B и C …) Я думаю, что я не могу создать его одним запросом, потому что столбец не может быть A и B одновременно, но я не уверен. Пример:

     const mobiles = await models.mobile.findAll({
      where: '???',
      attributes: ['id', 'name'],
      include: [
        {
          model: models.feature,
          where: '???',
          attributes: ['id', 'name],
          through: {
            attributes: [],
          },
        },
      ],
    });
  

Меня интересует Sequelize, а также SQL-решение.

Примеры моделей и ожидаемый результат:

 const Mobile = sequelize.define('Mobile', {
  id: {
     autoIncrement: true,
     primaryKey: true,
     type: DataTypes.INTEGER,
  },
  name: {
    type: DataTypes.STRING
  }
}, {});

const Feature = sequelize.define('Feature', {
  id: {
     autoIncrement: true,
     primaryKey: true,
     type: DataTypes.INTEGER,
  },
  name: {
    type: DataTypes.STRING
  }
}, {});

Mobile.belongsToMany(Feature, { through: 'MobileFeature' });
Feature.belongsToMany(Mobile, { through: 'MobileFeature' });

// Example Data in DB (from mobile context)
const exampleData = [
{
  "id": 1,
  "name": "Mobile1",
  "features": [
    {
      "id": 1,
      "name": "A",
    },
    {
      "id": 2,
      "name": "B",
    },
    {
      "id": 3,
      "name": "C",
    },
  ],
},
{
  "id": 2,
  "name": "Mobile2",
  "features": [],
},
{
  "id": 3,
  "name": "Mobile3",
  "features": [
    {
      "id": 1,
      "name": "A",
    },
  ]
}
];

// Expected result
// Scenario: I want to list those mobiles which have A and C feature
const result = [
{
  "id": 1,
  "name": "Mobile1",
  "features": [
    {
      "id": 1,
      "name": "A",
    },
    {
      "id": 2,
      "name": "B",
    },
    {
      "id": 3,
      "name": "C",
    },
  ]
},
];
  

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

1. Можете ли вы предоставить простой пример модели Sequelize для обоих mobile и feature и их ассоциации, пример данных и ожидаемый результат?

2. Я добавил несколько примеров, надеюсь, это поможет.

Ответ №1:

Я думаю, что здесь будет более сложный запрос, чем просто where .

Можете ли вы предоставить SQL-запрос, который подходит для вашей проблемы? Я думаю, вам нужно будет использовать GROUP BY, COUNT и HAVING (но это только предположение, сделайте по-своему ;))

Если я правильно понимаю вашу проблему: объедините мобильные и функциональные таблицы. Затем используйте where результат

 WHERE: {
 name: { [Op.or]: [A,B,C] } // here we pass array of selected feature names to where. It returns mobiles only with this features. But we are looking for mobile with ALL features passed in array)
}
  

сгруппируйте его по Mobile.name , подсчитайте функции в каждом мобильном телефоне и возьмите этот мобильный телефон, который имеет количество функций = выбранные функции пользователем (так что мобильный со всеми функциями, которые мы ищем).

Конечно, в конечном итоге это будет один оператор sequelize.

ОБНОВЛЕНИЕ: отвечая на ваш вопрос в комментарии к этому ответу:

Сначала вам нужно подсчитать функции. Поэтому мы будем использовать COUNT и сохранять выходные данные в CountedFeatures столбце:

 attributes: ['id', 'name', [sequelize.fn('COUNT', 'table.fieldToCount'), 'CountedFeatures']],
  

Затем вам нужно сгруппировать его с помощью group . Пример использования:

 group: ["table.name, table.id"]
  

И затем вы enter code here используете having using предыдущий созданный countedFeatures столбец:

 having: sequelize.where(sequelize.fn('COUNT', 'countedFeatures'), '=', searchedFeaturesArray.length)
  

Итак, структура в коде будет выглядеть примерно так:

 (...).findAll({
  attributes: [...],
  include : [
    (...)
  ],
  group: [...],
  having: [...]
})
  

Также вы можете включить ведение журнала SQL-инструкций в консоль, чтобы посмотреть, что на самом деле происходит после завершения.
Для этого добавьте logging: console.log атрибут as в findAll функцию.

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

1. Звучит разумно. У меня есть два вопроса: 1. Должен ли я использовать «sequelize literal» в «having»? 2. Как я могу собрать «функции», если я добавлю group by?