Продолжить топ-5 отношений «многие ко многим»

#mysql #node.js #sequelize.js

#mysql #node.js #sequelize.js

Вопрос:

В моем проекте есть две модели.

  1. Заказы
  2. Продукты
  3. Заказ продуктов (через) (есть столбец количества)

Мне нужны 5 наиболее покупаемых продуктов, заказ на которые подтвержден за определенный период

 OrderProducts.init({
    order_id: DataTypes.INTEGER(4),
    product_id: DataTypes.INTEGER(4),
    qtd: DataTypes.INTEGER(4),
    price: DataTypes.DECIMAL(15, 2),
})

Orders.init({
    order_id: {
        type: DataTypes.INTEGER(4),
        primaryKey: true,
        autoIncrement: true,
        allowNull: false,
    },
    client_id: DataTypes.INTEGER(4),
    amount: DataTypes.DECIMAL(15,2),
    status: {
        type: DataTypes.ENUM([
            'PENDING',
            'CONFIRMED',
            'PROGRESS',
            'CANCELED',
        ]),
        allowNull: false,
        defaultValue: 'PENDING',
    },
})

Products.init({
    product_id: {
        type: DataTypes.INTEGER(4),
        primaryKey: true,
        autoIncrement: true,
        allowNull: false,
    },
    title: DataTypes.STRING(256),
    description: DataTypes.TEXT,
})

Orders.belongsToMany(Products, {foreignKey: 'order_id', through: OrderProducts, as: 'products'})
Products.belongsToMany(Orders, {foreignKey: 'product_id', through: OrderProducts})
  

Одним из решений было бы выполнить поиск по таблице…

Ответ №1:

Я предполагаю, что qtd каким-то образом является полем «количество». Если нет, и у вас может быть только один продукт каждого типа для каждого заказа, попробуйте переключиться на COUNT() функцию. Я также предполагаю, что у вас есть дата-время покупки в таблице заказов, которую вы, естественно, вызвали бы purchaseDate .

 const queryStartDate = moment().subtract(1, 'months').startOf('day');
const queryEndDate = moment().endOf('day');
Products.findAll({
  offset: 0,
  limit: 5,
  attributes: ['product_id', 'title',
    [sequelize.literal(`(
      SELECT SUM(op.qty)
      FROM OrderProducts AS op
      INNER JOIN Orders o ON o.order_id = op.order_id
      WHERE 
        op.product_id = Product.product_id
        AND o.purchaseDate >= $startDate
        AND o.purchaseDate <= $endDate
        AND o.status = 'CONFIRMED'
      )`, {
        bind: {
          startDate: queryStartDate,
          endDate: queryEndDate,
        },
      }), 'summedQty']
  ],
  order: [[sequelize.literal('summedQty'), 'DESC']]
});
// Note: 
//   - I'm assuming [OrderProducts] and [Orders] are the names of the actual SQL tables.
//   - In contrast, the `Product.product_id` reference is to whatever Sequelize renders the Products table's alias as, in the SQL query.  It'll be a reference to the parent of the sub-query.  This may need tweaking but it should be good-to-go.
//   - Were I you, I would put those statuses in a Look-up-Table.  Enums are great in applications but strings/varchars are costly in databases.
//   - I can't remember if the `Sequelize.literal()` function takes a `bind` attribute, like the `Sequelize.query()` function does.  If not, you'll have to use string interpolation and format-out the dates.
  

Ссылки:

  1. Упорядочивание с помощью агрегированной функции (SUM или COUNT).
  2. Разбивка на страницы.
  3. Синтаксис привязки для Query() функции.