-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
many_to_many_same_model.js
executable file
·188 lines (169 loc) · 5.77 KB
/
many_to_many_same_model.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env node
// https://cirosantilli.com/sequelize-example
const assert = require('assert');
const path = require('path');
const { DataTypes } = require('sequelize');
const common = require('./common')
const sequelize = common.sequelize(__filename, process.argv[2], { define: { timestamps: false } })
;(async () => {
// Create the tables.
const User = sequelize.define('User', {
username: { type: DataTypes.STRING },
}, {});
User.belongsToMany(User, { through: 'UserFollowUser', as: 'Follows', foreignKey: 'UserId' });
// This is ony needed for the function followedFindAll. "foreignKey" could be removed otherwise.
User.belongsToMany(User, { through: 'UserFollowUser', as: 'Followed', foreignKey: 'FollowId' });
await sequelize.sync({ force: true });
// Create some users.
const user0 = await User.create({ username: 'user0' })
const user1 = await User.create({ username: 'user1' })
const user2 = await User.create({ username: 'user2' })
const user3 = await User.create({ username: 'user3' })
// Make user0 follow user1 and user2
await user0.addFollows([user1, user2])
// Make user2 and user3 follow user0
await user2.addFollow(user0)
await user3.addFollow(user0)
let rows
// Get users followed by user0.
rows = await user0.getFollows({order: [['username', 'ASC']]})
common.assertEqual(rows, [
{ username: 'user1' },
{ username: 'user2' },
])
// Reverse order.
rows = await user0.getFollows({order: [['username', 'DESC']]})
common.assertEqual(rows, [
{ username: 'user2' },
{ username: 'user1' },
])
// Get users followed by user1.
rows = await user1.getFollows({order: [['username', 'ASC']]})
common.assertEqual(rows, [])
// Get users followed by user2.
rows = await user2.getFollows({order: [['username', 'ASC']]})
common.assertEqual(rows, [
{ username: 'user0' },
])
// Get users followed by user3.
rows = await user3.getFollows({order: [['username', 'ASC']]})
common.assertEqual(rows, [
{ username: 'user0' },
])
// Get users followed by an user by username instead of user object using findOne.
// Also get rid of all useless fields from the through table.
// followedFindAll is almost always a better alternative to this.
async function followedFindOne(username, opts={}) {
return (await User.findOne(Object.assign({
where: { username },
attributes: [],
include: [{
model: User,
as: 'Follows',
through: {attributes: []},
}],
order: [['Follows', 'username', 'ASC']]
}, opts))).Follows
}
rows = await followedFindOne('user0')
common.assertEqual(rows, [
{ username: 'user1'},
{ username: 'user2'},
])
// TODO how to implement limit in this case? We managed it in followedFindAll however.
// Does not work like this because the limit would be applied to the outer findOne.
//rows = await followedFindOne('user0', { limit: 1 })
//common.assertEqual(rows, [
// { username: 'user1'},
//])
// Reverse order.
rows = await followedFindOne('user0', { order: [['Follows', 'username', 'DESC']] })
common.assertEqual(rows, [
{ username: 'user2'},
{ username: 'user1'},
])
// Same but with findAll. This is the preferred approach in most real world applications.
// Get users followed by an user by username instead of user object using findAll.
// Note that for this to work, we needed to add the extra relation:
// ``
// User.belongsToMany(User, { through: 'UserFollowUser', as: 'Followed', foreignKey: 'FollowId' });
// ``
async function followedFindAll(username, opts={}) {
return User.findAll(Object.assign({
include: [{
model: User,
as: 'Followed',
attributes: [],
through: { attributes: [] },
where: { username },
}],
// Required for limit to work.
subQuery: false,
order: [['username', 'ASC']]
}, opts))
}
rows = await followedFindAll('user0')
common.assertEqual(rows, [
{ username: 'user1'},
{ username: 'user2'},
])
rows = await followedFindAll('user0', { limit: 1 })
common.assertEqual(rows, [
{ username: 'user1'},
])
rows = await followedFindAll('user0', { order: [['username', 'DESC']] })
common.assertEqual(rows, [
{ username: 'user2'},
{ username: 'user1'},
])
// Now the inverse: find users that follow a given user.
// Find users that follow a given user by username.
// TODO Any way with the autogenerated methods? Anyways, this is good enough, almost always what we need.
// * https://github.com/sequelize/sequelize/issues/8263
// * https://stackoverflow.com/questions/27065154/how-to-get-all-children-or-parents-in-a-many-to-many-association-if-one-model-re
async function following(username, opts={}) {
return User.findAll(
Object.assign({
include: [{
model: User,
as: 'Follows',
where: { username },
attributes: [],
through: { attributes: [] }
}],
// subQuery is mandatory if you want to apply a limit, like in every real query.
subQuery: false,
order: [['username', 'ASC']],
}, opts)
)
}
common.assertEqual(await following('user0'), [
{ username: 'user2' },
{ username: 'user3' },
])
common.assertEqual(await following('user0', { order: [['username', 'DESC']] }), [
{ username: 'user3' },
{ username: 'user2' },
])
common.assertEqual(await following('user0', { limit: 1 }), [
{ username: 'user2' },
])
common.assertEqual(await following('user0', { limit: 1, order: [['username', 'DESC']] }), [
{ username: 'user3' },
])
common.assertEqual(await following('user1'), [
{ username: 'user0' },
])
common.assertEqual(await following('user2'), [
{ username: 'user0' },
])
common.assertEqual(await following('user3'), [])
// has methods
assert(!await user0.hasFollow(user0))
assert(!await user0.hasFollow(user0.id))
assert( await user0.hasFollow(user1))
assert( await user0.hasFollow(user2))
assert(!await user0.hasFollow(user3))
// Count method
assert.strictEqual(await user0.countFollows(), 2)
})().finally(() => { return sequelize.close() });