Unhandled rejection SequelizeDatabaseError when passing dynamic query in where clause - javascript

I am trying to decompose a user's get request and put it in a variable named query. then pass the var query into the sequelize's findAll method using it's where clause, it seems like Sequelize thinks i am looking for a table CALLED query when in reality i am trying to pass the object. I'm sorry if i can not explain very well, but here is the code and the error:
var info = [];
//link example: localhost:8081/filter/?descripiton=san+francisco&houseType=house&numOfBedroom=3&numOfBathroom=2&houseSize=500&price=1200
exports.filterListings = function(req) {
//create an object literal which we will return, and has a nested object named filteredList inside.
//filteredList contains an array named listings where we will put listings that match our filter inside
let response = {
filteredList: {listings: []},
};
//now we need to see how the user wants us to filter the listings
const query = req.query;
//do some logic where we decompose query
if(query.descripiton != undefined) {
//info = info + 'descripiton: ' + query.descripiton+', ';
info.push('descripiton: ' + query.descripiton+', ');
console.log(info);
}
if(query.houseType != undefined) {
//info = info + 'houseType: ' + query.houseType+', ';
info.push('houseType: ' + query.houseType+', ');
//console.log(info);
}
if(query.numOfBedroom != undefined) {
//info = info + 'numOfBedroom: ' + query.numOfBedroom+', ';
info.push('numOfBedroom: ' + query.numOfBedroom+', ');
}
if(query.numOfBathroom != undefined) {
//info = info + 'numOfBathroom: ' + query.numOfBathroom+', ';
info.push('numOfBathroom: ' + query.numOfBathroom+', ');
}
if(query.houseSize != undefined) {
//info = info + 'houseSize: ' + query.houseSize+', ';
info.push('houseSize: ' + query.houseSize+', ');
}
if(query.price != undefined) {
//info = info + 'price: ' + query.price;
info.push('price: ' + query.price);
}
and then when i try to pass the info variable
listingModel.findAll({
//error because it wont recognize the variable search nor will it recognize info
where: {info}
}).then(listings => {
// so we loop through listings and insert what we have found into the response (which we are going to return)
for(var i = 0; i < listings.length; i++) {
response.filteredList.listings.push(listings[i]);
}; // loop where we insert data into response done
I want it to find all listings based on the dynamic query but i am getting the error:
Unhandled rejection SequelizeDatabaseError: Unknown column 'Listing.info' in 'where clause'
Thank you very much for the potential help!

Let's try to sort through your problems one by one. Sorry for the pun :p
Instead of using multiple if for creating your filtered list. Use for ... in. Then use that array of objects along with Sequelize.Op to create your query.
Example:
const Op = require('sequelize').Op;
const whereClause = [];
const query = req.query;
for(const key in query) {
if(query[key] !== '' && query[key] !== null) {
//object will be pushed to the array like "houseType:big"
whereClause.push({key:query[key]})
}
}
//you now have the where clause
//use it in your query with Op.and
listingModel.findAll({
where: {
[Op.and]: whereClause,
}
});
More info about querying with Sequelize - Operators

Related

JavaScript How to create a new object in a function?

I'm writing up a discord bot that will store a request from a message via an object.
The idea is you call a function that will create a new object that can be referenced as a way to store information, rather than having 1 giant file or variable that has to be referenced every time a request to display said information.
Currently my code is setup with a rudimentary version of what I want.
var order1 = {
content: "",
author: "",
}
var order2 = {
content: "",
author: "",
}
var order3 = {
content: "",
author: "",
}
Even from my limited experience of programming, I know that is something is repeated, and often, there is usually a more effective way to write it.
client.on('message', message =>{
if(!message.content.startsWith(prefix) || message.author.bot) return;
// Interpret Command
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
var messagecont = message.content.replace('!haul.order', ""); // Remove command string
if(command === 'haul.order'){
message.channel.send("Hual order for:" + messagecont + " by: " + message.author.username);
orderNum++; // Update the current number order
if (orderNum > 3) {
message.channel.send("Sorry we only have 3 storage objects! Our programmer is to lazy to fix
this!");
}
if (orderNum == 1) {
order1.content = messagecont;
order1.author = message.author.username + ". ";
} else if (orderNum == 2) {
order2.content = messagecont;
order2.author = message.author.username + ". ";
} else if (orderNum == 3) {
order3.content = messagecont;
order3.author = message.author.username + ". ";
}
} else if (command =="show.orders") {
message.channel.send("Orderlist:" + order1.content + " by: " + order1.author + order2.content + " by: " + order2.author + order3.content + " by: " + order3.author);
}
});
For demonstration this code currently has only three storage objects, however adding more would "fix" the issue but in the wrong way. I ask again, is there a way to create a new object via a function? Something like order1, than order2 gets created. I know Create.object() exists, but from my knowledge, it only applies a template to a variable you had to declare.
It would be more dynamic by storing the orders in an array. To such array you may push() as many entries as you like.
//REM: Containing all the orders
const _listOfOrders = [];
document.querySelector('button').addEventListener('click', function(){
//REM: Getting input values
let tContent = document.getElementById('content')?.value || '';
let tAuthor = document.getElementById('author')?.value || '';
//REM: Adding the values to the list of orders
_listOfOrders.push({
Content: tContent,
Author: tAuthor
});
//REM: Outputting the current list
console.table(_listOfOrders)
});
<input type = 'text' id = 'content' value = 'Content'>
<input type = 'text' id = 'author' value = 'Author'>
<button>order</button>
Open the console to see the result.

Firebase - Toggling value with transactions

I'm trying to let users favorite a project. I'm storing these projects at 2 places so I have to update them simultaneously. After looking at the firebase docs, using transactions seemed to be the best option.
.
Function to toggle the favorite status:
function toggleFavorite (projectReference, uid) {
projectReference.transaction(function(project) {
console.log('Before-Favorites :' + project.favoriteCount);
if (project.favorites && project.favorites[uid]) {
project.favoriteCount--;
project.favorites[uid] = null;
} else {
project.favoriteCount++;
if(!project.favorites) {
project.favorites= {};
}
project.favorites[uid] = true;
}
console.log(' After-Favorites :' + project.favoriteCount);
return project;
});
};
Function to add the eventListeners to the projects:
function AddToFavorite (uid, authorId) {
const favoriteList = document.querySelectorAll('.btnFavorite');
for(var i = 0; i<favoriteList.length; i++) {
favoriteList[i].addEventListener('click', function(event) {
const projectId = this.dataset.id;
console.log(projectId);
const globalProjectRef = firebase.database().ref('/projects/' + projectId);
const userProjectRef = firebase.database().ref('/user-projects/' + authorId + '/' + projectId);
toggleFavorite(globalProjectRef,uid);
toggleFavorite(userProjectRef,uid);
});
}
}
I want to store the uid of the current user under a 'favorites' node within the project location.
When i want to store the data I can see it appearing in the database but removing it after instantly. Followed by that i get an error in the console that my project object is null.
What's the best way of solving this issue ?

Writing/adjusting object from within callback

I'm working on a meteor react project and want to use load data from local storage which happens async. Unfortunately I wasn't able to get the data out of callback even with binds. I tried multiple ways but could not get any of them to work, surely I'm missing something.
I stripped as much away as I could but had to keep some for the context.
From my understanding it can only be related to the track object as setting those simple Integers, Booleans works fine.
render() {
const { audioCollection } = this.props; // database collection, async hence the following if check
if (audioCollection) {
this.tracks = audioCollection.audio(); // setting tracks for later use
// make sure tracks are loaded and only run once, as we do this in the react renderer
if (this.tracks && !this.state.tracksLoaded) {
var trackLoadedCount = 0;
this.tracks.forEach((track, i) => { // I tried to use forEach and map here
// now we try to load the data from local storage and if it fails fall back to the remote server
LocalForage.getItem(track.file_id).then(function(err, file) {
if (!err && file) {
console.log(track.file_id + ' from cache')
var blob = new Blob([file]);
fileURI = window.URL.createObjectURL(blob);
} else {
console.log(track.file_id + ' from database')
fileURI = audioCollection.audioLink(track.file_id);
}
track.fileURI = fileURI; // assigning the retrieved data uri to the track object, also tried to set it on the original parent object not the extracted one from the forEach/map
console.log(fileURI + ' ' + track.fileURI) // yes, both are set
trackLoadedCount++; // increasing the tracks loaded count, to calculate if all have been loaded and to test if it can write outside of the callback, which it does
// if all files have been processed, set state loaded, this works too.
if (trackLoadedCount == this.tracks.length) {
this.setState({
tracksLoaded: true,
})
}
}.bind(track, this))
});
}
}
// once all has been processed we try to retrieve the fileURI, but it isn't set :(
if (audioCollection && this.tracks && this.state.tracksLoaded) {
console.log('all loaded ' + this.tracks.length)
this.tracks.map(track => {
console.log('track: ' + track.file_id + ' ' + track.fileURI) // file_id is set, fileURI is not
})
}
// we only log
return (
<Content {...this.props}>
<div>just console output</div>
</Content>
);
}
I tried more approaches:
Writing the tracks as an array to state like tracksLoaded (didn't work work)
Defining a new var before the async call and setting its values from within the callback, like trackLoadedCount (with and without bind) (doesn't work)
Why isn't this working while its working for tracksLoaded and trackLoadedCount?
Update regarding Firice Nguyen Answer
render() {
const { audioCollection } = this.props;
if (audioCollection) {
this.tracks = audioCollection.audio();
if (this.tracks && !this.state.tracksLoaded) {
var trackLoadedCount = 0;
this.tracks.forEach((track, i, trackArray) => {
LocalForage.getItem(track.file_id).then(function(err, file) {
if (!err && file) {
console.log(track.file_id + ' from cache')
var blob = new Blob([file]);
fileURI = window.URL.createObjectURL(blob);
} else {
console.log(track.file_id + ' from database')
fileURI = audioCollection.audioLink(track.file_id);
}
track.fileURI = fileURI;
console.log('1. ' + track.file_id + ' ' + track.fileURI);
trackArray[i] = track;
console.log('2. ' + track.file_id + ' ' + trackArray[i].fileURI);
trackLoadedCount++;
if (trackLoadedCount == this.tracks.length) {
this.setState({
tracksLoaded: true,
})
}
}.bind(track, this))
});
}
}
if (audioCollection && this.tracks && this.state.tracksLoaded) {
console.log('all loaded ' + this.tracks.length)
this.tracks.map(track => {
console.log('3. ' + track.file_id + ' ' + track.fileURI) // file_id is set, fileURI is not
})
}
return (
<Content {...this.props}>
<div>just console output</div>
</Content>
);
}
returns
MXqniBNnq4zCfZz5Q from database
1. http://localhost:3000/cdn/storage/files/MXqniBNnq4zCfZz5Q/original/MXqniBNnq4zCfZz5Q.m4a
2. http://localhost:3000/cdn/storage/files/MXqniBNnq4zCfZz5Q/original/MXqniBNnq4zCfZz5Q.m4a
keBWP6xb9PyEJhEzo from database
1. http://localhost:3000/cdn/storage/files/keBWP6xb9PyEJhEzo/original/keBWP6xb9PyEJhEzo.m4a
2. http://localhost:3000/cdn/storage/files/keBWP6xb9PyEJhEzo/original/keBWP6xb9PyEJhEzo.m4a
K2J2W9W26DDBNoCcg from database
1. http://localhost:3000/cdn/storage/files/K2J2W9W26DDBNoCcg/original/K2J2W9W26DDBNoCcg.m4a
2. http://localhost:3000/cdn/storage/files/K2J2W9W26DDBNoCcg/original/K2J2W9W26DDBNoCcg.m4a
all loaded 3
3. MXqniBNnq4zCfZz5Q undefined
3. keBWP6xb9PyEJhEzo undefined
3. K2J2W9W26DDBNoCcg undefined
hence the issue persists.
The forEach give out a copy of the element. The track is just a copy, not the original one. It does not point to the element in your array. You can try this:
this.tracks.forEach(track, i, trackArray) => {
// change `track` value
...
trackArray[i] = track;
});
Or try map method

pg-promise create custom filters for select query

The function that I am working on is getting an input object that has 7 different key-values and each of them could be undefined or not. I want to filter my database based on those key-values that exists in the input. For example if only input.userID exists I want to run this query:
db.query("...WHERE userID = ${userID}", {userID: input.userID});
else if both input.userID and input.startTime exist, I want to do this:
db.query("...WHERE userID = ${userID} and startTime= ${startTime}", {userID: input.userID, startTime: input.startTime});
What I have done is I created a params and keys object like this:
if(input.userID) {
keys.push('userID');
params.push(input.userID);
query = addFilterToTheQuery(query, 'userID', input.userID, filteredItemsCount);
filteredItemsCount = filteredItemsCount +1;
}
addFilterToTheQuery is a simple function I implemented myself. I basically make 7 if cases. Then I have to use those keys and param values to pass to the query function in a way that might need another huge switch case code.
Is this the only way to do this? Is there a better way to get rid of the redundancies in this code?
Custom Type Formatting is the most suitable here.
For example, if we want to convert an object with properties - filter values, we could do it like this:
var pgp = require('pg-promise')(/* initialization options */);
function FilterSet(filters) {
if (!filters || typeof filters !== 'object') {
throw new TypeError('Parameter \'filters\' must be an object.');
}
this._rawDBType = true; // property renamed later - see UPDATE below
this.formatDBType = function () {
var keys = Object.keys(filters);
var s = keys.map(function (k) {
return pgp.as.name(k) + ' = ${' + k + '}';
}).join(' AND ');
return pgp.as.format(s, filters);
};
}
TEST
var filter = new FilterSet({
first: 1,
second: 'two'
});
var test = pgp.as.format('WHERE $1', filter);
console.log(test);
This outputs:
WHERE "first" = 1 AND "second" = 'two'
If your filters are to be used as %value% with LIKE or ILIKE, then you would need to change your custom type accordingly.
See related questions:
42, 49, 89, 90,
UPDATE
Below is the same example re-written for the latest pg-promise (version 8.x or newer):
const pgp = require('pg-promise')(/* initialization options */);
class FilterSet {
constructor(filters) {
if (!filters || typeof filters !== 'object') {
throw new TypeError('Parameter \'filters\' must be an object.');
}
this.filters = filters;
this.rawType = true; // do not escape the result from toPostgres()
}
toPostgres(/*self*/) {
// self = this
const keys = Object.keys(this.filters);
const s = keys.map(k => pgp.as.name(k) + ' = ${' + k + '}').join(' AND ');
return pgp.as.format(s, this.filters);
}
}
See Custom Type Formatting.

SQL Insert Statement not Working in Javascript

I'm trying to get my WebSQL database to popluate using a JSON array (Object is called myJSONObject, array is called costcodes).
The function runs on click, the database successfully creates, but the data is not inserted into the database. It doesn't even throw and error, it just doesn't do anything.
My initial thought was that the data isn't escaped properly, but I don't know exactly where/how to escape it. So I'm stumped.
localDB = null;
function initDB()
{
if (localDB==null)
{
var shortName = 'Costcode';
var version = '1.0';
var displayName = 'Costcode';
var maxSize = 217802; // in bytes
localDB = window.openDatabase(shortName, version, displayName, maxSize);
}
}
function buildTable()
{
var sQuery = 'CREATE TABLE IF NOT EXISTS Costcode ('+
'id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,' +
'cost_code_no VARCHAR NULL,' +
'row_unique_no VARCHAR NULL,' +
'cost_class_no VARCHAR NULL,' +
'Table_Version VARCHAR DEFAULT "1.0");';
try
{
initDB();
localDB.transaction(function(transaction)
{
transaction.executeSql(sQuery, []);
console.log('sucess');
});
}
catch (e)
{
alert("Error: Unable to create table 'x" + "' " + e + ".");
return;
}
}
function exeSQLFast()
{
initDB();
localDB.transaction(function(transaction)
{
for (var x = 0; x <myJSONObject.costcodes.length; x++)
{
var costcodeno = myJSONObject.costcodes[x].cost_code_no;
var rowuniqueid = myJSONObject.costcodes[x].row_unique_id;
var costclassno = myJSONObject.costcodes[x].cost_class_no;
console.log(costcodeno);
console.log(rowuniqueid);
console.log(costclassno);
transaction.executeSql('INSERT INTO Costcode (cost_code_no, row_unique_id, cost_class_no) VALUES (? , ? , ?)',
[costcodeno,
rowuniqueid,
costclassno]
, function(transaction, results)
{
console.log(costcodeno);
console.log('hooray');
}
)};
}
)}
</script>
</head>
<body onLoad="buildTable();">
<input type="button" onClick="exeSQLFast();" value='button'>
</body>
</html>
The console log shows that the variables are all being properly defined, but it's not running the insert statement. Any ideas?
Here's an example of myJSONObject.costcodes[2]
cost_class_no: " 3"
cost_code_no: " 1000"
row_unique_id: 335
That looks like a problem doesn't it...
The problem was that I called the row_unique_no column row_unique_id in the insert statement. Now I feel stupid.
But if anyone is curious how to populate a WebSQL database properly, this is how you do it. Just don't mistype the column names.

Categories

Resources