From d9386421a96c37db3ef7af4c73a77cadd11cee20 Mon Sep 17 00:00:00 2001 From: Mathspy Date: Sun, 10 Jun 2018 08:47:24 +0200 Subject: [PATCH 1/3] Created failing tests --- test/test.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/test.js b/test/test.js index 674630d..6b374e3 100644 --- a/test/test.js +++ b/test/test.js @@ -347,6 +347,38 @@ describe('Search query syntax parser', function () { }]); }); + it('should always return an array if alwaysArray is set to true', function () { + var searchQuery = 'from:jul@foo.com to:a@b.c ouch!#'; + + var options = {keywords: ['from', 'to'], alwaysArray: true}; + var parsedSearchQuery = searchquery.parse(searchQuery, options); + + parsedSearchQuery.should.be.an.Object; + parsedSearchQuery.should.have.property('text', 'ouch!#'); + parsedSearchQuery.should.have.property('from'); + parsedSearchQuery.should.have.property('to'); + parsedSearchQuery.from.should.be.an.Array; + parsedSearchQuery.to.should.be.an.Array; + parsedSearchQuery.from.length.should.equal(1); + parsedSearchQuery.to.length.should.equal(1); + parsedSearchQuery.from.should.containEql('jul@foo.com'); + parsedSearchQuery.to.should.containEql('a@b.c'); + parsedSearchQuery.should.have.property('offsets', [{ + keyword: 'from', + value: 'jul@foo.com', + offsetStart: 0, + offsetEnd: 16 + }, { + keyword: 'to', + value: 'a@b.c', + offsetStart: 17, + offsetEnd: 25 + }, { + text: 'ouch!#', + offsetStart: 26, + offsetEnd: 32 + }]); + }); it('should parse range with only 1 end and free text', function () { var searchQuery = 'date:12/12/2012 ahaha'; @@ -586,4 +618,24 @@ describe('Search query syntax parser', function () { offsetEnd: 47 }]); }); + + it('should not return offset when offsets option is set to false', function() { + var searchQuery = '-from:jul@foo.com,mar@foo.com to:bar@hey.ya about date:12/12/2012'; + var options = { + keywords: ['from', 'to'], + ranges: ['date'], + offsets: false + }; + var parsedSearchQuery = searchquery.parse(searchQuery, options); + + parsedSearchQuery.should.be.an.Object; + parsedSearchQuery.exclude.should.be.an.Object; + parsedSearchQuery.exclude.from.should.containEql('jul@foo.com'); + parsedSearchQuery.exclude.from.should.containEql('mar@foo.com'); + parsedSearchQuery.to.should.containEql('bar@hey.ya'); + parsedSearchQuery.should.have.property('text', 'about'); + parsedSearchQuery.should.have.property('date'); + parsedSearchQuery.date.from.should.containEql('12/12/2012'); + parsedSearchQuery.should.not.have.property('offsets'); + }); }); \ No newline at end of file From 69624744f7041b6c82df74a4a959c5f79e01df28 Mon Sep 17 00:00:00 2001 From: Mathspy Date: Sun, 10 Jun 2018 08:47:52 +0200 Subject: [PATCH 2/3] Added offsets and alwaysArray boolean controls --- lib/search-query-parser.js | 60 ++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/lib/search-query-parser.js b/lib/search-query-parser.js index c6354ba..bf7d9f0 100644 --- a/lib/search-query-parser.js +++ b/lib/search-query-parser.js @@ -6,9 +6,12 @@ exports.parse = function (string, options) { - // Set an empty options object when none provided + // Set a default options object when none is provided if (!options) { - options = {}; + options = {offsets: true}; + } else { + // If options offsets was't passed, set it to true + options.offsets = (typeof options.offsets === 'undefined' ? true : options.offsets) } if (!string) { @@ -26,7 +29,11 @@ exports.parse = function (string, options) { // Otherwise parse the advanced query syntax else { // Our object to store the query object - var query = {text: [], offsets: []}; + var query = {text: []}; + // When offsets is true, create their array + if (options.offsets) { + query.offsets = []; + } var exclusion = {}; var terms = []; // Get a list of search terms respecting single and double quotes @@ -113,7 +120,10 @@ exports.parse = function (string, options) { if (term.text) { // We add it as pure text query.text.push(term.text); - query.offsets.push(term); + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push(term); + } } // We got an advanced search syntax else { @@ -138,12 +148,15 @@ exports.parse = function (string, options) { var isRange = !(-1 === options.ranges.indexOf(key)); // When the key matches a keyword if (isKeyword) { - query.offsets.push({ - keyword: key, - value: term.value, - offsetStart: isExclusion ? term.offsetStart + 1 : term.offsetStart, - offsetEnd: term.offsetEnd - }); + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push({ + keyword: key, + value: term.value, + offsetStart: isExclusion ? term.offsetStart + 1 : term.offsetStart, + offsetEnd: term.offsetEnd + }); + } var value = term.value; // When value is a thing @@ -217,8 +230,13 @@ exports.parse = function (string, options) { } // Got only a single value this time else { - // Record its value as a string - query[key] = value; + if (options.alwaysArray) { + // ...but we always return an array if option alwaysArray is true + query[key] = [value]; + } else { + // Record its value as a string + query[key] = value; + } } } } @@ -226,7 +244,10 @@ exports.parse = function (string, options) { } // The key allows a range else if (isRange) { - query.offsets.push(term); + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push(term); + } var value = term.value; // Range are separated with a dash @@ -253,11 +274,14 @@ exports.parse = function (string, options) { var text = term.keyword + ':' + term.value; query.text.push(text); - query.offsets.push({ - text: text, - offsetStart: term.offsetStart, - offsetEnd: term.offsetEnd - }); + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push({ + text: text, + offsetStart: term.offsetStart, + offsetEnd: term.offsetEnd + }); + } } } } From 5d47171b68f2134607783aab3b4d739e0c3c283f Mon Sep 17 00:00:00 2001 From: Mathspy Date: Tue, 12 Jun 2018 08:18:44 +0200 Subject: [PATCH 3/3] Updated readme to reflect additions! --- README.md | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 535cff4..8c83e3f 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,12 @@ var searchQueryObj = searchQuery.parse(query, options); ``` You can configure what keywords and ranges the parser should accept with the options argument. -It accepts 3 values: +It accepts 5 values: * `keywords`, that can be separated by commas (,). Accepts an array of strings. * `ranges`, that can be separated by a hyphen (-). Accepts an array of strings. * `tokenize`, that controls the behaviour of text search terms. If set to `true`, non-keyword text terms are returned as an array of strings where each term in the array is a whitespace-separated word, or a multi-word term surrounded by single- or double-quotes. +* `alwaysArray`, a boolean that controls the behaviour of the returned query. If set to `true`, all matched keywords would always be arrays instead of strings. If set to `false` they will be strings if matched a single value. Defaults to `false`. +* `offsets`, a boolean that controls the behaviour of the returned query. If set to `true`, the query will contain the offsets object. If set to `false`, the query will not contain the offsets object. Defaults to `true`. If no keywords or ranges are specified, or if none are present in the given search query, then `searchQuery.parse` will return a string if `tokenize` is false, or an array of strings under the key `text` if `tokenize` is true. @@ -87,9 +89,34 @@ You can also use exclusion syntax, like `-from:sep@foobar.io name:hello,world`. } ``` +Sometimes checking against whether a keyword holds string or not can be excessive and prone to errors; it's often easier to simply expect everything is an array even if it means doing 1-iteration loops often. + +```javascript +var searchQuery = require('search-query-parser'); + +var query = 'test:helloworld fun:yay,happy'; +var options = {keywords: ['test', 'fun']}; +var parsedQueryWithOptions = searchQuery.parse(query, options); +// parsedQueryWithOptions is now: +// { +// test: 'helloworld', +// fun: ['yay', 'happy'] +// } + +var optionsAlwaysArray = {keywords: ['test', 'fun'], alwaysArray: true}; +var parsedQueryWithOptions = searchQuery.parse(query, options); +// parsedQueryWithOptions is now: +// { +// test: ['helloworld'], //No need to check whether test is a string or not! +// fun: ['yay', 'happy'] +// } +``` + +The offsets object could become pretty huge with long search queries which could be an unnecessary use of space if no functionality depends on it. It can simply be turned off using the option `offsets: false` + ## Testing -The 27 tests are written using the BDD testing framework should.js, and run with mocha. +The 29 tests are written using the BDD testing framework should.js, and run with mocha. Run `npm install should` and `npm install -g mocha` to install them both.