You've already forked search-query-parser
17
README.md
17
README.md
@@ -48,8 +48,8 @@ var searchQueryObj = searchQuery.parse(query, options);
|
||||
|
||||
You can configure what keywords and ranges the parser should accept with the options argument.
|
||||
It accepts 2 values:
|
||||
* `keywords`, that can be separated by commas (,)
|
||||
* `ranges`, that can be separated by a hyphen (-)
|
||||
* `keywords`, that can be separated by commas (,)
|
||||
* `ranges`, that can be separated by a hyphen (-)
|
||||
|
||||
Both values take an array of strings, as in the example just above.
|
||||
|
||||
@@ -67,9 +67,20 @@ var parsedQueryWithOptions = searchQuery.parse(query, options);
|
||||
// parsedQueryWithOptions is now 'a query with just text'
|
||||
```
|
||||
|
||||
You can also use exclusion syntax, like `-from:sep@foobar.io name:hello,world` . And it will return :
|
||||
|
||||
```javascript
|
||||
{
|
||||
name: ['hello', 'world'],
|
||||
exclusion: {
|
||||
from: ['sep@foobar.io']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The 17 tests are written using the BDD testing framework should.js, and run with mocha.
|
||||
The 20 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.
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ exports.parse = function (string, options) {
|
||||
else {
|
||||
// Our object to store the query object
|
||||
var query = {text: []};
|
||||
var exclusion = {};
|
||||
// Get a list of search terms respecting single and double quotes
|
||||
var terms = string.match(/(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|\S+|\S+:\S+/g);
|
||||
for (var i = 0; i < terms.length; i++) {
|
||||
@@ -74,7 +75,18 @@ exports.parse = function (string, options) {
|
||||
var key = term.slice(0, sepIdx);
|
||||
// Check if the key is a registered keyword
|
||||
options.keywords = options.keywords || [];
|
||||
var isKeyword = !(-1 === options.keywords.indexOf(key));
|
||||
var isKeyword = false;
|
||||
var isExclusion = false;
|
||||
if (!/^-/.test(key)) {
|
||||
isKeyword = !(-1 === options.keywords.indexOf(key));
|
||||
} else if (key[0] === '-') {
|
||||
var _key = key.slice(1);
|
||||
isKeyword = !(-1 === options.keywords.indexOf(_key))
|
||||
if (isKeyword) {
|
||||
key = _key;
|
||||
isExclusion = true;
|
||||
}
|
||||
}
|
||||
// Check if the key is a registered range
|
||||
options.ranges = options.ranges || [];
|
||||
var isRange = !(-1 === options.ranges.indexOf(key));
|
||||
@@ -85,42 +97,79 @@ exports.parse = function (string, options) {
|
||||
if (value.length) {
|
||||
// Get an array of values when several are there
|
||||
var values = value.split(',');
|
||||
// If we already have seen that keyword...
|
||||
if (query[key]) {
|
||||
// ...many times...
|
||||
if (query[key] instanceof Array) {
|
||||
if (isExclusion) {
|
||||
if (exclusion[key]) {
|
||||
// ...many times...
|
||||
if (exclusion[key] instanceof Array) {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ... concatenate both arrays.
|
||||
exclusion[key] = exclusion[key].concat(values);
|
||||
}
|
||||
else {
|
||||
// ... append the current single value.
|
||||
exclusion[key].push(value);
|
||||
}
|
||||
}
|
||||
// We saw that keyword only once before
|
||||
else {
|
||||
// Put both the current value and the new
|
||||
// value in an array
|
||||
exclusion[key] = [exclusion[key]];
|
||||
exclusion[key].push(value);
|
||||
}
|
||||
}
|
||||
// First time we see that keyword
|
||||
else {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ... concatenate both arrays.
|
||||
query[key] = query[key].concat(values);
|
||||
// ...add all values seen.
|
||||
exclusion[key] = values;
|
||||
}
|
||||
// Got only a single value this time
|
||||
else {
|
||||
// ... append the current single value.
|
||||
// Record its value as a string
|
||||
exclusion[key] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we already have seen that keyword...
|
||||
if (query[key]) {
|
||||
// ...many times...
|
||||
if (query[key] instanceof Array) {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ... concatenate both arrays.
|
||||
query[key] = query[key].concat(values);
|
||||
}
|
||||
else {
|
||||
// ... append the current single value.
|
||||
query[key].push(value);
|
||||
}
|
||||
}
|
||||
// We saw that keyword only once before
|
||||
else {
|
||||
// Put both the current value and the new
|
||||
// value in an array
|
||||
query[key] = [query[key]];
|
||||
query[key].push(value);
|
||||
}
|
||||
}
|
||||
// We saw that keyword only once before
|
||||
// First time we see that keyword
|
||||
else {
|
||||
// Put both the current value and the new
|
||||
// value in an array
|
||||
query[key] = [query[key]];
|
||||
query[key].push(value);
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ...add all values seen.
|
||||
query[key] = values;
|
||||
}
|
||||
// Got only a single value this time
|
||||
else {
|
||||
// Record its value as a string
|
||||
query[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// First time we see that keyword
|
||||
else {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ...add all values seen.
|
||||
query[key] = values;
|
||||
}
|
||||
// Got only a single value this time
|
||||
else {
|
||||
// Record its value as a string
|
||||
query[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The key allows a range
|
||||
else if (isRange) {
|
||||
@@ -161,8 +210,8 @@ exports.parse = function (string, options) {
|
||||
}
|
||||
|
||||
// Return forged query object
|
||||
query.exclude = exclusion;
|
||||
return query;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
35
test/test.js
35
test/test.js
@@ -223,4 +223,39 @@ describe('Search query syntax parser', function () {
|
||||
});
|
||||
|
||||
|
||||
it('should parse a single keyword query in exclusion syntax', function() {
|
||||
var searchQuery = '-from:jul@foo.com';
|
||||
var options = {keywords: ['from']};
|
||||
var parsedSearchQuery = searchquery.parse(searchQuery, options);
|
||||
|
||||
parsedSearchQuery.should.be.an.Object;
|
||||
parsedSearchQuery.exclude.should.be.an.Object;
|
||||
parsedSearchQuery.exclude.should.have.property('from', 'jul@foo.com');
|
||||
parsedSearchQuery.should.not.have.property('text');
|
||||
});
|
||||
|
||||
it('should concatenate a keyword multiple values in exclusion syntax', function() {
|
||||
var searchQuery = '-from:jul@foo.com,mar@foo.com';
|
||||
var options = {keywords: ['from']};
|
||||
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.should.not.have.property('text');
|
||||
});
|
||||
|
||||
it('should support keywords which appear multiple times with exclusion syntax', function() {
|
||||
var searchQuery = '-from:jul@foo.com,mar@foo.com -from:jan@foo.com';
|
||||
var options = {keywords: ['from']};
|
||||
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.exclude.from.should.containEql('jan@foo.com');
|
||||
parsedSearchQuery.should.not.have.property('text');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user