Skip to content

Commit 7fcd56f

Browse files
Ivan-RogerIvan ROGER
authored andcommitted
Add option to customize negate prefix
1 parent 8158d09 commit 7fcd56f

File tree

4 files changed

+49
-15
lines changed

4 files changed

+49
-15
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ var searchQueryObj = searchQuery.parse(query, options);
5353
```
5454

5555
You can configure what keywords and ranges the parser should accept with the options argument.
56-
It accepts 5 values:
56+
It accepts 6 values:
5757
* `keywords`, that can be separated by commas (,). Accepts an array of strings.
5858
* `ranges`, that can be separated by a hyphen (-). Accepts an array of strings.
5959
* `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.
6060
* `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`.
6161
* `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`.
62+
* `negatePrefix`, a string that controls the behaviour of the returned query. It defines what prefix is used to mark a term as excluded. Defaults to `'-'`.
6263

6364
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.
6465

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface SearchParserOptions {
1010
keywords?: string[];
1111
ranges?: string[];
1212
alwaysArray?: boolean;
13+
negatePrefix?: string;
1314
}
1415

1516
export interface ISearchParserDictionary {

lib/search-query-parser.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ exports.parse = function (string, options) {
88

99
// Set a default options object when none is provided
1010
if (!options) {
11-
options = {offsets: true};
11+
options = {offsets: true, negatePrefix: '-'};
1212
} else {
1313
// If options offsets was't passed, set it to true
1414
options.offsets = (typeof options.offsets === 'undefined' ? true : options.offsets)
15+
// If options negatePrefix was't passed, set it to '-'
16+
options.negatePrefix = (typeof options.negatePrefix === 'undefined' ? '-' : options.negatePrefix)
1517
}
1618

1719
if (!string) {
@@ -69,9 +71,9 @@ exports.parse = function (string, options) {
6971
});
7072
} else {
7173
var isExcludedTerm = false;
72-
if (term[0] === '-') {
74+
if (term.startsWith(options.negatePrefix)) {
7375
isExcludedTerm = true;
74-
term = term.slice(1);
76+
term = term.slice(options.negatePrefix.length);
7577
}
7678

7779
// Strip surrounding quotes
@@ -132,15 +134,15 @@ exports.parse = function (string, options) {
132134
options.keywords = options.keywords || [];
133135
var isKeyword = false;
134136
var isExclusion = false;
135-
if (!/^-/.test(key)) {
136-
isKeyword = !(-1 === options.keywords.indexOf(key));
137-
} else if (key[0] === '-') {
138-
var _key = key.slice(1);
139-
isKeyword = !(-1 === options.keywords.indexOf(_key))
140-
if (isKeyword) {
141-
key = _key;
142-
isExclusion = true;
143-
}
137+
if (key.startsWith(options.negatePrefix)) {
138+
var _key = key.slice(options.negatePrefix.length);
139+
isKeyword = !(-1 === options.keywords.indexOf(_key))
140+
if (isKeyword) {
141+
key = _key;
142+
isExclusion = true;
143+
}
144+
} else {
145+
isKeyword = !(-1 === options.keywords.indexOf(key));
144146
}
145147

146148
// Check if the key is a registered range
@@ -314,7 +316,10 @@ exports.stringify = function (queryObject, options, prefix) {
314316

315317
// Set a default options object when none is provided
316318
if (!options) {
317-
options = {offsets: true};
319+
options = {offsets: true, negatePrefix: '-'};
320+
} else {
321+
// If options negatePrefix was't passed, set it to '-'
322+
options.negatePrefix = (typeof options.negatePrefix === 'undefined' ? '-' : options.negatePrefix)
318323
}
319324

320325
// If the query object is falsy we can just return an empty string
@@ -414,7 +419,7 @@ exports.stringify = function (queryObject, options, prefix) {
414419
// Exclude
415420
if (queryObject.exclude) {
416421
if (Object.keys(queryObject.exclude).length > 0) {
417-
parts.push(exports.stringify(queryObject.exclude, options, '-'));
422+
parts.push(exports.stringify(queryObject.exclude, options, options.negatePrefix));
418423
}
419424
}
420425

test/test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,21 @@ describe('Search query syntax parser', function () {
7373
parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery);
7474
});
7575

76+
it('should return a tokenized string with custom negation prefix', function () {
77+
var searchQuery = "fancy !pyjama !wear";
78+
var options = { tokenize: true, negatePrefix: '!' };
79+
var parsedSearchQuery = searchquery.parse(searchQuery, options);
80+
81+
parsedSearchQuery.should.be.an.Object;
82+
parsedSearchQuery.should.have.property('text', ['fancy']);
83+
parsedSearchQuery.should.have.property('exclude', {text: ['pyjama', 'wear']});
84+
85+
var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options);
86+
parsedAfterStringifySearchQuery.offsets = undefined;
87+
parsedSearchQuery.offsets = undefined;
88+
parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery);
89+
});
90+
7691
it('should return a tokenized string with negation of single-quoted terms', function () {
7792
var searchQuery = "fancy -'pyjama -wear'";
7893
var options = { tokenize: true };
@@ -788,4 +803,16 @@ describe('Search query syntax parser', function () {
788803
parsedSearchQuery.offsets = undefined;
789804
parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery);
790805
});
806+
807+
it('should stringify properly with default negate prefix', function () {
808+
var searchQueryObject = {
809+
text: [ 'fancy' ],
810+
exclude: { text: [ 'pyjama', 'wear' ] }
811+
};
812+
var options = { tokenize: true };
813+
stringifiedSearchQuery = searchquery.stringify(searchQueryObject, options);
814+
815+
stringifiedSearchQuery.should.be.a.string;
816+
stringifiedSearchQuery.should.be.eql('fancy -pyjama -wear');
817+
});
791818
});

0 commit comments

Comments
 (0)