Skip to content

Instantly share code, notes, and snippets.

@nickschot
Last active June 14, 2017 23:21
Show Gist options
  • Save nickschot/8c8abcaf77b62fa8cd18bffba75d98b4 to your computer and use it in GitHub Desktop.
Save nickschot/8c8abcaf77b62fa8cd18bffba75d98b4 to your computer and use it in GitHub Desktop.
Lux + Postgres search middleware
/*
This middleware is to be used in the controller beforeAction hook.
You can search text, int and multi-column.
Example use in a controller:
query = ['search'];
beforeAction = [
search(MyModel, {
searchText: ['description'],
searchMultiCol: {
'fullName': ['firstName', 'suffix', 'lastName']
}
})
];
Example query:
/my-model?search[fullName]=query&search[description]=word
Where:
- MyModel is a reference to the model which is to be searched
- options contains an object with one or more of searchText, searchInt and searchMultiCol
+ searchText is an array with the column names to be searched. String search is substring based and case insensitive.
+ searchInt is an array with the column names to be searched. Integer search is meant for numbers and is left to right.
+ searchMultiCol is an object with as the key a name for the multicol search parameter and as a value an array of the
columns to be concatted and then searched in the same manner as normal text search.
FilterIds is a small function which intersects the found id's with the id's already present in the filter array (if any).
*/
import snakeCase from 'lodash.snakecase';
import filterIds from 'app/utils/filter-ids';
export default function(model, options){
const {
searchText = [],
searchInt = [],
searchMultiCol = {},
forActions = []
} = options;
return async (request, response) => {
const {
action,
method,
params: {
search
}
} = request;
if(method === 'GET' && (action === 'index' || forActions.includes(action)) && search){
const keys = Object.keys(search);
let _searchText = new Map();
searchText.forEach((key) => {
if(keys.includes(key)){
_searchText.set(snakeCase(key), search[key]);
}
});
let _searchInt = new Map();
searchInt.forEach((key) => {
if(keys.includes(key)){
_searchInt.set(snakeCase(key), search[key]);
}
});
let _searchMultiCol = new Map();
Object.keys(searchMultiCol).forEach((key) => {
if(keys.includes(key)){
_searchMultiCol.set(key, search[key]);
}
});
if(_searchText.size || _searchInt.size || _searchMultiCol.size){
let queryArr = [];
let queryValues = [];
_searchText.forEach((value, key) => {
queryArr.push(`${key} ILIKE ?`);
queryValues.push(`%${value}%`);
});
_searchInt.forEach((value, key) => {
queryArr.push(`CAST(${key} AS TEXT) LIKE ?`);
queryValues.push(`${value}%`);
});
_searchMultiCol.forEach((value, key) => {
let cols = searchMultiCol[key].map((value) => `coalesce(${snakeCase(value)}, '')`);
queryArr.push(cols.join(` || `) + ' ILIKE ?');
// remove whitespaces from search string
queryValues.push(`%${value.replace(/\s/g, '')}%`);
});
let queryString = queryArr.join(' AND ');
const filterIdsArr = await model.table()
.whereRaw(queryString, queryValues)
.reduce((idsRes, row) => {
idsRes.push(row.id);
return idsRes;
}, []);
request.params.filter = filterIds(request, filterIdsArr);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment