Skip to content

Commit

Permalink
feat(helper): set up state management for recommendations (algolia#6100)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhayab authored Mar 26, 2024
1 parent 8328b13 commit bc5122f
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 3 deletions.
1 change: 1 addition & 0 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
],
"buildCommand": "build --no-private --ignore *-maps --ignore *-native",
"packages": [
"packages/algoliasearch-helper",
"packages/instantsearch.js",
"packages/react-instantsearch",
"packages/react-instantsearch-core",
Expand Down
6 changes: 3 additions & 3 deletions bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"files": [
{
"path": "packages/algoliasearch-helper/dist/algoliasearch.helper.js",
"maxSize": "38.75 kB"
"maxSize": "39 kB"
},
{
"path": "packages/algoliasearch-helper/dist/algoliasearch.helper.min.js",
Expand All @@ -14,11 +14,11 @@
},
{
"path": "./packages/instantsearch.js/dist/instantsearch.development.js",
"maxSize": "168.5 kB"
"maxSize": "168.75 kB"
},
{
"path": "packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js",
"maxSize": "46.25 kB"
"maxSize": "46.5 kB"
},
{
"path": "packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js",
Expand Down
27 changes: 27 additions & 0 deletions packages/algoliasearch-helper/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import type {
SearchResponse,
SnippetResult,
} from './types/algoliasearch';
// @ts-ignore
import type {
RecommendationsQuery,
RecommendedForYouQuery,
TrendingQuery,
} from '@algolia/recommend';

/**
* The algoliasearchHelper module is the function that will let its
Expand All @@ -32,6 +38,7 @@ declare namespace algoliasearchHelper {

export class AlgoliaSearchHelper extends EventEmitter {
state: SearchParameters;
recommendState: RecommendParameters;
lastResults: SearchResults | null;
derivedHelpers: DerivedHelper[];

Expand Down Expand Up @@ -1457,6 +1464,26 @@ declare namespace algoliasearchHelper {
exhaustive: boolean;
}
}

export type PlainRecommendParameters =
| RecommendationsQuery
| TrendingQuery
| RecommendedForYouQuery;

export type PlainRecommendParametersWithId = PlainRecommendParameters & {
$$id: string;
};

export type RecommendParametersOptions = {
params?: PlainRecommendParametersWithId[];
};

export class RecommendParameters {
params: PlainRecommendParametersWithId[];
constructor(opts?: RecommendParametersOptions);
addParams(params: PlainRecommendParametersWithId): RecommendParameters;
removeParams(id: string): RecommendParameters;
}
}

export = algoliasearchHelper;
1 change: 1 addition & 0 deletions packages/algoliasearch-helper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"types/algoliasearch.js"
],
"devDependencies": {
"@algolia/recommend": "4.22.1",
"@metalsmith/sass": "1.4.0",
"@types/algoliasearch": "3.34.11",
"browserify": "14.5.0",
Expand Down
37 changes: 37 additions & 0 deletions packages/algoliasearch-helper/src/RecommendParameters/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

/**
* RecommendParameters is the data structure that contains all the information
* usable for getting recommendations from the Algolia API. It doesn't do the
* search itself, nor does it contains logic about the parameters.
* It is an immutable object, therefore it has been created in a way that each
* changes does not change the object itself but returns a copy with the
* modification.
* This object should probably not be instantiated outside of the helper. It
* will be provided when needed.
* @constructor
* @classdesc contains all the parameters for recommendations
* @param {RecommendParametersOptions} opts the options to create the object
*/
function RecommendParameters(opts) {
opts = opts || {};
this.params = opts.params || [];
}

RecommendParameters.prototype = {
constructor: RecommendParameters,

addParams: function (params) {
return new RecommendParameters({ params: this.params.concat(params) });
},

removeParams: function (id) {
return new RecommendParameters({
params: this.params.filter(function (param) {
return param.$$id !== id;
}),
});
},
};

module.exports = RecommendParameters;
15 changes: 15 additions & 0 deletions packages/algoliasearch-helper/src/algoliasearch.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var inherits = require('./functions/inherits');
var merge = require('./functions/merge');
var objectHasKeys = require('./functions/objectHasKeys');
var omit = require('./functions/omit');
var RecommendParameters = require('./RecommendParameters');
var requestBuilder = require('./requestBuilder');
var SearchParameters = require('./SearchParameters');
var SearchResults = require('./SearchResults');
Expand Down Expand Up @@ -126,6 +127,9 @@ function AlgoliaSearchHelper(client, index, options, searchResultsOptions) {
var opts = options || {};
opts.index = index;
this.state = SearchParameters.make(opts);
this.recommendState = new RecommendParameters({
params: opts.recommendState,
});
this.lastResults = null;
this._queryId = 0;
this._lastQueryIdReceived = -1;
Expand Down Expand Up @@ -1509,6 +1513,17 @@ AlgoliaSearchHelper.prototype._change = function (event) {
}
};

AlgoliaSearchHelper.prototype._recommendChange = function (event) {
var state = event.state;

if (state !== this.recommendState) {
this.recommendState = state;

// eslint-disable-next-line no-warning-comments
// TODO: emit "change" event when events for Recommend are implemented
}
};

/**
* Clears the cache of the underlying Algolia client.
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

var RecommendParameters = require('../../../src/RecommendParameters');

test('accepts initial parameters', () => {
var params = [{ $$id: '1', objectID: 'objectID', model: 'bought-together' }];

var recommendParameters = new RecommendParameters({ params });
expect(recommendParameters.params).toEqual(params);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

var RecommendParameters = require('../../../src/RecommendParameters');

var params1 = { $$id: '1', objectID: 'objectID1', model: 'bought-together' };
var params2 = { $$id: '2', facetName: 'brand', model: 'trending-facets' };

describe('addParams', () => {
test('appends new parameters to the existing ones', () => {
var recommendParameters = new RecommendParameters();

recommendParameters = recommendParameters.addParams(params1);
expect(recommendParameters.params).toHaveLength(1);
expect(recommendParameters.params).toEqual([params1]);

recommendParameters = recommendParameters.addParams(params2);
expect(recommendParameters.params).toHaveLength(2);
expect(recommendParameters.params).toEqual([params1, params2]);
});
});

describe('removeParams', () => {
test('removes parameters for a specific id', () => {
var recommendParameters = new RecommendParameters({
params: [params1, params2],
});

recommendParameters = recommendParameters.removeParams('1');
expect(recommendParameters.params).toHaveLength(1);
expect(recommendParameters.params).toEqual([params2]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,15 @@ test('not vulnerable to prototype pollution', () => {

expect({}.test).toBeUndefined();
});

test('initializes RecommendParameters with params if provided through opts', () => {
var state = {
query: 'a query',
recommendState: [
{ $$id: '1', objectID: 'objectID', model: 'bought-together' },
],
};

var helper = algoliasearchHelper({}, null, state);
expect(helper.recommendState.params).toEqual(state.recommendState);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

var algoliasearchHelper = require('../../../index');

test('_recommendChange should update the recommend state', () => {
var params1 = { $$id: '1', objectID: 'objectID1', model: 'bought-together' };
var params2 = { $$id: '2', facetName: 'brand', model: 'trending-facets' };

var helper = algoliasearchHelper({}, null, {});

helper._recommendChange({ state: helper.recommendState.addParams(params1) });
expect(helper.recommendState.params).toHaveLength(1);
expect(helper.recommendState.params).toEqual([params1]);

helper._recommendChange({ state: helper.recommendState.addParams(params2) });
expect(helper.recommendState.params).toHaveLength(2);
expect(helper.recommendState.params).toEqual([params1, params2]);
});

// eslint-disable-next-line jest/no-disabled-tests, jest/expect-expect
test.skip('_recommendChange should trigger a change event', () => {});
17 changes: 17 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@
dependencies:
"@algolia/logger-common" "4.22.1"

"@algolia/[email protected]":
version "4.22.1"
resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-4.22.1.tgz#861344a276748c7dee655329d21291b1670305da"
integrity sha512-M3FCHKNdlDrxFs+uXZN9pWZ9j0YGe0q5GyL+f9w+GG0OXn0W16ryLHtum0sda4yTGM0MVfvAND6HXwoUvKKbVQ==
dependencies:
"@algolia/cache-browser-local-storage" "4.22.1"
"@algolia/cache-common" "4.22.1"
"@algolia/cache-in-memory" "4.22.1"
"@algolia/client-common" "4.22.1"
"@algolia/client-search" "4.22.1"
"@algolia/logger-common" "4.22.1"
"@algolia/logger-console" "4.22.1"
"@algolia/requester-browser-xhr" "4.22.1"
"@algolia/requester-common" "4.22.1"
"@algolia/requester-node-http" "4.22.1"
"@algolia/transporter" "4.22.1"

"@algolia/[email protected]":
version "4.22.1"
resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz#f04df6fe9690a071b267c77d26b83a3be9280361"
Expand Down

0 comments on commit bc5122f

Please sign in to comment.