Last active
June 14, 2017 23:21
-
-
Save nickschot/8c8abcaf77b62fa8cd18bffba75d98b4 to your computer and use it in GitHub Desktop.
Lux + Postgres search middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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