Skip to content
This repository was archived by the owner on May 28, 2023. It is now read-only.

Commit 5437344

Browse files
author
tkostuch
committed
3948 add comments, add types, refactor, handle range and options
1 parent e37a4ab commit 5437344

File tree

2 files changed

+85
-21
lines changed

2 files changed

+85
-21
lines changed

src/api/attribute/service.ts

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ import TagCache from 'redis-tag-cache'
44
import get from 'lodash/get';
55
import cache from '../../lib/cache-instance'
66

7-
interface AttributeListParam {
7+
export interface AttributeListParam {
88
[key: string]: number[]
99
}
1010

11-
function getUri (config, indexName) {
11+
/**
12+
* Build ES uri fro attributes
13+
*/
14+
function getUri (config, indexName: string): string {
1215
return `${config.elasticsearch.protocol}://${config.elasticsearch.host}:${config.elasticsearch.port}/${indexName}/attribute/_search`
1316
}
1417

18+
/**
19+
* Returns attributes from cache
20+
*/
1521
async function getAttributeFromCache (attributeCode: string, config) {
1622
if (config.server.useOutputCache && cache) {
1723
try {
@@ -26,6 +32,9 @@ async function getAttributeFromCache (attributeCode: string, config) {
2632
}
2733
}
2834

35+
/**
36+
* Save attributes in cache
37+
*/
2938
async function setAttributeInCache (attributeList, config) {
3039
if (config.server.useOutputCache && cache) {
3140
try {
@@ -41,36 +50,49 @@ async function setAttributeInCache (attributeList, config) {
4150
}
4251
}
4352

53+
/**
54+
* Returns attribute with only needed options
55+
* @param attribute - attribute object
56+
* @param optionsIds - list of only needed options ids
57+
*/
4458
function clearAttributeOpitons (attribute, optionsIds: number[]) {
4559
const stringOptionsIds = optionsIds.map(String)
4660
return {
4761
...attribute,
48-
options: (attribute.options || []).filter(option => stringOptionsIds.includes(option.value))
62+
options: (attribute.options || []).filter(option => stringOptionsIds.includes(String(option.value)))
4963
}
5064
}
5165

52-
function list (attributesParam: AttributeListParam, config, indexName) {
66+
function list (attributesParam: AttributeListParam, config, indexName: string): Promise<any[]> {
5367
return new Promise(async (resolve, reject) => {
68+
// we start with all attributeCodes that are requested
5469
let attributeCodes = Object.keys(attributesParam)
5570

71+
// here we check if some of attribute are in cache
5672
const rawCachedAttributeList = await Promise.all(
5773
attributeCodes.map(attributeCode => getAttributeFromCache(attributeCode, config))
5874
)
5975

6076
const cachedAttributeList = rawCachedAttributeList
77+
.filter(Boolean) // remove empty results from cache.get
6178
.map((cachedAttribute, index) => {
6279
if (cachedAttribute) {
6380
const attributeOptionsIds = attributesParam[cachedAttribute.attribute_code]
64-
attributeCodes.splice(index, 1) // side effect - reduce elements in needed attribute list
81+
82+
// side effect - we want to reduce starting 'attributeCodes' because some of them are in cache
83+
attributeCodes.splice(index, 1)
84+
85+
// clear unused options
6586
return clearAttributeOpitons(cachedAttribute, attributeOptionsIds)
6687
}
6788
})
68-
.filter(Boolean)
6989

90+
// if all requested attributes are in cache then we can return here
7091
if (!attributeCodes.length) {
71-
return cachedAttributeList
92+
return resolve(cachedAttributeList)
7293
}
7394

95+
// fetch attributes for rest attributeCodes
7496
request({
7597
uri: getUri(config, indexName),
7698
method: 'POST',
@@ -81,17 +103,29 @@ function list (attributesParam: AttributeListParam, config, indexName) {
81103
reject(err)
82104
}
83105
const fetchedAttributeList = get(body, 'hits.hits', []).map(hit => hit._source)
106+
107+
// save atrributes in cache
84108
await setAttributeInCache(fetchedAttributeList, config)
85-
resolve(cachedAttributeList.concat(
86-
fetchedAttributeList.map(fetchedAttribute => {
109+
110+
// cached and fetched attributes
111+
const allAttributes = [
112+
...cachedAttributeList,
113+
...fetchedAttributeList.map(fetchedAttribute => {
87114
const attributeOptionsIds = attributesParam[fetchedAttribute.attribute_code]
115+
116+
// clear unused options
88117
return clearAttributeOpitons(fetchedAttribute, attributeOptionsIds)
89-
}))
90-
)
118+
})
119+
]
120+
121+
return resolve(allAttributes)
91122
})
92123
})
93124
}
94125

126+
/**
127+
* Returns only needed data for filters in vsf
128+
*/
95129
function transformToMetadata ({
96130
is_visible_on_front,
97131
is_visible,
@@ -103,7 +137,8 @@ function transformToMetadata ({
103137
is_comparable,
104138
attribute_code,
105139
slug,
106-
options
140+
options = [],
141+
buckets = []
107142
}) {
108143
return {
109144
is_visible_on_front,
@@ -116,7 +151,8 @@ function transformToMetadata ({
116151
is_comparable,
117152
attribute_code,
118153
slug,
119-
options
154+
options,
155+
buckets
120156
}
121157
}
122158

src/api/catalog.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ProcessorFactory from '../processor/factory';
44
import { adjustBackendProxyUrl } from '../lib/elastic'
55
import cache from '../lib/cache-instance'
66
import { sha3_224 } from 'js-sha3'
7-
import AttributeService from './attribute/service'
7+
import AttributeService, { AttributeListParam } from './attribute/service'
88
import bodybuilder from 'bodybuilder'
99
import { elasticsearch, SearchQuery } from 'storefront-query-builder'
1010

@@ -20,17 +20,45 @@ function _cacheStorageHandler (config, result, hash, tags) {
2020
}
2121
}
2222

23-
async function getProductsAttributesMetadata (body, config, indexName): Promise<any> {
24-
const attributeListParam = Object.keys(body.aggregations)
23+
/**
24+
* Transforms ES aggregates into valid format for AttributeService - {[attribute_code]: [bucketId1, bucketId2]}
25+
* @param body - products response body
26+
* @param config - global config
27+
* @param indexName - current indexName
28+
*/
29+
async function getProductsAttributesMetadata (body, config, indexName: string): Promise<any> {
30+
const attributeListParam: AttributeListParam = Object.keys(body.aggregations)
2531
.filter(key => body.aggregations[key].buckets.length) // leave only buckets with values
2632
.reduce((acc, key) => {
27-
const attributeCode = key.replace(/agg_terms_|agg_range_/, '')
28-
return {
29-
...acc,
30-
[attributeCode]: body.aggregations[key].buckets.map(bucket => bucket.key)
33+
const attributeCode = key.replace(/^(agg_terms_|agg_range_)|(_options)$/g, '')
34+
const bucketsIds = body.aggregations[key].buckets.map(bucket => bucket.key)
35+
36+
if (!acc[attributeCode]) {
37+
acc[attributeCode] = []
3138
}
39+
40+
// there can be more then one attributes for example 'agg_terms_color' and 'agg_terms_color_options'
41+
// we need to get buckets from both
42+
acc[attributeCode] = [...new Set([...acc[attributeCode], ...bucketsIds])]
43+
44+
return acc
3245
}, {})
33-
const attributeList = await AttributeService.list(attributeListParam, config, indexName)
46+
47+
// find attribute list
48+
const attributeList: any[] = await AttributeService.list(attributeListParam, config, indexName)
49+
50+
// add buckets for range attributes
51+
Object.keys(body.aggregations)
52+
.filter(key => key.match(/^(agg_range_)/g))
53+
.forEach(key => {
54+
const attributeCode = key.replace(/^(agg_range_)/g, '')
55+
const buckets = body.aggregations[key].buckets
56+
const attribute = attributeList.find(attr => attr.attribute_code === attributeCode)
57+
if (attribute) {
58+
attribute.buckets = buckets
59+
}
60+
})
61+
3462
return attributeList
3563
}
3664

0 commit comments

Comments
 (0)