Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bodybuilder removed (#2167), bundle size optimizations (#2232) #3962

Merged
merged 22 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
56df0f1
Feature: #2167 (remove bodybuilder from the frontend) towards #3949
pkarw Dec 28, 2019
9baab9f
Preparation to separate ElasticSearchQuery to separate repository; Un…
pkarw Dec 28, 2019
f51f492
queryBuilder extracted to https://github.com/DivanteLtd/storefront-qu…
pkarw Dec 28, 2019
97bfaa4
Better error handling for the adapter module loader
pkarw Dec 28, 2019
b17fce2
storefront-query-builder reference upgrade
pkarw Dec 28, 2019
5c0d3ad
SearchQuery moved to separate package
pkarw Dec 28, 2019
2f85a45
bodybuilder passed as a object instance
pkarw Dec 28, 2019
023b77e
Compatibility fixes for new, TS supporting storefront-query-builder
pkarw Dec 29, 2019
fe8a48e
compact format support added, media_gallery added to `excludeFields`,…
pkarw Dec 30, 2019
23f2c3f
Object decompression support added to the `compact` mode
pkarw Dec 30, 2019
e973478
compression algorithm bulletproof
pkarw Dec 30, 2019
124f98f
Changelog + upgrade notes
pkarw Dec 30, 2019
2aca6c7
Update default.json
pkarw Dec 30, 2019
c30df62
Unit tests to the green
pkarw Dec 30, 2019
613b158
chunk optiomization added
pkarw Dec 30, 2019
8ad1d65
webpack bundle analyzer disabled
pkarw Dec 30, 2019
c7369d1
chunks optimization disabled (it doesnt' work well by some reason)
pkarw Dec 30, 2019
a58d120
Update yarn.lock
pkarw Dec 30, 2019
83cfaea
storefront-query-builder added to ts-loader
pkarw Dec 30, 2019
0f9f9df
reverted default config
pkarw Dec 30, 2019
74545d0
storefront-query-builder upgrade
pkarw Dec 30, 2019
28b0d47
storefront-query-builder reference changed to npm registry
pkarw Jan 7, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<head>` 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)
Expand Down
34 changes: 31 additions & 3 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 5 additions & 4 deletions core/build/webpack.base.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion core/data-resolver/CategoryService.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
2 changes: 1 addition & 1 deletion core/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
154 changes: 154 additions & 0 deletions core/lib/search/adapter/api-search-query/searchAdapter.ts
Original file line number Diff line number Diff line change
@@ -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)
}
})
})
}
}
16 changes: 0 additions & 16 deletions core/lib/search/adapter/api/elasticsearch/boost.js

This file was deleted.

15 changes: 0 additions & 15 deletions core/lib/search/adapter/api/elasticsearch/mapping.js

This file was deleted.

29 changes: 0 additions & 29 deletions core/lib/search/adapter/api/elasticsearch/multimatch.js

This file was deleted.

34 changes: 0 additions & 34 deletions core/lib/search/adapter/api/elasticsearch/score.js

This file was deleted.

Loading