diff --git a/rootfs_overlay/lkmc/nodejs/sequelize/association.js b/rootfs_overlay/lkmc/nodejs/sequelize/association.js index b22f85b..21de415 100755 --- a/rootfs_overlay/lkmc/nodejs/sequelize/association.js +++ b/rootfs_overlay/lkmc/nodejs/sequelize/association.js @@ -9,6 +9,7 @@ const sequelize = new Sequelize({ dialect: 'sqlite', storage: 'tmp.' + path.basename(__filename) + '.sqlite', }); + (async () => { const Comment = sequelize.define('Comment', { body: { type: DataTypes.STRING }, @@ -45,7 +46,10 @@ await Comment.create({body: 'u1c0', UserId: u1.id}); const u0Comments = await Comment.findAll({ where: { UserId: u0.id }, order: [['id', 'ASC']], - include: [{ model: User }], + include: User, + // Equivalent alternatives in this case. + //include: [User], + //include: [{ model: User }], }); assert(u0Comments[0].body === 'u0c0'); assert(u0Comments[1].body === 'u0c1'); @@ -58,6 +62,7 @@ await Comment.create({body: 'u1c0', UserId: u1.id}); // Nicer higher level way. { + console.log(Object.getOwnPropertyNames(u0)); const u0Comments = await u0.getComments({ include: [{ model: User }], }); @@ -79,14 +84,65 @@ await Comment.create({body: 'u1c0', UserId: u1.id}); } // Removal auto-cascades. -const u0id = u0.id -await u0.destroy() -assert((await Comment.findAll({ - where: { UserId: u0id }, -})).length === 0); -assert((await Comment.findAll({ - where: { UserId: u1.id }, -})).length === 1); +{ + const u0id = u0.id + await u0.destroy() + assert((await Comment.findAll({ + where: { UserId: u0id }, + })).length === 0); + assert((await Comment.findAll({ + where: { UserId: u1.id }, + })).length === 1); +} + +// as aliases. +// Allows us to use a nicer name for a relation rather than the exact class name. +// E.g. here we name the User of a Comment as a "author". +// And mandatory do diambiguate multiple associations with a single type. +{ + const CommentAs = sequelize.define('CommentAs', { + body: { type: DataTypes.STRING }, + }, {}); + const UserAs = sequelize.define('UserAs', { + name: { type: DataTypes.STRING }, + }, {}); + UserAs.hasMany(CommentAs) + CommentAs.belongsTo(UserAs, {as: 'author'}) + await sequelize.sync({force: true}); + const u0 = await UserAs.create({name: 'u0'}) + const u1 = await UserAs.create({name: 'u1'}) + await CommentAs.create({body: 'u0c0', authorId: u0.id}); + await CommentAs.create({body: 'u0c1', authorId: u0.id}); + await CommentAs.create({body: 'u1c0', authorId: u1.id}); + + { + const u0Comments = await CommentAs.findAll({ + where: { authorId: u0.id }, + order: [['id', 'ASC']], + // Instead of include: UserAs + include: 'author', + }); + assert(u0Comments[0].body === 'u0c0'); + assert(u0Comments[1].body === 'u0c1'); + assert(u0Comments[0].authorId === u0.id); + assert(u0Comments[1].authorId === u0.id); + assert(u0Comments[0].author.name === 'u0'); + assert(u0Comments[1].author.name === 'u0'); + } + + // Trying with the higher level getter. + { + // TODO + // u0.getComments is not a function + //const u0Comments = await u0.getComments({ + // include: 'author', + //}); + //assert(u0Comments[0].body === 'u0c0'); + //assert(u0Comments[1].body === 'u0c1'); + //assert(u0Comments[0].author.name === 'u0'); + //assert(u0Comments[1].author.name === 'u0'); + } +} await sequelize.close(); })(); diff --git a/rootfs_overlay/lkmc/nodejs/sequelize/index.js b/rootfs_overlay/lkmc/nodejs/sequelize/index.js index 1c31b4e..fb098ff 100755 --- a/rootfs_overlay/lkmc/nodejs/sequelize/index.js +++ b/rootfs_overlay/lkmc/nodejs/sequelize/index.js @@ -44,7 +44,8 @@ await sequelize.authenticate(); const IntegerNames = sequelize.define('IntegerNames', { value: { type: DataTypes.INTEGER, - allowNull: false + allowNull: false, + unique: true, }, name: { type: DataTypes.STRING, diff --git a/rootfs_overlay/lkmc/nodejs/sequelize/sync_alter.js b/rootfs_overlay/lkmc/nodejs/sequelize/sync_alter.js new file mode 100755 index 0000000..5ac5e77 --- /dev/null +++ b/rootfs_overlay/lkmc/nodejs/sequelize/sync_alter.js @@ -0,0 +1,110 @@ +#!/usr/bin/env node + +// https://stackoverflow.com/questions/17708620/sequelize-changing-model-schema-on-production + +const assert = require('assert'); +const path = require('path'); + +const { Sequelize, DataTypes } = require('sequelize'); + +(async () => { +{ + const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'tmp.' + path.basename(__filename) + '.sqlite', + }); + const IntegerNames = sequelize.define('IntegerNames', { + value: { type: DataTypes.INTEGER, }, + name: { type: DataTypes.STRING, }, + }, {}); + await IntegerNames.sync({force: true}) + await IntegerNames.create({value: 2, name: 'two'}); + await IntegerNames.create({value: 3, name: 'three'}); + await sequelize.close(); +} + +// Alter by adding column.. +{ + const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'tmp.' + path.basename(__filename) + '.sqlite', + }); + const IntegerNames = sequelize.define('IntegerNames', { + value: { type: DataTypes.INTEGER, }, + name: { type: DataTypes.STRING, }, + nameEs: { type: DataTypes.STRING, }, + }, {}); + await IntegerNames.sync({alter: true}) + await IntegerNames.create({value: 5, name: 'five' , nameEs: 'cinco'}); + await IntegerNames.create({value: 7, name: 'seven', nameEs: 'siete'}); + const integerNames = await IntegerNames.findAll({ + order: [['value', 'ASC']], + }); + assert(integerNames[0].value === 2); + assert(integerNames[0].name === 'two'); + assert(integerNames[0].nameEs === null); + assert(integerNames[1].name === 'three'); + assert(integerNames[1].nameEs === null); + assert(integerNames[2].name === 'five'); + assert(integerNames[2].nameEs === 'cinco'); + assert(integerNames[3].name === 'seven'); + assert(integerNames[3].nameEs === 'siete'); + await sequelize.close(); +} + +// Alter by removing column. undefined instead of null, +// because the values really aren't present in the database anymore. +{ + const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'tmp.' + path.basename(__filename) + '.sqlite', + }); + const IntegerNames = sequelize.define('IntegerNames', { + value: { type: DataTypes.INTEGER, }, + name: { type: DataTypes.STRING, }, + }, {}); + await IntegerNames.sync({alter: true}) + const integerNames = await IntegerNames.findAll({ + order: [['value', 'ASC']], + }); + assert(integerNames[0].value === 2); + assert(integerNames[0].name === 'two'); + assert(integerNames[0].nameEs === undefined); + assert(integerNames[1].name === 'three'); + assert(integerNames[1].nameEs === undefined); + assert(integerNames[2].name === 'five'); + assert(integerNames[2].nameEs === undefined); + assert(integerNames[3].name === 'seven'); + assert(integerNames[3].nameEs === undefined); + await sequelize.close(); +} + +// Alter a type. +// Hmm, docs suggest data would get dropped, but I only see typecast without alter.drop, +// so shy does it work still?. sqlite from CLI does confirm that it is now a VARCHAR(255) +// column. +{ + const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'tmp.' + path.basename(__filename) + '.sqlite', + }); + const IntegerNames = sequelize.define('IntegerNames', { + value: { type: DataTypes.STRING, }, + name: { type: DataTypes.STRING, }, + }, {}); + await IntegerNames.sync({alter: true}) + const integerNames = await IntegerNames.findAll({ + order: [['value', 'ASC']], + }); + assert(integerNames[0].value === '2'); + assert(integerNames[0].name === 'two'); + assert(integerNames[1].value === '3'); + assert(integerNames[1].name === 'three'); + assert(integerNames[2].value === '5'); + assert(integerNames[2].name === 'five'); + assert(integerNames[3].value === '7'); + assert(integerNames[3].name === 'seven'); + await sequelize.close(); +} + +})();