-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
elasticsearchQuery.js
122 lines (114 loc) · 5.01 KB
/
elasticsearchQuery.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
filter.value = filter.value[Object.keys(filter.value)[0]]
if (!Array.isArray(filter.value)) {
filter.value = [filter.value]
}
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.orFilter('bool', (b) => attrFilterBuilder(b))
.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
}