Skip to content

Commit fa0766e

Browse files
committed
more sequelize will it ever end
1 parent 8c86e0a commit fa0766e

File tree

6 files changed

+358
-8
lines changed

6 files changed

+358
-8
lines changed

rootfs_overlay/lkmc/nodejs/sequelize/association_many_to_many.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const post2 = await Post.create({body: 'post2'});
4949

5050
// Make user0 like post0
5151
await user0.addPost(post0)
52+
// Also works.
53+
//await user0.addPost(post0.id)
5254
// Make user0 and user2 like post1
5355
await post1.addUsers([user0, user2])
5456

@@ -68,6 +70,20 @@ const user2Likes = await user2.getPosts({order: [['body', 'ASC']]})
6870
assert(user2Likes[0].body === 'post1');
6971
assert(user2Likes.length === 1);
7072

73+
// Same as get* but with the user ID instead of the model object.
74+
{
75+
const user0Likes = await Post.findAll({
76+
include: [{
77+
model: User,
78+
where: {id: user0.id},
79+
}],
80+
order: [['body', 'ASC']],
81+
})
82+
assert(user0Likes[0].body === 'post0');
83+
assert(user0Likes[1].body === 'post1');
84+
assert(user0Likes.length === 2);
85+
}
86+
7187
// Get users that liked a given likes.
7288

7389
const post0Likers = await post0.getUsers({order: [['name', 'ASC']]})
@@ -88,6 +104,7 @@ assert(post1Likers.length === 2);
88104

89105
// Check if user likes post.
90106
assert( await user0.hasPost(post0))
107+
assert( await user0.hasPost(post0.id)) // same
91108
assert( await user0.hasPost(post1))
92109
assert(!await user0.hasPost(post2))
93110

@@ -104,7 +121,7 @@ assert(!await user0.hasPosts([post0, post1, post2]))
104121
assert(await user0.countPosts() === 2)
105122
assert(await post0.countUsers() === 1)
106123

107-
// Autogenerated remove* method
124+
// Autogenerated remove* methods
108125

109126
// user0 doesn't like post0 anymore.
110127
await user0.removePost(post0)

rootfs_overlay/lkmc/nodejs/sequelize/association_many_to_many_custom_table.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,40 @@ const post2 = await Post.create({body: 'post2'});
6767

6868
// Autogenerated add* methods
6969

70-
// Make user0 like post0
71-
await user0.addPost(post0, {through: { score: 1 }})
70+
// Make some useres like some posts.
71+
await user0.addPost(post0, {through: {score: 1}})
72+
await user1.addPost(post1, {through: {score: 2}})
73+
await user1.addPost(post2, {through: {score: 3}})
7274

75+
// Find what user0 likes.
7376
const user0Likes = await user0.getPosts({order: [['body', 'ASC']]})
7477
assert(user0Likes[0].body === 'post0');
7578
assert(user0Likes[0].UserLikesPost.score === 1);
7679
assert(user0Likes.length === 1);
7780

81+
// Find what user1 likes.
82+
const user1Likes = await user1.getPosts({order: [['body', 'ASC']]})
83+
assert(user1Likes[0].body === 'post1');
84+
assert(user1Likes[0].UserLikesPost.score === 2);
85+
assert(user1Likes[1].body === 'post2');
86+
assert(user1Likes[1].UserLikesPost.score === 3);
87+
assert(user1Likes.length === 2);
88+
89+
// Where on the custom through table column.
90+
// https://stackoverflow.com/questions/38857156/how-to-query-many-to-many-relationship-sequelize
91+
{
92+
const user1LikesWithScore3 = await Post.findAll({
93+
include: [{
94+
model: User,
95+
where: {id: user1.id},
96+
through: {where: {score: 3}},
97+
}],
98+
})
99+
assert(user1LikesWithScore3[0].body === 'post2');
100+
assert(user1LikesWithScore3[0].UserLikesPost.score === 3);
101+
assert(user1LikesWithScore3.length === 1);
102+
}
103+
78104
// TODO: this doesn't work. Possible at all in a single addUsers call?
79105
// Make user0 and user2 like post1
80106
// This method automatically generated.

rootfs_overlay/lkmc/nodejs/sequelize/association_many_to_many_same_model.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const sequelize = new Sequelize({
1818
const User = sequelize.define('User', {
1919
name: { type: DataTypes.STRING },
2020
}, {});
21-
User.belongsToMany(User, {through: 'UserFollowsUser', as: 'Follow'});
21+
User.belongsToMany(User, {through: 'UserFollowUser', as: 'Follows'});
2222
await sequelize.sync({force: true});
2323

2424
// Create some users.
@@ -29,12 +29,48 @@ const user2 = await User.create({name: 'user2'})
2929
const user3 = await User.create({name: 'user3'})
3030

3131
// Make user0 follow user1 and user2
32-
await user0.addFollow(user1)
33-
await user0.addFollow(user2)
34-
const user0Follows = await user0.getFollow({order: [['name', 'ASC']]})
32+
await user0.addFollows([user1, user2])
33+
// Make user2 and user3 follow user0
34+
await user2.addFollow(user0)
35+
await user3.addFollow(user0)
36+
37+
// Check that the follows worked.
38+
const user0Follows = await user0.getFollows({order: [['name', 'ASC']]})
3539
assert(user0Follows[0].name === 'user1');
3640
assert(user0Follows[1].name === 'user2');
3741
assert(user0Follows.length === 2);
3842

43+
const user1Follows = await user1.getFollows({order: [['name', 'ASC']]})
44+
assert(user1Follows.length === 0);
45+
46+
const user2Follows = await user2.getFollows({order: [['name', 'ASC']]})
47+
assert(user2Follows[0].name === 'user0');
48+
assert(user2Follows.length === 1);
49+
50+
const user3Follows = await user3.getFollows({order: [['name', 'ASC']]})
51+
assert(user3Follows[0].name === 'user0');
52+
assert(user3Follows.length === 1);
53+
54+
// Same but with ID instead of object.
55+
{
56+
const user0Follows = (await User.findOne({
57+
where: {id: user0.id},
58+
include: [{model: User, as: 'Follows'}],
59+
})).Follows
60+
assert(user0Follows[0].name === 'user1');
61+
assert(user0Follows[1].name === 'user2');
62+
assert(user0Follows.length === 2);
63+
}
64+
65+
// has methods
66+
assert(!await user0.hasFollow(user0))
67+
assert(!await user0.hasFollow(user0.id))
68+
assert( await user0.hasFollow(user1))
69+
assert( await user0.hasFollow(user2))
70+
assert(!await user0.hasFollow(user3))
71+
72+
// Count method
73+
assert(await user0.countFollows() === 2)
74+
3975
await sequelize.close();
4076
})();
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#!/usr/bin/env node
2+
3+
// Find all posts by users that a given user follows.
4+
// https://stackoverflow.com/questions/42632943/sequelize-multiple-where-clause
5+
6+
const assert = require('assert');
7+
const path = require('path');
8+
9+
const { Sequelize, DataTypes } = require('sequelize');
10+
11+
const sequelize = new Sequelize({
12+
dialect: 'sqlite',
13+
storage: 'tmp.' + path.basename(__filename) + '.sqlite',
14+
});
15+
16+
(async () => {
17+
18+
// Create the tables.
19+
const User = sequelize.define('User', {
20+
name: { type: DataTypes.STRING },
21+
}, {});
22+
const Post = sequelize.define('Post', {
23+
body: { type: DataTypes.STRING },
24+
}, {});
25+
User.belongsToMany(User, {through: 'UserFollowUser', as: 'Follows'});
26+
User.hasMany(Post);
27+
Post.belongsTo(User);
28+
await sequelize.sync({force: true});
29+
30+
// Create data.
31+
const users = await User.bulkCreate([
32+
{name: 'user0'},
33+
{name: 'user1'},
34+
{name: 'user2'},
35+
{name: 'user3'},
36+
])
37+
38+
const posts = await Post.bulkCreate([
39+
{body: 'body00', UserId: users[0].id},
40+
{body: 'body11', UserId: users[0].id},
41+
{body: 'body10', UserId: users[1].id},
42+
{body: 'body11', UserId: users[1].id},
43+
{body: 'body20', UserId: users[2].id},
44+
{body: 'body21', UserId: users[2].id},
45+
{body: 'body30', UserId: users[3].id},
46+
{body: 'body31', UserId: users[3].id},
47+
])
48+
49+
await users[0].addFollows([users[1], users[2]])
50+
51+
// Get all posts by authors that user0 follows.
52+
// The posts are placed inside their respetive authors under .Posts
53+
// so we loop to gather all of them.
54+
{
55+
const user0Follows = (await User.findByPk(users[0].id, {
56+
include: [
57+
{
58+
model: User,
59+
as: 'Follows',
60+
include: [
61+
{
62+
model: Post,
63+
}
64+
],
65+
},
66+
],
67+
})).Follows
68+
const postsFound = []
69+
for (const followedUser of user0Follows) {
70+
postsFound.push(...followedUser.Posts)
71+
}
72+
postsFound.sort((x, y) => { return x.body < y.body ? -1 : x.body > y.body ? 1 : 0 })
73+
assert(postsFound[0].body === 'body10')
74+
assert(postsFound[1].body === 'body11')
75+
assert(postsFound[2].body === 'body20')
76+
assert(postsFound[3].body === 'body21')
77+
assert(postsFound.length === 4)
78+
}
79+
80+
// With ordering, offset and limit.
81+
// The posts are placed inside their respetive authors under .Posts
82+
// The only difference is that posts that we didn't select got removed.
83+
84+
{
85+
const user0Follows = (await User.findByPk(users[0].id, {
86+
offset: 1,
87+
limit: 2,
88+
// TODO why is this needed? It does try to make a subquery otherwise, and then it doesn't work.
89+
// https://selleo.com/til/posts/ddesmudzmi-offset-pagination-with-subquery-in-sequelize-
90+
subQuery: false,
91+
include: [
92+
{
93+
model: User,
94+
as: 'Follows',
95+
include: [
96+
{
97+
model: Post,
98+
}
99+
],
100+
},
101+
],
102+
})).Follows
103+
assert(user0Follows[0].name === 'user1')
104+
assert(user0Follows[1].name === 'user2')
105+
assert(user0Follows.length === 2)
106+
const postsFound = []
107+
for (const followedUser of user0Follows) {
108+
postsFound.push(...followedUser.Posts)
109+
}
110+
postsFound.sort((x, y) => { return x.body < y.body ? -1 : x.body > y.body ? 1 : 0 })
111+
// Note that what happens is that some of the
112+
assert(postsFound[0].body === 'body11')
113+
assert(postsFound[1].body === 'body20')
114+
assert(postsFound.length === 2)
115+
116+
// Same as above, but now with DESC ordering.
117+
{
118+
const user0Follows = (await User.findByPk(users[0].id, {
119+
order: [[
120+
{model: User, as: 'Follows'},
121+
Post,
122+
'body',
123+
'DESC'
124+
]],
125+
offset: 1,
126+
limit: 2,
127+
subQuery: false,
128+
include: [
129+
{
130+
model: User,
131+
as: 'Follows',
132+
include: [
133+
{
134+
model: Post,
135+
}
136+
],
137+
},
138+
],
139+
})).Follows
140+
// Note how user ordering is also reversed from an ASC.
141+
// it likely takes the use that has the first post.
142+
assert(user0Follows[0].name === 'user2')
143+
assert(user0Follows[1].name === 'user1')
144+
assert(user0Follows.length === 2)
145+
const postsFound = []
146+
for (const followedUser of user0Follows) {
147+
postsFound.push(...followedUser.Posts)
148+
}
149+
// In this very specific data case, this would not be needed.
150+
// because user2 has the second post body and user1 has the first
151+
// alphabetically.
152+
postsFound.sort((x, y) => { return x.body < y.body ? 1 : x.body > y.body ? -1 : 0 })
153+
// Note that what happens is that some of the
154+
assert(postsFound[0].body === 'body20')
155+
assert(postsFound[1].body === 'body11')
156+
assert(postsFound.length === 2)
157+
}
158+
159+
// Here user2 would have no post hits due to the limit,
160+
// so it is entirely pruned from the user list as desired.
161+
// Otherwise we would fetch a lot of unwanted user data
162+
// in a large database.
163+
const user0FollowsLimit2 = (await User.findByPk(users[0].id, {
164+
limit: 2,
165+
subQuery: false,
166+
include: [
167+
{
168+
model: User,
169+
as: 'Follows',
170+
include: [ { model: Post } ],
171+
},
172+
],
173+
})).Follows
174+
assert(user0FollowsLimit2[0].name === 'user1')
175+
assert(user0FollowsLimit2.length === 1)
176+
177+
// Case in which our post-sorting is needed.
178+
// TODO: possible to get sequelize to do this for us by returning
179+
// a flat array directly?
180+
// It's not big deal since the LIMITed result should be small,
181+
// but feels wasteful.
182+
// https://stackoverflow.com/questions/41502699/return-flat-object-from-sequelize-with-association
183+
// https://github.com/sequelize/sequelize/issues/4419
184+
{
185+
await Post.truncate({restartIdentity: true})
186+
const posts = await Post.bulkCreate([
187+
{body: 'body0', UserId: users[0].id},
188+
{body: 'body1', UserId: users[1].id},
189+
{body: 'body2', UserId: users[2].id},
190+
{body: 'body3', UserId: users[3].id},
191+
{body: 'body4', UserId: users[0].id},
192+
{body: 'body5', UserId: users[1].id},
193+
{body: 'body6', UserId: users[2].id},
194+
{body: 'body7', UserId: users[3].id},
195+
])
196+
const user0Follows = (await User.findByPk(users[0].id, {
197+
order: [[
198+
{model: User, as: 'Follows'},
199+
Post,
200+
'body',
201+
'DESC'
202+
]],
203+
subQuery: false,
204+
include: [
205+
{
206+
model: User,
207+
as: 'Follows',
208+
include: [
209+
{
210+
model: Post,
211+
}
212+
],
213+
},
214+
],
215+
})).Follows
216+
assert(user0Follows[0].name === 'user2')
217+
assert(user0Follows[1].name === 'user1')
218+
assert(user0Follows.length === 2)
219+
const postsFound = []
220+
for (const followedUser of user0Follows) {
221+
postsFound.push(...followedUser.Posts)
222+
}
223+
// We need this here, otherwise we would get all user2 posts first:
224+
// body6, body2, body5, body1
225+
postsFound.sort((x, y) => { return x.body < y.body ? 1 : x.body > y.body ? -1 : 0 })
226+
assert(postsFound[0].body === 'body6')
227+
assert(postsFound[1].body === 'body5')
228+
assert(postsFound[2].body === 'body2')
229+
assert(postsFound[3].body === 'body1')
230+
assert(postsFound.length === 4)
231+
}
232+
}
233+
234+
await sequelize.close();
235+
})();

rootfs_overlay/lkmc/nodejs/sequelize/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ await IntegerNames.create({value: 5, name: 'five'});
9696
// 3 | 5 | five | 2021-03-19 19:12:08.437+00 | 2021-03-19 19:12:08.437+00
9797
// (3 rows)
9898

99-
let integerNames = await IntegerNames.findAll({
99+
const integerNames = await IntegerNames.findAll({
100100
where: {
101101
value: 2
102102
}

0 commit comments

Comments
 (0)