#!/usr/bin/env node // https://stackoverflow.com/questions/22958683/how-to-implement-many-to-many-association-in-sequelize/67973948#67973948 const assert = require('assert'); const path = require('path'); const { Sequelize, DataTypes } = require('sequelize'); const sequelize = new Sequelize({ dialect: 'sqlite', storage: 'tmp.' + path.basename(__filename) + '.sqlite', }); (async () => { // Create the tables. const User = sequelize.define('User', { name: { type: DataTypes.STRING }, }, {}); const Post = sequelize.define('Post', { body: { type: DataTypes.STRING }, }, {}); // UserLikesPost is the name of the relation table. // Sequelize creates it automatically for us. // On SQLite that table looks like this: // CREATE TABLE `UserLikesPost` ( // `createdAt` DATETIME NOT NULL, // `updatedAt` DATETIME NOT NULL, // `UserId` INTEGER NOT NULL REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, // `PostId` INTEGER NOT NULL REFERENCES `Posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, // PRIMARY KEY (`UserId`, `PostId`) // ); User.belongsToMany(Post, {through: 'UserLikesPost'}); Post.belongsToMany(User, {through: 'UserLikesPost'}); await sequelize.sync({force: true}); // Create some users and likes. const user0 = await User.create({name: 'user0'}) const user1 = await User.create({name: 'user1'}) const user2 = await User.create({name: 'user2'}) const post0 = await Post.create({body: 'post0'}); const post1 = await Post.create({body: 'post1'}); const post2 = await Post.create({body: 'post2'}); // Autogenerated add* methods // Make user0 like post0 await user0.addPost(post0) // Also works. //await user0.addPost(post0.id) // Make user0 and user2 like post1 await post1.addUsers([user0, user2]) // Autogenerated get* methods // Get likes by a user. const user0Likes = await user0.getPosts({order: [['body', 'ASC']]}) assert(user0Likes[0].body === 'post0'); assert(user0Likes[1].body === 'post1'); assert(user0Likes.length === 2); const user1Likes = await user1.getPosts({order: [['body', 'ASC']]}) assert(user1Likes.length === 0); const user2Likes = await user2.getPosts({order: [['body', 'ASC']]}) assert(user2Likes[0].body === 'post1'); assert(user2Likes.length === 1); // Same as get but with the user ID instead of the model object. { const user0Likes = await Post.findAll({ include: [{ model: User, where: {id: user0.id}, }], order: [['body', 'ASC']], }) assert(user0Likes[0].body === 'post0'); assert(user0Likes[1].body === 'post1'); assert(user0Likes.length === 2); } // Yet another way that can be more useful in nested includes. { const user0Likes = (await User.findOne({ where: {id: user0.id}, include: [{ model: Post, }], order: [[Post, 'body', 'ASC']], })).Posts assert(user0Likes[0].body === 'post0'); assert(user0Likes[1].body === 'post1'); assert(user0Likes.length === 2); } // Get users that liked a given likes. const post0Likers = await post0.getUsers({order: [['name', 'ASC']]}) assert(post0Likers[0].name === 'user0'); assert(post0Likers.length === 1); const post1Likers = await post1.getUsers({order: [['name', 'ASC']]}) assert(post1Likers[0].name === 'user0'); assert(post1Likers[1].name === 'user2'); assert(post1Likers.length === 2); const post2Likers = await post2.getUsers({order: [['name', 'ASC']]}) assert(post2Likers.length === 0); // Autogenerated has* methods // Check if user likes post. assert( await user0.hasPost(post0)) assert( await user0.hasPost(post0.id)) // same assert( await user0.hasPost(post1)) assert(!await user0.hasPost(post2)) // Check if post is liked by user. assert( await post0.hasUser(user0)) assert(!await post0.hasUser(user1)) assert(!await post0.hasUser(user2)) // AND of multiple has checks at once. assert( await user0.hasPosts([post0, post1])) assert(!await user0.hasPosts([post0, post1, post2])) // Autogenerated count* methods assert(await user0.countPosts() === 2) assert(await post0.countUsers() === 1) // Autogenerated remove* methods // user0 doesn't like post0 anymore. await user0.removePost(post0) // user0 and user 2 don't like post1 anymore. await post1.removeUsers([user0, user2]) // Check that no-one likes anything anymore. assert(await user0.countPosts() === 0) assert(await post0.countUsers() === 0) // Autogenerated create* method // Create a new post and automatically make user0 like it. const post3 = await user0.createPost({'body': 'post3'}) assert(await user0.hasPost(post3)) assert(await post3.hasUser(user0)) // Autogenerated set* method // Make user0 like exactly these posts. Unlike anything else. await user0.setPosts([post1, post2]) assert(!await user0.hasPost(post0)) assert( await user0.hasPost(post1)) assert( await user0.hasPost(post2)) assert(!await user0.hasPost(post3)) await sequelize.close(); })();