From a527d459ead8df36adcd87278553184c93cf4148 Mon Sep 17 00:00:00 2001 From: JimmyDaddy Date: Thu, 1 Feb 2024 18:29:35 +0800 Subject: [PATCH 1/2] fix: the mounted property type of arbitrary join fix #417 --- src/spell.js | 9 ++++- test/integration/suite/basics.test.js | 4 +- test/integration/suite/querying.test.js | 53 +++++++++++++++++++++++-- test/start.sh | 4 +- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/spell.js b/src/spell.js index 5731ad21..79b78f62 100644 --- a/src/spell.js +++ b/src/spell.js @@ -853,7 +853,14 @@ class Spell { if (qualifier in joins) { throw new Error(`invalid join target. ${qualifier} already defined.`); } - joins[qualifier] = { Model, on: parseConditions(Model, onConditions, ...values)[0] }; + + const association = this.Model.associations[qualifier] || this.Model.associations[pluralize(qualifier, 1)]; + + if (!association) { + joins[qualifier] = { Model, on: parseConditions(Model, onConditions, ...values)[0] }; + } else { + joinAssociation(this, this.Model, this.Model.tableAlias, qualifier, { onConditions, values }); + } return this; } diff --git a/test/integration/suite/basics.test.js b/test/integration/suite/basics.test.js index 11dfb43f..706ba401 100644 --- a/test/integration/suite/basics.test.js +++ b/test/integration/suite/basics.test.js @@ -268,12 +268,12 @@ describe('=> Basic', () => { }, /unable to use virtual attribute realname as condition in model User/); await assert.rejects(async () => { - Post.hasOne('user', { + Post.belongsTo('user', { foreignKey: 'realname' }); }, /unable to use virtual attribute realname as foreign key in model User/); - Post.hasOne('user', { + Post.belongsTo('user', { foreignKey: 'authorId' }); diff --git a/test/integration/suite/querying.test.js b/test/integration/suite/querying.test.js index f9abee54..e6c0ebc1 100644 --- a/test/integration/suite/querying.test.js +++ b/test/integration/suite/querying.test.js @@ -13,6 +13,7 @@ const Like = require('../../models/like'); const Post = require('../../models/post'); const Tag = require('../../models/tag'); const TagMap = require('../../models/tagMap'); +const User = require('../../models/user'); const { logger } = require('../../../src/utils'); describe('=> Query', function() { @@ -502,11 +503,14 @@ describe('=> Count / Group / Having', function() { describe('=> Group / Join / Subqueries', function() { before(async function() { await Post.remove({}, true); + await User.remove({}, true); + const user = await User.create({ name: 'Tyrael', nickname: 'Tyrael', email: 'tyrael@console.com' }); + const user1 = await User.create({ name: 'Lazarus', nickname: 'Lazarus', email: 'lazarus@console.com' }); const posts = await Post.bulkCreate([ - { id: 1, title: 'New Post' }, - { id: 2, title: 'Archbishop Lazarus' }, - { id: 3, title: 'Archangel Tyrael' }, - { id: 4, title: 'New Post 2' }, + { id: 1, title: 'New Post', authorId: user.id }, + { id: 2, title: 'Archbishop Lazarus', authorId: user1.id }, + { id: 3, title: 'Archangel Tyrael', authorId: user.id }, + { id: 4, title: 'New Post 2', authorId: user1.id }, ]); await Attachment.bulkCreate([ @@ -530,6 +534,47 @@ describe('=> Group / Join / Subqueries', function() { ]); }); + it('Bone.find().join() with association', async function() { + // https://github.com/cyjake/leoric/issues/417 + // SELECT `posts`.*, `comments`.* FROM `articles` AS `posts` LEFT JOIN `comments` AS `comments` ON `comments`.`article_id` = `posts`.`id` WHERE `posts`.`gmt_deleted` IS NULL + assert.equal(Post.find().join(Comment, 'comments.articleId = posts.id').toSqlString(), Post.include('comments').toSqlString()); + const posts = await Post.find().join(Comment, 'comments.articleId = posts.id'); + assert.equal(posts.length, 4); + + assert.equal(posts[0].comments.length, 0); + assert.equal(posts[1].comments.length, 2); + assert.equal(posts[2].comments.length, 1); + assert.equal(posts[3].comments.length, 0); + assert.ok(posts[1].comments[0] instanceof Comment); + }); + + it('Bone.find().join().limit() with association', async function() { + // https://github.com/cyjake/leoric/issues/417 + const posts = await Post.find().limit(1).join(Comment, 'comments.articleId = posts.id').where({ + 'posts.title': { $like: 'Archb%' }, + }); + assert.equal(posts.length, 1); + assert.ok(posts[0].comments[0] instanceof Comment); + }); + + it('Bone.find().join() without association', async function() { + // https://github.com/cyjake/leoric/issues/417 + const posts = await Post.find().join(User, 'posts.authorId = users.id'); + assert.equal(posts.length, 4); + + assert.ok(posts[0].users); + assert.ok(posts[1].users instanceof User); + }); + + it('Bone.find().join().limit() without association', async function() { + // https://github.com/cyjake/leoric/issues/417 + const posts = await Post.find().limit(1).join(User, 'posts.authorId = users.id').where({ + 'posts.title': { $like: 'Archb%' }, + }); + assert.equal(posts.length, 1); + assert.ok(posts[0].users instanceof User); + }); + it('Bone.group() subquery', async function() { const posts = await Post.find({ id: Comment.select('articleId').from( diff --git a/test/start.sh b/test/start.sh index 9f9a1414..97fae90f 100755 --- a/test/start.sh +++ b/test/start.sh @@ -10,8 +10,8 @@ function run { args=("${args[@]:1}"); fi echo ""; - printf '"%s" ' "${args[@]}" | xargs echo "> DEBUG=leoric mocha --node-option require=ts-node/register,enable-source-maps -R dot --exit --timeout 5000 ${file}"; - printf '"%s" ' "${args[@]}" | DEBUG=leoric xargs mocha --node-option require=ts-node/register,enable-source-maps -R dot --exit --timeout 5000 ${file} || exit $?; + printf '"%s" ' "${args[@]}" | xargs echo "> mocha --node-option require=ts-node/register,enable-source-maps -R spec --exit --timeout 5000 ${file}"; + printf '"%s" ' "${args[@]}" | xargs mocha --node-option require=ts-node/register,enable-source-maps -R spec --exit --timeout 5000 ${file} || exit $?; } ## From 33694644d6ea97df24078b5a0e0621456cc2543b Mon Sep 17 00:00:00 2001 From: JimmyDaddy Date: Thu, 1 Feb 2024 19:19:18 +0800 Subject: [PATCH 2/2] fix: fix unittest --- test/integration/suite/querying.test.js | 8 ++++---- test/unit/spell.test.js | 4 ++-- tsconfig.json | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/test/integration/suite/querying.test.js b/test/integration/suite/querying.test.js index e6c0ebc1..781efb5a 100644 --- a/test/integration/suite/querying.test.js +++ b/test/integration/suite/querying.test.js @@ -538,7 +538,7 @@ describe('=> Group / Join / Subqueries', function() { // https://github.com/cyjake/leoric/issues/417 // SELECT `posts`.*, `comments`.* FROM `articles` AS `posts` LEFT JOIN `comments` AS `comments` ON `comments`.`article_id` = `posts`.`id` WHERE `posts`.`gmt_deleted` IS NULL assert.equal(Post.find().join(Comment, 'comments.articleId = posts.id').toSqlString(), Post.include('comments').toSqlString()); - const posts = await Post.find().join(Comment, 'comments.articleId = posts.id'); + const posts = await Post.find().join(Comment, 'comments.articleId = posts.id').order('posts.id'); assert.equal(posts.length, 4); assert.equal(posts[0].comments.length, 0); @@ -552,14 +552,14 @@ describe('=> Group / Join / Subqueries', function() { // https://github.com/cyjake/leoric/issues/417 const posts = await Post.find().limit(1).join(Comment, 'comments.articleId = posts.id').where({ 'posts.title': { $like: 'Archb%' }, - }); + }).order('posts.id'); assert.equal(posts.length, 1); assert.ok(posts[0].comments[0] instanceof Comment); }); it('Bone.find().join() without association', async function() { // https://github.com/cyjake/leoric/issues/417 - const posts = await Post.find().join(User, 'posts.authorId = users.id'); + const posts = await Post.find().join(User, 'posts.authorId = users.id').order('posts.id'); assert.equal(posts.length, 4); assert.ok(posts[0].users); @@ -570,7 +570,7 @@ describe('=> Group / Join / Subqueries', function() { // https://github.com/cyjake/leoric/issues/417 const posts = await Post.find().limit(1).join(User, 'posts.authorId = users.id').where({ 'posts.title': { $like: 'Archb%' }, - }); + }).order('posts.id'); assert.equal(posts.length, 1); assert.ok(posts[0].users instanceof User); }); diff --git a/test/unit/spell.test.js b/test/unit/spell.test.js index 060be49d..1d1ab159 100644 --- a/test/unit/spell.test.js +++ b/test/unit/spell.test.js @@ -587,7 +587,7 @@ describe('=> Spell', function() { it('arbitrary join', function() { assert.equal( Post.join(Comment, 'comments.articleId = posts.id').toString(), - 'SELECT `posts`.*, `comments`.* FROM `articles` AS `posts` LEFT JOIN `comments` AS `comments` ON `comments`.`article_id` = `posts`.`id` WHERE `posts`.`gmt_deleted` IS NULL' + 'SELECT `posts`.*, `comments`.* FROM `articles` AS `posts` LEFT JOIN `comments` AS `comments` ON `posts`.`id` = `comments`.`article_id` AND `comments`.`gmt_deleted` IS NULL WHERE `posts`.`gmt_deleted` IS NULL' ); assert.equal(Post.include('comments').limit(1).toSqlString(), heresql(function () { @@ -615,7 +615,7 @@ describe('=> Spell', function() { assert.equal( Post.join(Comment, 'comments.articleId = posts.id').limit(1).toString(), - 'SELECT `posts`.*, `comments`.* FROM `articles` AS `posts` LEFT JOIN `comments` AS `comments` ON `comments`.`article_id` = `posts`.`id` WHERE `posts`.`gmt_deleted` IS NULL LIMIT 1' + 'SELECT `posts`.*, `comments`.* FROM `articles` AS `posts` LEFT JOIN `comments` AS `comments` ON `posts`.`id` = `comments`.`article_id` AND `comments`.`gmt_deleted` IS NULL WHERE `posts`.`gmt_deleted` IS NULL LIMIT 1' ); }); diff --git a/tsconfig.json b/tsconfig.json index 1b3ae529..a051bab9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,8 @@ "target": "ES2020", }, "include": [ - "src/**/*" + "src/**/*", + "test/**/*.ts" ], "exclude": [ "dist",