diff --git a/src/search/AdvancedSearchManager.ts b/src/search/AdvancedSearchManager.ts new file mode 100644 index 0000000..b85484f --- /dev/null +++ b/src/search/AdvancedSearchManager.ts @@ -0,0 +1,146 @@ +// set up environment +import * as dotenv from 'dotenv'; +dotenv.config(); + +import { CveResult } from '../result/CveResult.js'; +import { SearchResultData } from "./SearchResultData.js"; +import { BasicSearchManager, SearchOptions } from "./BasicSearchManager.js" + +export class SearchAPIOptions extends SearchOptions { + searchFields: Array; + filters: Array; + resultsPerPage: number; + pageNumber: number; + rangeObjects: Array + rangeStart: Date; + rangeEnd: Date; + rangeField: string; +} + +//support for date ranges +export class rangeObject { + rangeField: string; + rangeStart: Date; + rangeEnd: Date; +} + +export class AdvancedSearchManager extends BasicSearchManager { + /** search for text at search provider + * @param searchText the text string to search for + * @param options options to specify how to search, with well-defined defaults + * @param queryString query strings for each filter on the search request + */ + async apiSearch(searchText: string, options: Partial = undefined, queryStrings?: Array): Promise { + let response = undefined; + + if (!options) { + options = { + useCache: true, + searchFields: null, + filters: null, + resultsPerPage: 20, + pageNumber: 0, + rangeObjects: null, + rangeStart: null, + rangeEnd: null, + rangeField: null, + track_total_hits: true, + default_operator: "AND", + metadataOnly: false, + fields: [] + }; + } + + const validateResult = this.validateSearchText(searchText, options.filters) + + //Build range object to add to the query + // let rangeObj = null; + const rangeArray = [...(options.rangeObjects ?? [])]; + + // if (options.rangeObjects) { + // options.rangeObjects.forEach(rangeObject => { + // rangeObj = { + // range: { + // [rangeObject.rangeField]: { + // "gte": rangeObject.rangeStart, + // "lte": rangeObject.rangeEnd, + // } + // } + // }; + // rangeArray.push(rangeObj); + // }) + + // } + //Build query object + const queryObj = { + must: [], + filter: [ + ...options.filters + ], + }; + + //Add rangeObj only if it exists + if (rangeArray) { + queryObj.filter.push(...rangeArray); + } + + if (searchText != null) { + queryObj.must = [ + { + query_string: { + query: searchText, + fields: ["containers.cna.descriptions.value"] + }, + } + ]; + } + + //Add query_string only if there is text to search + else if (queryStrings != null) { + queryObj.must = [ + ...queryStrings + ]; + } else { + delete queryObj.must + } + + if (validateResult.isOk()) { + + response = await this._searchReader._client.search({ + index: this._searchReader._cveIndex, + body: { + query: { + bool: queryObj + + }, + track_total_hits: true, + size: options.resultsPerPage, + from: options.pageNumber + } + }); + + return CveResult.ok(response.body as SearchResultData); + } + else { + // return CveResult.error(new CveError(1, `Unknown error when searching for ${text}`)); + return validateResult + } + } + + /** validates search text string and marks up CveResult + * with errors and/or notes, if any + */ + // @todo + validateSearchText(text: string, filters: Array): CveResult { + + let result: CveResult + if (!text && !filters) { + result = CveResult.error(9002) + } + else { + result = CveResult.ok("", ["no validation was done"]) + } + + return result + } +} diff --git a/src/search/BasicSearchManager.ts b/src/search/BasicSearchManager.ts index 2392463..c725641 100644 --- a/src/search/BasicSearchManager.ts +++ b/src/search/BasicSearchManager.ts @@ -2,11 +2,10 @@ import * as dotenv from 'dotenv'; dotenv.config(); -import { CveErrorCodes, CveResult } from '../result/CveResult.js'; +import { CveResult } from '../result/CveResult.js'; import { SearchProviderSpec } from '../adapters/search/SearchAdapter.js'; import { SearchQueryBuilder } from './SearchQueryBuilder.js'; import { SearchReader } from '../adapters/search/SearchReader.js'; -import { SearchResultData } from "./SearchResultData.js"; /** options when using search() @@ -21,26 +20,8 @@ export class SearchOptions { sort: {}[]; from: number; size: number; - -} - -export class SearchAPIOptions extends SearchOptions { - searchFields: Array; - filters: Array; - resultsPerPage: number; - rangeObjects: Array - rangeStart: Date; - rangeEnd: Date; - rangeField: string; } -//support for date ranges -export class rangeObject { - rangeField: string; - rangeStart: Date; - rangeEnd: Date; - -} /** A manager class that provides basic search capabilities * including a flexible search() that provides consistent @@ -86,121 +67,4 @@ export class BasicSearchManager { return result } - /** search for text at search provider - * @param searchText the text string to search for - * @param options options to specify how to search, with well-defined defaults - */ - async apiSearch(searchText: string, options: Partial = undefined, queryStrings?: Array): Promise { - let response = undefined; - - if (!options) { - options = { - useCache: true, - searchFields: null, - filters: null, - resultsPerPage: 20, - rangeObjects: null, - rangeStart: null, - rangeEnd: null, - rangeField: null, - track_total_hits: true, - default_operator: "AND", - metadataOnly: false, - fields: [] - }; - } - - let validateResult = this.validateSearchText(searchText, options.filters) - - //Build range object to add to the query - let rangeObj = null; - let rangeArray = []; - - if (options.rangeObjects) { - options.rangeObjects.forEach(rangeObject => { - rangeObj = { - range: { - [rangeObject.rangeField]: { - "gte": rangeObject.rangeStart, - "lte": rangeObject.rangeEnd, - } - } - }; - rangeArray.push(rangeObj); - }) - - } - //Build query object - const queryObj = { - must: [], - filter: [ - ...options.filters - ], - }; - - //Add rangeObj only if it exists - if (rangeArray) { - queryObj.filter.push(...rangeArray); - } - - if (searchText != null) { - queryObj.must = [ - { - query_string: { - query: searchText, - fields: ["containers.cna.descriptions.value"] - }, - } - ]; - } - - //Add query_string only if there is text to search - else if (queryStrings != null) { - queryObj.must = [ - ...queryStrings - ]; - } else { - delete queryObj.must - } - - if (validateResult.isOk()) { - - response = await this._searchReader._client.search({ - index: this._searchReader._cveIndex, - body: { - query: { - bool: queryObj - - }, - track_total_hits: true, - size: options.resultsPerPage - } - }); - - return CveResult.ok(response.body as SearchResultData); - } - else { - // return CveResult.error(new CveError(1, `Unknown error when searching for ${text}`)); - return validateResult - } - } - - /** validates search text string and marks up CveResult - * with errors and/or notes, if any - */ - // @todo - validateSearchText(text: string, filters: Array): CveResult { - - let result: CveResult - if (!text && !filters) { - result = CveResult.error(9002) - } - else { - result = CveResult.ok("", ["no validation was done"]) - } - - return result - } - - } \ No newline at end of file