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