Bookshelf(knex) - belongsToMany relation not working - mysql

I have been trying to setup a relation between Posts and Tags using belongsToMany relation (Bookshelf). Here is my code:
db.js
const Post = bookshelf.Model.extend({
tableName: 'posts',
hasTimestamps: true,
tags: function(){
return this.belongsToMany(Tag)
}
})
const Tag = bookshelf.Model.extend({
tableName: 'tags',
posts: function(){
return this.belongsToMany(Post)
}
})
// Pivot table
const PostTag = bookshelf.Model.extend({
tableName: 'posts_tags',
post: function(){
return this.belongsTo(Post)
},
tag: function(){
return this.belongsTo(Tag)
}
})
Get route is:
.get('/:id', (req, res, next) => {
db
.Post
.where('id', req.params.id)
.fetch({widthRelated: ['tags'], require:true})
.then((data)=> {
return res.json({data, ralation: data.related('tags').toJSON()})
})
})
I have already added a table 'posts_tags' in database and all the database is seeded including this pivot table. So when I query in route, the relationship query does not even initiate. knex debug: sql: 'select posts.* from posts where id = ? limit ?'
Tables
posts - id title text created_at updated_at
tags - id name created_at updated_at
posts_tags - id post_id tag_id created_at updated_at
Is there any mistake(s) in code?

Sorry for this Post - I had just the typo:
.fetch({widthRelated: ['tags'], require:true})
widthRelated = withRelated!!!!!!

Related

Getting wrong query generated by Sequelize Assocation

I have two table. One is Order and second one is OrderStatus.
In the orders table order_status_code is foreignKey that references on id to the OrderStatus table.
I have below model association definition.
Order.associate = function(models) {
// associations can be defined here
Order.hasOne(models.OrderItem,{foreignKey: "order_id"}),
Order.hasOne(models.OrderStatus, {foreignKey: "order_status_code"})
};
I am getting below error:
Unknown column 'OrderStatus.order_status_code' in 'field list
when I try to eager loading the OrderStatus.
const orders = await Order.findAll({
where: filter,
include: {
model: OrderStatus
}
})
Below is the query that is being shown on the console.
SELECT `Order`.`id`, `Order`.`buyer_id`, `Order`.`order_status_code`, `Order`.`order_detail`, `Order`.`order_date`, `Order`.`order_number`, `Order`.`created_at`, `Order`.`updated_at`, `OrderStatus`.`id` AS `OrderStatus.id`, `OrderStatus`.`order_status_code` AS `OrderStatus.order_status_code`, `OrderStatus`.`status` AS `OrderStatus.status`, `OrderStatus`.`created_at` AS `OrderStatus.created_at`, `OrderStatus`.`updated_at` AS `OrderStatus.updated_at` FROM `Orders` AS `Order` LEFT OUTER JOIN `OrderStatuses` AS `OrderStatus` ON `Order`.`order_status_code` = `OrderStatus`.`id` WHERE `Order`.`buyer_id` = 23;
I don't know why it is selecting OrderStatus.order_status_code
I fixed it by defining attributes to select from the included model and It fixed the problem for now.
const orders = await Order.findAll({
where: filter,
include: {
model: OrderStatus,
attributes:["status"]
}
})

Query records that does not have an entry in another table using Sequelize include clause

Given Users table and Ratings table
How do I query all user records from Users table that does not have any rating record in Ratings table using Sequelize include clause
Note: Sequelize version 5.x
Thanks in advance
You can do this in two ways depending on how your models are defined.
1. Get all Users along with Ratings by using Sequelize Eager Loading. Then filter where user does not have any ratings.
const users = Users.findAll({
include: [Ratings]
});
const filteredUsers = users.filter(user => user.ratings.length === 0);
2. Get all userIds from the Ratings table and then pass these userIds to the where clause using the notIn Sequelize operator
const ratings = Ratings.findAll({
attributes: ["userId"],
group: ["userId"]
});
const userIds = ratings.map(rating => rating.userId);
const filteredUsers = Users.findAll({
where: {
userId: { [Op.notIn]: userIds }
}
});
Try incorporating a sequelize literal in the where clause:
const ratings = Ratings.findAll({
attributes: ["userId"],
group: ["userId"],
where: {
$and: [
sequelize.literal(`NOT EXISTS (
SELECT 1 FROM Ratings r
WHERE r.userId = User.id
)`),
],
},
});
Assuming you have a relationship between Users and Ratings in your models, this can be accomplished in a single query by using a left outer join followed by a filter on the client side.
In your model definition:
Users.hasMany(Ratings, { foreignKey: 'user_id' });
Ratings.belongsTo(Users, { foreignKey: 'user_id' });
In your query:
const users = await Users.findAll({
include: [
{
model: Ratings,
required: false // left outer join
}
]
});
const usersWithoutRatings = users.filter(u => u.user_ratings.length === 0);

Convert SQL query to Sequelize Query Format

I'm new to Sequelize ORM. I would like to convert SQL query to Sequelize Query.
This is my SQL query, I want to convert this query to sequelize query:
SELECT * FROM `Posts` AS `Posts`
WHERE `Posts`.user_id IN
(SELECT `Follows`.receiver_id FROM `follows` AS `Follows`
WHERE `Follows`.user_id = user_id and `Follows`.status = "accept");
I have tried this but it does not return any data:
Posts
.findAll({ where: {
user_id: { [Op.in]: [{
include: [{
model: Follows,
attributes: ['receiver_id'],
where: {
user_id: user_id,
status:status
}
}]
}]
}
}})
.then(users => { res.send(users); })
After Executing above code it gives error in console
SELECT `event_id`, `user_id`, `event_message`, `e_imagepath`,
`createdAt`, `updatedAt`, `receiver_id`
FROM `Posts` AS `Posts`
WHERE `Posts`.`user_id` IN ('[object Object]');
I would like to convert SQL query to Sequelize Query.
You put your incude in the wrong position. Sequelize does not have a subquery feature as far I am aware of.
So you could do instead:
Posts
.findAll({ where: { user_id: user_id},
include: [{
model: Follows,
attributes: ['receiver_id'],
where: {
user_id: user_id,
status:status
}
}]
})
.then(users => { res.send(users); })
If the example above does not suits your need. You can also try to use a subquery by mixing raw SQL with Sequelize as the link below describes:
stackoverflow.com/questions/28286811/sequelize-subquery-as-field
This works fine.
router.get('/posts', function(req, res)
{
const user_id = req.session.user_id;
const status = "accept";
Posts.findAndCountAll({include:[{ model: Likes},{ model: Comments},{ model: Users}],
where:{user_id:{[Op.in]:[sequelize.literal('SELECT `Follows`.receiver_id FROM `follows` AS `Follows` WHERE `Follows`.user_id=1 and `Follows`.status="accept')]}}
})
.then((postdata)=>
{
Users.findAll({where:{user_id:user_id}})
.then((userdata)=>
{
res.send(postdata.rows)
// res.render('home',{title:'home',items:postdata.rows,user:userdata});
})
.catch((err)=>
{
})
})
.catch((err)=>
{
})
});

Mysql, Node, query within a query, how to populate property in map function from another query

Firstly, if anyone can edit my question title or question to make more sense, please do.
I have a node/express app making mysql queries with mysql.js. I have a query that looks up a table of questions and then runs a map function on the results. Within that map function, I need to query another table, of answers, corresponding to each record in the questions table. The value I need is the number of answers to that question, ie the number of records in each answers table. I've tried all kinds of different examples, but nothing quite fits my case in a way that makes sense to me. New at Node and Express, and even MySQL so having a hard time picking out quite what to.
I understand that the problem is the async nature of node. getAnswersCount() returns "count" before the query finishes. Below is my code. Need some advice on how to achieve this.
The value 123 is assigned to count just to clarify the trace results.
app.get('/', (req, res) => {
db.query('SELECT * FROM questions LIMIT 0, 100',
(error, results) => {
if (error) throw error;
questions = results.map(q => ({
id: q.id,
title: q.title,
description: q.description,
answers: getAnswersCount( q.id )
}));
res.send( questions );
});
});
const getAnswersCount = ( id ) =>
{
const tableName = 'answers_' + id;
var count = 123;
var sql = `CREATE TABLE IF NOT EXISTS ${tableName}(
id int primary key not null,
answer varchar(250) not null
)`;
db.query( sql,
(error, results) => {
if (error) throw error;
//console.log( 'answers table created!' );
});
sql = `SELECT COUNT(*) AS answersCount FROM ${tableName}`;
db.query( sql,
(error, results) => {
if (error) throw error;
//console.log( count ); // will=123
count = results[0].answersCount;
//console.log( count ); // will = results[0].answerCount
});
// I know this code runs before the query finishes, so what to do?
//console.log( count ); //still 123 instead of results[0].answersCount
return count;
}
EDIT: After attempting various versions of Michael Platt's suggestion in his answer without success, I finally worked out a solution using Express callbacks and a promise, adding the answers values to the questions array afterwards:
app.get( '/', (req, res, next ) => {
db.query('SELECT * FROM questions LIMIT 0, 100',
(error, results) => {
if (error) throw error;
questions = results.map(q => ({
id: q.id,
title: q.title,
description: q.description,
}));
next();
});
}, (req, res ) => {
questions.map( currentElem => {
getAnswersCount( currentElem.id ).then( rowData => {
currentElem.answers = rowData[0].answersCount;
if( currentElem.id == questions.length ) res.send( questions );
});
});
});
const getAnswersCount = ( id ) => {
const tableName = 'answers_' + id;
var sql = `CREATE TABLE IF NOT EXISTS ${tableName}(
id int primary key not null,
answer varchar(250) not null
)`;
db.query( sql,
(error, results) => {
if (error) throw error;
//console.log( 'answers table created!' );
});
sql = `SELECT COUNT(*) AS answersCount FROM ${tableName}`;
return new Promise( ( resolve, reject ) => {
db.query( sql, ( error, results ) => {
if ( error ) return reject( err );
resolve( results );
});
});
}
I'm not sure which database module you are using to connect to and query the database but you could make the method async and then await the response from the query like so:
const getAnswersCount = async ( id ) =>
{
const tableName = 'answers_' + id;
var count = 123;
var sql = `CREATE TABLE IF NOT EXISTS ${tableName}(
id int primary key not null,
answer varchar(250) not null
)`;
var results = await db.query(sql);
sql = `SELECT COUNT(*) AS answersCount FROM ${tableName}`;
var count = db.query(sql)[0].answerCount;
// I know this code runs before the query finishes, so what to do?
//console.log( count ); //still 123 instead of results[0].answersCount
return count;
}
app.get('/', async (req, res) => {
db.query('SELECT * FROM questions LIMIT 0, 100',
(error, results) => {
if (error) throw error;
questions = results.map(q => {
const answerCount = await getAnswersCount( q.id )
return {
id: q.id,
title: q.title,
description: q.description,
answers: answerCount
}
}));
res.send( questions );
});
});
I think that will give you what you want and run correctly but it might require a bit of tweaking. You may need to async the function on the actual route itself as well and await the call for getAnswersCount but that should just about do it.

How to Join two tables in bookshelfjs in Node.js

I Have two tables in MySQl DB which are:
Customer:
cust_ID (PK)
cust_name
trans_ID (FK)
Transaction
trans_id (PK)
trans_amount
In Node.js I have created the two models for both of these tables , Now i want to do Inner Join on both these table based on trans_id. I am not getting the Idea to how to do.
var Transaction = bookshelf.Model.extend({
tableName: 'Transaction'
});
var Customer = bookshelf.Model.extend({
tableName: 'Customer'
});
I'm a bookshelf.js beginner myself, but if I'm not mistaken, bookshelf.js abstracts away the notion of inner joins. Instead, if your question can be translated as 'how do I get a transaction/transactions and their related customers?' the answer would be something like this:
transaction.js:
var Transaction = bookshelf.Model.extend({
tableName: 'Transaction',
customers: function() {
return this.hasMany(Customer, 'trans_ID');
}
});
customer.js:
var Customer = bookshelf.Model.extend({
tableName: 'Customer',
transaction: function() {
return this.belongsTo(Transaction, 'trans_ID');
}
});
To get a transaction with all its related customers you do something like this:
new Transaction()
.where('trans_id', 1)
.fetch({withRelated: ['customers']})
.then(function(theTransaction) {
var arrayOfCustomers = theTransaction.customers;
//...
});
Please see bookshelf.js's documentation for hasMany and belongsTo for further information.
I hope this answer was in the ballpark of what you were looking for.