1
0

Merge pull request #20 from Mathspy/boolean-controls

New Boolean controls (alwaysArray, offsets)
This commit is contained in:
Julien Buty
2018-06-12 14:41:42 +08:00
committed by GitHub
3 changed files with 123 additions and 20 deletions

View File

@@ -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.

View File

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

View File

@@ -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');
});
});