Skip to content

Commit 38eb67a

Browse files
committed
managed
1 parent 87049eb commit 38eb67a

8 files changed

+410
-43
lines changed

rootfs_overlay/lkmc/nodejs/sequelize/association.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ const sequelize = new Sequelize({
1313
(async () => {
1414
const Comment = sequelize.define('Comment', {
1515
body: { type: DataTypes.STRING },
16-
}, {});
16+
});
1717
const User = sequelize.define('User', {
1818
name: { type: DataTypes.STRING },
19-
}, {});
19+
});
2020
User.hasMany(Comment)
2121
Comment.belongsTo(User)
22+
console.dir(User);
2223
await sequelize.sync({force: true});
2324
const u0 = await User.create({name: 'u0'})
2425
const u1 = await User.create({name: 'u1'})
@@ -62,7 +63,6 @@ await Comment.create({body: 'u1c0', UserId: u1.id});
6263

6364
// Nicer higher level way.
6465
{
65-
console.log(Object.getOwnPropertyNames(u0));
6666
const u0Comments = await u0.getComments({
6767
include: [{ model: User }],
6868
order: [['id', 'ASC']],
@@ -73,15 +73,16 @@ await Comment.create({body: 'u1c0', UserId: u1.id});
7373
assert(u0Comments[1].User.name === 'u0');
7474
}
7575

76-
// No way to create new item with association without explicit foreign key??
76+
// If you REALLY wanted to not repeat the UserId magic constant everywhere, you could use User.associations.Comments.foreignKey
77+
// But it is such a mouthful, that nobody likely ever uses it?
7778
// https://stackoverflow.com/questions/34059081/how-do-i-reference-an-association-when-creating-a-row-in-sequelize-without-assum
78-
// This does not work as we would like:
7979
{
80-
await Comment.create({body: 'u0c2', User: u0});
81-
// We'd want 3 here.
80+
await Comment.create({body: 'u0c2', [User.associations.Comments.foreignKey]: u0.id});
81+
// Syntax that we really would like instead.
82+
//await Comment.create({body: 'u0c2', User: u0});
8283
assert((await Comment.findAll({
83-
where: { UserId: u0.id },
84-
})).length === 2);
84+
where: { [User.associations.Comments.foreignKey]: u0.id },
85+
})).length === 3);
8586
}
8687

8788
// Removal auto-cascades.

rootfs_overlay/lkmc/nodejs/sequelize/association_many_to_many_double.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ const post2Followers = await post2.getFollowers({order: [['name', 'ASC']]})
108108
assert(post2Followers.length === 0);
109109

110110
// Same as getLikedPosts but with the user ID instead of the model object.
111+
// as is mandatory to disambiguate which one we want to get.
111112
{
112113
const user0Likes = await Post.findAll({
113114
include: [{
@@ -122,6 +123,21 @@ assert(post2Followers.length === 0);
122123
assert(user0Likes.length === 2);
123124
}
124125

126+
// Alternatively, we can also pass the association object instead of model + as.
127+
// This is actually nicer!
128+
{
129+
const user0Likes = await Post.findAll({
130+
include: [{
131+
association: Post.associations.likers,
132+
where: {id: user0.id},
133+
}],
134+
order: [['body', 'ASC']],
135+
})
136+
assert(user0Likes[0].body === 'post0');
137+
assert(user0Likes[1].body === 'post1');
138+
assert(user0Likes.length === 2);
139+
}
140+
125141
// Yet another way that can be more useful in nested includes.
126142
{
127143
const user0Likes = (await User.findOne({

rootfs_overlay/lkmc/nodejs/sequelize/association_many_to_many_same_model.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ const { Sequelize, DataTypes } = require('sequelize');
1010
const sequelize = new Sequelize({
1111
dialect: 'sqlite',
1212
storage: 'tmp.' + path.basename(__filename) + '.sqlite',
13+
define: {
14+
timestamps: false
15+
},
1316
});
1417

1518
(async () => {
@@ -52,16 +55,61 @@ assert(user3Follows[0].name === 'user0');
5255
assert(user3Follows.length === 1);
5356

5457
// Same but with ID instead of object.
58+
// Also get rid of all useless fields from the trough table.
5559
{
5660
const user0Follows = (await User.findOne({
5761
where: {id: user0.id},
58-
include: [{model: User, as: 'Follows'}],
62+
attributes: [],
63+
include: [{
64+
model: User,
65+
as: 'Follows',
66+
through: {attributes: []},
67+
}],
5968
})).Follows
6069
assert(user0Follows[0].name === 'user1');
6170
assert(user0Follows[1].name === 'user2');
6271
assert(user0Follows.length === 2);
6372
}
6473

74+
//// Yet another method with the many-to-many reversed.
75+
//// TODO close to working, but on is being ignored...
76+
//{
77+
// const user0Follows = await User.findAll({
78+
// include: [{
79+
// model: User,
80+
// as: 'Follows',
81+
// on: {
82+
// '$User.UserFollowUser.FollowIdasdf$': { [Sequelize.Op.col]: 'User.user_id' },
83+
// '$User.UserFollowUser.UserId$': user0.id,
84+
// },
85+
// attributes: [],
86+
// through: {attributes: []},
87+
// }],
88+
// order: [['name', 'ASC']],
89+
// })
90+
// // TODO
91+
// //assert(user0Follows[0].name === 'user1');
92+
// //assert(user0Follows[1].name === 'user2');
93+
// //assert(user0Follows.length === 2);
94+
//}
95+
96+
// Find users that follow user0
97+
{
98+
const followsUser0 = await User.findAll({
99+
include: [{
100+
model: User,
101+
as: 'Follows',
102+
where: {id: user0.id},
103+
attributes: [],
104+
through: {attributes: []}
105+
}],
106+
order: [['name', 'ASC']],
107+
})
108+
assert(followsUser0[0].name === 'user2');
109+
assert(followsUser0[1].name === 'user3');
110+
assert(followsUser0.length === 2);
111+
}
112+
65113
// has methods
66114
assert(!await user0.hasFollow(user0))
67115
assert(!await user0.hasFollow(user0.id))
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env node
2+
3+
// Like association_many_to_many_same_model but with a super many to many,
4+
// i.e. explicit through table relations).
5+
6+
const assert = require('assert');
7+
const path = require('path');
8+
9+
const { Sequelize, DataTypes, Op } = require('sequelize');
10+
11+
const sequelize = new Sequelize({
12+
dialect: 'sqlite',
13+
storage: 'tmp.' + path.basename(__filename) + '.sqlite',
14+
define: {
15+
timestamps: false
16+
},
17+
});
18+
19+
(async () => {
20+
21+
// Create the tables.
22+
const User = sequelize.define('User', {
23+
name: { type: DataTypes.STRING },
24+
});
25+
const UserFollowUser = sequelize.define('UserFollowUser', {
26+
UserId: {
27+
type: DataTypes.INTEGER,
28+
references: {
29+
model: User,
30+
key: 'id'
31+
}
32+
},
33+
FollowId: {
34+
type: DataTypes.INTEGER,
35+
references: {
36+
model: User,
37+
key: 'id'
38+
}
39+
},
40+
}
41+
);
42+
43+
// Super many to many. Only works with explicit table for some reason.
44+
User.belongsToMany(User, {through: UserFollowUser, as: 'Follows'});
45+
UserFollowUser.belongsTo(User)
46+
User.hasMany(UserFollowUser)
47+
48+
await sequelize.sync({force: true});
49+
50+
// Create some users.
51+
52+
const user0 = await User.create({name: 'user0'})
53+
const user1 = await User.create({name: 'user1'})
54+
const user2 = await User.create({name: 'user2'})
55+
const user3 = await User.create({name: 'user3'})
56+
await user0.addFollows([user1, user2])
57+
await user2.addFollow(user0)
58+
await user3.addFollow(user0)
59+
60+
// Find all users that a user follows.
61+
const user0Follows = await user0.getFollows({order: [['name', 'ASC']]})
62+
assert(user0Follows[0].name === 'user1');
63+
assert(user0Follows[1].name === 'user2');
64+
assert(user0Follows.length === 2);
65+
66+
const user1Follows = await user1.getFollows({order: [['name', 'ASC']]})
67+
assert(user1Follows.length === 0);
68+
69+
const user2Follows = await user2.getFollows({order: [['name', 'ASC']]})
70+
assert(user2Follows[0].name === 'user0');
71+
assert(user2Follows.length === 1);
72+
73+
const user3Follows = await user3.getFollows({order: [['name', 'ASC']]})
74+
assert(user3Follows[0].name === 'user0');
75+
assert(user3Follows.length === 1);
76+
77+
// Same but with explicit id.
78+
{
79+
const user0Follows = (await User.findOne({
80+
where: {id: user0.id},
81+
attributes: [],
82+
include: [{
83+
model: User,
84+
as: 'Follows',
85+
through: {attributes: []},
86+
}],
87+
})).Follows
88+
assert(user0Follows[0].name === 'user1');
89+
assert(user0Follows[1].name === 'user2');
90+
assert(user0Follows.length === 2);
91+
}
92+
93+
// Another method with the many-to-many reversed.
94+
// Using the super many to many is the only way I know of doing this so far.
95+
// which is a pain.
96+
{
97+
const user0Follows = await User.findAll({
98+
include: [{
99+
model: UserFollowUser,
100+
attributes: [],
101+
on: {
102+
FollowId: { [Op.col]: 'User.id' },
103+
},
104+
where: {UserId: user0.id}
105+
}],
106+
order: [['name', 'ASC']],
107+
})
108+
assert(user0Follows[0].name === 'user1');
109+
assert(user0Follows[1].name === 'user2');
110+
assert(user0Follows.length === 2);
111+
}
112+
113+
await sequelize.close();
114+
})();

rootfs_overlay/lkmc/nodejs/sequelize/association_nested_include.js

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,20 @@ const { Sequelize, DataTypes } = require('sequelize');
1111
const sequelize = new Sequelize({
1212
dialect: 'sqlite',
1313
storage: 'tmp.' + path.basename(__filename) + '.sqlite',
14+
define: {
15+
timestamps: false
16+
},
1417
});
1518

1619
(async () => {
1720

1821
// Create the tables.
1922
const User = sequelize.define('User', {
2023
name: { type: DataTypes.STRING },
21-
}, {});
24+
});
2225
const Post = sequelize.define('Post', {
2326
body: { type: DataTypes.STRING },
24-
}, {});
27+
});
2528
User.belongsToMany(User, {through: 'UserFollowUser', as: 'Follows'});
2629
User.hasMany(Post);
2730
Post.belongsTo(User);
@@ -173,6 +176,31 @@ await users[0].addFollows([users[1], users[2]])
173176
assert(user0FollowsLimit2[0].name === 'user1')
174177
assert(user0FollowsLimit2.length === 1)
175178

179+
// Get just the count of the posts authored by useres followed by user0.
180+
// attributes: [] excludes all other data from the SELECT of the querries
181+
// to optimize things a bit.
182+
// https://stackoverflow.com/questions/37817808/counting-associated-entries-with-sequelize
183+
{
184+
const user0Follows = await User.findByPk(users[0].id, {
185+
attributes: [
186+
[Sequelize.fn('COUNT', Sequelize.col('Follows.Posts.id')), 'count']
187+
],
188+
include: [
189+
{
190+
model: User,
191+
as: 'Follows',
192+
attributes: [],
193+
through: {attributes: []},
194+
include: [{
195+
model: Post,
196+
attributes: [],
197+
}],
198+
},
199+
],
200+
})
201+
assert.strictEqual(user0Follows.dataValues.count, 4);
202+
}
203+
176204
// Case in which our post-sorting is needed.
177205
// TODO: possible to get sequelize to do this for us by returning
178206
// a flat array directly?
@@ -229,36 +257,37 @@ await users[0].addFollows([users[1], users[2]])
229257
assert(postsFound.length === 4)
230258
}
231259

232-
//// This is likely almost it. We just have to understand the undocumented custom on:
233-
//// to specify from which side of the UserFollowsUser we are coming.
234-
//{
235-
// const postsFound = await Post.findAll({
236-
// order: [[
237-
// 'body',
238-
// 'DESC'
239-
// ]],
240-
// subQuery: false,
241-
// include: [
242-
// {
243-
// model: User,
244-
// on: {'id': '$Post.User.FollowId$'},
245-
// include: [
246-
// {
247-
// model: User,
248-
// as: 'Follows',
249-
// where: {id: users[0].id},
250-
// }
251-
// ],
252-
// },
253-
// ],
254-
// })
255-
// console.error(postsFound.length);
256-
// assert.strictEqual(postsFound[0].body, 'body6')
257-
// assert.strictEqual(postsFound[1].body, 'body5')
258-
// assert.strictEqual(postsFound[0].body, 'body1')
259-
// assert.strictEqual(postsFound[1].body, 'body2')
260-
// assert.strictEqual(postsFound.length, 4)
261-
//}
260+
//// This almost achieves the flat array return. We just have to understand the undocumented custom on:
261+
//// to specify from which side of the UserFollowsUser we are coming. The on:
262+
//// is ignored without super many to many unfortunately, the below just returns all posts.
263+
{
264+
const postsFound = await Post.findAll({
265+
order: [[
266+
'body',
267+
'DESC'
268+
]],
269+
subQuery: false,
270+
include: [
271+
{
272+
model: User,
273+
//on: {idasdf: '$Post.User.FollowId$'},
274+
include: [
275+
{
276+
model: User,
277+
as: 'Follows',
278+
where: {id: users[0].id},
279+
}
280+
],
281+
},
282+
],
283+
})
284+
console.error(postsFound.length);
285+
//assert.strictEqual(postsFound[0].body, 'body6')
286+
//assert.strictEqual(postsFound[1].body, 'body5')
287+
//assert.strictEqual(postsFound[2].body, 'body2')
288+
//assert.strictEqual(postsFound[3].body, 'body1')
289+
assert.strictEqual(postsFound.length, 4)
290+
}
262291
}
263292

264293
await sequelize.close();

0 commit comments

Comments
 (0)