Skip to content

Commit 6882ec8

Browse files
feat(recommend): Add trending types and methods (#1396)
Co-authored-by: Clément Vannicatte <[email protected]>
1 parent ede7a4b commit 6882ec8

15 files changed

+258
-7
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
},
106106
{
107107
"path": "packages/recommend/dist/recommend.umd.js",
108-
"maxSize": "4.1KB"
108+
"maxSize": "4.2KB"
109109
}
110110
]
111111
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { TestSuite } from '../../../client-common/src/__tests__/TestSuite';
2+
3+
const recommend = new TestSuite('recommend').recommend;
4+
5+
function createMockedClient() {
6+
const client = recommend('appId', 'apiKey');
7+
jest.spyOn(client.transporter, 'read').mockImplementation(() => Promise.resolve());
8+
9+
return client;
10+
}
11+
12+
describe('getTrendingFacets', () => {
13+
test('builds the request', async () => {
14+
const client = createMockedClient();
15+
16+
await client.getTrendingFacets(
17+
[
18+
{
19+
indexName: 'products',
20+
facetName: 'company',
21+
},
22+
],
23+
{}
24+
);
25+
26+
expect(client.transporter.read).toHaveBeenCalledTimes(1);
27+
expect(client.transporter.read).toHaveBeenCalledWith(
28+
{
29+
cacheable: true,
30+
data: {
31+
requests: [
32+
{
33+
indexName: 'products',
34+
model: 'trending-facets',
35+
facetName: 'company',
36+
threshold: 0,
37+
},
38+
],
39+
},
40+
method: 'POST',
41+
path: '1/indexes/*/recommendations',
42+
},
43+
{}
44+
);
45+
});
46+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { TestSuite } from '../../../client-common/src/__tests__/TestSuite';
2+
3+
const recommend = new TestSuite('recommend').recommend;
4+
5+
function createMockedClient() {
6+
const client = recommend('appId', 'apiKey');
7+
jest.spyOn(client.transporter, 'read').mockImplementation(() => Promise.resolve());
8+
9+
return client;
10+
}
11+
12+
describe('getTrendingItems', () => {
13+
test('builds the request', async () => {
14+
const client = createMockedClient();
15+
16+
await client.getTrendingItems(
17+
[
18+
{
19+
indexName: 'products',
20+
},
21+
],
22+
{}
23+
);
24+
25+
expect(client.transporter.read).toHaveBeenCalledTimes(1);
26+
expect(client.transporter.read).toHaveBeenCalledWith(
27+
{
28+
cacheable: true,
29+
data: {
30+
requests: [
31+
{
32+
indexName: 'products',
33+
model: 'trending-items',
34+
threshold: 0,
35+
},
36+
],
37+
},
38+
method: 'POST',
39+
path: '1/indexes/*/recommendations',
40+
},
41+
{}
42+
);
43+
});
44+
});

packages/recommend/src/builds/browser.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import { createBrowserXhrRequester } from '@algolia/requester-browser-xhr';
88
import { createUserAgent } from '@algolia/transporter';
99

1010
import { createRecommendClient } from '../createRecommendClient';
11-
import { getFrequentlyBoughtTogether, getRecommendations, getRelatedProducts } from '../methods';
11+
import {
12+
getFrequentlyBoughtTogether,
13+
getRecommendations,
14+
getRelatedProducts,
15+
getTrendingFacets,
16+
getTrendingItems,
17+
} from '../methods';
1218
import { BaseRecommendClient, RecommendOptions, WithRecommendMethods } from '../types';
1319

1420
export default function recommend(
@@ -47,6 +53,8 @@ export default function recommend(
4753
getFrequentlyBoughtTogether,
4854
getRecommendations,
4955
getRelatedProducts,
56+
getTrendingFacets,
57+
getTrendingItems,
5058
},
5159
});
5260
}

packages/recommend/src/builds/node.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import { createNodeHttpRequester } from '@algolia/requester-node-http';
77
import { createUserAgent } from '@algolia/transporter';
88

99
import { createRecommendClient } from '../createRecommendClient';
10-
import { getFrequentlyBoughtTogether, getRecommendations, getRelatedProducts } from '../methods';
10+
import {
11+
getFrequentlyBoughtTogether,
12+
getRecommendations,
13+
getRelatedProducts,
14+
getTrendingFacets,
15+
getTrendingItems,
16+
} from '../methods';
1117
import { BaseRecommendClient, RecommendOptions, WithRecommendMethods } from '../types';
1218

1319
export default function recommend(
@@ -41,6 +47,8 @@ export default function recommend(
4147
getFrequentlyBoughtTogether,
4248
getRecommendations,
4349
getRelatedProducts,
50+
getTrendingFacets,
51+
getTrendingItems,
4452
},
4553
});
4654
}

packages/recommend/src/methods/getRecommendations.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import { MethodEnum } from '@algolia/requester-common';
22

3-
import { BaseRecommendClient, RecommendationsQuery, WithRecommendMethods } from '../types';
3+
import {
4+
BaseRecommendClient,
5+
RecommendationsQuery,
6+
TrendingQuery,
7+
WithRecommendMethods,
8+
} from '../types';
49

510
type GetRecommendations = (
611
base: BaseRecommendClient
712
) => WithRecommendMethods<BaseRecommendClient>['getRecommendations'];
813

914
export const getRecommendations: GetRecommendations = base => {
10-
return (queries: readonly RecommendationsQuery[], requestOptions) => {
11-
const requests: readonly RecommendationsQuery[] = queries.map(query => ({
15+
return (queries: ReadonlyArray<RecommendationsQuery | TrendingQuery>, requestOptions) => {
16+
const requests: ReadonlyArray<RecommendationsQuery | TrendingQuery> = queries.map(query => ({
1217
...query,
1318
// The `threshold` param is required by the endpoint to make it easier
1419
// to provide a default value later, so we default it in the client
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { MethodEnum } from '@algolia/requester-common';
2+
3+
import { BaseRecommendClient, TrendingFacetsQuery, WithRecommendMethods } from '../types';
4+
5+
type GetTrendingFacets = (
6+
base: BaseRecommendClient
7+
) => WithRecommendMethods<BaseRecommendClient>['getTrendingFacets'];
8+
9+
export const getTrendingFacets: GetTrendingFacets = base => {
10+
return (queries: readonly TrendingFacetsQuery[], requestOptions) => {
11+
const requests: readonly TrendingFacetsQuery[] = queries.map(query => ({
12+
...query,
13+
model: 'trending-facets',
14+
// The `threshold` param is required by the endpoint to make it easier
15+
// to provide a default value later, so we default it in the client
16+
// so that users don't have to provide a value.
17+
threshold: query.threshold || 0,
18+
}));
19+
20+
return base.transporter.read(
21+
{
22+
method: MethodEnum.Post,
23+
path: '1/indexes/*/recommendations',
24+
data: {
25+
requests,
26+
},
27+
cacheable: true,
28+
},
29+
requestOptions
30+
);
31+
};
32+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { MethodEnum } from '@algolia/requester-common';
2+
3+
import { BaseRecommendClient, TrendingItemsQuery, WithRecommendMethods } from '../types';
4+
5+
type GetTrendingItems = (
6+
base: BaseRecommendClient
7+
) => WithRecommendMethods<BaseRecommendClient>['getTrendingItems'];
8+
9+
export const getTrendingItems: GetTrendingItems = base => {
10+
return (queries: readonly TrendingItemsQuery[], requestOptions) => {
11+
const requests: readonly TrendingItemsQuery[] = queries.map(query => ({
12+
...query,
13+
model: 'trending-items',
14+
// The `threshold` param is required by the endpoint to make it easier
15+
// to provide a default value later, so we default it in the client
16+
// so that users don't have to provide a value.
17+
threshold: query.threshold || 0,
18+
}));
19+
20+
return base.transporter.read(
21+
{
22+
method: MethodEnum.Post,
23+
path: '1/indexes/*/recommendations',
24+
data: {
25+
requests,
26+
},
27+
cacheable: true,
28+
},
29+
requestOptions
30+
);
31+
};
32+
};

packages/recommend/src/methods/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
export * from './getFrequentlyBoughtTogether';
66
export * from './getRecommendations';
77
export * from './getRelatedProducts';
8+
export * from './getTrendingFacets';
9+
export * from './getTrendingItems';
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export type RecommendModel = 'related-products' | 'bought-together';
1+
export type RecommendModel = 'related-products' | 'bought-together' | TrendingModel;
2+
export type TrendingModel = 'trending-items' | 'trending-facets';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { TrendingQuery } from './TrendingQuery';
2+
3+
export type TrendingFacetsQuery = Omit<TrendingQuery, 'model' | 'facetValue'>;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { TrendingQuery } from './TrendingQuery';
2+
3+
export type TrendingItemsQuery = Omit<TrendingQuery, 'model'>;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { TrendingModel } from './RecommendModel';
2+
import { RecommendSearchOptions } from './RecommendSearchOptions';
3+
4+
export type TrendingQuery = {
5+
/**
6+
* The name of the target index.
7+
*/
8+
readonly indexName: string;
9+
10+
/**
11+
* The name of the Recommendation model to use.
12+
*/
13+
readonly model: TrendingModel;
14+
15+
/**
16+
* Threshold for the recommendations confidence score (between 0 and 100). Only recommendations with a greater score are returned.
17+
*/
18+
readonly threshold?: number;
19+
20+
/**
21+
* How many recommendations to retrieve.
22+
*/
23+
readonly maxRecommendations?: number;
24+
25+
/**
26+
* List of [search parameters](https://www.algolia.com/doc/api-reference/search-api-parameters/) to send.
27+
*/
28+
readonly queryParameters?: RecommendSearchOptions;
29+
30+
/**
31+
* List of [search parameters](https://www.algolia.com/doc/api-reference/search-api-parameters/) to send.
32+
*
33+
* Additional filters to use as fallback when there aren’t enough recommendations.
34+
*/
35+
readonly fallbackParameters?: RecommendSearchOptions;
36+
37+
/**
38+
* Used for trending model
39+
*/
40+
readonly facetName?: string;
41+
42+
/**
43+
* Used for trending model
44+
*/
45+
readonly facetValue?: string;
46+
};

packages/recommend/src/types/WithRecommendMethods.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { RequestOptions } from '@algolia/transporter';
44
import { FrequentlyBoughtTogetherQuery } from './FrequentlyBoughtTogetherQuery';
55
import { RecommendationsQuery } from './RecommendationsQuery';
66
import { RelatedProductsQuery } from './RelatedProductsQuery';
7+
import { TrendingFacetsQuery } from './TrendingFacetsQuery';
8+
import { TrendingItemsQuery } from './TrendingItemsQuery';
79

810
export type WithRecommendMethods<TType> = TType & {
911
/**
@@ -29,4 +31,20 @@ export type WithRecommendMethods<TType> = TType & {
2931
queries: readonly FrequentlyBoughtTogetherQuery[],
3032
requestOptions?: RequestOptions & SearchOptions
3133
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;
34+
35+
/**
36+
* Returns trending items
37+
*/
38+
readonly getTrendingItems: <TObject>(
39+
queries: readonly TrendingItemsQuery[],
40+
requestOptions?: RequestOptions & SearchOptions
41+
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;
42+
43+
/**
44+
* Returns trending items per facet
45+
*/
46+
readonly getTrendingFacets: <TObject>(
47+
queries: readonly TrendingFacetsQuery[],
48+
requestOptions?: RequestOptions & SearchOptions
49+
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;
3250
};

packages/recommend/src/types/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ export * from './RecommendOptions';
1010
export * from './RecommendSearchOptions';
1111
export * from './RecommendationsQuery';
1212
export * from './RelatedProductsQuery';
13+
export * from './TrendingFacetsQuery';
14+
export * from './TrendingQuery';
15+
export * from './TrendingItemsQuery';
1316
export * from './WithRecommendMethods';

0 commit comments

Comments
 (0)