How to Join two tables in bookshelfjs in Node.js - mysql

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.

Related

How can i fetch data from another table referenced by foreign key?

Heres my tables that a wanna fetch the data, i am using express to make a get request from the next app:
model Provider {
id String #id #default(cuid())
name String
email String
password String
phone String
photo String?
service Service? #relation(fields: [serviceId], references: [id])
serviceId String?
location Location? #relation(fields: [locationId], references: [id])
locationId String?
createdAt DateTime #default(now())
text String
starAverage Float
medals Medals[]
comment Comment[]
}
model Service {
id String #id #default(cuid())
type String
provider Provider[]
}
I wanna fetch the type of the service of the services table of a provider, not the serviceId, and this is my route.
router.get('/list', async (req: Request, res: Response) => {
const allClients = await prisma.client.findMany()
res.json({allClients})
})
this is how i am fetching the data of the rest API using axios
const [providers, setProviders] = useState([] as any[])
useEffect(() => {
axios.get('http://localhost:5140/providers/list')//my list of all providers
.then(res => {
console.log(res)
setProviders(res.data)
}).catch(err => {
console.log(err)
})
}, )
const renderedProviders = Object.values(providers).map(provider => {
return (
<div
className="card"
style={{ width: "18rem"}}
key={provider.id}
>
<img className="card-img-top" src="..."/>
<div className="card-body">
<h3>{provider.name}</h3>
<p>{provider.starAverage} estrekas</p>
<p>{provider.serviceId}</p>
</div>
</div>
);
});
return (
<div className="d-flex flex-row flex-wrap justify-content-between">
{renderedProviders}
</div>
)
for now a only get the serviceId of a provider, not the type of the service
To fetch data from another table referenced by a foreign key in a database, you can use a JOIN clause in your SQL query. A JOIN clause allows you to combine rows from two or more tables based on a related column between the tables.
this how you can use a JOIN clause to fetch data from two tables, users and orders, that are related by a foreign key.
SELECT users.*, orders.*
FROM users
JOIN orders ON orders.user_id = users.id
the JOIN clause combines rows from the users and orders tables based on the user_id column in the orders table and the id column in the users table. The SELECT clause specifies the columns to be retrieved from the users and orders tables.
Edited
how you can reference this in the express route and in the http request from axios ?
you can use the sequelize.query(Sequelize is a promise-based Node.js ORM) method to execute a raw SQL query.
app.get('/users/:id', (req, res) => {
const { id } = req.params;
const query = `
SELECT users.*, orders.*
FROM users
JOIN orders ON orders.user_id = users.id
WHERE users.id = :id
`;
const replacements = { id };
sequelize.query(query).then(([results, metadata]) => {
res.send(results);
});
});
the sequelize.query method is used to execute a raw SQL query that includes a JOIN clause to fetch data from the users and orders tables.

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);

Replacing object id with coresponding name from other object

Im very new to angular so ill try to describe the problem as cleary as i can.
Im tring to make and single page app with some information about trading between people. AS for backend im using MySQL database + nodejs as a backed server and angular as frontend.
The problem is i have a person table with ID and a Name, Balance for each person.
Then i have a table where i store all the transactions between the people ( Id, Giver ID , Taker ID , Amount ).
At the frontend when i get the information about the transactions i get IDS of the giver and taker, but i want to replace them with the coresponding name from persons table / object.
I have clearly no idea how to manag this. I thought about looping trought the transactions objects and replace each ID in the array with a name.
Something like in this post
The Transaction object:
[Object]0: id: 1 giver_id: 1 taker_id: 5 amount: 50
Persons object:
[Object]0: balance:"-50" id:1 name:"Edgars"[Object]1: balance:"0" id:2 name:"Jānis"
So i want to replace the giver_id 1 with the name Edgars because as FK for giver id maches the persons id.
I dont want a clear answear but atleast a way to dig in.
My suggestion would be to handle the join between the two tables on the server side.
The UI will make one webservice call to get the transactions. The response should be an array of transactions and each transaction object should have the name of the giver and the taker.
You will need a SQL query to join the two tables. A simple join SQL would look like this.
select t.id as TRANS_ID, gp.name as GIVER, tp.name as TAKER, t.amount
from transaction t
join person gp on t.giver_id = gp.id
join person tp on t.taker_id = tp.id;
The JSON response to the UI would look like this:
[
{
"trans_id": 1,
"giver_name": "James",
"taker_name": "Michael",
"amount": 50
},
{
"trans_id": 2,
"giver_name": "Jim",
"taker_name": "Mike",
"amount": 100
}
]
This way, all of your logic would be on the server side and your UI only has to display the data.
You could map a new list from both lists:
var newList = transactionsList.map(function(t) {
var giver,taker;
giver = personList.find(function(p) {
return p.id == t.giver_id;
});
taker = personList.find(function(p) {
return p.id == t.taker_id;
});
if(giver && taker) {
t.giver_name = giver.name;
t.taker_name = taker.name;
return t;
}
else {
return undefined;
}
});
Or if you only need to this on one object:
function transformTransaction(t) {
var giver,taker;
var newTransaction = angular.copy(t);
giver = personList.find(function(p) {
return p.id == t.giver_id;
});
taker = personList.find(function(p) {
return p.id == t.taker_id;
});
if( giver && taker ) {
newTransaction.giver_name = giver.name;
newTransaction.taker_name = taker.name;
return newTransaction;
}
else {
return undefined;
}
}

Bookshelf(knex) - belongsToMany relation not working

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!!!!!!