In my ReactJS app I'm fetching information from my NodeJS app to display on the page the album by id. When I add songs to my database it has to go through the POST request and add the info to the database but the database has a constraint where the index key has to be unique. What I'm trying to do in the code below is use Promises to select album by id then insert the data on that album_id so that the insert and constraint is only ran on that id. This doesn't work and the constraint is still ran on all objects the same. Does anyone have a solution as on what I could do in order to run the constraint separately so that different albums can have the same index?
const addSong = (request, response) => {
const id = parseInt(request.params.id)
const { name, link, index, album_id } = request.body;
let p = new Promise((resolve, reject) => {
if(true) {
resolve('Success')
} else {
reject('Failed')
}
})
for (let i = 0; i < request.body.length; i++) {
const object = request.body[i]
p.then(() => {
pool.query('SELECT * FROM songs WHERE album_id = $1', [album_id], (error, results) => {
if (error) {
throw error
} else {
console.log("SELECT " + JSON.stringify(request.body));
}
})
})
.then(() => {
pool.query(
'INSERT INTO songs (name, link, index, album_id) VALUES ($1, $2, $3, $4) ON CONFLICT (index) DO NOTHING RETURNING *',
[
object.name,
object.link,
object.index,
object.album_id,
],
(error, results) => {
if (error != null) {
console.log(error)
} else {
console.log('INSERT ' + JSON.stringify(request.body))
}
}
)
})
.catch(err => {
console.log(err)
})
}
}
Related
I am trying to fetch a list of all companies listed in stock market from an external API, and after getting the list, I am trying to fetch all details regarding individual companies including graph data. It was all working fine. However, today I am getting socket hangup error. I have tried going through other posts here in stackoverflow. However, none of them works.
const request = require('request');
const fetchAPI = apiPath => {
return new Promise(function (resolve, reject) {
request(apiPath, function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
// get list of all companies listed in
const fetchCompanyDetails = () => {
return new Promise(function (resolve, reject) {
let details = [];
fetchAPI('https://api//')
.then(res => {
res = JSON.parse(res)
details.push(res);
resolve(details);
})
.catch(err => {
console.log("error at fetchcompany details" + err);
})
});
}
const getDateAndPriceForGraphData = (graphData) => {
let res = []
graphData.forEach(data => {
let d = {}
d["x"] = new Date(data.businessDate).getTime() / 1000
d["y"] = data.lastTradedPrice
res.push(d)
})
return res
}
// get graph data for individual assets
const getGraphDataForAssets = (assetID) => {
return new Promise((resolve, reject) => {
let details = {};
fetchAPI(`https://api/${assetID}`)
.then(async (res) => {
res = JSON.parse(res)
let data = await getDateAndPriceForGraphData(res)
details = data
resolve(details);
})
.catch(err => {
console.log("error at getGraphDataForAssets" + err);
})
});
}
// fetch data about individual assets
const fetchAssetDetailsOfIndividualCompanies = (assetID) => {
return new Promise((resolve, reject) => {
let details = {"assetData" : {}, "graphData": {}};
fetchAPI(`https://api/${assetID}`)
.then(async (res1) => {
res1 = JSON.parse(res1)
details["assetData"] = res1
// get graph data
var graphData = await getGraphDataForAssets(assetID)
details["graphData"] = graphData
resolve(details);
})
.catch(err => {
console.log("error at fetchAssetDetailsOfIndividualCompanies" + err);
reject(err)
})
});
}
// returns list of details of all tradeable assets (Active and Suspended but not delisted)
const fetchDetailsForEachCompany = async (companyList) => {
let result = []
await Promise.all(companyList.map(async (company) => {
try {
// return data for active and suspended assets
if(company.status != "D") {
let companyData = await fetchAssetDetailsOfIndividualCompanies(company.id)
result.push(companyData)
}
} catch (error) {
console.log('error at fetchDetailsForEachCompany'+ error);
}
}))
return result
}
exports.fetchAssetDetails = async () => {
let companyDetails = await fetchCompanyDetails()
let det = await fetchDetailsForEachCompany(companyDetails[0])
return det
}
To expand on what I meant with not needing those new Promise()s, this would be an idiomatic async function refactoring for the above code.
I eliminated getGraphDataForAssets, since it was eventually not used; fetchAssetDetailsOfIndividualCompanies fetched the same data (based on URL, anyway), and then had getGraphDataForAssets fetch it again.
const request = require("request");
function fetchAPI(apiPath) {
return new Promise(function (resolve, reject) {
request(apiPath, function (error, response, body) {
if (!error && response.statusCode === 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
async function fetchJSON(url) {
return JSON.parse(await fetchAPI(url));
}
async function fetchCompanyDetails() {
return [await fetchAPI("https://api//")];
}
function getDateAndPriceForGraphData(graphData) {
return graphData.map((data) => ({
x: new Date(data.businessDate).getTime() / 1000,
y: data.lastTradedPrice,
}));
}
// fetch data about individual assets
async function fetchAssetDetailsOfIndividualCompanies(assetID) {
const assetData = await fetchJSON(`https://api/${assetID}`);
const graphData = getDateAndPriceForGraphData(assetData);
return { assetID, assetData, graphData };
}
// returns list of details of all tradeable assets (Active and Suspended but not delisted)
async function fetchDetailsForEachCompany(companyList) {
const promises = companyList.map(async (company) => {
if (company.status === "D") return null;
return fetchAssetDetailsOfIndividualCompanies(company.id);
});
const results = await Promise.all(promises);
return results.filter(Boolean); // drop nulls
}
async function fetchAssetDetails() {
const companyDetails = await fetchCompanyDetails();
return await fetchDetailsForEachCompany(companyDetails[0]);
}
exports.fetchAssetDetails = fetchAssetDetails;
I’m working through some inherited code using promises and having a major issue. I have two functions, both returning a promise (or a series of promises in the findOne case), where findAll makes calls to findOne to loop through a number of records to query the data from those records in other tables using Sequelize. I've tried to lay out the code so that all promises passed to findOne are resolved or rejected before returning to the parent function, findAll.
Given a number of matching records in findAll, I pass each record to findOne, which by the time findOne resolves it calls another function, constructJson, which returns a value and not a promise. The issue I'm seeing is that once all records pass through findOne and are returned to findAll, the code actually goes back to the resolve(constructJson(_map, scrub)) line the findOne function a number of times. In my test use case where I’m supposed to be returned four distinct record IDs (that are passed into findOne to be queried), findOne is returning duplicated or out-of-order IDs. I very rarely get the same four sequential IDs returned in the same order they were passed in to findOne, though based on the flow of the function it seems like this should be the case. There is a piece to the asynchronicity I am missing in order to ensure the promise returned by findOne is fully completed the the associated IDs (queries object) passed into it are returned in order. What am I not seeing?
const Sequelize = require('sequelize');
function findAll(dbConn, _map, queries, joins, primaryTable, primaryKey, whereClause, limit, offset, scrub) {
return new Promise((resolve, reject) => {
const sequelize = new Sequelize(dbConn.db, dbConn.username, dbConn.password, dbConn.options);
offset = isNaN(offset) ? 0 : offset;
whereClause = !!whereClause ? ` WHERE true${whereClause}` : ' WHERE true';
sequelize.query(`SELECT count(${primaryKey}) FROM ${primaryTable}${whereClause}`, {type: sequelize.QueryTypes.SELECT})
.then(result => {
totalRows = result[0]["count"];
});
whereClause = isNaN(limit) ? whereClause : whereClause + ` LIMIT ${limit} OFFSET ${offset}`;
sequelize.query(`SELECT ${primaryKey} FROM ${primaryTable}${whereClause}`, {type: sequelize.QueryTypes.SELECT})
.then(matchingRecords => {
let promiseStack = [];
matchingRecords.forEach(record => {
const cloneOfMap = JSON.parse(JSON.stringify(_map));
promiseStack.push(
findOne(dbConn, cloneOfMap, queries, joins, primaryTable, primaryKey, record.id, scrub)
.then(result => {
return result;
})
.catch(error => {
return {err: `error collecting data for id = ${record.id}\n${error}`};
})
)
});
Promise.all(promiseStack).then(bundleEntries => {
sequelize.close();
let errors = bundleEntries.filter(r => {return !!r.err}).map(r => r.err);
if (errors.length === 0) {
resolve(bundleEntries);
} else {
reject("The following queries failed:\n" + errors.join('\n'));
}
});
})
.catch(error => {
sequelize.close();
reject(error);
});
});
}
function findOne(dbConn, _map, queries, joins, primaryTable, primaryKey, id, scrub) {
return new Promise((resolve, reject) => {
let promiseStack = [];
let noMatchesReturned = true;
const sequelize = new Sequelize(dbConn.db, dbConn.username, dbConn.password, dbConn.options);
let queryString;
queries.forEach(query => {
if (!!query.sql && query.sql !== '') {
switch (dbConn.options.dialect) {
case "postgres":
queryString = query.sql + ` WHERE ${primaryTable}.${primaryKey}='${id}' LIMIT 1`;
break;
case "mssql":
queryString = query.sql + ` WHERE ${primaryTable}.${primaryKey}='${id}'`;
break;
}
}
else {
let otherTable = '';
let joinString = '';
if (query.table !== primaryTable) {
otherTable = ", " + primaryTable;
joins.filter(j => {return j.fkTable === primaryTable && j.pkTable === query.table}).forEach(j => {
joinString += ` AND ${j.fkTable}.${j.fkColumn} = ${j.pkTable}.${j.pkColumn}`;
});
}
switch (dbConn.options.dialect) {
case "postgres":
queryString = `SELECT ${query.table}.${query.column} FROM ${query.table}${otherTable} WHERE ${primaryTable}.${primaryKey}='${id}'${joinString} LIMIT 1`;
break;
case "mssql":
queryString = `SELECT TOP 1 ${query.table}.${query.column} FROM ${query.table}${otherTable} WHERE ${primaryTable}.${primaryKey}='${id}'${joinString}`;
break;
}
}
// console.log(queryString);
promiseStack.push(
sequelize.query(queryString, {type: sequelize.QueryTypes.SELECT})
.then(row => {
let newData;
if (row.length === 0) {
newData = null;
} else {
noMatchesReturned = false;
const x = row[0];
const y = Object.keys(x)[0];
newData = x[y];
}
return Object.assign(query, {data: newData});
})
.catch(error => {
return {err: `Error executing query ${error.sql}\n\t${error}`};
})
);
});
Promise.all(promiseStack).then(rows => {
// identify queries that returned an error
sequelize.close();
let errors = rows.filter(r => {return !!r.err}).map(r => r.err);
if (errors.length === 0) {
if (noMatchesReturned) {
resolve([]);
}
else {
// all queries completed without error
rows.forEach(result => {
_map.rows[result.id].data = result.data;
});
resolve(constructJson(_map, scrub));
}
}
else {
sequelize.close();
reject("The following queries failed:\n" + errors.join('\n'));
}
}, error => {
sequelize.close();
reject(error);
});
});
}
I am trying to finish a login functionality with mysql and express. I got a work_id and a user_password, and I want to use the work_id to find whether the user exists in my database. I use promise to do this, I can log the selected user information in the console, but the promise is always pending, and the web storm console didn't terminate.
What I want is a boolean value from the promise, whether the user exists or not.
Here is my code:
query.js.
const pool = require('./connect');
module.exports = {
query: function (sqlString, params) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err)
} else {
connection.query(sqlString, params, (err, rows) => {
if (err) {
reject(err)
} else {
resolve(rows)
}
connection.release()
})
}
})
})
}
}
sqlCRUD.js, about the sql statement
const user = {
queryByWorkId: 'select * from user_info where work_id=?',
queryAll: 'select * from user_info',
resetPassword: 'update user_info set user_password = ? where work_id = ?',
};
user.js, I execute the test here.
const Model = require('./main')
const crypto = require('crypto')
const _ = require('./query')
const $sqlQuery = require('./sqlCRUD').user
class User{
// others
static findOne(form={}) {
const { work_id, user_password } = form
return _.query($sqlQuery.queryByWorkId, work_id)
.then(res => {
console.log(res)
if (res.length > 0) {
const u = res[0]
return u
}
return false
})
.catch(err => {
console.log('User.findOne error', err)
return {
errmsg: JSON.stringify(err)
}
})
}
Here is my test, in user.js
const test = () => {
const form = {
work_id: '007',
user_password: 'root',
}
const r = User.findOne(form)
console.log('r', r)
}
And this is the output:
I am not allowed to embed a picture here, so SO generates a link
I got confused about this: in my query.js file, I return a promise, in my User.findOne(form={}) method, I call it with a then and catch,
return _.query($sqlQuery.queryByWorkId, work_id).then(res => console.log(res)).catch(err => console.log(err)), but the console did't terminate, and I just got a Promise { }.
What's wrong with my code? How can I get a value returned from a then clause in promise when select data using mysql? Thanks in advance.
I am new to promises, I am trying to use RSVP promises in Node.js with PostgreSQL and I am doing it wrong, most probably.
Any suggestions on how to fix that or how to improve the code are appreciated.
What I try to achieve is: after receiving data - process the data to create SQL update queries and when they are ready - execute them. Data here is array of user ids.
What does not work: I get array of array of promises that doesn't resolve, I tried to resolve the array like so:
var promise4all = RSVP.all(
updateQueries.map((innerPromiseArray) => {
return RSVP.all(innerPromiseArray);
})
);
promise4all.then((promiseGroupResult) => {
// doesn't get here
});
But it didn't work also.
The code:
1) The function 'update' that receives data and calls function 'promiseQuery' to process the data:
const RSVP = require('rsvp');
let db;
const update = (data) => {
let users = {
items: data.user, // data to be updated in db - array of user ids
item_type: 1,
id: data.department
}
let updateQueries = [];
// adding query promises to updateQueries
updateQueries.push(promiseQuery(users.id, users.item_type, users.items));
RSVP.all(updateQueries).then((results) => {
/* here 'results' looks like that:
[ [ { query: 'INSERT INTO link_to_department (item_type, department, item) VALUES ($item_type, $department, $item)',
values: [Object] },
{ query: 'DELETE FROM link_to_department WHERE department = $(department) AND item_type = $(item_type) AND item=$(item)',
values: [Object] } ] ]
db update below fails with '[Empty or undefined query.]'*/
db.tx((trx) => {
let sqlUpdates = [];
results.forEach((query) => {
sqlUpdates.push(trx.none(query.query, query.values))
})
return trx.batch(sqlUpdates);
}).then(() => {
res.sendStatus(204);
}).catch((err) => {
console.log('error', err.message);
// handle errors
});
});
};
2) The function 'promiseQuery' processes data (it compares received data and data in db to update db with the new data):
const promiseQuery = (department_id, item_type, items) => {
return new RSVP.Promise((resolve, reject) => {
db.query('SELECT item FROM link_to_department WHERE department=' + department_id + ' AND item_type=' + item_type)
.then((db_items) => {
let promises = [];
let itemsToBeRemoved = [];
let itemsToBeAdded = [];
/* here we have array of user ids we received: 'items'
and array of user ids from db: 'db_items' */
// populating 'itemsToBeAdded' and 'itemsToBeRemoved' with user ids that need to added or removed:
populateUpdateArray(items, db_items, itemsToBeAdded);
populateUpdateArray(db_items, items, itemsToBeRemoved);
let insert_query = 'INSERT INTO link_to_department (item_type, department, item) VALUES ($item_type, $department, $item)'
let delete_query = 'DELETE FROM link_to_department WHERE department = $(department) AND item_type = $(item_type) AND item=$(item)';
// creating update sql queries
populateUpdateQuery(insert_query, itemsToBeAdded, department_id, item_type, promises);
populateUpdateQuery(delete_query, itemsToBeRemoved, department_id, item_type, promises);
RSVP.all(promises).then((results) => {
/* here 'results' looks like this:
[ { query: 'INSERT INTO link_to_department (item_type, department, item) VALUES ($item_type, $department, $item)',
values: { item_type: 19, department: 1, item: '1' } },
{ query: 'DELETE FROM link_to_department WHERE department = $(department) AND item_type = $(item_type) AND item=$(item)',
values: { item_type: 19, department: 1, item: 1 } }] */
return resolve(results);
});
}).catch(() => {
reject();
})
});
};
3) That function 'populateUpdateArray' populates array of user ids that need to be updated (basically, received user ids should replace ids in the db - for that we check what ids we received are not in db and what ids in db are not in the received ids):
const populateUpdateArray = (array_0, array_1, updateArray) => {
array_0.forEach((item) => {
if (array_1.indexOf(item) === -1) {
updateArray.push(item);
}
});
};
4) That function 'populateUpdateQuery' returns sql update queries:
const populateUpdateQuery = (query, id_array, department_id, item_type, promises) => {
return new RSVP.Promise((resolve, reject) => {
id_array.forEach((item) => {
let values = {
item_type: item_type,
department: department_id,
item: item
};
promises.push({query, values});
});
resolve(promises);
});
};
Thank you!
EDIT: I changed the code to have only one db connection and I simplified the code a little. I do not get any errors, but queries are not executed, still. I think I am missing something basic here:
const update = (data) => {
let users = {
items: data.user,
item_type: 1,
id: data.department
}
db.tx((tx) => {
let updateQueries = [];
updateQueries.push(promiseQuery(department.id, users.item_type, users.items, tx));
RSVP.all(updateQueries).then((results) => {
// results is array of array, so i flatten it
let sqlUpdates = results.reduce((a, b) => { return a.concat(b); }, []);
/* sqlUpdates here:
[ Promise {
_bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined } ]
*/
return tx.batch(sqlUpdates);
});
}).then(() => {
res.sendStatus(204);
}).catch((err) => {
console.log('error', err.message);
});
};
const promiseQuery = (department_id, item_type, items, tx) => {
return new RSVP.Promise((resolve, reject) => {
tx.query('SELECT item FROM belongs_to_departments WHERE department=' + department_id + ' AND item_type=' + item_type)
.then((db_items)=> {
let queries = [];
let itemsToBeAdded = [];
let insert_query = 'INSERT INTO belongs_to_departments (item_type, department, item) VALUES ($(item_type), $(department), $(item))';
populateUpdateArray(items, db_items, itemsToBeAdded);
populateUpdateQuery(insert_query, itemsToBeAdded, department_id, item_type, queries, tx);
resolve(queries);
}).catch(() => {
reject();
});
});
};
const populateUpdateArray = (array_0, array_1, updateArray) => {
array_0.forEach((item) => {
if (array_1.indexOf(item) === -1) {
updateArray.push(item);
}
});
};
const populateUpdateQuery = (query, id_array, department_id, item_type, queries, tx) => {
id_array.forEach((item) => {
let values = {
item_type: item_type,
department: department_id,
item: item
};
queries.push(tx.none(query, values));
});
};
Thanks to Vitaly for the help.
That worked for me:
const update = data => {
const users = {
items: data.user,
item_type: 1,
id: data.department
}
db.tx(tx => {
const updateQueries = [];
updateQueries.push(promiseQuery(department.id, users.item_type, users.items, tx));
RSVP.all(updateQueries).then(results => {
// results is array of array, so i flatten it
const sqlUpdates = results.reduce((a, b) => { return a.concat(b); }, []);
return tx.batch(sqlUpdates);
});
}).then(() => {
res.sendStatus(204);
}).catch(err => {
console.log('error', err.message);
});
};
const promiseQuery = (department_id, item_type, items, tx) => {
return new RSVP.Promise((resolve, reject) => {
tx.query('SELECT item FROM belongs_to_departments WHERE department=' + department_id + ' AND item_type=' + item_type)
.then(db_items => {
const queries = [];
const itemsToBeAdded = [];
const insert_query = 'INSERT INTO belongs_to_departments (item_type, department, item) VALUES ($(item_type), $(department), $(item))';
populateUpdateArray(items, db_items, itemsToBeAdded);
populateUpdateQuery(insert_query, itemsToBeAdded, department_id, item_type, queries, tx);
resolve(queries);
}).catch(() => {
reject();
});
});
};
const populateUpdateArray = (array_0, array_1, updateArray) => {
array_0.forEach((item) => {
if (array_1.indexOf(item) === -1) {
updateArray.push(item);
}
});
};
const populateUpdateQuery = (query, id_array, department_id, item_type, queries, tx) => {
id_array.forEach(item => {
const values = {
item_type: item_type,
department: department_id,
item: item
};
queries.push(tx.none(query, values));
});
};
The flow is:
I generate currentKey (key consist from 3 words separated by -)
I check if any document in my collection already have currentKey.
If it is not - currentKey will be pasted into my specific document in my collection.
But if it is - currentKey will be regenerated and all over again.
And the problem is mongoose have asynchronous callback that will run when query will finish executing. And i don't know how to implement my flow with this asynchronously flow. As the example (this is just example for convenience, it's not a existing framework or etc.) what i did on php is like next:
$currentKey = someService.GenerateKey();
//exists() returns true or false
while($db->find('tablename', ['key' => $currentKey])->exists())
{
$currentKey = someService.GenerateKey();
}
//It's update 'tablename' where `id` equals to $someUserId and set `key` to $currentKey value
$db->update('tablename', ['id' => $someUserId], ['key' => $currentKey]);
Can you help me, pls?
Promises are designed for that.
function generateUniqueKey(modelId) {
return new Promise((resolve, reject) => {
currentKey = someService.GenerateKey();
MyModel.count({ key: currentKey })
.then(count => {
if (count > 0) {
generateUniqueKey(modelId);
} else {
MyModel.update({ id: modelId }, { $set: { key: currentKey } })
.then(resolve(currentKey));
}
})
.catch(err => reject(err));
});
}
generateUniqueKey(someUserId).then(key => console.log('Generated unique key:', key));
But if you don't care about the result you can just remove the Promise mechanism and let the generateUniqueKey() method end on its own.
function generateUniqueKey(modelId) {
currentKey = someService.GenerateKey();
MyModel.count({ key: currentKey })
.then(count => {
if (count > 0) {
generateUniqueKey(modelId);
} else {
MyModel.update({ id: modelId }, { $set: { key: currentKey } })
.then(() => console.log('Generated unique key:', currentKey))
.catch(err => console.log(err));
}
})
.catch(err => console.log(err));
}
generateUniqueKey(someUserId);