Skip to content

Commit cc7102d

Browse files
bors[bot]bidoubiwa
andauthored
Merge #533
533: Add cache system r=bidoubiwa a=bidoubiwa TODO: - [x] tests Feature: - Use cache on pagination changes - Same requests uses same cache fixes: #520 Co-authored-by: Charlotte Vermandel <[email protected]> Co-authored-by: cvermand <[email protected]>
2 parents 42d173d + 7593b70 commit cc7102d

33 files changed

+878
-864
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ module.exports = {
149149
'@typescript-eslint/explicit-module-boundary-types': 'off',
150150
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
151151
'@typescript-eslint/no-unsafe-member-access': 'off',
152+
'@typescript-eslint/ban-ts-comment': 'off',
152153
'@typescript-eslint/member-delimiter-style': [
153154
'error',
154155
{

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
"test:watch": "yarn test --watch",
99
"test": "jest --runInBand --selectProjects dom --selectProjects node",
1010
"test:build": "yarn build && jest --runInBand --selectProjects build",
11-
"test:e2e": "concurrently --kill-others -s first \"NODE_ENV=test yarn playground:angular\" \"cypress run --env playground=angular\"",
11+
"test:e2e": "concurrently --kill-others -s first \"yarn playground:vue\" \"cypress run --env playground=vue\"",
1212
"test:e2e:all": "sh scripts/e2e.sh",
13-
"test:e2e:watch": "concurrently --kill-others -s first \"NODE_ENV=test yarn playground:angular\" \"cypress open --env playground=angular\"",
13+
"test:e2e:watch": "concurrently --kill-others -s first \"yarn playground:vue\" \"cypress open --env playground=vue\"",
1414
"test:all": "yarn test:e2e:all && yarn test && test:build",
1515
"cy:open": "cypress open",
1616
"playground:vue": "yarn --cwd ./playgrounds/vue && yarn --cwd ./playgrounds/vue serve",
@@ -71,7 +71,7 @@
7171
"babel-jest": "^27.2.2",
7272
"concurrently": "^6.2.1",
7373
"cssnano": "^4.1.10",
74-
"cypress": "^7.3.0",
74+
"cypress": "^8.5.0",
7575
"eslint": "^7.21.0",
7676
"eslint-config-prettier": "^8.1.0",
7777
"eslint-config-standard": "^16.0.0",

src/adapter/__tests__/utils.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/adapter/facets-distribution-adapter.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/adapter/hits-adapter.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/adapter/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
11
export * from './search-request-adapter'
22
export * from './search-response-adapter'
3-
export * from './hits-adapter'
4-
export * from './highlight-adapter'
5-
export * from './pagination-adapter'
6-
export * from './facets-distribution-adapter'
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
1-
import { facetsDistributionAdapter } from '../'
1+
import { assignMissingFilters } from '../filters'
22

33
test('One field in cache present in distribution', () => {
4-
const returnedDistribution = facetsDistributionAdapter(
4+
const returnedDistribution = assignMissingFilters(
55
{ genre: ['comedy'] },
66
{ genre: { comedy: 1 } }
77
)
88
expect(returnedDistribution).toMatchObject({ genre: { comedy: 1 } })
99
})
1010

1111
test('One field in cache not present in distribution', () => {
12-
const returnedDistribution = facetsDistributionAdapter(
13-
{ genre: ['comedy'] },
14-
{}
15-
)
12+
const returnedDistribution = assignMissingFilters({ genre: ['comedy'] }, {})
1613
expect(returnedDistribution).toMatchObject({ genre: { comedy: 0 } })
1714
})
1815

1916
test('two field in cache only one present in distribution', () => {
20-
const returnedDistribution = facetsDistributionAdapter(
17+
const returnedDistribution = assignMissingFilters(
2118
{ genre: ['comedy'], title: ['hamlet'] },
2219
{ genre: { comedy: 12 } }
2320
)
@@ -28,7 +25,7 @@ test('two field in cache only one present in distribution', () => {
2825
})
2926

3027
test('two field in cache w/ different facet name none present in distribution', () => {
31-
const returnedDistribution = facetsDistributionAdapter(
28+
const returnedDistribution = assignMissingFilters(
3229
{ genre: ['comedy'], title: ['hamlet'] },
3330
{}
3431
)
@@ -39,7 +36,7 @@ test('two field in cache w/ different facet name none present in distribution',
3936
})
4037

4138
test('two field in cache w/ different facet name both present in distribution', () => {
42-
const returnedDistribution = facetsDistributionAdapter(
39+
const returnedDistribution = assignMissingFilters(
4340
{ genre: ['comedy'], title: ['hamlet'] },
4441
{ genre: { comedy: 12 }, title: { hamlet: 1 } }
4542
)
@@ -50,7 +47,7 @@ test('two field in cache w/ different facet name both present in distribution',
5047
})
5148

5249
test('Three field in cache w/ different facet name two present in distribution', () => {
53-
const returnedDistribution = facetsDistributionAdapter(
50+
const returnedDistribution = assignMissingFilters(
5451
{ genre: ['comedy', 'horror'], title: ['hamlet'] },
5552
{ genre: { comedy: 12 }, title: { hamlet: 1 } }
5653
)
@@ -61,31 +58,31 @@ test('Three field in cache w/ different facet name two present in distribution',
6158
})
6259

6360
test('Cache is undefined and facets distribution is not', () => {
64-
const returnedDistribution = facetsDistributionAdapter(undefined, {
61+
const returnedDistribution = assignMissingFilters(undefined, {
6562
genre: { comedy: 12 },
6663
})
6764
expect(returnedDistribution).toMatchObject({ genre: { comedy: 12 } })
6865
})
6966

7067
test('Cache is empty object and facets distribution is not', () => {
71-
const returnedDistribution = facetsDistributionAdapter(
68+
const returnedDistribution = assignMissingFilters(
7269
{},
7370
{ genre: { comedy: 12 } }
7471
)
7572
expect(returnedDistribution).toMatchObject({ genre: { comedy: 12 } })
7673
})
7774

7875
test('Cache is empty object and facets distribution empty object', () => {
79-
const returnedDistribution = facetsDistributionAdapter({}, {})
76+
const returnedDistribution = assignMissingFilters({}, {})
8077
expect(returnedDistribution).toMatchObject({})
8178
})
8279

8380
test('Cache is undefined and facets distribution empty object', () => {
84-
const returnedDistribution = facetsDistributionAdapter(undefined, {})
81+
const returnedDistribution = assignMissingFilters(undefined, {})
8582
expect(returnedDistribution).toMatchObject({})
8683
})
8784

8885
test('Cache is undefined and facets distribution is undefined', () => {
89-
const returnedDistribution = facetsDistributionAdapter(undefined, undefined)
86+
const returnedDistribution = assignMissingFilters(undefined, undefined)
9087
expect(returnedDistribution).toMatchObject({})
9188
})

src/cache/__tests__/cache.tests.ts renamed to src/adapter/search-request-adapter/__tests__/filter-cache.tests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cacheFilters } from '..'
1+
import { cacheFilters } from '../filters'
22

33
const facetCacheData = [
44
{

src/adapter/filter-adapter.ts renamed to src/adapter/search-request-adapter/filter-adapter.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
import type { Filter, AlgoliaSearchOptions } from '../types'
2-
import { replaceColonByEqualSign } from '../utils'
1+
import type { Filter, SearchContext } from '../../types'
2+
import { replaceColonByEqualSign } from '../../utils'
33

44
/**
55
* Transform InstantSearch filter to MeiliSearch filter.
66
* Change sign from `:` to `=` in nested filter object.
77
* example: [`genres:comedy`] becomes [`genres=comedy`]
88
*
9-
* @param {AlgoliaSearchOptions['facetFilters']} filters?
10-
* @returns Filter
9+
* @param {SearchContext['facetFilters']} filters?
10+
* @returns {Filter}
1111
*/
12-
function transformFilter(
13-
filters?: AlgoliaSearchOptions['facetFilters']
14-
): Filter {
12+
function transformFilter(filters?: SearchContext['facetFilters']): Filter {
1513
if (typeof filters === 'string') {
1614
return replaceColonByEqualSign(filters)
1715
} else if (Array.isArray(filters))
@@ -34,7 +32,7 @@ function transformFilter(
3432
* If filter is array, return without change.
3533
*
3634
* @param {Filter} filter
37-
* @returns Array
35+
* @returns {Array}
3836
*/
3937
function filterToArray(filter: Filter): Array<string | string[]> {
4038
// Filter is a string
@@ -51,7 +49,7 @@ function filterToArray(filter: Filter): Array<string | string[]> {
5149
* @param {Filter} facetFilters
5250
* @param {Filter} numericFilters
5351
* @param {string} filters
54-
* @returns Filter
52+
* @returns {Filter}
5553
*/
5654
function mergeFilters(
5755
facetFilters: Filter,
@@ -82,14 +80,14 @@ function mergeFilters(
8280
* combining and transforming all provided filters.
8381
*
8482
* @param {string|undefined} filters
85-
* @param {AlgoliaSearchOptions['numericFilters']} numericFilters
86-
* @param {AlgoliaSearchOptions['facetFilters']} facetFilters
87-
* @returns Filter
83+
* @param {SearchContext['numericFilters']} numericFilters
84+
* @param {SearchContext['facetFilters']} facetFilters
85+
* @returns {Filter}
8886
*/
8987
export function adaptFilters(
9088
filters: string | undefined,
91-
numericFilters: AlgoliaSearchOptions['numericFilters'],
92-
facetFilters: AlgoliaSearchOptions['facetFilters']
89+
numericFilters: SearchContext['numericFilters'],
90+
facetFilters: SearchContext['facetFilters']
9391
): Filter {
9492
const transformedFilter = transformFilter(facetFilters || [])
9593
const transformedNumericFilter = transformFilter(numericFilters || [])
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
Filter,
3+
ParsedFilter,
4+
FacetsDistribution,
5+
FilterCache,
6+
} from '../../types'
7+
import { removeUndefined } from '../../utils'
8+
9+
/**
10+
* @param {string} filter
11+
*/
12+
const adaptFilterSyntax = (filter: string) => {
13+
const matches = filter.match(/([^=]*)="?([^\\"]*)"?$/)
14+
if (matches) {
15+
const [_, filterName, value] = matches
16+
return [{ filterName, value }]
17+
}
18+
return []
19+
}
20+
21+
/**
22+
* @param {Filter} filters?
23+
* @returns {Array}
24+
*/
25+
function extractFilters(filters?: Filter): Array<ParsedFilter | undefined> {
26+
if (typeof filters === 'string') {
27+
return adaptFilterSyntax(filters)
28+
} else if (Array.isArray(filters)) {
29+
return filters
30+
.map((nestedFilter) => {
31+
if (Array.isArray(nestedFilter)) {
32+
return nestedFilter.map((filter) => adaptFilterSyntax(filter))
33+
}
34+
return adaptFilterSyntax(nestedFilter)
35+
})
36+
.flat(2)
37+
}
38+
return []
39+
}
40+
41+
/**
42+
* @param {Filter} filters?
43+
* @returns {FilterCache}
44+
*/
45+
export function cacheFilters(filters?: Filter): FilterCache {
46+
const extractedFilters = extractFilters(filters)
47+
const cleanFilters = removeUndefined(extractedFilters)
48+
return cleanFilters.reduce<FilterCache>(
49+
(cache, parsedFilter: ParsedFilter) => {
50+
const { filterName, value } = parsedFilter
51+
const prevFields = cache[filterName] || []
52+
cache = {
53+
...cache,
54+
[filterName]: [...prevFields, value],
55+
}
56+
return cache
57+
},
58+
{} as FilterCache
59+
)
60+
}
61+
62+
/**
63+
* Assign missing filters to facetsDistribution.
64+
* All facet passed as filter should appear in the facetsDistribution.
65+
* If not present, the facet is added with 0 as value.
66+
*
67+
*
68+
* @param {FilterCache} cache?
69+
* @param {FacetsDistribution} distribution?
70+
* @returns {FacetsDistribution}
71+
*/
72+
export function assignMissingFilters(
73+
cachedFilters?: FilterCache,
74+
distribution?: FacetsDistribution
75+
): FacetsDistribution {
76+
distribution = distribution || {}
77+
78+
// If cachedFilters contains something
79+
if (cachedFilters && Object.keys(cachedFilters).length > 0) {
80+
// for all filters in cached filters
81+
for (const cachedFacet in cachedFilters) {
82+
// if facet does not exist on returned distribution, add an empty object
83+
if (!distribution[cachedFacet]) distribution[cachedFacet] = {}
84+
// for all fields in every filter
85+
for (const cachedField of cachedFilters[cachedFacet]) {
86+
// if the field is not present in the returned distribution
87+
// set it at 0
88+
if (!Object.keys(distribution[cachedFacet]).includes(cachedField)) {
89+
// add 0 value
90+
distribution[cachedFacet][cachedField] = 0
91+
}
92+
}
93+
}
94+
}
95+
return distribution
96+
}

0 commit comments

Comments
 (0)