minisearch
Advanced tools
Comparing version 3.0.4 to 3.1.0
@@ -5,2 +5,7 @@ # Changelog | ||
# v3.1.0 | ||
- Add possibility for advanced combination of subqueries as query expression | ||
trees | ||
# v3.0.4 | ||
@@ -7,0 +12,0 @@ |
@@ -950,17 +950,37 @@ /*! ***************************************************************************** | ||
* | ||
* @param queryString Query string to search for | ||
* ### Advanced combination of queries: | ||
* | ||
* It is possible to combine different subqueries with OR and AND, and even | ||
* with different search options, by passing a query expression tree object as | ||
* the first argument, instead of a string. | ||
* | ||
* ```javascript | ||
* // Search for documents that contain "zen" AND ("motorcycle" OR "archery") | ||
* miniSearch.search({ | ||
* combineWith: 'AND', | ||
* queries: [ | ||
* 'zen', | ||
* { | ||
* combineWith: 'OR', | ||
* queries: ['motorcycle', 'archery'] | ||
* } | ||
* ] | ||
* }) | ||
* ``` | ||
* | ||
* Each node in the expression tree can be either a string, or an object that | ||
* supports all `SearchOptions` fields, plus a `queries` array field for | ||
* subqueries. | ||
* | ||
* Note that, while this can become complicated to do by hand for complex or | ||
* deeply nested queries, it provides a formalized expression tree API for | ||
* external libraries that implement a parser for custom query languages. | ||
* | ||
* @param query Search query | ||
* @param options Search options. Each option, if not given, defaults to the corresponding value of `searchOptions` given to the constructor, or to the library default. | ||
*/ | ||
MiniSearch.prototype.search = function (queryString, searchOptions) { | ||
MiniSearch.prototype.search = function (query, searchOptions) { | ||
var _this = this; | ||
if (searchOptions === void 0) { searchOptions = {}; } | ||
var _a = this._options, tokenize = _a.tokenize, processTerm = _a.processTerm, globalSearchOptions = _a.searchOptions; | ||
var options = __assign(__assign({ tokenize: tokenize, processTerm: processTerm }, globalSearchOptions), searchOptions); | ||
var searchTokenize = options.tokenize, searchProcessTerm = options.processTerm; | ||
var terms = searchTokenize(queryString) | ||
.map(function (term) { return searchProcessTerm(term); }) | ||
.filter(function (term) { return !!term; }); | ||
var queries = terms.map(termToQuery(options)); | ||
var results = queries.map(function (query) { return _this.executeQuery(query, options); }); | ||
var combinedResults = this.combineResults(results, options.combineWith); | ||
var combinedResults = this.executeQuery(query, searchOptions); | ||
return Object.entries(combinedResults) | ||
@@ -976,3 +996,3 @@ .reduce(function (results, _a) { | ||
Object.assign(result, _this._storedFields[docId]); | ||
if (options.filter == null || options.filter(result)) { | ||
if (searchOptions.filter == null || searchOptions.filter(result)) { | ||
results.push(result); | ||
@@ -1155,2 +1175,35 @@ } | ||
var _this = this; | ||
if (searchOptions === void 0) { searchOptions = {}; } | ||
if (typeof query === 'string') { | ||
return this.executeSearch(query, searchOptions); | ||
} | ||
else { | ||
var results = query.queries.map(function (subquery) { | ||
var options = __assign(__assign(__assign({}, searchOptions), query), { queries: undefined }); | ||
return _this.executeQuery(subquery, options); | ||
}); | ||
return this.combineResults(results, query.combineWith); | ||
} | ||
}; | ||
/** | ||
* @ignore | ||
*/ | ||
MiniSearch.prototype.executeSearch = function (queryString, searchOptions) { | ||
var _this = this; | ||
if (searchOptions === void 0) { searchOptions = {}; } | ||
var _a = this._options, tokenize = _a.tokenize, processTerm = _a.processTerm, globalSearchOptions = _a.searchOptions; | ||
var options = __assign(__assign({ tokenize: tokenize, processTerm: processTerm }, globalSearchOptions), searchOptions); | ||
var searchTokenize = options.tokenize, searchProcessTerm = options.processTerm; | ||
var terms = searchTokenize(queryString) | ||
.map(function (term) { return searchProcessTerm(term); }) | ||
.filter(function (term) { return !!term; }); | ||
var queries = terms.map(termToQuerySpec(options)); | ||
var results = queries.map(function (query) { return _this.executeQuerySpec(query, options); }); | ||
return this.combineResults(results, options.combineWith); | ||
}; | ||
/** | ||
* @ignore | ||
*/ | ||
MiniSearch.prototype.executeQuerySpec = function (query, searchOptions) { | ||
var _this = this; | ||
var options = __assign(__assign({}, this._options.searchOptions), searchOptions); | ||
@@ -1419,3 +1472,3 @@ var boosts = (options.fields || this._options.fields).reduce(function (boosts, field) { | ||
}; | ||
var termToQuery = function (options) { return function (term, i, terms) { | ||
var termToQuerySpec = function (options) { return function (term, i, terms) { | ||
var fuzzy = (typeof options.fuzzy === 'function') | ||
@@ -1422,0 +1475,0 @@ ? options.fuzzy(term, i, terms) |
@@ -950,17 +950,37 @@ /*! ***************************************************************************** | ||
* | ||
* @param queryString Query string to search for | ||
* ### Advanced combination of queries: | ||
* | ||
* It is possible to combine different subqueries with OR and AND, and even | ||
* with different search options, by passing a query expression tree object as | ||
* the first argument, instead of a string. | ||
* | ||
* ```javascript | ||
* // Search for documents that contain "zen" AND ("motorcycle" OR "archery") | ||
* miniSearch.search({ | ||
* combineWith: 'AND', | ||
* queries: [ | ||
* 'zen', | ||
* { | ||
* combineWith: 'OR', | ||
* queries: ['motorcycle', 'archery'] | ||
* } | ||
* ] | ||
* }) | ||
* ``` | ||
* | ||
* Each node in the expression tree can be either a string, or an object that | ||
* supports all `SearchOptions` fields, plus a `queries` array field for | ||
* subqueries. | ||
* | ||
* Note that, while this can become complicated to do by hand for complex or | ||
* deeply nested queries, it provides a formalized expression tree API for | ||
* external libraries that implement a parser for custom query languages. | ||
* | ||
* @param query Search query | ||
* @param options Search options. Each option, if not given, defaults to the corresponding value of `searchOptions` given to the constructor, or to the library default. | ||
*/ | ||
MiniSearch.prototype.search = function (queryString, searchOptions) { | ||
MiniSearch.prototype.search = function (query, searchOptions) { | ||
var _this = this; | ||
if (searchOptions === void 0) { searchOptions = {}; } | ||
var _a = this._options, tokenize = _a.tokenize, processTerm = _a.processTerm, globalSearchOptions = _a.searchOptions; | ||
var options = __assign(__assign({ tokenize: tokenize, processTerm: processTerm }, globalSearchOptions), searchOptions); | ||
var searchTokenize = options.tokenize, searchProcessTerm = options.processTerm; | ||
var terms = searchTokenize(queryString) | ||
.map(function (term) { return searchProcessTerm(term); }) | ||
.filter(function (term) { return !!term; }); | ||
var queries = terms.map(termToQuery(options)); | ||
var results = queries.map(function (query) { return _this.executeQuery(query, options); }); | ||
var combinedResults = this.combineResults(results, options.combineWith); | ||
var combinedResults = this.executeQuery(query, searchOptions); | ||
return Object.entries(combinedResults) | ||
@@ -976,3 +996,3 @@ .reduce(function (results, _a) { | ||
Object.assign(result, _this._storedFields[docId]); | ||
if (options.filter == null || options.filter(result)) { | ||
if (searchOptions.filter == null || searchOptions.filter(result)) { | ||
results.push(result); | ||
@@ -1155,2 +1175,35 @@ } | ||
var _this = this; | ||
if (searchOptions === void 0) { searchOptions = {}; } | ||
if (typeof query === 'string') { | ||
return this.executeSearch(query, searchOptions); | ||
} | ||
else { | ||
var results = query.queries.map(function (subquery) { | ||
var options = __assign(__assign(__assign({}, searchOptions), query), { queries: undefined }); | ||
return _this.executeQuery(subquery, options); | ||
}); | ||
return this.combineResults(results, query.combineWith); | ||
} | ||
}; | ||
/** | ||
* @ignore | ||
*/ | ||
MiniSearch.prototype.executeSearch = function (queryString, searchOptions) { | ||
var _this = this; | ||
if (searchOptions === void 0) { searchOptions = {}; } | ||
var _a = this._options, tokenize = _a.tokenize, processTerm = _a.processTerm, globalSearchOptions = _a.searchOptions; | ||
var options = __assign(__assign({ tokenize: tokenize, processTerm: processTerm }, globalSearchOptions), searchOptions); | ||
var searchTokenize = options.tokenize, searchProcessTerm = options.processTerm; | ||
var terms = searchTokenize(queryString) | ||
.map(function (term) { return searchProcessTerm(term); }) | ||
.filter(function (term) { return !!term; }); | ||
var queries = terms.map(termToQuerySpec(options)); | ||
var results = queries.map(function (query) { return _this.executeQuerySpec(query, options); }); | ||
return this.combineResults(results, options.combineWith); | ||
}; | ||
/** | ||
* @ignore | ||
*/ | ||
MiniSearch.prototype.executeQuerySpec = function (query, searchOptions) { | ||
var _this = this; | ||
var options = __assign(__assign({}, this._options.searchOptions), searchOptions); | ||
@@ -1419,3 +1472,3 @@ var boosts = (options.fields || this._options.fields).reduce(function (boosts, field) { | ||
}; | ||
var termToQuery = function (options) { return function (term, i, terms) { | ||
var termToQuerySpec = function (options) { return function (term, i, terms) { | ||
var fuzzy = (typeof options.fuzzy === 'function') | ||
@@ -1422,0 +1475,0 @@ ? options.fuzzy(term, i, terms) |
@@ -455,3 +455,11 @@ declare type RadixTree<T> = { | ||
}; | ||
declare type QueryCombination = SearchOptions & { | ||
queries: Query[]; | ||
}; | ||
/** | ||
* Search query expression, either a query string or an expression tree | ||
* combining several queries with a combination of AND or OR. | ||
*/ | ||
declare type Query = QueryCombination | string; | ||
/** | ||
* [[MiniSearch]] is the main entrypoint class, implementing a full-text search | ||
@@ -732,6 +740,34 @@ * engine in memory. | ||
* | ||
* @param queryString Query string to search for | ||
* ### Advanced combination of queries: | ||
* | ||
* It is possible to combine different subqueries with OR and AND, and even | ||
* with different search options, by passing a query expression tree object as | ||
* the first argument, instead of a string. | ||
* | ||
* ```javascript | ||
* // Search for documents that contain "zen" AND ("motorcycle" OR "archery") | ||
* miniSearch.search({ | ||
* combineWith: 'AND', | ||
* queries: [ | ||
* 'zen', | ||
* { | ||
* combineWith: 'OR', | ||
* queries: ['motorcycle', 'archery'] | ||
* } | ||
* ] | ||
* }) | ||
* ``` | ||
* | ||
* Each node in the expression tree can be either a string, or an object that | ||
* supports all `SearchOptions` fields, plus a `queries` array field for | ||
* subqueries. | ||
* | ||
* Note that, while this can become complicated to do by hand for complex or | ||
* deeply nested queries, it provides a formalized expression tree API for | ||
* external libraries that implement a parser for custom query languages. | ||
* | ||
* @param query Search query | ||
* @param options Search options. Each option, if not given, defaults to the corresponding value of `searchOptions` given to the constructor, or to the library default. | ||
*/ | ||
search(queryString: string, searchOptions?: SearchOptions): SearchResult[]; | ||
search(query: Query, searchOptions?: SearchOptions): SearchResult[]; | ||
/** | ||
@@ -850,2 +886,10 @@ * Provide suggestions for the given search query | ||
*/ | ||
private executeSearch; | ||
/** | ||
* @ignore | ||
*/ | ||
private executeQuerySpec; | ||
/** | ||
* @ignore | ||
*/ | ||
private combineResults; | ||
@@ -916,2 +960,2 @@ /** | ||
export default MiniSearch; | ||
export { AsPlainObject, MatchInfo, Options, SearchOptions, SearchResult, Suggestion }; | ||
export { AsPlainObject, MatchInfo, Options, Query, QueryCombination, SearchOptions, SearchResult, Suggestion }; |
{ | ||
"name": "minisearch", | ||
"version": "3.0.4", | ||
"version": "3.1.0", | ||
"description": "Tiny but powerful full-text search engine for browser and Node", | ||
@@ -5,0 +5,0 @@ "main": "dist/umd/index.js", |
@@ -82,3 +82,3 @@ # MiniSearch | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/minisearch@3.0.2/dist/umd/index.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/minisearch@3.1.0/dist/umd/index.min.js"></script> | ||
``` | ||
@@ -85,0 +85,0 @@ |
@@ -611,2 +611,43 @@ /* eslint-env jest */ | ||
describe('when passing a query tree', () => { | ||
it('searches according to the given combination of AND and OR', () => { | ||
const results = ms.search({ | ||
combineWith: 'OR', | ||
queries: [ | ||
{ | ||
combineWith: 'AND', | ||
queries: ['vita', 'cammin'] | ||
}, | ||
'como sottomarino', | ||
{ | ||
combineWith: 'AND', | ||
queries: ['nova', 'pappagallo'] | ||
} | ||
] | ||
}) | ||
expect(results.length).toEqual(2) | ||
expect(results.map(({ id }) => id)).toEqual([1, 2]) | ||
}) | ||
it('uses the given options for each subquery, cascading them properly', () => { | ||
const results = ms.search({ | ||
combineWith: 'OR', | ||
fuzzy: true, | ||
queries: [ | ||
{ | ||
prefix: true, | ||
fields: ['title'], | ||
queries: ['vit'] | ||
}, | ||
{ | ||
combineWith: 'AND', | ||
queries: ['bago', 'coomo'] | ||
} | ||
] | ||
}) | ||
expect(results.length).toEqual(2) | ||
expect(results.map(({ id }) => id)).toEqual([3, 2]) | ||
}) | ||
}) | ||
describe('match data', () => { | ||
@@ -613,0 +654,0 @@ const documents = [ |
@@ -264,3 +264,11 @@ import SearchableMap from './SearchableMap/SearchableMap' | ||
type Query = { | ||
export type QueryCombination = SearchOptions & { queries: Query[] } | ||
/** | ||
* Search query expression, either a query string or an expression tree | ||
* combining several queries with a combination of AND or OR. | ||
*/ | ||
export type Query = QueryCombination | string | ||
type QuerySpec = { | ||
prefix: boolean, | ||
@@ -670,15 +678,35 @@ fuzzy: number | boolean, | ||
* | ||
* @param queryString Query string to search for | ||
* ### Advanced combination of queries: | ||
* | ||
* It is possible to combine different subqueries with OR and AND, and even | ||
* with different search options, by passing a query expression tree object as | ||
* the first argument, instead of a string. | ||
* | ||
* ```javascript | ||
* // Search for documents that contain "zen" AND ("motorcycle" OR "archery") | ||
* miniSearch.search({ | ||
* combineWith: 'AND', | ||
* queries: [ | ||
* 'zen', | ||
* { | ||
* combineWith: 'OR', | ||
* queries: ['motorcycle', 'archery'] | ||
* } | ||
* ] | ||
* }) | ||
* ``` | ||
* | ||
* Each node in the expression tree can be either a string, or an object that | ||
* supports all `SearchOptions` fields, plus a `queries` array field for | ||
* subqueries. | ||
* | ||
* Note that, while this can become complicated to do by hand for complex or | ||
* deeply nested queries, it provides a formalized expression tree API for | ||
* external libraries that implement a parser for custom query languages. | ||
* | ||
* @param query Search query | ||
* @param options Search options. Each option, if not given, defaults to the corresponding value of `searchOptions` given to the constructor, or to the library default. | ||
*/ | ||
search (queryString: string, searchOptions: SearchOptions = {}): SearchResult[] { | ||
const { tokenize, processTerm, searchOptions: globalSearchOptions } = this._options | ||
const options = { tokenize, processTerm, ...globalSearchOptions, ...searchOptions } | ||
const { tokenize: searchTokenize, processTerm: searchProcessTerm } = options | ||
const terms = searchTokenize(queryString) | ||
.map((term: string) => searchProcessTerm(term)) | ||
.filter((term) => !!term) as string[] | ||
const queries: Query[] = terms.map(termToQuery(options)) | ||
const results = queries.map(query => this.executeQuery(query, options)) | ||
const combinedResults: RawResult = this.combineResults(results, options.combineWith) | ||
search (query: Query, searchOptions: SearchOptions = {}): SearchResult[] { | ||
const combinedResults = this.executeQuery(query, searchOptions) | ||
@@ -694,3 +722,3 @@ return Object.entries(combinedResults) | ||
Object.assign(result, this._storedFields[docId]) | ||
if (options.filter == null || options.filter(result)) { | ||
if (searchOptions.filter == null || searchOptions.filter(result)) { | ||
results.push(result) | ||
@@ -872,3 +900,34 @@ } | ||
*/ | ||
private executeQuery (query: Query, searchOptions: SearchOptions): RawResult { | ||
private executeQuery (query: Query, searchOptions: SearchOptions = {}): RawResult { | ||
if (typeof query === 'string') { | ||
return this.executeSearch(query, searchOptions) | ||
} else { | ||
const results = query.queries.map((subquery) => { | ||
const options = { ...searchOptions, ...query, queries: undefined } | ||
return this.executeQuery(subquery, options) | ||
}) | ||
return this.combineResults(results, query.combineWith) | ||
} | ||
} | ||
/** | ||
* @ignore | ||
*/ | ||
private executeSearch (queryString: string, searchOptions: SearchOptions = {}): RawResult { | ||
const { tokenize, processTerm, searchOptions: globalSearchOptions } = this._options | ||
const options = { tokenize, processTerm, ...globalSearchOptions, ...searchOptions } | ||
const { tokenize: searchTokenize, processTerm: searchProcessTerm } = options | ||
const terms = searchTokenize(queryString) | ||
.map((term: string) => searchProcessTerm(term)) | ||
.filter((term) => !!term) as string[] | ||
const queries: QuerySpec[] = terms.map(termToQuerySpec(options)) | ||
const results = queries.map(query => this.executeQuerySpec(query, options)) | ||
return this.combineResults(results, options.combineWith) | ||
} | ||
/** | ||
* @ignore | ||
*/ | ||
private executeQuerySpec (query: QuerySpec, searchOptions: SearchOptions): RawResult { | ||
const options: SearchOptionsWithDefaults = { ...this._options.searchOptions, ...searchOptions } | ||
@@ -1146,3 +1205,3 @@ | ||
const termToQuery = (options: SearchOptions) => (term: string, i: number, terms: string[]): Query => { | ||
const termToQuerySpec = (options: SearchOptions) => (term: string, i: number, terms: string[]): QuerySpec => { | ||
const fuzzy = (typeof options.fuzzy === 'function') | ||
@@ -1149,0 +1208,0 @@ ? options.fuzzy(term, i, terms) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
629977
9762