From e7b52837d1c1becb64b9e03ebe9f0d883c0ff2eb Mon Sep 17 00:00:00 2001 From: Vaillant Samuel Date: Fri, 28 Jun 2019 12:16:57 +0200 Subject: [PATCH 1/9] feat(index): add mergeSearchParameters function ```js mergeSearchParameters(a, b, c) // new search parameters ``` - overrides left-to-right on uncontrolled parameters - merges controlled parameters - overrides left-to-right on duplicate attributes Co-authored-by: samouss --- .../__tests__/mergeSearchParameters-test.ts | 370 ++++++++++++++++++ src/lib/utils/mergeSearchParameters.ts | 125 ++++++ 2 files changed, 495 insertions(+) create mode 100644 src/lib/utils/__tests__/mergeSearchParameters-test.ts create mode 100644 src/lib/utils/mergeSearchParameters.ts diff --git a/src/lib/utils/__tests__/mergeSearchParameters-test.ts b/src/lib/utils/__tests__/mergeSearchParameters-test.ts new file mode 100644 index 0000000000..d0afcf475a --- /dev/null +++ b/src/lib/utils/__tests__/mergeSearchParameters-test.ts @@ -0,0 +1,370 @@ +import algoliasearchHelper from 'algoliasearch-helper'; +import merge from '../mergeSearchParameters'; + +describe('mergeSearchParameters', () => { + it('merges non-managed parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + // Inherit + hitsPerPage: 2, + attributesToSnippet: ['description'], + // Overridden + query: 'Samsung', + attributesToHighlight: ['name'], + }), + algoliasearchHelper.SearchParameters.make({ + // hitsPerPage: 2, + // attributesToSnippet: ['description'], + query: 'Apple', + attributesToHighlight: ['name', 'author'], + }), + algoliasearchHelper.SearchParameters.make({ + // hitsPerPage: 2, + // attributesToSnippet: ['description'], + // attributesToHighlight: ['name', 'author'], + query: 'Apple iPhone', + distinct: true, + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + hitsPerPage: 2, + attributesToSnippet: ['description'], + attributesToHighlight: ['name', 'author'], + query: 'Apple iPhone', + distinct: true, + }) + ); + }); + + it('merges `facets` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + facets: ['brand'], + }), + algoliasearchHelper.SearchParameters.make({ + facets: ['categories'], + }), + algoliasearchHelper.SearchParameters.make({ + facets: ['brand', 'colors'], + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + facets: ['brand', 'categories', 'colors'], + }) + ); + }); + + it('merges `disjunctiveFacets` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['brand'], + }), + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['categories'], + }), + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['brand', 'colors'], + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['brand', 'categories', 'colors'], + }) + ); + }); + + it('merges `facetsRefinements` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + facets: ['brand'], + facetsRefinements: { + brand: ['Samsung'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + facets: ['categories'], + facetsRefinements: { + categories: ['TVs'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + facets: ['brand', 'colors'], + facetsRefinements: { + brand: ['Apple'], + colors: ['Red'], + }, + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + facets: ['brand', 'categories', 'colors'], + facetsRefinements: { + brand: ['Apple'], + categories: ['TVs'], + colors: ['Red'], + }, + }) + ); + }); + + it('merges `facetsExcludes` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + facets: ['brand'], + facetsExcludes: { + brand: ['Samsung'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + facets: ['categories'], + facetsExcludes: { + categories: ['TVs'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + facets: ['brand', 'colors'], + facetsExcludes: { + brand: ['Apple'], + colors: ['Red'], + }, + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + facets: ['brand', 'categories', 'colors'], + facetsExcludes: { + brand: ['Apple'], + categories: ['TVs'], + colors: ['Red'], + }, + }) + ); + }); + + it('merges `disjunctiveFacetsRefinements` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['brand'], + disjunctiveFacetsRefinements: { + brand: ['Samsung'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['categories'], + disjunctiveFacetsRefinements: { + categories: ['TVs'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['brand', 'colors'], + disjunctiveFacetsRefinements: { + brand: ['Apple'], + colors: ['Red'], + }, + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + disjunctiveFacets: ['brand', 'categories', 'colors'], + disjunctiveFacetsRefinements: { + brand: ['Apple'], + categories: ['TVs'], + colors: ['Red'], + }, + }) + ); + }); + + it('merges `numericRefinements` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + numericRefinements: { + price: { + '>=': [10], + '<=': [100], + }, + }, + }), + algoliasearchHelper.SearchParameters.make({ + numericRefinements: { + rating: { + '>=': [3], + }, + }, + }), + algoliasearchHelper.SearchParameters.make({ + numericRefinements: { + price: { + '>': [100], + }, + vote: { + '>=': [50], + }, + }, + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + numericRefinements: { + price: { + '>': [100], + }, + rating: { + '>=': [3], + }, + vote: { + '>=': [50], + }, + }, + }) + ); + }); + + it('merges `tagRefinements` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + tagRefinements: ['brand'], + }), + algoliasearchHelper.SearchParameters.make({ + tagRefinements: ['categories'], + }), + algoliasearchHelper.SearchParameters.make({ + tagRefinements: ['brand', 'colors'], + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + tagRefinements: ['brand', 'categories', 'colors'], + }) + ); + }); + + it('merges `hierarchicalFacets` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'categories', + attributes: ['categories.lvl0', 'categories.lvl1'], + }, + ], + }), + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'department', + attributes: ['department.lvl0', 'department.lvl1'], + }, + ], + }), + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'categories', + attributes: ['topLevelCategories', 'subLevelCategories'], + }, + { + name: 'folders', + attributes: ['folders.lvl0', 'folders.lvl1'], + }, + ], + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'categories', + attributes: ['topLevelCategories', 'subLevelCategories'], + }, + { + name: 'department', + attributes: ['department.lvl0', 'department.lvl1'], + }, + { + name: 'folders', + attributes: ['folders.lvl0', 'folders.lvl1'], + }, + ], + }) + ); + }); + + it('merges `hierarchicalFacetsRefinements` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'categories', + attributes: ['categories.lvl0', 'categories.lvl1'], + }, + ], + hierarchicalFacetsRefinements: { + categories: ['Appliances > Fans'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'department', + attributes: ['department.lvl0', 'department.lvl1'], + }, + ], + hierarchicalFacetsRefinements: { + department: ['Engineering > Squad'], + }, + }), + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'categories', + attributes: ['topLevelCategories', 'subLevelCategories'], + }, + { + name: 'folders', + attributes: ['folders.lvl0', 'folders.lvl1'], + }, + ], + hierarchicalFacetsRefinements: { + categories: ['Cell Phones > Prepaid Phones'], + folders: ['Music > Artist > Mike Miller'], + }, + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + hierarchicalFacets: [ + { + name: 'categories', + attributes: ['topLevelCategories', 'subLevelCategories'], + }, + { + name: 'department', + attributes: ['department.lvl0', 'department.lvl1'], + }, + { + name: 'folders', + attributes: ['folders.lvl0', 'folders.lvl1'], + }, + ], + hierarchicalFacetsRefinements: { + categories: ['Cell Phones > Prepaid Phones'], + department: ['Engineering > Squad'], + folders: ['Music > Artist > Mike Miller'], + }, + }) + ); + }); +}); diff --git a/src/lib/utils/mergeSearchParameters.ts b/src/lib/utils/mergeSearchParameters.ts new file mode 100644 index 0000000000..32184cb1c5 --- /dev/null +++ b/src/lib/utils/mergeSearchParameters.ts @@ -0,0 +1,125 @@ +import { SearchParameters } from '../../types'; + +type Merge = (value: SearchParameters) => SearchParameters; + +type Merger = (right: SearchParameters) => Merge; + +const compose = (...fns: Array<(x: TValue) => TValue>) => ( + initial: TValue +) => fns.reduceRight((_, fn) => fn(_), initial); + +const mergeWithRest: Merger = right => left => { + const { + facets, + disjunctiveFacets, + facetsRefinements, + facetsExcludes, + disjunctiveFacetsRefinements, + numericRefinements, + tagRefinements, + hierarchicalFacets, + hierarchicalFacetsRefinements, + ...rest + } = right; + + return left.setQueryParameters(rest); +}; + +// Merge facets +const mergeFacets: Merger = right => left => + right.facets!.reduce((_, name) => _.addFacet(name), left); + +const mergeDisjunctiveFacets: Merger = right => left => + right.disjunctiveFacets!.reduce( + (_, name) => _.addDisjunctiveFacet(name), + left + ); + +const mergeTagRefinements: Merger = right => left => + right.tagRefinements!.reduce((_, value) => _.addTagRefinement(value), left); + +type HierarchicalFacet = { + name: string; + attributes: string[]; + separator: string; +}; + +const mergeHierarchicalFacets: Merger = right => left => + left.setQueryParameters({ + // @TODO: remove this cast when typings in helper are merged + hierarchicalFacets: (right.hierarchicalFacets as HierarchicalFacet[]).reduce( + (facets, facet) => { + const index = facets.findIndex(_ => _.name === facet.name); + + if (index === -1) { + return facets.concat(facet); + } + + const nextFacets = facets.slice(); + nextFacets.splice(index, 1, facet); + + return nextFacets; + }, + left.hierarchicalFacets as HierarchicalFacet[] + ), + }); + +// Merge facets refinements +const mergeFacetRefinements: Merger = right => left => + left.setQueryParameters({ + facetsRefinements: { + ...left.facetsRefinements, + ...right.facetsRefinements, + }, + }); + +const mergeFacetsExcludes: Merger = right => left => + left.setQueryParameters({ + facetsExcludes: { + ...left.facetsExcludes, + ...right.facetsExcludes, + }, + }); + +const mergeDisjunctiveFacetsRefinements: Merger = right => left => + left.setQueryParameters({ + disjunctiveFacetsRefinements: { + ...left.disjunctiveFacetsRefinements, + ...right.disjunctiveFacetsRefinements, + }, + }); + +const mergeNumericRefinements: Merger = right => left => + left.setQueryParameters({ + numericRefinements: { + ...left.numericRefinements, + ...right.numericRefinements, + }, + }); + +const mergeHierarchicalFacetsRefinements: Merger = right => left => + left.setQueryParameters({ + hierarchicalFacetsRefinements: { + ...left.hierarchicalFacetsRefinements, + ...right.hierarchicalFacetsRefinements, + }, + }); + +const merge = (...parameters: SearchParameters[]): SearchParameters => { + return parameters.reduce((left, right) => + compose( + mergeHierarchicalFacetsRefinements(right), + mergeHierarchicalFacets(right), + mergeTagRefinements(right), + mergeNumericRefinements(right), + mergeDisjunctiveFacetsRefinements(right), + mergeFacetsExcludes(right), + mergeFacetRefinements(right), + mergeDisjunctiveFacets(right), + mergeFacets(right), + mergeWithRest(right) + )(left) + ); +}; + +export default merge; From 6e2b8a7390898eed6053eb8753d04c03de710535 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Mon, 8 Jul 2019 14:28:00 +0200 Subject: [PATCH 2/9] fix(support): add polyfill for findIndex This is the same one as in helper v3 --- src/lib/utils/__tests__/findIndex-test.ts | 33 +++++++++++++++++++++++ src/lib/utils/findIndex.ts | 23 ++++++++++++++++ src/lib/utils/mergeSearchParameters.ts | 3 ++- 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/lib/utils/__tests__/findIndex-test.ts create mode 100644 src/lib/utils/findIndex.ts diff --git a/src/lib/utils/__tests__/findIndex-test.ts b/src/lib/utils/__tests__/findIndex-test.ts new file mode 100644 index 0000000000..8ec57cdb7e --- /dev/null +++ b/src/lib/utils/__tests__/findIndex-test.ts @@ -0,0 +1,33 @@ +import findIndex from '../findIndex'; + +describe('findIndex', () => { + describe('with polyfill', () => { + test('with empty array', () => { + const items = []; + const actual = findIndex(items, item => item === 'hello'); + + expect(actual).toEqual(-1); + }); + + test('with unknown item in array', () => { + const items = ['hey']; + const actual = findIndex(items, item => item === 'hello'); + + expect(actual).toEqual(-1); + }); + + test('with an array of strings', () => { + const items = ['hello', 'goodbye']; + const actual = findIndex(items, item => item === 'hello'); + + expect(actual).toEqual(0); + }); + + test('with an array of objects', () => { + const items = [{ name: 'John' }, { name: 'Jane' }]; + const actual = findIndex(items, item => item.name === 'John'); + + expect(actual).toEqual(0); + }); + }); +}); diff --git a/src/lib/utils/findIndex.ts b/src/lib/utils/findIndex.ts new file mode 100644 index 0000000000..6d3994171f --- /dev/null +++ b/src/lib/utils/findIndex.ts @@ -0,0 +1,23 @@ +// We aren't using the native `Array.prototype.findIndex` because the refactor away from Lodash is not +// published as a major version. +// Relying on the `findIndex` polyfill on user-land, which before was only required for niche use-cases, +// was decided as too risky. +// @MAJOR Replace with the native `Array.prototype.findIndex` method +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex +function findIndex( + array: TItem[], + comparator: (value: TItem) => boolean +): number { + if (!Array.isArray(array)) { + return -1; + } + + for (var i = 0; i < array.length; i++) { + if (comparator(array[i])) { + return i; + } + } + return -1; +} + +export default findIndex; diff --git a/src/lib/utils/mergeSearchParameters.ts b/src/lib/utils/mergeSearchParameters.ts index 32184cb1c5..0cec90f32e 100644 --- a/src/lib/utils/mergeSearchParameters.ts +++ b/src/lib/utils/mergeSearchParameters.ts @@ -1,4 +1,5 @@ import { SearchParameters } from '../../types'; +import findIndex from './findIndex'; type Merge = (value: SearchParameters) => SearchParameters; @@ -49,7 +50,7 @@ const mergeHierarchicalFacets: Merger = right => left => // @TODO: remove this cast when typings in helper are merged hierarchicalFacets: (right.hierarchicalFacets as HierarchicalFacet[]).reduce( (facets, facet) => { - const index = facets.findIndex(_ => _.name === facet.name); + const index = findIndex(facets, _ => _.name === facet.name); if (index === -1) { return facets.concat(facet); From f2a3c354f878e6d31793eada42c043ffaa3d179a Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Mon, 8 Jul 2019 15:31:32 +0200 Subject: [PATCH 3/9] chore: fix lint --- src/lib/utils/findIndex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/findIndex.ts b/src/lib/utils/findIndex.ts index 6d3994171f..442b66e9ab 100644 --- a/src/lib/utils/findIndex.ts +++ b/src/lib/utils/findIndex.ts @@ -12,7 +12,7 @@ function findIndex( return -1; } - for (var i = 0; i < array.length; i++) { + for (let i = 0; i < array.length; i++) { if (comparator(array[i])) { return i; } From 9c254ed12c79797071563573fc3d14d9d5e04476 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Tue, 9 Jul 2019 17:49:31 +0200 Subject: [PATCH 4/9] chore: remove compose --- src/lib/utils/mergeSearchParameters.ts | 85 ++++++++++++++++---------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/src/lib/utils/mergeSearchParameters.ts b/src/lib/utils/mergeSearchParameters.ts index 0cec90f32e..3025646a89 100644 --- a/src/lib/utils/mergeSearchParameters.ts +++ b/src/lib/utils/mergeSearchParameters.ts @@ -1,15 +1,12 @@ import { SearchParameters } from '../../types'; import findIndex from './findIndex'; -type Merge = (value: SearchParameters) => SearchParameters; +type Merger = ( + left: SearchParameters, + right: SearchParameters +) => SearchParameters; -type Merger = (right: SearchParameters) => Merge; - -const compose = (...fns: Array<(x: TValue) => TValue>) => ( - initial: TValue -) => fns.reduceRight((_, fn) => fn(_), initial); - -const mergeWithRest: Merger = right => left => { +const mergeWithRest: Merger = (left, right) => { const { facets, disjunctiveFacets, @@ -27,16 +24,16 @@ const mergeWithRest: Merger = right => left => { }; // Merge facets -const mergeFacets: Merger = right => left => +const mergeFacets: Merger = (left, right) => right.facets!.reduce((_, name) => _.addFacet(name), left); -const mergeDisjunctiveFacets: Merger = right => left => +const mergeDisjunctiveFacets: Merger = (left, right) => right.disjunctiveFacets!.reduce( (_, name) => _.addDisjunctiveFacet(name), left ); -const mergeTagRefinements: Merger = right => left => +const mergeTagRefinements: Merger = (left, right) => right.tagRefinements!.reduce((_, value) => _.addTagRefinement(value), left); type HierarchicalFacet = { @@ -45,7 +42,7 @@ type HierarchicalFacet = { separator: string; }; -const mergeHierarchicalFacets: Merger = right => left => +const mergeHierarchicalFacets: Merger = (left, right) => left.setQueryParameters({ // @TODO: remove this cast when typings in helper are merged hierarchicalFacets: (right.hierarchicalFacets as HierarchicalFacet[]).reduce( @@ -66,7 +63,7 @@ const mergeHierarchicalFacets: Merger = right => left => }); // Merge facets refinements -const mergeFacetRefinements: Merger = right => left => +const mergeFacetRefinements: Merger = (left, right) => left.setQueryParameters({ facetsRefinements: { ...left.facetsRefinements, @@ -74,7 +71,7 @@ const mergeFacetRefinements: Merger = right => left => }, }); -const mergeFacetsExcludes: Merger = right => left => +const mergeFacetsExcludes: Merger = (left, right) => left.setQueryParameters({ facetsExcludes: { ...left.facetsExcludes, @@ -82,7 +79,7 @@ const mergeFacetsExcludes: Merger = right => left => }, }); -const mergeDisjunctiveFacetsRefinements: Merger = right => left => +const mergeDisjunctiveFacetsRefinements: Merger = (left, right) => left.setQueryParameters({ disjunctiveFacetsRefinements: { ...left.disjunctiveFacetsRefinements, @@ -90,7 +87,7 @@ const mergeDisjunctiveFacetsRefinements: Merger = right => left => }, }); -const mergeNumericRefinements: Merger = right => left => +const mergeNumericRefinements: Merger = (left, right) => left.setQueryParameters({ numericRefinements: { ...left.numericRefinements, @@ -98,7 +95,7 @@ const mergeNumericRefinements: Merger = right => left => }, }); -const mergeHierarchicalFacetsRefinements: Merger = right => left => +const mergeHierarchicalFacetsRefinements: Merger = (left, right) => left.setQueryParameters({ hierarchicalFacetsRefinements: { ...left.hierarchicalFacetsRefinements, @@ -106,21 +103,43 @@ const mergeHierarchicalFacetsRefinements: Merger = right => left => }, }); -const merge = (...parameters: SearchParameters[]): SearchParameters => { - return parameters.reduce((left, right) => - compose( - mergeHierarchicalFacetsRefinements(right), - mergeHierarchicalFacets(right), - mergeTagRefinements(right), - mergeNumericRefinements(right), - mergeDisjunctiveFacetsRefinements(right), - mergeFacetsExcludes(right), - mergeFacetRefinements(right), - mergeDisjunctiveFacets(right), - mergeFacets(right), - mergeWithRest(right) - )(left) - ); -}; +const merge = (...parameters: SearchParameters[]): SearchParameters => + parameters.reduce((left, right) => { + const hierarchicalFacetsRefinementsMerged = mergeHierarchicalFacetsRefinements( + left, + right + ); + const hierarchicalFacetsMerged = mergeHierarchicalFacets( + hierarchicalFacetsRefinementsMerged, + right + ); + const tagRefinementsMerged = mergeTagRefinements( + hierarchicalFacetsMerged, + right + ); + const numericRefinementsMerged = mergeNumericRefinements( + tagRefinementsMerged, + right + ); + const disjunctiveFacetsRefinementsMerged = mergeDisjunctiveFacetsRefinements( + numericRefinementsMerged, + right + ); + const facetsExcludesMerged = mergeFacetsExcludes( + disjunctiveFacetsRefinementsMerged, + right + ); + const facetRefinementsMerged = mergeFacetRefinements( + facetsExcludesMerged, + right + ); + const disjunctiveFacetsMerged = mergeDisjunctiveFacets( + facetRefinementsMerged, + right + ); + const facetsMerged = mergeFacets(disjunctiveFacetsMerged, right); + + return mergeWithRest(facetsMerged, right); + }); export default merge; From 39f3d097d5cc64a301c90685ba3a41bf11dfc7cf Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Tue, 9 Jul 2019 18:04:33 +0200 Subject: [PATCH 5/9] change test titles --- .../__tests__/mergeSearchParameters-test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/utils/__tests__/mergeSearchParameters-test.ts b/src/lib/utils/__tests__/mergeSearchParameters-test.ts index d0afcf475a..b7f0fc4860 100644 --- a/src/lib/utils/__tests__/mergeSearchParameters-test.ts +++ b/src/lib/utils/__tests__/mergeSearchParameters-test.ts @@ -2,7 +2,7 @@ import algoliasearchHelper from 'algoliasearch-helper'; import merge from '../mergeSearchParameters'; describe('mergeSearchParameters', () => { - it('merges non-managed parameters', () => { + it('overrides non-managed parameters', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ // Inherit @@ -78,7 +78,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `facetsRefinements` parameters', () => { + it('merges `facetsRefinements` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ facets: ['brand'], @@ -113,7 +113,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `facetsExcludes` parameters', () => { + it('merges `facetsExcludes` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ facets: ['brand'], @@ -148,7 +148,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `disjunctiveFacetsRefinements` parameters', () => { + it('merges `disjunctiveFacetsRefinements` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ disjunctiveFacets: ['brand'], @@ -183,7 +183,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `numericRefinements` parameters', () => { + it('merges `numericRefinements` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ numericRefinements: { @@ -229,7 +229,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `tagRefinements` parameters', () => { + it('merges `tagRefinements` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ tagRefinements: ['brand'], @@ -249,7 +249,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `hierarchicalFacets` parameters', () => { + it('merges `hierarchicalFacets` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ hierarchicalFacets: [ @@ -301,7 +301,7 @@ describe('mergeSearchParameters', () => { ); }); - it('merges `hierarchicalFacetsRefinements` parameters', () => { + it('merges `hierarchicalFacetsRefinements` parameters, overrides conflicts', () => { const actual = merge( algoliasearchHelper.SearchParameters.make({ hierarchicalFacets: [ From 960ccbb627115e13d8548edc46004684ede430e1 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Mon, 15 Jul 2019 10:50:06 +0200 Subject: [PATCH 6/9] chore(merge): use in index widget --- src/lib/utils/index.ts | 1 + src/widgets/index/index.ts | 15 ++++----------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 015fae037d..23b3920b2f 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -21,6 +21,7 @@ export { default as isEqual } from './isEqual'; export { default as escape } from './escape'; export { default as find } from './find'; export { default as mergeDeep } from './mergeDeep'; +export { default as mergeSearchParameters } from './mergeSearchParameters'; export { default as resolveSearchParameters } from './resolveSearchParameters'; export { warning, deprecate } from './logger'; export { diff --git a/src/widgets/index/index.ts b/src/widgets/index/index.ts index 615f7956c0..5d6034ff87 100644 --- a/src/widgets/index/index.ts +++ b/src/widgets/index/index.ts @@ -14,6 +14,7 @@ import { createDocumentationMessageGenerator, resolveSearchParameters, enhanceConfiguration, + mergeSearchParameters, } from '../../lib/utils'; const withUsage = createDocumentationMessageGenerator({ @@ -205,17 +206,9 @@ const index = (props: IndexProps): Index => { ); }; - derivedHelper = mainHelper.derive(() => { - const parameters = resolveSearchParameters(this); - - // @TODO: replace this dummy merge with the correct function - return parameters.reduce((previous, current) => - algoliasearchHelper.SearchParameters.make({ - ...previous, - ...current, - }) - ); - }); + derivedHelper = mainHelper.derive(() => + mergeSearchParameters(...resolveSearchParameters(this)) + ); // We have to use `!` at the moment because `dervive` is not correctly typed. derivedHelper!.on('search', () => { From 3e3083e0150115e696b6ab728c330f7b72eff702 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Mon, 15 Jul 2019 10:51:14 +0200 Subject: [PATCH 7/9] chore: reorder --- src/lib/utils/mergeSearchParameters.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/utils/mergeSearchParameters.ts b/src/lib/utils/mergeSearchParameters.ts index 3025646a89..b5ae63b218 100644 --- a/src/lib/utils/mergeSearchParameters.ts +++ b/src/lib/utils/mergeSearchParameters.ts @@ -33,9 +33,6 @@ const mergeDisjunctiveFacets: Merger = (left, right) => left ); -const mergeTagRefinements: Merger = (left, right) => - right.tagRefinements!.reduce((_, value) => _.addTagRefinement(value), left); - type HierarchicalFacet = { name: string; attributes: string[]; @@ -62,7 +59,10 @@ const mergeHierarchicalFacets: Merger = (left, right) => ), }); -// Merge facets refinements +// Merge facet refinements +const mergeTagRefinements: Merger = (left, right) => + right.tagRefinements!.reduce((_, value) => _.addTagRefinement(value), left); + const mergeFacetRefinements: Merger = (left, right) => left.setQueryParameters({ facetsRefinements: { From febb4f82e1452201fdc31b29e23d5e8660288c39 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Mon, 15 Jul 2019 11:18:40 +0200 Subject: [PATCH 8/9] fix typo --- src/widgets/index/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/index/index.ts b/src/widgets/index/index.ts index 5d6034ff87..577e1efa4d 100644 --- a/src/widgets/index/index.ts +++ b/src/widgets/index/index.ts @@ -206,7 +206,7 @@ const index = (props: IndexProps): Index => { ); }; - derivedHelper = mainHelper.derive(() => + derivedHelper = mainHelper.derive(() => mergeSearchParameters(...resolveSearchParameters(this)) ); From fa2ec3d1ebf73f1642dd67562343a11909173216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chalifour?= Date: Fri, 12 Jul 2019 15:23:25 +0200 Subject: [PATCH 9/9] feat(index): merge `ruleContexts` search parameter --- .../__tests__/mergeSearchParameters-test.ts | 27 +++++++++++++++++++ src/lib/utils/mergeSearchParameters.ts | 25 ++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/__tests__/mergeSearchParameters-test.ts b/src/lib/utils/__tests__/mergeSearchParameters-test.ts index b7f0fc4860..03fbbcfa02 100644 --- a/src/lib/utils/__tests__/mergeSearchParameters-test.ts +++ b/src/lib/utils/__tests__/mergeSearchParameters-test.ts @@ -367,4 +367,31 @@ describe('mergeSearchParameters', () => { }) ); }); + + it('merges and dedupes `ruleContexts` parameters', () => { + const actual = merge( + algoliasearchHelper.SearchParameters.make({ + ruleContexts: ['ais-genre-comedy'], + }), + algoliasearchHelper.SearchParameters.make({ + ruleContexts: ['ais-genre-thriller', 'ais-genre-comedy'], + }), + algoliasearchHelper.SearchParameters.make({ + ruleContexts: ['ais-rating-4'], + }), + algoliasearchHelper.SearchParameters.make({ + ruleContexts: [], + }) + ); + + expect(actual).toEqual( + algoliasearchHelper.SearchParameters.make({ + ruleContexts: [ + 'ais-genre-comedy', + 'ais-genre-thriller', + 'ais-rating-4', + ], + }) + ); + }); }); diff --git a/src/lib/utils/mergeSearchParameters.ts b/src/lib/utils/mergeSearchParameters.ts index b5ae63b218..6b22f96e3e 100644 --- a/src/lib/utils/mergeSearchParameters.ts +++ b/src/lib/utils/mergeSearchParameters.ts @@ -1,5 +1,6 @@ import { SearchParameters } from '../../types'; import findIndex from './findIndex'; +import uniq from './uniq'; type Merger = ( left: SearchParameters, @@ -17,6 +18,7 @@ const mergeWithRest: Merger = (left, right) => { tagRefinements, hierarchicalFacets, hierarchicalFacetsRefinements, + ruleContexts, ...rest } = right; @@ -103,6 +105,23 @@ const mergeHierarchicalFacetsRefinements: Merger = (left, right) => }, }); +const mergeRuleContexts: Merger = (left, right) => { + const ruleContexts: string[] = uniq( + ([] as any) + .concat(left.ruleContexts) + .concat(right.ruleContexts) + .filter(Boolean) + ); + + if (ruleContexts.length > 0) { + return left.setQueryParameters({ + ruleContexts, + }); + } + + return left; +}; + const merge = (...parameters: SearchParameters[]): SearchParameters => parameters.reduce((left, right) => { const hierarchicalFacetsRefinementsMerged = mergeHierarchicalFacetsRefinements( @@ -137,7 +156,11 @@ const merge = (...parameters: SearchParameters[]): SearchParameters => facetRefinementsMerged, right ); - const facetsMerged = mergeFacets(disjunctiveFacetsMerged, right); + const ruleContextsMerged = mergeRuleContexts( + disjunctiveFacetsMerged, + right + ); + const facetsMerged = mergeFacets(ruleContextsMerged, right); return mergeWithRest(facetsMerged, right); });