Async Qestiong Node.js - javascript

I am new to Node.js, and I was so used to async language and not familiar with synchronized processing style.
I am working on a basic check out function, that check out items in a users cart and get the total. The sql works fine but I have trouble to get the total. Please see my code below
exports.checkout = (req, res) =>{
const schema = {
user_id: joi.number().required(),
}
const results = joi.validate(req.body,schema)
if (results.error){
//or someother redirect
res.status(400).send(results.error)
return;
}
let data = {
user_id: req.body.user_id
};
let order_id ;
let total = 0 ;
//first create order entry for the products and movies in the shopping cart
query = "INSERT INTO Order_table (user_id,order_time) VALUES (?, NOW());";
db.query(query,[data.user_id], async(err, resp) => {
if(err){
res.status(500).send(err);
console.log(`Check out Order [SQL ERROR]: ${err}`)
}
else{
console.log('Created order');
order_id=await resp.insertId;
console.log(order_id)
await console.log(await get_total(data.user_id,await order_id,res));
//await res.send(Promise.all(total));
//res.status(200).send(await total);
}
})
}
async function check_out_product(user_id,order_id,res){
var total = 0;
//Find all the products with details that is in the shopping cart belong to the given user_id.
query = "Select product_id,quantity,unit_price,product_tax_code from Shoping_Cart_Product join product on Shoping_Cart_Product.product_id = product.id where user_id =?";
await db.query(query,[user_id], async (err, resp) => {
if(err){
res.status(500).send(err);
console.log(`Check out Product [SQL ERROR]: ${err}`)
}
else{
console.log(`Check out product`)
if(resp){
// For each product insert them into the Order Detail table
await resp.forEach(async function(value){
//console.log(value['product_id']);
query = "INSERT INTO Order_Detail_Products (id, product_id,order_id,quantity) VALUES ((select id from (select * from Order_Detail_Products) AS temp where temp.order_id = ? and product_id=? ),?,?,?) ON DUPLICATE KEY UPDATE quantity = ?"
await db.query(query,[order_id,value['product_id'],value['product_id'],order_id,value['quantity'],value['quantity']], async (err, resp) => {
if(err){
res.status(500).send(err);
console.log(`Product order Insert [SQL ERROR]: ${err}`)
}
else{
console.log(`Product Order is inserted`)
total = await total + (value['quantity'] * value['unit_price'])
await console.log('current total is',total)
}
})
});
//after the insertion is done, delete the original entry from the shopping cart product table.
query = "Delete from Shoping_Cart_Product where user_id=?"
await db.query(query,[user_id], (err, resp) => {
if(err){
res.status(500).send(err);
console.log(`Shopping Cart Product delete [SQL ERROR]: ${err}`)
}
else{
console.log(`Shopping Cart Product is emptied`)
}
})
}
//res.status(200).send(resp[0]);
}
})
return Promise.resolve(total);
}
async function check_out_movie(user_id,order_id,res){
var total = 0;
//Find all the movies with details that is in the shopping cart belong to the given user_id.
query = "Select movie_id,method,price_rent,price_buy from Shoping_Cart_Movie join movie on Shoping_Cart_Movie.movie_id = movie.id where user_id =?";
await db.query(query,[user_id],async (err, resp) => {
if(err){
res.status(500).send(err);
console.log(`Check out Movie [SQL ERROR]: ${err}`)
}
else{
await console.log(`Check out Movie`)
if(resp){
// For each product insert them into the Order Detail table
await resp.forEach(async function(value){
var isrent;
if (value['method']=="rent"){
isrent = 0;
}else{
isrent =1;
}
query = "INSERT INTO Order_Detail_Movie (id, movie_id,order_id,isrent,duration) VALUES ((select id from (select * from Order_Detail_Movie) AS temp where temp.order_id = ? and movie_id=? ),?,?,?,DATE_ADD(NOW(), INTERVAL 7 DAY)) ON DUPLICATE KEY UPDATE isrent = ?"
await db.query(query,[order_id,value['movie_id'],value['movie_id'],order_id,isrent,isrent], async (err, resp) => {
if(err){
res.status(500).send(err);
console.log(`Movie order Insert [SQL ERROR]: ${err}`)
}
else{
console.log(`Movie Order is inserted`)
if(isrent == 1){
total = await total + value['price_rent']
await console.log('current total is',total)
}else{
total = await total + value['price_buy']
await console.log('current total is',total)
}
}
})
});
//after the insertion is done, delete the original entry from the shopping cart movie table.
query = "Delete from Shoping_Cart_Movie where user_id=?"
await db.query(query,[user_id], async (err, resp) => {
if(err){
res.status(500).send(err);
await console.log(`Shopping Cart Movie delete [SQL ERROR]: ${err}`)
}
else{
await console.log(`Shopping Cart Movie is emptied`)
}
})
}
}
})
return Promise.resolve(total);
}
async function get_total(user_id,order_id,res){
total = check_out_movie(user_id,order_id,res);
total += check_out_product(user_id,order_id,res,total);
return await total;
}
The output that I got is following:
Created order
77
[object Promise][object Promise]
Check out Movie
Check out product
Shopping Cart Movie is emptied
Product Order is inserted
current total is 3.99
Shopping Cart Product is emptied
I am wondering why is the promise would be logged before the functions are processed, despite that I set await for this function call.
and how do I get numeric out put from this console.log()

Related

I have a problem SQLITE_RANGE: column index out of range

That's my code.
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./db/mob1.db')
let dostup = "SELECT dostup FROM users WHERE idds = 506043416812191764";
db.get(dostup, [dostup], (err, row) => {
if (err) {
return console.error(err.message);
}
return row
? console.log(row.id, row.name)
: console.log(`No text found with the id ${dostup}`);
});
// close the database connection
db.close();
I tried to put everything in a variable and sign ${iddsmember}, but writes that SQLITE_ERROR: unrecognized token: "$"

Search query function and router to search for books based on author, date and title, refactoring

I have made a full stack app. I am trying to see if I could make a search component to search for books by their author, title and published date.
I have these query functions that are fully working.
` const getBooksByAuthor = async (author) => {
const result = await query(`SELECT * FROM books WHERE author ILIKE $1`, [`%${author}%`])
console.log(" I am the result from model", result.rows[0].author)
return result.rows
}
const getBooksByTitle = async (title) => {
const result = await query(`SELECT * FROM books WHERE title ILIKE $1`, [`%${title}%`])
console.log(" I am the result from model", result.rows[0].author)
return result.rows
}
const getBooksByDate = async (dates) => {
const result = await query(`SELECT * FROM books WHERE date_published = $1`, [`${dates}`])
console.log(" I am the result from model", result.rows[0].author)
return result.rows
}`
I have these routes that are also fully working
router.get("/", async(req, res)=>{
const author = req.query.author
const title = req.query.title
const dates = req.query.date
try {
if (req.query.author!== undefined) {
const result = await getBooksByAuthor(req.query.author);
console.log(`this is books by author ${req.query.author}`);
return res.status(200).json({ success: true, payload: result });
} else if (req.query.title !== undefined){
const result = await getBooksByTitle(req.query.title);
console.log(`this is books by title ${req.query.title}`);
return res.status(200).json({ success: true, payload: result });
} else if( req.query.dates !==undefined){
const result = await getBooksByDate(req.query.dates);
console.log(`this is books by dates ${req.query.dates}`);
return res.status(200).json({ success: true, payload: result });
}
else{
const result = await getAllBooks();
const data = result.rows
res.json({success: true, payload: data });
}
} catch (err) {
console.error(err.message);
}
})
So my question is that when I do my fetch on the front end, could I make only one fetch that will conditionally search for the book based on author, title or date so that I could put author, title or date in some input field and could click search and it would display the books depending on the search criteria. Or, would I need to create three different fetch requests for three different searches. I know how to make three different fetch requests but was just wondering if I could do something to display required books by author, title or date by only writing only one fetch request in some smarter way
Maybe something like this?
Any ideas, or someone could direct me on the right path or give the actual code?
Thanks!
router.get("/", async(req, res)=>{
try {
if (req.query.search!== undefined) {
const result = await getBooksByAuthor(req.query.search);
console.log(`this is books by author ${req.query.search}`);
return res.status(200).json({ success: true, payload: result });
} else if (req.query.search !== undefined){
const result = await getBooksByTitle(req.query.search);
console.log(`this is books by title ${req.query.search}`);
return res.status(200).json({ success: true, payload: result });
} else if( req.query.search !==undefined){
const result = await getBooksByDate(req.query.search);
console.log(`this is books by dates ${req.query.search}`);
return res.status(200).json({ success: true, payload: result });
}
else{
const result = await getAllBooks();
const data = result.rows
res.json({success: true, payload: data });
}
} catch (err) {
console.error(err.message);
}
})
I tried this the latter but the search stops as soon as the first if expression executes.
Is it compulsory to have separate queries?
You could do just one getBooks query where searchQuery ilike searchQuery since it's just 1 table you're working with.
So,
SELECT * FROM books WHERE author ilike searchQuery OR title ilike searchQuery ...;
This way, whatever the query body entails will search through the author, title and date columns for possible matches
What you need is to get all books of certain author, then display the books on both title and date fields.
const [books, setbooks] = useState([]);
// fetch the books by author and set the books array
// then use this array to fetch by title and date from the frontend, you don't need to request it from the backend.
const [book, setBook] = (values=>({...values, ['author']: '', ['title']: '', ['date']: ''}));
const onAuthorChange = (value)=>{
// call the fetch function to get books by author from backend.
}
const onTitleChange = (value)=>{
// value passed should be book unique id
// then fetch this book from books array and set the data.
books.map(bo =>
{
if(bo.id === value){
setBook(values=>({...values, ['author']: 'bo.author', ['title']:
'bo.title', ['date']: 'bo.date'}));
}
});
}
const onDateChange = (value)=>{
// value passed should be book unique id
// then fetch this book from books array and set the data.
books.map(bo =>
{
if(bo.id === value){
setBook(values=>({...values, ['author']: 'bo.author', ['title']:
'bo.title', ['date']: 'bo.date'}));
}
});
}
// in your return function
return (
// to reduse the code I am not writing all select tags
// for author you need input field
<input type="text" onChange={onAuthorChange} value={author}/>
// in your selects tags for titles and date you should map to option
<select onChange={onTitleChange}>
{books.map(bo => <option value={bo.id}>{bo.title}</option>}
</select>
// do same for date.
)

Node.js file blocks terminal after correct execution

I'm new to using Node and fetch and of course I'm having some problems with my code. Essentially I'm trying to implement a project where I get some json data through an API request and store it into a mysql database. These data is contained in multiple pages and therefore I used a simple for cycle for multiple fetching. I do this 2 times as I have to get data from 2 different object lists. For storing the data I first established a mysql connection and later I execute the sql query inside another for iterating the single object data.
It performes correctly both extraction of json data and storage in mysql database but once I execute node index.js on the terminal, the process keeps on running and the terminal gets suspended until I force the process to terminate.
I used why-is-node-running and found out this:
Here's the code of index.js:
import mysql from 'mysql';
import fetch from 'node-fetch';
import log from 'why-is-node-running';
const URL0 = "https://atlas.ripe.net/api/v2/probes/?status=1";
const sql = "INSERT INTO probes (id, country, longitude, latitude) VALUES (?,?,?,?)";
const sql1 = "INSERT INTO anchors (id, country, longitude, latitude) VALUES (?,?,?,?)";
const PG_SIZE = 100;
let num_pages_probes=120;
let i=0, j=1, k=1, a=0;
const con = mysql.createConnection({
host:'localhost',
user:'root',
password:'',
database:'probes&anchors'
});
con.connect((err)=>{
if(err){
console.log("Connection not proper");
}else{
console.log("connected");
}
});
/*
fetch(URL0)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error! status: "
+ response.status);
} else {
return response.json();
}
})
.then((data) => {
num_pages_probes = Math.ceil(data.count/PG_SIZE);
console.log(num_pages_probes);
});
*/
for (j; j<=2; j++){
console.log("j="+j);
let URLi = "https://atlas.ripe.net/api/v2/probes/?page="+j+"&status=1";
fetch(URLi)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error! status: "
+ response.status);
} else {
return response.json();
}
})
.then((data) => {
for (let probe of data.results){
i++;
let id0 = probe.id;
let country = probe.country_code;
let longitude = probe.geometry.coordinates[0];
let latitude = probe.geometry.coordinates[1];
con.query(sql, [id0, country, longitude, latitude], function (err, result) {
if (err) throw err;
console.log("1 record inserted");
});
console.log("id0: "+id0+"\t"+"cc: "+country+"\t"+"long: "+longitude+"\t"+"lati: "+latitude);
console.log(i);
}
// con.end();
});
}
for (k; k<=2; k++){
console.log("k="+k);
let URLi = "https://atlas.ripe.net/api/v2/anchors/?page="+k;
fetch(URLi)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error! status: "
+ response.status);
} else {
return response.json();
}
})
.then((data) => {
for (let anchor of data.results){
a++;
let id0 = anchor.id;
let country = anchor.country;
let longitude = anchor.geometry.coordinates[0];
let latitude = anchor.geometry.coordinates[1];
con.query(sql1, [id0, country, longitude, latitude], function (err, result) {
if (err) throw err;
console.log("1 record inserted");
});
console.log("id0: "+id0+"\t"+"cc: "+country+"\t"+"long: "+longitude+"\t"+"lati: "+latitude);
console.log(a);
}
});
}
setTimeout(function () {
log() // logs out active handles that are keeping node running
}, 100)
Can someone help me out please? I don't know where to put my hands on.
PS. I purposely limited the cycle to 2 but it would actually be like 120.
You are not closing your mysql connection which keep your proccess up.
You probably want to close your connection when all your fetch/inserts are done, the tricks here is to ensure you've completed all your inserts before closing your connection.
You can have a look at async/await syntax, it will help you ensure you are closing only when you've done your inserts.
A very simplified version would look like:
const fn = async () => {
const con = mysql.createConnection({ ... });
for (...) {
const res = await fetch({ ... });
const data = await res.json();
await con.query({ ... });
  }
await con.close();
}
fn();
NOTE: The mysql lib seems to only work with callback, so you will probably have to promisify the methods you need (see utils.promisify)

How to add pagination with all page details in node js and mongodb

HI I have to add pagination in NodeJS and MongoDB I have done this code
router.get("/PO_pagination", verify, async (req, res) => {
console.log("req.query", req.query)
try {
let { page, size } = req.query
if (!page) {
page = 1
}
if (!size) {
size = 10
}
const limit = parseInt(size)
const skip = (page - 1) * size
const po = await PurchaseOrders.find().limit(limit).skip(skip)
return res.send({
page: page,
size: size,
data: po
})
} catch (error) {
console.log("error", error)
return res.status(400).json({ error: error })
}
})
I am getting data according to req.query but I also want to return the total number of pages on the basis of limit and skip getting from the query
like if I said page=1&limit=200 so it arrange a total number of pages according to query params.
Also, I want to add the next and prev in res. Like how many pages are next and how many pages are in prev.
You can use countDocuments() to get total number of documents, and then you can calculate your requested data.
router.get("/PO_pagination", verify, async (req, res) => {
console.log("req.query", req.query)
try {
let { page, size } = req.query
if (!page) page = 1;
if (!size) size = 10;
const limit = parseInt(size)
const skip = (page - 1) * size
const po = await PurchaseOrders.find().limit(limit).skip(skip);
const total_documents = await PurchaseOrders.countDocuments();
const previous_pages = page - 1;
const next_pages = Math.ceil((total_documents-skip)/size);
return res.send({
page: page,
size: size,
data: po,
previous: previous_pages,
next: next_pages
})
} catch (error) {
console.log("error", error)
return res.status(400).json({ error: error })
}
})

How to sort an item which is inside an array by query params?

I am learning Express.js. I am trying to sort these services by price. All the services are inside an array. Firstly I am filtering the items by their name which is working fine but when I am trying to sort them by their price it isn't working. Here's my code for sorting -
const getCategory = async (req, res) => {
let query;
const reqQuery = { ...req.query };
console.log(req.query);
if (req.query.name) {
query = Category.find(reqQuery);
} else {
query = Category.find();
}
if (req.query.sort) {
const sortByArr = req.query.sort.split(',').join(' ');
query = query.sort(sortByArr);
}
try {
const categories = await query.populate('services');
res.status(200).json({
message: 'All Category',
result: categories,
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};
Here's the screenshot of my query in postman -
The highest price is 225000. So the sorting is not working here.
Arrays are sorted by alphabetical values by default. Use this instead:
arr.sort((a, b) => a-b);

Categories