diff --git a/CHANGELOG.md b/CHANGELOG.md index 20ddcd65e9..5ba3b199e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add unified fetch in mappingFallback for all searched entities - @gibkigonzo (#3942) - add npm-run-all for parallel build - @gibkigonzo (#3819) - Add OutputCaching support for x-vs-store-code - @benjick (#3979) +- The new search adapter `api-search-query` has been added. When you switch to it, by setting the `config.server.api = "api-search-query"` the ElasticSearch query is being built in the [`vue-storefront-api`](https://github.com/DivanteLtd/vue-storefront-api/pull/390) which saves around 400kB in the bundle size as `bodybuilder` is no longer needed in the frontend - @pkarw - #2167 +- This new `api-search-query` adapter supports the `response_format` query parameter which now is sent to the `/api/catalog` endpoint. Currently there is just one additional format supported: `response_format=compact`. When used, the response format got optimized by: a) remapping the results, removing the `_source` from the `hits.hits`; b) compressing the JSON fields names according to the `config.products.fieldsToCompact`; c) removing the JSON fields from the `product.configurable_children` when their values === parent product values; overall response size reduced over -70% - @pkarw +- The `amp-renderer` module has been disabled by default to save the bundle size; If you'd like to enable it uncomment the module from the `src/modules` and uncomment the `product-amp` and `category-amp` links that are added to the `
` section in the `src/themes/default/Product.vue` and `src/themes/default/Category.vue` ### Fixed - Fixed Search product fails for category filter when categoryId is string - @adityasharma7 (#3929) diff --git a/config/default.json b/config/default.json index 1f77bf2768..ea0cea5715 100644 --- a/config/default.json +++ b/config/default.json @@ -190,17 +190,17 @@ "productList": { "sort": "updated_at:desc", "includeFields": [ "activity", "type_id", "*sku", "product_links", "tax_class_id", "special_price", "special_to_date", "special_from_date", "name", "price", "price_incl_tax", "original_price_incl_tax", "original_price", "special_price_incl_tax", "id", "image", "sale", "new", "url_path", "url_key", "status", "tier_prices", "configurable_children.sku", "configurable_children.price", "configurable_children.special_price", "configurable_children.price_incl_tax", "configurable_children.special_price_incl_tax", "configurable_children.original_price", "configurable_children.original_price_incl_tax", "*image","*small_image", "configurable_children.color", "configurable_children.size", "configurable_children.tier_prices", "final_price", "configurable_children.final_price"], - "excludeFields": [ "description", "configurable_options", "sgn", "*.sgn", "msrp_display_actual_price_type", "*.msrp_display_actual_price_type", "required_options" ] + "excludeFields": [ "attribute_set_id", "description", "configurable_options", "sgn", "*.sgn", "msrp_display_actual_price_type", "*.msrp_display_actual_price_type", "required_options", "media_gallery", "stock.use_config_min_qty", "stock.use_config_notify_stock_qty", "stock.stock_id", "stock.use_config_backorders", "stock.use_config_enable_qty_inc", "stock.enable_qty_increments", "stock.use_config_manage_stock", "stock.use_config_min_sale_qty", "stock.notify_stock_qty", "stock.use_config_max_sale_qty", "stock.use_config_max_sale_qty", "stock.qty_increments", "stock.stock_status_changed_auto", "stock.show_default_notification_message", "stock.use_config_qty_increments", "stock.is_decimal_divided"] }, "productListWithChildren": { "includeFields": [ "activity", "type_id", "sku", "name", "tax_class_id", "final_price", "special_price", "special_to_date", "special_from_date", "price", "price_incl_tax", "original_price_incl_tax", "original_price", "special_price_incl_tax", "id", "image", "sale", "new", "configurable_children.image", "configurable_children.sku", "configurable_children.price", "configurable_children.special_price", "configurable_children.price_incl_tax", "configurable_children.special_price_incl_tax", "configurable_children.original_price", "configurable_children.original_price_incl_tax", "configurable_children.color", "configurable_children.size", "configurable_children.id", "configurable_children.tier_prices", "product_links", "url_path", "url_key", "status", "tier_prices", "configurable_children.special_to_date", "configurable_children.special_from_date", "configurable_children.regular_price", "configurable_children.final_price"], - "excludeFields": [ "description", "sgn", "*.sgn", "msrp_display_actual_price_type", "*.msrp_display_actual_price_type", "required_options"] + "excludeFields": [ "attribute_set_id", "description", "sgn", "*.sgn", "msrp_display_actual_price_type", "*.msrp_display_actual_price_type", "required_options", "media_gallery", "stock.use_config_min_qty", "stock.use_config_notify_stock_qty", "stock.stock_id", "stock.use_config_backorders", "stock.use_config_enable_qty_inc", "stock.enable_qty_increments", "stock.use_config_manage_stock", "stock.use_config_min_sale_qty", "stock.notify_stock_qty", "stock.use_config_max_sale_qty", "stock.use_config_max_sale_qty", "stock.qty_increments", "stock.stock_status_changed_auto", "stock.show_default_notification_message", "stock.use_config_qty_increments", "stock.is_decimal_divided"] }, "review": { "excludeFields": ["review_entity", "review_status"] }, "product": { - "excludeFields": [ "*.msrp_display_actual_price_type", "required_options", "updated_at", "created_at", "attribute_set_id", "options_container", "msrp_display_actual_price_type", "has_options", "stock.manage_stock", "stock.use_config_min_qty", "stock.use_config_notify_stock_qty", "stock.stock_id", "stock.use_config_backorders", "stock.use_config_enable_qty_inc", "stock.enable_qty_increments", "stock.use_config_manage_stock", "stock.use_config_min_sale_qty", "stock.notify_stock_qty", "stock.use_config_max_sale_qty", "stock.use_config_max_sale_qty", "stock.qty_increments", "stock.stock_status_changed_auto", "stock.show_default_notification_message", "stock.use_config_qty_increments", "stock.is_decimal_divided", "small_image", "sgn", "*.sgn"], + "excludeFields": [ "*.msrp_display_actual_price_type", "required_options", "updated_at", "created_at", "attribute_set_id", "options_container", "msrp_display_actual_price_type", "has_options", "stock.use_config_min_qty", "stock.use_config_notify_stock_qty", "stock.stock_id", "stock.use_config_backorders", "stock.use_config_enable_qty_inc", "stock.enable_qty_increments", "stock.use_config_manage_stock", "stock.use_config_min_sale_qty", "stock.notify_stock_qty", "stock.use_config_max_sale_qty", "stock.use_config_max_sale_qty", "stock.qty_increments", "stock.stock_status_changed_auto", "stock.show_default_notification_message", "stock.use_config_qty_increments", "stock.is_decimal_divided", "small_image", "sgn", "*.sgn"], "includeFields": null, "useDynamicAttributeLoader": true, "standardSystemFields": [ @@ -298,6 +298,34 @@ "disablePersistentAttributesCache": false }, "products": { + "fieldsToCompact": { + "minimal_price": "mp", + "has_options": "ho", + "url_key": "u", + "status": "s", + "required_options": "ro", + "name": "nm", + "tax_class_id": "tci", + "description": "desc", + "minimal_regular_price": "mrp", + "final_price": "fp", + "price": "p", + "special_price": "sp", + "original_final_price": "ofp", + "original_price": "op", + "original_special_price": "osp", + "final_price_incl_tax": "fpit", + "original_price_incl_tax": "opit", + "price_incl_tax": "pit", + "special_price_incl_tax": "spit", + "final_price_tax": "fpt", + "price_tax": "pt", + "special_price_tax": "spt", + "original_price_tax": "opt", + "image": "i", + "small_image": "si", + "thumbnail": "t" + }, "disablePersistentProductsCache": true, "useMagentoUrlKeys": true, "setFirstVarianAsDefaultInURL": false, diff --git a/core/build/webpack.base.config.ts b/core/build/webpack.base.config.ts index 1cf00cb3eb..8cc7a0b969 100644 --- a/core/build/webpack.base.config.ts +++ b/core/build/webpack.base.config.ts @@ -6,10 +6,11 @@ import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; import VueLoaderPlugin from 'vue-loader/lib/plugin'; import autoprefixer from 'autoprefixer'; import HTMLPlugin from 'html-webpack-plugin'; -// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; import webpack from 'webpack'; import dayjs from 'dayjs'; +// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; + fs.writeFileSync( path.resolve(__dirname, './config.json'), JSON.stringify(config) @@ -63,9 +64,9 @@ export default { plugins: [ new webpack.ContextReplacementPlugin(/dayjs[/\\]locale$/, buildLocaleIgnorePattern()), new webpack.ProgressPlugin(), - // new BundleAnalyzerPlugin({ - // generateStatsFile: true - // }), + /* new BundleAnalyzerPlugin({ + generateStatsFile: true + }), */ new CaseSensitivePathsPlugin(), new VueLoaderPlugin(), // generate output HTML diff --git a/core/data-resolver/CategoryService.ts b/core/data-resolver/CategoryService.ts index 8eed001af3..6eab92a5e9 100644 --- a/core/data-resolver/CategoryService.ts +++ b/core/data-resolver/CategoryService.ts @@ -1,5 +1,5 @@ import { quickSearchByQuery } from '@vue-storefront/core/lib/search'; -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery'; +import { SearchQuery } from 'storefront-query-builder' import config from 'config'; import { DataResolver } from './types/DataResolver'; import { Category } from 'core/modules/catalog-next/types/Category'; diff --git a/core/helpers/index.ts b/core/helpers/index.ts index a1b15f6c0f..8def58832f 100644 --- a/core/helpers/index.ts +++ b/core/helpers/index.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import { remove as removeAccents } from 'remove-accents' import { formatCategoryLink } from '@vue-storefront/core/modules/url/helpers' import Vue from 'vue' diff --git a/core/lib/search/adapter/api-search-query/searchAdapter.ts b/core/lib/search/adapter/api-search-query/searchAdapter.ts new file mode 100644 index 0000000000..551e13b58c --- /dev/null +++ b/core/lib/search/adapter/api-search-query/searchAdapter.ts @@ -0,0 +1,154 @@ +import map from 'lodash-es/map' +import fetch from 'isomorphic-fetch' +import { slugify, processURLAddress } from '@vue-storefront/core/helpers' +import queryString from 'query-string' +import { currentStoreView, prepareStoreView } from '@vue-storefront/core/lib/multistore' +import { SearchQuery } from 'storefront-query-builder' +import HttpQuery from '@vue-storefront/core/types/search/HttpQuery' +import { SearchResponse } from '@vue-storefront/core/types/search/SearchResponse' +import config from 'config' +import getApiEndpointUrl from '@vue-storefront/core/helpers/getApiEndpointUrl'; + +export class SearchAdapter { + public entities: any + + public constructor () { + this.entities = [] + this.initBaseTypes() + } + + protected decompactItem (item, fieldsToCompact) { + for (let key in fieldsToCompact) { + const value = fieldsToCompact[key] + if (typeof item[value] !== 'undefined') { + item[key] = item[value] + delete item[value] + } + } + return item + } + + public async search (Request) { + const rawQueryObject = Request.searchQuery + if (!this.entities[Request.type]) { + throw new Error('No entity type registered for ' + Request.type) + } + if (!(Request.searchQuery instanceof SearchQuery)) { + throw new Error('The only supported type of the "Request.searchQuery" is "SearchQuery"') + } + if (Request.hasOwnProperty('groupId') && Request.groupId !== null) { + rawQueryObject['groupId'] = Request.groupId + } + if (Request.hasOwnProperty('groupToken') && Request.groupToken !== null) { + rawQueryObject['groupToken'] = Request.groupToken + } + const storeView = (Request.store === null) ? currentStoreView() : await prepareStoreView(Request.store) + Request.index = storeView.elasticsearch.index + + let url = processURLAddress(getApiEndpointUrl(storeView.elasticsearch, 'host')) + + if (this.entities[Request.type].url) { + url = getApiEndpointUrl(this.entities[Request.type], 'url') + } + + const httpQuery: HttpQuery = { + size: Request.size, + from: Request.from, + sort: Request.sort, + request_format: 'search-query', + response_format: 'compact' + } + + if (Request._sourceExclude) { + httpQuery._source_exclude = Request._sourceExclude.join(',') + } + if (Request._sourceInclude) { + httpQuery._source_include = Request._sourceInclude.join(',') + } + if (Request.q) { + httpQuery.q = Request.q + } + + if (!Request.index || !Request.type) { + throw new Error('Query.index and Query.type are required arguments for executing ElasticSearch query') + } + if (config.elasticsearch.queryMethod === 'GET') { + httpQuery.request = JSON.stringify(rawQueryObject) + } + url = url + '/' + encodeURIComponent(Request.index) + '/' + encodeURIComponent(Request.type) + '/_search' + url = url + '?' + queryString.stringify(httpQuery) + return fetch(url, { method: config.elasticsearch.queryMethod, + mode: 'cors', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: config.elasticsearch.queryMethod === 'POST' ? JSON.stringify(rawQueryObject) : null + }) + .then(resp => { return resp.json() }) + .catch(error => { + throw new Error('FetchError in request to API: ' + error.toString()) + }) + } + + public handleResult (resp, type, start = 0, size = 50): SearchResponse { + if (resp === null) { + throw new Error('Invalid API result - null not exepcted') + } + if (resp.hasOwnProperty('hits')) { + return { + items: map(resp.hits, hit => { + if (type === 'product') { + hit = this.decompactItem(hit, config.products.fieldsToCompact) + if (hit.configurable_children) { + hit.configurable_children = hit.configurable_children.map(childItem => { + return this.decompactItem(childItem, config.products.fieldsToCompact) + }) + } + } + return Object.assign(hit, { slug: hit.slug ? hit.slug : ((hit.hasOwnProperty('url_key') && config.products.useMagentoUrlKeys) ? hit.url_key : (hit.hasOwnProperty('name') ? slugify(hit.name) + '-' + hit.id : '')) }) // TODO: assign slugs server side + }), // TODO: add scoring information + total: resp.total, + start: start, + perPage: size, + aggregations: resp.aggregations, + suggestions: resp.suggest + } + } else { + if (resp.error) { + throw new Error(JSON.stringify(resp.error)) + } else { + throw new Error('Unknown error with API catalog result in resultPorcessor for entity type \'' + type + '\'') + } + } + } + + public registerEntityType (entityType, { url = '', url_ssr = '', queryProcessor, resultPorcessor }) { + this.entities[entityType] = { + queryProcessor: queryProcessor, + resultPorcessor: resultPorcessor + } + if (url !== '') { + this.entities[entityType]['url'] = url + } + if (url_ssr !== '') { + this.entities[entityType]['url_ssr'] = url_ssr + } + return this + } + + public initBaseTypes () { + const baseTypes = ['product', 'attribute', 'category', 'taxrule', 'review', 'cms_page', 'cms_block', 'cms_hierarchy'] + baseTypes.forEach(type => { + this.registerEntityType(type, { + queryProcessor: (query) => { + // function that can modify the query each time before it's being executed + return query + }, + resultPorcessor: (resp, start, size) => { + return this.handleResult(resp, type, start, size) + } + }) + }) + } +} diff --git a/core/lib/search/adapter/api/elasticsearch/boost.js b/core/lib/search/adapter/api/elasticsearch/boost.js deleted file mode 100644 index 946a97497f..0000000000 --- a/core/lib/search/adapter/api/elasticsearch/boost.js +++ /dev/null @@ -1,16 +0,0 @@ -import config from 'config' - -export default function getBoosts (attribute = '') { - let searchableAttributes = [ - ] - - if (config.elasticsearch.hasOwnProperty('searchableAttributes') && config.elasticsearch.searchableAttributes[attribute]) { - searchableAttributes = config.elasticsearch.searchableAttributes[attribute] - } - - if (searchableAttributes.hasOwnProperty('boost')) { - return searchableAttributes['boost'] - } - - return 1 -} diff --git a/core/lib/search/adapter/api/elasticsearch/mapping.js b/core/lib/search/adapter/api/elasticsearch/mapping.js deleted file mode 100644 index 3fc8ee7ee1..0000000000 --- a/core/lib/search/adapter/api/elasticsearch/mapping.js +++ /dev/null @@ -1,15 +0,0 @@ -import config from 'config' -export default function getMapping (attribute, entityType = 'products') { - let mapping = [ - ] - - if (config.hasOwnProperty(entityType) && config[entityType].hasOwnProperty('filterFieldMapping')) { - mapping = config[entityType].filterFieldMapping - } - - if (mapping.hasOwnProperty(attribute)) { - return mapping[attribute] - } - - return attribute -} diff --git a/core/lib/search/adapter/api/elasticsearch/multimatch.js b/core/lib/search/adapter/api/elasticsearch/multimatch.js deleted file mode 100644 index 1abd481c3a..0000000000 --- a/core/lib/search/adapter/api/elasticsearch/multimatch.js +++ /dev/null @@ -1,29 +0,0 @@ -import config from 'config' - -function getConfig (queryText) { - let scoringConfig = config.elasticsearch.hasOwnProperty('searchScoring') ? config.elasticsearch.searchScoring : {} - let minimumShouldMatch = scoringConfig.hasOwnProperty('minimum_should_match') ? scoringConfig.minimum_should_match : '75%' - if (config.elasticsearch.queryMethod === 'GET') { - // minimum_should_match param must be have a "%" suffix, which is an illegal char while sending over query string - minimumShouldMatch = encodeURIComponent(minimumShouldMatch) - } - // Create config for multi match query - let multiMatchConfig = { - 'query': queryText, - 'operator': scoringConfig.operator ? scoringConfig.operator : 'or', - 'fuzziness': scoringConfig.fuzziness ? scoringConfig.fuzziness : '2', - 'cutoff_frequency': scoringConfig.cutoff_frequency ? scoringConfig.cutoff_frequency : '0.01', - 'max_expansions': scoringConfig.max_expansions ? scoringConfig.max_expansions : '3', - 'prefix_length': scoringConfig.prefix_length ? scoringConfig.prefix_length : '1', - 'minimum_should_match': minimumShouldMatch, - 'tie_breaker': scoringConfig.tie_breaker ? scoringConfig.tie_breaker : '1' - } - if (scoringConfig.hasOwnProperty('analyzer')) { - multiMatchConfig['analyzer'] = scoringConfig.analyzer - } - return multiMatchConfig -} - -export default function getMultiMatchConfig (queryText) { - return getConfig(queryText) -} diff --git a/core/lib/search/adapter/api/elasticsearch/score.js b/core/lib/search/adapter/api/elasticsearch/score.js deleted file mode 100644 index 4ddb8974b3..0000000000 --- a/core/lib/search/adapter/api/elasticsearch/score.js +++ /dev/null @@ -1,34 +0,0 @@ -import config from 'config' -export default function getFunctionScores () { - if (!config.elasticsearch.hasOwnProperty('searchScoring')) { - return false - } - let filter = [] - let esScoringAttributes = config.elasticsearch.searchScoring.attributes - - if (!Object.keys(esScoringAttributes).length) { - return false - } - for (const attribute of Object.keys(esScoringAttributes)) { - for (const scoreValue of Object.keys(esScoringAttributes[attribute].scoreValues)) { - let data = { - 'filter': { - 'match': { - [attribute]: scoreValue - } - }, - 'weight': esScoringAttributes[attribute].scoreValues[scoreValue].weight - } - filter.push(data) - } - } - if (filter.length) { - return {'functions': filter, - 'score_mode': config.score_mode ? config.score_mode : 'multiply', - 'boost_mode': config.boost_mode ? config.boost_mode : 'multiply', - 'max_boost': config.max_boost ? config.max_boost : 100, - 'min_score': config.function_min_score ? config.function_min_score : 1 - } - } - return false -} diff --git a/core/lib/search/adapter/api/elasticsearchQuery.js b/core/lib/search/adapter/api/elasticsearchQuery.js deleted file mode 100644 index 315f276337..0000000000 --- a/core/lib/search/adapter/api/elasticsearchQuery.js +++ /dev/null @@ -1,136 +0,0 @@ -import getFunctionScores from './elasticsearch/score' -import getMultiMatchConfig from './elasticsearch/multimatch' -import getBoosts from './elasticsearch/boost' -import getMapping from './elasticsearch/mapping' -import cloneDeep from 'lodash-es/cloneDeep' -import config from 'config' - -export async function prepareElasticsearchQueryBody (searchQuery) { - const bodybuilder = await import(/* webpackChunkName: "bodybuilder" */ 'bodybuilder') - const optionsPrefix = '_options' - const queryText = searchQuery.getSearchText() - const rangeOperators = ['gt', 'lt', 'gte', 'lte', 'moreq', 'from', 'to'] - let query = bodybuilder.default() - - // process applied filters - const appliedFilters = cloneDeep(searchQuery.getAppliedFilters()) // copy as function below modifies the object - if (appliedFilters.length > 0) { - let hasCatalogFilters = false - - // apply default filters - appliedFilters.forEach(filter => { - if (filter.scope === 'default') { - if (Object.keys(filter.value).every(v => rangeOperators.includes(v))) { - // process range filters - query = query.filter('range', filter.attribute, filter.value) - } else { - // process terms filters - const operator = Object.keys(filter.value)[0] - filter.value = filter.value[Object.keys(filter.value)[0]] - if (!Array.isArray(filter.value) && filter.value !== null) { - filter.value = [filter.value] - } - if (operator === 'or') { - if (filter.value === null) { - query = query.orFilter('bool', (b) => { - return b.notFilter('exists', getMapping(filter.attribute)) - }) - } else { - query = query.orFilter('terms', getMapping(filter.attribute), filter.value) - } - } else { - if (filter.value === null) { - query = query.notFilter('exists', getMapping(filter.attribute)) - } else { - query = query.filter('terms', getMapping(filter.attribute), filter.value) - } - } - } - } else if (filter.scope === 'catalog') { - hasCatalogFilters = true - } - }) - - // apply catalog scope filters - let attrFilterBuilder = (filterQr, attrPostfix = '') => { - appliedFilters.forEach(catalogfilter => { - const valueKeys = Object.keys(catalogfilter.value) - if (catalogfilter.scope === 'catalog' && valueKeys.length) { - const isRange = valueKeys.filter(value => rangeOperators.indexOf(value) !== -1) - if (isRange.length) { - let rangeAttribute = catalogfilter.attribute - // filter by product fiunal price - if (rangeAttribute === 'price') { - rangeAttribute = 'final_price' - } - // process range filters - filterQr = filterQr.andFilter('range', rangeAttribute, catalogfilter.value) - } else { - // process terms filters - let newValue = catalogfilter.value[Object.keys(catalogfilter.value)[0]] - if (!Array.isArray(newValue)) { - newValue = [newValue] - } - if (attrPostfix === '') { - filterQr = filterQr.andFilter('terms', getMapping(catalogfilter.attribute), newValue) - } else { - filterQr = filterQr.andFilter('terms', catalogfilter.attribute + attrPostfix, newValue) - } - } - } - }) - return filterQr - } - - if (hasCatalogFilters) { - query = query.filterMinimumShouldMatch(1).orFilter('bool', attrFilterBuilder) - .orFilter('bool', (b) => attrFilterBuilder(b, optionsPrefix).filter('match', 'type_id', 'configurable')) // the queries can vary based on the product type - } - } - - // Add aggregations for catalog filters - const allFilters = searchQuery.getAvailableFilters() - if (allFilters.length > 0) { - for (let attrToFilter of allFilters) { - if (attrToFilter.scope === 'catalog') { - if (attrToFilter.field !== 'price') { - let aggregationSize = { size: config.products.filterAggregationSize[attrToFilter.field] || config.products.filterAggregationSize.default } - query = query.aggregation('terms', getMapping(attrToFilter.field), aggregationSize) - query = query.aggregation('terms', attrToFilter.field + optionsPrefix, aggregationSize) - } else { - query = query.aggregation('terms', attrToFilter.field) - query.aggregation('range', 'price', config.products.priceFilters) - } - } - } - } - // Get searchable fields based on user-defined config. - let getQueryBody = function (b) { - let searchableAttributes = config.elasticsearch.hasOwnProperty('searchableAttributes') ? config.elasticsearch.searchableAttributes : {'name': {'boost': 1}} - let searchableFields = [ - ] - for (const attribute of Object.keys(searchableAttributes)) { - searchableFields.push(attribute + '^' + getBoosts(attribute)) - } - return b.orQuery('multi_match', 'fields', searchableFields, getMultiMatchConfig(queryText)) - .orQuery('bool', b => b.orQuery('terms', 'configurable_children.sku', queryText.split('-')) - .orQuery('match_phrase', 'sku', { query: queryText, boost: 1 }) - .orQuery('match_phrase', 'configurable_children.sku', { query: queryText, boost: 1 }) - ) - } - if (queryText !== '') { - let functionScore = getFunctionScores() - // Build bool or function_scrre accordingly - if (functionScore) { - query = query.query('function_score', functionScore, getQueryBody) - } else { - query = query.query('bool', getQueryBody) - } - } - const queryBody = query.build() - if (searchQuery.suggest) { - queryBody.suggest = searchQuery.suggest - } - - return queryBody -} diff --git a/core/lib/search/adapter/api/searchAdapter.ts b/core/lib/search/adapter/api/searchAdapter.ts index 0c4b958e5c..a8bfcad1ba 100644 --- a/core/lib/search/adapter/api/searchAdapter.ts +++ b/core/lib/search/adapter/api/searchAdapter.ts @@ -1,10 +1,10 @@ import map from 'lodash-es/map' -import { prepareElasticsearchQueryBody } from './elasticsearchQuery' +import { elasticsearch } from 'storefront-query-builder' import fetch from 'isomorphic-fetch' import { slugify, processURLAddress } from '@vue-storefront/core/helpers' import queryString from 'query-string' import { currentStoreView, prepareStoreView } from '../../../multistore' -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import HttpQuery from '@vue-storefront/core/types/search/HttpQuery' import { SearchResponse } from '@vue-storefront/core/types/search/SearchResponse' import config from 'config' @@ -24,7 +24,8 @@ export class SearchAdapter { } let ElasticsearchQueryBody = {} if (Request.searchQuery instanceof SearchQuery) { - ElasticsearchQueryBody = await prepareElasticsearchQueryBody(Request.searchQuery) + const bodybuilder = await import(/* webpackChunkName: "bodybuilder" */ 'bodybuilder') + ElasticsearchQueryBody = await elasticsearch.buildQueryBodyFromSearchQuery({ config, queryChain: bodybuilder.default(), searchQuery: Request.searchQuery }) if (Request.searchQuery.getSearchText() !== '') { ElasticsearchQueryBody['min_score'] = config.elasticsearch.min_score } @@ -73,8 +74,9 @@ export class SearchAdapter { } url = url + '/' + encodeURIComponent(Request.index) + '/' + encodeURIComponent(Request.type) + '/_search' url = url + '?' + queryString.stringify(httpQuery) - - return fetch(url, { method: config.elasticsearch.queryMethod, + + return fetch(url, { + method: config.elasticsearch.queryMethod, mode: 'cors', headers: { 'Accept': 'application/json', @@ -82,10 +84,10 @@ export class SearchAdapter { }, body: config.elasticsearch.queryMethod === 'POST' ? JSON.stringify(ElasticsearchQueryBody) : null }) - .then(resp => { return resp.json() }) - .catch(error => { - throw new Error('FetchError in request to ES: ' + error.toString()) - }) + .then(resp => { return resp.json() }) + .catch(error => { + throw new Error('FetchError in request to ES: ' + error.toString()) + }) } public handleResult (resp, type, start = 0, size = 50): SearchResponse { @@ -204,4 +206,4 @@ export class SearchAdapter { } }) } -} \ No newline at end of file +} diff --git a/core/lib/search/adapter/graphql/searchAdapter.ts b/core/lib/search/adapter/graphql/searchAdapter.ts index 7a6106add1..dd44de30df 100644 --- a/core/lib/search/adapter/graphql/searchAdapter.ts +++ b/core/lib/search/adapter/graphql/searchAdapter.ts @@ -2,7 +2,7 @@ import { prepareQueryVars } from './gqlQuery' import { currentStoreView, prepareStoreView } from '../../../multistore' import fetch from 'isomorphic-fetch' import {processESResponseType, processProductsType, processCmsType} from './processor/processType' -import SearchQuery from '../../searchQuery' +import { SearchQuery } from 'storefront-query-builder' import config from 'config' import { isServer } from '@vue-storefront/core/helpers' import getApiEndpointUrl from '@vue-storefront/core/helpers/getApiEndpointUrl'; @@ -46,7 +46,6 @@ export class SearchAdapter { if (getApiEndpointUrl(this.entities[Request.type], 'url')) { urlGql = getApiEndpointUrl(this.entities[Request.type], 'url') } else { - const serverProtocol = isServer ? getApiEndpointUrl(config.server, 'protocol') : config.server.protocol const host = isServer ? getApiEndpointUrl(config.graphql, 'host') : config.graphql.host const port = isServer ? getApiEndpointUrl(config.graphql, 'port') : config.graphql.port @@ -293,4 +292,4 @@ export class SearchAdapter { } }) } -} \ No newline at end of file +} diff --git a/core/lib/search/adapter/searchAdapterFactory.js b/core/lib/search/adapter/searchAdapterFactory.js index 02f1380df4..fb76b09bbd 100644 --- a/core/lib/search/adapter/searchAdapterFactory.js +++ b/core/lib/search/adapter/searchAdapterFactory.js @@ -1,5 +1,4 @@ import { server } from 'config' -import { Logger } from '@vue-storefront/core/lib/logger' let instances = {} const isImplementingSearchAdapterInterface = (obj) => { @@ -20,7 +19,7 @@ export const getSearchAdapter = async (adapterName = server.api) => { } if (!SearchAdapterModule) { - throw new Error('Search adapter module was not found in `serc/search/adapter` neither in the `core/lib/search/addapter` folders') + throw new Error('Search adapter module was not found in `src/search/adapter` neither in the `core/lib/search/adapter` folders') } const SearchAdapter = SearchAdapterModule.SearchAdapter diff --git a/core/lib/search/searchQuery.js b/core/lib/search/searchQuery.js deleted file mode 100644 index 27ea8dfd5c..0000000000 --- a/core/lib/search/searchQuery.js +++ /dev/null @@ -1,80 +0,0 @@ -class SearchQuery { - /** - */ - constructor () { - this._availableFilters = [] - this._appliedFilters = [] - this._searchText = '' - } - - /** - * @return {Array} array of all available filters objects - */ - getAvailableFilters () { - return this._availableFilters - } - - /** - * @return {Array} array of applied filters objects - */ - getAppliedFilters () { - return this._appliedFilters - } - - /** - * @return {String} - */ - getSearchText () { - return this._searchText - } - - /** - * @param {Object} - * @return {Object} - */ - applyFilter ({key, value, scope = 'default', options = Object}) { - this._appliedFilters.push({ - attribute: key, - value: value, - scope: scope, - options: options - }) - - return this - } - - /** - * @param {Object} - * @return {Object} - */ - addAvailableFilter ({field, scope = 'default', options = {}}) { - // value can has only String, Array or numeric type - this._availableFilters.push({ - field: field, - scope: scope, - options: options - }) - - return this - } - - /** - * @param {Array} filters - * @return {Object} - */ - setAvailableFilters (filters) { - this._availableFilters = filters - return this - } - - /** - * @param {String} searchText - * @return {Object} - */ - setSearchText (searchText) { - this._searchText = searchText - return this - } -} - -export default SearchQuery diff --git a/core/modules/cart/store/actions/productActions.ts b/core/modules/cart/store/actions/productActions.ts index b698c6438f..d0abbce14b 100644 --- a/core/modules/cart/store/actions/productActions.ts +++ b/core/modules/cart/store/actions/productActions.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const productActions = { async findProductOption ({ dispatch }, { serverItem }) { diff --git a/core/modules/cart/test/unit/store/connectActions.spec.ts b/core/modules/cart/test/unit/store/connectActions.spec.ts index 107e52198e..9e5748c923 100644 --- a/core/modules/cart/test/unit/store/connectActions.spec.ts +++ b/core/modules/cart/test/unit/store/connectActions.spec.ts @@ -34,7 +34,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn()); jest.mock('@vue-storefront/core/helpers', () => ({ get isServer () { return true diff --git a/core/modules/cart/test/unit/store/couponActions.spec.ts b/core/modules/cart/test/unit/store/couponActions.spec.ts index 21adaca9d0..cfcbaa9bec 100644 --- a/core/modules/cart/test/unit/store/couponActions.spec.ts +++ b/core/modules/cart/test/unit/store/couponActions.spec.ts @@ -31,7 +31,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn()); jest.mock('@vue-storefront/core/helpers', () => ({ get isServer () { return true diff --git a/core/modules/cart/test/unit/store/itemActions.spec.ts b/core/modules/cart/test/unit/store/itemActions.spec.ts index dda55f99d1..ee4ffc6e13 100644 --- a/core/modules/cart/test/unit/store/itemActions.spec.ts +++ b/core/modules/cart/test/unit/store/itemActions.spec.ts @@ -34,7 +34,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })) -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn()) jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); diff --git a/core/modules/cart/test/unit/store/mergeActions.spec.ts b/core/modules/cart/test/unit/store/mergeActions.spec.ts index 6342f7c4a1..0ff877235b 100644 --- a/core/modules/cart/test/unit/store/mergeActions.spec.ts +++ b/core/modules/cart/test/unit/store/mergeActions.spec.ts @@ -42,7 +42,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn()); jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); diff --git a/core/modules/cart/test/unit/store/methodsActions.spec.ts b/core/modules/cart/test/unit/store/methodsActions.spec.ts index 907410a941..80d8857dcd 100644 --- a/core/modules/cart/test/unit/store/methodsActions.spec.ts +++ b/core/modules/cart/test/unit/store/methodsActions.spec.ts @@ -39,7 +39,7 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn()); +jest.mock('storefront-query-builder', () => jest.fn()); jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); diff --git a/core/modules/cart/test/unit/store/productActions.spec.ts b/core/modules/cart/test/unit/store/productActions.spec.ts index 21491f4163..5f8a9d90fe 100644 --- a/core/modules/cart/test/unit/store/productActions.spec.ts +++ b/core/modules/cart/test/unit/store/productActions.spec.ts @@ -36,7 +36,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn(() => ({ applyFilter: jest.fn() }))); jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); @@ -77,7 +76,7 @@ describe('Cart productActions', () => { (contextMock.dispatch as jest.Mock).mockImplementationOnce(() => ({ items: [serverItem] })); const result = await (cartActions as any).findProductOption(contextMock, { serverItem }); - expect(contextMock.dispatch).toBeCalledWith('product/list', { start: 0, size: 1, updateState: false }, { root: true }) + expect(contextMock.dispatch).toBeCalledWith('product/list', { query: { _appliedFilters: [{ attribute: 'configurable_children.sku', options: Object, scope: 'default', value: { eq: 1 } }], _availableFilters: [], _searchText: '' }, size: 1, start: 0, updateState: false }, { root: true }) expect(result).toEqual({ childSku: 1, sku: 1 }) }); diff --git a/core/modules/cart/test/unit/store/quantityActions.spec.ts b/core/modules/cart/test/unit/store/quantityActions.spec.ts index f41a9c3ff4..ca6436e3f3 100644 --- a/core/modules/cart/test/unit/store/quantityActions.spec.ts +++ b/core/modules/cart/test/unit/store/quantityActions.spec.ts @@ -37,7 +37,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn(() => ({ applyFilter: jest.fn() }))); jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); diff --git a/core/modules/cart/test/unit/store/synchronizeActions.spec.ts b/core/modules/cart/test/unit/store/synchronizeActions.spec.ts index 918acc9785..e8db4e6b35 100644 --- a/core/modules/cart/test/unit/store/synchronizeActions.spec.ts +++ b/core/modules/cart/test/unit/store/synchronizeActions.spec.ts @@ -41,7 +41,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn(() => ({ applyFilter: jest.fn() }))); jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); diff --git a/core/modules/cart/test/unit/store/totalsActions.spec.ts b/core/modules/cart/test/unit/store/totalsActions.spec.ts index c461cb8c02..3319703948 100644 --- a/core/modules/cart/test/unit/store/totalsActions.spec.ts +++ b/core/modules/cart/test/unit/store/totalsActions.spec.ts @@ -43,7 +43,6 @@ jest.mock('@vue-storefront/core/lib/storage-manager', () => ({ } })); jest.mock('@vue-storefront/core/app', () => ({ router: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => jest.fn(() => ({ applyFilter: jest.fn() }))); jest.mock('@vue-storefront/core/modules/catalog/helpers', () => ({ configureProductAsync: jest.fn() })); diff --git a/core/modules/catalog/helpers/createAttributesListQuery.ts b/core/modules/catalog/helpers/createAttributesListQuery.ts index a8d9c930fc..697735be3d 100644 --- a/core/modules/catalog/helpers/createAttributesListQuery.ts +++ b/core/modules/catalog/helpers/createAttributesListQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createAttributesListQuery = ({ filterValues, diff --git a/core/modules/catalog/helpers/createCategoryListQuery.ts b/core/modules/catalog/helpers/createCategoryListQuery.ts index d06ac7eae4..0be5e158d4 100644 --- a/core/modules/catalog/helpers/createCategoryListQuery.ts +++ b/core/modules/catalog/helpers/createCategoryListQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import { isServer } from '@vue-storefront/core/helpers' import config from 'config' diff --git a/core/modules/catalog/queries/common.js b/core/modules/catalog/queries/common.js index 44f541c96b..d6e02c03d5 100644 --- a/core/modules/catalog/queries/common.js +++ b/core/modules/catalog/queries/common.js @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import config from 'config' export function prepareQuery ({queryText = '', filters = [], queryConfig = ''}) { diff --git a/core/modules/catalog/queries/related.js b/core/modules/catalog/queries/related.js index 369a30f8c3..8606b16f4d 100644 --- a/core/modules/catalog/queries/related.js +++ b/core/modules/catalog/queries/related.js @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import config from 'config' export function prepareRelatedQuery (key, sku) { diff --git a/core/modules/catalog/queries/searchPanel.js b/core/modules/catalog/queries/searchPanel.js index f2142906b5..dc6c20289f 100644 --- a/core/modules/catalog/queries/searchPanel.js +++ b/core/modules/catalog/queries/searchPanel.js @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import config from 'config' export function prepareQuickSearchQuery (queryText) { diff --git a/core/modules/catalog/store/category/actions.ts b/core/modules/catalog/store/category/actions.ts index 45e7a4fad2..82678a44bc 100644 --- a/core/modules/catalog/store/category/actions.ts +++ b/core/modules/catalog/store/category/actions.ts @@ -11,7 +11,6 @@ import toString from 'lodash-es/toString' import { optionLabel } from '../../helpers/optionLabel' import RootState from '@vue-storefront/core/types/RootState' import CategoryState from '../../types/CategoryState' -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' import { currentStoreView, localizedDispatcherRoute, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore' import { Logger } from '@vue-storefront/core/lib/logger' import { isServer } from '@vue-storefront/core/helpers' diff --git a/core/modules/catalog/store/product/actions.ts b/core/modules/catalog/store/product/actions.ts index fc40977234..c8e49481ab 100644 --- a/core/modules/catalog/store/product/actions.ts +++ b/core/modules/catalog/store/product/actions.ts @@ -13,7 +13,7 @@ import { configureProductAsync, configurableChildrenImages, attributeImages } from '../../helpers' import { preConfigureProduct, getOptimizedFields, configureChildren, storeProductToCache, canCache, isGroupedOrBundle } from '@vue-storefront/core/modules/catalog/helpers/search' -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import { entityKeyName } from '@vue-storefront/core/lib/store/entities' import { optionLabel } from '../../helpers/optionLabel' import { isOnline } from '@vue-storefront/core/lib/search' diff --git a/core/modules/catalog/store/tax/actions.ts b/core/modules/catalog/store/tax/actions.ts index 8847c2fa5e..66559c2fca 100644 --- a/core/modules/catalog/store/tax/actions.ts +++ b/core/modules/catalog/store/tax/actions.ts @@ -1,7 +1,7 @@ import { ActionTree } from 'vuex' import * as types from './mutation-types' import { quickSearchByQuery } from '@vue-storefront/core/lib/search' -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import RootState from '@vue-storefront/core/types/RootState' import TaxState from '../../types/TaxState' import { Logger } from '@vue-storefront/core/lib/logger' diff --git a/core/modules/cms/helpers/createHierarchyLoadQuery.ts b/core/modules/cms/helpers/createHierarchyLoadQuery.ts index af47fa0514..6710720694 100644 --- a/core/modules/cms/helpers/createHierarchyLoadQuery.ts +++ b/core/modules/cms/helpers/createHierarchyLoadQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createHierarchyLoadQuery = ({ id }): SearchQuery => { let query = new SearchQuery() diff --git a/core/modules/cms/helpers/createLoadingBlockQuery.ts b/core/modules/cms/helpers/createLoadingBlockQuery.ts index efb913fdc5..b62298da4e 100644 --- a/core/modules/cms/helpers/createLoadingBlockQuery.ts +++ b/core/modules/cms/helpers/createLoadingBlockQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createLoadingBlockQuery = ({ filterField, filterValues }): SearchQuery => { let query = new SearchQuery() diff --git a/core/modules/cms/helpers/createPageLoadingQuery.ts b/core/modules/cms/helpers/createPageLoadingQuery.ts index 3c1a03f81b..b93f514fe7 100644 --- a/core/modules/cms/helpers/createPageLoadingQuery.ts +++ b/core/modules/cms/helpers/createPageLoadingQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createPageLoadingQuery = ({ filterField, filterValues }): SearchQuery => { let query = new SearchQuery() diff --git a/core/modules/cms/helpers/createSingleBlockQuery.ts b/core/modules/cms/helpers/createSingleBlockQuery.ts index 3f975105a8..1277a5d76f 100644 --- a/core/modules/cms/helpers/createSingleBlockQuery.ts +++ b/core/modules/cms/helpers/createSingleBlockQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createSingleBlockQuery = ({ key, value }): SearchQuery => { let query = new SearchQuery() diff --git a/core/modules/cms/helpers/createSinglePageLoadQuery.ts b/core/modules/cms/helpers/createSinglePageLoadQuery.ts index 79463bc572..ba7db2e08e 100644 --- a/core/modules/cms/helpers/createSinglePageLoadQuery.ts +++ b/core/modules/cms/helpers/createSinglePageLoadQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createSinglePageLoadQuery = ({ key, value }): SearchQuery => { let query = new SearchQuery() diff --git a/core/modules/cms/test/unit/createHierarchyLoadQuery.spec.ts b/core/modules/cms/test/unit/createHierarchyLoadQuery.spec.ts index 09b7495cce..bd9e32df6d 100644 --- a/core/modules/cms/test/unit/createHierarchyLoadQuery.spec.ts +++ b/core/modules/cms/test/unit/createHierarchyLoadQuery.spec.ts @@ -8,7 +8,7 @@ describe('createHierarchyLoadQuery', () => { expect(loadQuery).toHaveProperty('_availableFilters') expect(loadQuery).toHaveProperty('_searchText') - let [ appliedFilter ] = loadQuery._appliedFilters + let [ appliedFilter ] = loadQuery.getAppliedFilters() expect(appliedFilter).toHaveProperty('value', { eq: filter.id }) expect(appliedFilter).toHaveProperty('attribute', 'identifier') diff --git a/core/modules/cms/test/unit/createLoadingBlockQuery.spec.ts b/core/modules/cms/test/unit/createLoadingBlockQuery.spec.ts index ceb4edb147..625d2393cf 100644 --- a/core/modules/cms/test/unit/createLoadingBlockQuery.spec.ts +++ b/core/modules/cms/test/unit/createLoadingBlockQuery.spec.ts @@ -5,7 +5,7 @@ describe('createLoadingBlockQuery', () => { const filter = { filterField: 'test', filterValues: ['test1', 'test2'] } let loadingBlockQuery = createLoadingBlockQuery(filter) - let [ appliedFilter ] = loadingBlockQuery._appliedFilters + let [ appliedFilter ] = loadingBlockQuery.getAppliedFilters() expect(appliedFilter).toHaveProperty('attribute', filter.filterField) expect(appliedFilter).toHaveProperty('value', { like: filter.filterValues }) diff --git a/core/modules/cms/test/unit/createPageLoadingQuery.spec.ts b/core/modules/cms/test/unit/createPageLoadingQuery.spec.ts index 290e9fb96e..4876ae4f27 100644 --- a/core/modules/cms/test/unit/createPageLoadingQuery.spec.ts +++ b/core/modules/cms/test/unit/createPageLoadingQuery.spec.ts @@ -5,7 +5,7 @@ describe('createPageLoadingQuery', () => { const filter = { filterField: 'test', filterValues: ['test1', 'test2'] } let pageLoadingQuery = createPageLoadingQuery(filter) - let [ appliedFilter ] = pageLoadingQuery._appliedFilters + let [ appliedFilter ] = pageLoadingQuery.getAppliedFilters() expect(appliedFilter).toHaveProperty('attribute', filter.filterField) expect(appliedFilter).toHaveProperty('value', { like: filter.filterValues }) diff --git a/core/modules/cms/test/unit/createSingleBlockQuery.spec.ts b/core/modules/cms/test/unit/createSingleBlockQuery.spec.ts index 8409621e22..1b5964a329 100644 --- a/core/modules/cms/test/unit/createSingleBlockQuery.spec.ts +++ b/core/modules/cms/test/unit/createSingleBlockQuery.spec.ts @@ -5,7 +5,7 @@ describe('createSingleBlockLoadQuery should', () => { const argsMock = { key: 'test', value: ['test1', 'test2'] } let mockSingleBlockQuery = createSingleBlockQuery(argsMock) - let [ appliedFilter ] = mockSingleBlockQuery._appliedFilters + let [ appliedFilter ] = mockSingleBlockQuery.getAppliedFilters() expect(appliedFilter).toHaveProperty('attribute', argsMock.key) expect(appliedFilter).toHaveProperty('value', { like: argsMock.value }) diff --git a/core/modules/cms/test/unit/createSinglePageLoadQuery.spec.ts b/core/modules/cms/test/unit/createSinglePageLoadQuery.spec.ts index ab518b7d92..cbc0f630da 100644 --- a/core/modules/cms/test/unit/createSinglePageLoadQuery.spec.ts +++ b/core/modules/cms/test/unit/createSinglePageLoadQuery.spec.ts @@ -5,7 +5,7 @@ describe('createSinglePageLoadQuery should', () => { const filter = { key: 'test', value: ['test1', 'test2'] } let singlePageMockQuery = createSinglePageLoadQuery(filter) - let [ appliedFilter ] = singlePageMockQuery._appliedFilters + let [ appliedFilter ] = singlePageMockQuery.getAppliedFilters() expect(appliedFilter).toHaveProperty('attribute', filter.key) expect(appliedFilter).toHaveProperty('value', { like: filter.value }) diff --git a/core/modules/review/helpers/createLoadReviewsQuery.ts b/core/modules/review/helpers/createLoadReviewsQuery.ts index 73c4318286..2d5180507c 100644 --- a/core/modules/review/helpers/createLoadReviewsQuery.ts +++ b/core/modules/review/helpers/createLoadReviewsQuery.ts @@ -1,4 +1,4 @@ -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' const createLoadReviewsQuery = ({ productId, approved }) => { let query = new SearchQuery() diff --git a/core/modules/review/test/unit/helpers/createLoadReviewsQuery.spec.ts b/core/modules/review/test/unit/helpers/createLoadReviewsQuery.spec.ts index c67cc97145..3c0610914b 100644 --- a/core/modules/review/test/unit/helpers/createLoadReviewsQuery.spec.ts +++ b/core/modules/review/test/unit/helpers/createLoadReviewsQuery.spec.ts @@ -4,7 +4,11 @@ const SearchQuery = { applyFilter: jest.fn(() => SearchQuery) } -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => () => SearchQuery) +jest.mock('storefront-query-builder', () => ({ + SearchQuery: function () { + return SearchQuery + } +})); describe('createLoadReviewsQuery', () => { beforeEach(() => { diff --git a/core/modules/review/test/unit/store/actions.spec.ts b/core/modules/review/test/unit/store/actions.spec.ts index 05085e91a5..6823b3935b 100644 --- a/core/modules/review/test/unit/store/actions.spec.ts +++ b/core/modules/review/test/unit/store/actions.spec.ts @@ -3,7 +3,7 @@ import reviewActions from '../../../store/actions'; import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus' import { createLoadReviewsQuery } from '@vue-storefront/core/modules/review/helpers' import { quickSearchByQuery } from '@vue-storefront/core/lib/search' -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +import { SearchQuery } from 'storefront-query-builder' import { ReviewsService } from '@vue-storefront/core/data-resolver' jest.mock('@vue-storefront/core/helpers', () => ({ @@ -27,9 +27,6 @@ jest.mock('@vue-storefront/core/lib/search', () => ({ jest.mock('@vue-storefront/core/modules/review/helpers', () => ({ createLoadReviewsQuery: jest.fn() })); -jest.mock('@vue-storefront/core/lib/search/searchQuery', () => ({ - SearchQuery: jest.fn() -})); jest.mock('@vue-storefront/core/lib/sync', () => ({ TaskQueue: { execute: jest.fn(() => Promise.resolve({code: 200})) diff --git a/core/modules/url/store/actions.ts b/core/modules/url/store/actions.ts index cbf87c91f6..44fcdd5277 100644 --- a/core/modules/url/store/actions.ts +++ b/core/modules/url/store/actions.ts @@ -6,6 +6,7 @@ import { ActionTree } from 'vuex'; import { cacheStorage } from '../' import queryString from 'query-string' import config from 'config' +import { SearchQuery } from 'storefront-query-builder' import { preProcessDynamicRoutes, normalizeUrlPath, parametrizeRouteData, getFallbackRouteData } from '../helpers' import { removeStoreCodeFromRoute, currentStoreView, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore' import storeCodeFromRoute from '@vue-storefront/core/lib/storeCodeFromRoute' @@ -14,7 +15,6 @@ import { Logger } from '@vue-storefront/core/lib/logger' import { processURLAddress } from '@vue-storefront/core/helpers'; import * as categoryMutationTypes from '@vue-storefront/core/modules/catalog-next/store/category/mutation-types' import * as cmsPageMutationTypes from '@vue-storefront/core/modules/cms/store/page/mutation-types' -import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' // it's a good practice for all actions to return Promises with effect of their execution export const actions: ActionTree