简体   繁体   中英

Use AND operation with many to many relationship in Sequelize

My problem in nutshell: I want to use the AND operation in a joined table. I think it is a very general use-case in the real life, but I didn't find any related article, blog, or issue yet. (My bad I think:D)

Let me describe an example of what I mean: I would like to create a webshop and I have a Mobile and a Feature model and there is many-to-many relation between them. There is a multi-select filter for the feature (on my website) and I want to list those mobiles which have selected features. (eg.: A and B and C...) I think I cannot create it one query because a column cannot be A and B at the same time, but I'm not sure. Example:

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

I'm interested in the Sequelize and also the SQL solution too.

Example models and expected result:

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",
    },
  ]
},
];

I think here will be more complex query than just where .

Can you provide SQL query which suits to your problem? I guess you will need to use GROUP BY, COUNT and HAVING (but this is only ma assumption, make it your way;) )

If I understand your problem right: Join Mobile and Feature tables. Then use where on result

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)
}

group it by Mobile.name, count Features in every mobile and take this mobile which having features number = selected feateures by user (so mobile with all features we are looking for).

Of course finally it will be one sequelize statement.

UPDATE : answering your question in comment to this answer:

First you need to count features. So we will use COUNT and store output in CountedFeatures column:

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

Then you need to group it using group . Example of use:

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

And then you enter code here use having using previous created countedFeatures column:

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

So structure in code it will look something like this:

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

Also you can turn on SQL statement logging to console to see what really happens uderneath. To do this add logging: console.log as attribute in findAll function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM