Skip to content

Commit

Permalink
Merge pull request #15 from brandsExclusive/LBO-611
Browse files Browse the repository at this point in the history
[LBO-611] Added additional functionality for full text search
  • Loading branch information
reb2020 authored May 15, 2018
2 parents 0eaa526 + a24abfe commit 3385a32
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 13 deletions.
7 changes: 6 additions & 1 deletion lib/functions/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@

module.exports = {
similarity: require('./similarity')
similarity: require('./similarity'),
plainto_tsquery: require('./plainto_tsquery'),
make_tsvector: require('./make_tsvector'),
add_schema: require('./schema'),
ts_rank: require('./ts_rank')
}


6 changes: 6 additions & 0 deletions lib/functions/make_tsvector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const add_schema = require('./schema');

module.exports = function (fields, schema) {
return add_schema(schema, `make_tsvector(${fields.join(',')})`);
}

4 changes: 4 additions & 0 deletions lib/functions/plainto_tsquery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = function (placeholder) {
return `plainto_tsquery(${placeholder}) AS to_tsquery_query`;
}

4 changes: 4 additions & 0 deletions lib/functions/schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = function (schema, name) {
return (typeof schema !== 'undefined' ? schema + '.' : '') + name;
}

6 changes: 2 additions & 4 deletions lib/functions/similarity.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
const escape = require("pg-escape");

module.exports = function (field, word) {
return escape('similarity(%I, %L)', field, word)
module.exports = function (field, placeholder) {
return `similarity(${field}, ${placeholder})`;
}

4 changes: 4 additions & 0 deletions lib/functions/ts_rank.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = function (make_tsvector) {
return `ts_rank(${make_tsvector}, to_tsquery_query)`;
}

33 changes: 29 additions & 4 deletions lib/queryBuilder.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// See test/queryBuilder_test.js for usage examples

const {similarity} = require('./functions/index');
const {similarity, plainto_tsquery, add_schema, make_tsvector, ts_rank} = require('./functions/index');

function queryBuilder(initial_state) {
let out = {};
Expand All @@ -18,10 +18,13 @@ function queryBuilder(initial_state) {
sql = sql + " SELECT " + this.state.select.join(", ")
}
if (this.state.from) {
sql = sql + ` FROM ${this.state.from} `
sql = sql + ` FROM ${add_schema(this.state.schema, this.state.from)} `
} else {
throw "From must be supplied";
}
if (this.state.tsquery) {
sql = sql + ` ,${plainto_tsquery("$" + bound_vars.push(this.state.tsquery))} `
}

if (this.state.wheres) {
let statements = [],
Expand Down Expand Up @@ -49,6 +52,8 @@ function queryBuilder(initial_state) {

} else if ((operator == 'IS') && (value == 'NULL')) {
statements.push(`${column} IS NULL`)
} else if (operator == 'make_tsvector') {
statements.push(`${column} @@ to_tsquery_query`)
} else {
placeholder = "$" + bound_vars.push(value);
statements.push(` ${column} ${operator} ${placeholder} `)
Expand All @@ -67,7 +72,7 @@ function queryBuilder(initial_state) {

if(orderParams){
if(orderParams.function == 'similarity'){
column = similarity(column, orderParams.values.pop());
column = similarity(column, "$" + bound_vars.push(orderParams.values.pop()));
}
}

Expand Down Expand Up @@ -109,7 +114,7 @@ function queryBuilder(initial_state) {
return this;
};
out.addSelect = addSelect.bind(out);

let orderBy = function (field, direction) {
delete this.state.count;
this.state.orderBy = [];
Expand Down Expand Up @@ -145,6 +150,26 @@ function queryBuilder(initial_state) {
};
out.where = where.bind(out);

let schema = function (name) {
this.state.schema = name;

return this;
};
out.schema = schema.bind(out);

let fullTextSearch = function (fields, word, direction) {
this.state.tsquery = word;
let tsvector = make_tsvector(fields, this.state.schema);
this.where(tsvector, 'make_tsvector');

if(typeof direction !== 'undefined'){
this.addOrderBy(ts_rank(tsvector), direction);
}

return this;
};
out.fullTextSearch = fullTextSearch.bind(out);

let paginate = function (page, per_page) {
if (!per_page) {
per_page = 10;
Expand Down
54 changes: 50 additions & 4 deletions test/queryBuilder_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,54 @@ describe('queryBuilder', function() {
query.addOrderBy("first_name", "DESC");
query.addOrderByFunction('similarity', 'species', ['cavendish'], 'DESC')
expect([
" SELECT first_name, last_name FROM banana WHERE species in ($1,$2) AND feeder = $3 ORDER BY species ASC, first_name DESC, similarity(species, 'cavendish') DESC"
,["cavendish","alchemist","ape"]]).to.eql(query.finalize());
" SELECT first_name, last_name FROM banana WHERE species in ($1,$2) AND feeder = $3 ORDER BY species ASC, first_name DESC, similarity(species, $4) DESC"
,["cavendish","alchemist","ape", "cavendish"]]).to.eql(query.finalize());
});

it('should compose all query types and full text search', function() {
query = queryBuilder();
query.from("banana");
query.orderBy("species", "ASC");
query.where("species","=", "cavendish");
query.where("state","=", "ripe");
query.fullTextSearch(['name'], 'Test Name', 'DESC');
second_query = query.clone();
second_query.count();
query.paginate(5, 5);

expect([" SELECT * FROM banana ,plainto_tsquery($1) AS to_tsquery_query WHERE species = $2 AND state = $3 AND make_tsvector(name) @@ to_tsquery_query ORDER BY species ASC, ts_rank(make_tsvector(name), to_tsquery_query) DESC LIMIT 5 OFFSET 20 " , ["Test Name", "cavendish", "ripe"]]).to.eql(query.finalize());
expect([" SELECT COUNT(*) FROM banana ,plainto_tsquery($1) AS to_tsquery_query WHERE species = $2 AND state = $3 AND make_tsvector(name) @@ to_tsquery_query",["Test Name", "cavendish", "ripe"]]).to.eql(second_query.finalize());
});

it('should compose all query types and full text search without order', function() {
query = queryBuilder();
query.from("banana");
query.orderBy("species", "ASC");
query.where("species","=", "cavendish");
query.where("state","=", "ripe");
query.fullTextSearch(['name'], 'Test Name');
second_query = query.clone();
second_query.count();
query.paginate(5, 5);

expect([" SELECT * FROM banana ,plainto_tsquery($1) AS to_tsquery_query WHERE species = $2 AND state = $3 AND make_tsvector(name) @@ to_tsquery_query ORDER BY species ASC LIMIT 5 OFFSET 20 " , ["Test Name", "cavendish", "ripe"]]).to.eql(query.finalize());
expect([" SELECT COUNT(*) FROM banana ,plainto_tsquery($1) AS to_tsquery_query WHERE species = $2 AND state = $3 AND make_tsvector(name) @@ to_tsquery_query",["Test Name", "cavendish", "ripe"]]).to.eql(second_query.finalize());
});

it('should compose all query types and full text search and add schema', function() {
query = queryBuilder();
query.schema("test_schema");
query.from("banana");
query.orderBy("species", "ASC");
query.where("species","=", "cavendish");
query.where("state","=", "ripe");
query.fullTextSearch(['name'], 'Test Name', 'DESC');
second_query = query.clone();
second_query.count();
query.paginate(5, 5);

expect([" SELECT * FROM test_schema.banana ,plainto_tsquery($1) AS to_tsquery_query WHERE species = $2 AND state = $3 AND test_schema.make_tsvector(name) @@ to_tsquery_query ORDER BY species ASC, ts_rank(test_schema.make_tsvector(name), to_tsquery_query) DESC LIMIT 5 OFFSET 20 " , ["Test Name", "cavendish", "ripe"]]).to.eql(query.finalize());
expect([" SELECT COUNT(*) FROM test_schema.banana ,plainto_tsquery($1) AS to_tsquery_query WHERE species = $2 AND state = $3 AND test_schema.make_tsvector(name) @@ to_tsquery_query",["Test Name", "cavendish", "ripe"]]).to.eql(second_query.finalize());
});

it('test injection', function() {
Expand All @@ -245,8 +291,8 @@ describe('queryBuilder', function() {
query.addOrderBy("first_name", "DESC");
query.addOrderByFunction('similarity', 'species', ['\'; (select * from banana);'], 'DESC')
expect([
" SELECT first_name, last_name FROM banana WHERE species in ($1,$2) AND feeder = $3 ORDER BY species ASC, first_name DESC, similarity(species, '''; (select * from banana);') DESC"
,["cavendish","alchemist","ape"]]).to.eql(query.finalize());
" SELECT first_name, last_name FROM banana WHERE species in ($1,$2) AND feeder = $3 ORDER BY species ASC, first_name DESC, similarity(species, $4) DESC"
,["cavendish","alchemist","ape",'\'; (select * from banana);']]).to.eql(query.finalize());
});

});
Expand Down

0 comments on commit 3385a32

Please sign in to comment.