diff --git a/package.json b/package.json index 569037f8e..38a530862 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ }, { "path": "packages/recommend/dist/recommend.umd.js", - "maxSize": "4.1KB" + "maxSize": "4.2KB" } ] } diff --git a/packages/recommend/src/__tests__/getTrendingFacets.test.ts b/packages/recommend/src/__tests__/getTrendingFacets.test.ts new file mode 100644 index 000000000..69a13f2cb --- /dev/null +++ b/packages/recommend/src/__tests__/getTrendingFacets.test.ts @@ -0,0 +1,46 @@ +import { TestSuite } from '../../../client-common/src/__tests__/TestSuite'; + +const recommend = new TestSuite('recommend').recommend; + +function createMockedClient() { + const client = recommend('appId', 'apiKey'); + jest.spyOn(client.transporter, 'read').mockImplementation(() => Promise.resolve()); + + return client; +} + +describe('getTrendingFacets', () => { + test('builds the request', async () => { + const client = createMockedClient(); + + await client.getTrendingFacets( + [ + { + indexName: 'products', + facetName: 'company', + }, + ], + {} + ); + + expect(client.transporter.read).toHaveBeenCalledTimes(1); + expect(client.transporter.read).toHaveBeenCalledWith( + { + cacheable: true, + data: { + requests: [ + { + indexName: 'products', + model: 'trending-facets', + facetName: 'company', + threshold: 0, + }, + ], + }, + method: 'POST', + path: '1/indexes/*/recommendations', + }, + {} + ); + }); +}); diff --git a/packages/recommend/src/__tests__/getTrendingItems.test.ts b/packages/recommend/src/__tests__/getTrendingItems.test.ts new file mode 100644 index 000000000..03dfcd294 --- /dev/null +++ b/packages/recommend/src/__tests__/getTrendingItems.test.ts @@ -0,0 +1,44 @@ +import { TestSuite } from '../../../client-common/src/__tests__/TestSuite'; + +const recommend = new TestSuite('recommend').recommend; + +function createMockedClient() { + const client = recommend('appId', 'apiKey'); + jest.spyOn(client.transporter, 'read').mockImplementation(() => Promise.resolve()); + + return client; +} + +describe('getTrendingItems', () => { + test('builds the request', async () => { + const client = createMockedClient(); + + await client.getTrendingItems( + [ + { + indexName: 'products', + }, + ], + {} + ); + + expect(client.transporter.read).toHaveBeenCalledTimes(1); + expect(client.transporter.read).toHaveBeenCalledWith( + { + cacheable: true, + data: { + requests: [ + { + indexName: 'products', + model: 'trending-items', + threshold: 0, + }, + ], + }, + method: 'POST', + path: '1/indexes/*/recommendations', + }, + {} + ); + }); +}); diff --git a/packages/recommend/src/builds/browser.ts b/packages/recommend/src/builds/browser.ts index 649c321d6..60db69674 100644 --- a/packages/recommend/src/builds/browser.ts +++ b/packages/recommend/src/builds/browser.ts @@ -8,7 +8,13 @@ import { createBrowserXhrRequester } from '@algolia/requester-browser-xhr'; import { createUserAgent } from '@algolia/transporter'; import { createRecommendClient } from '../createRecommendClient'; -import { getFrequentlyBoughtTogether, getRecommendations, getRelatedProducts } from '../methods'; +import { + getFrequentlyBoughtTogether, + getRecommendations, + getRelatedProducts, + getTrendingFacets, + getTrendingItems, +} from '../methods'; import { BaseRecommendClient, RecommendOptions, WithRecommendMethods } from '../types'; export default function recommend( @@ -47,6 +53,8 @@ export default function recommend( getFrequentlyBoughtTogether, getRecommendations, getRelatedProducts, + getTrendingFacets, + getTrendingItems, }, }); } diff --git a/packages/recommend/src/builds/node.ts b/packages/recommend/src/builds/node.ts index 6cfbe1584..b7646c384 100644 --- a/packages/recommend/src/builds/node.ts +++ b/packages/recommend/src/builds/node.ts @@ -7,7 +7,13 @@ import { createNodeHttpRequester } from '@algolia/requester-node-http'; import { createUserAgent } from '@algolia/transporter'; import { createRecommendClient } from '../createRecommendClient'; -import { getFrequentlyBoughtTogether, getRecommendations, getRelatedProducts } from '../methods'; +import { + getFrequentlyBoughtTogether, + getRecommendations, + getRelatedProducts, + getTrendingFacets, + getTrendingItems, +} from '../methods'; import { BaseRecommendClient, RecommendOptions, WithRecommendMethods } from '../types'; export default function recommend( @@ -41,6 +47,8 @@ export default function recommend( getFrequentlyBoughtTogether, getRecommendations, getRelatedProducts, + getTrendingFacets, + getTrendingItems, }, }); } diff --git a/packages/recommend/src/methods/getRecommendations.ts b/packages/recommend/src/methods/getRecommendations.ts index 5690ddaa5..8fda3aa2a 100644 --- a/packages/recommend/src/methods/getRecommendations.ts +++ b/packages/recommend/src/methods/getRecommendations.ts @@ -1,14 +1,19 @@ import { MethodEnum } from '@algolia/requester-common'; -import { BaseRecommendClient, RecommendationsQuery, WithRecommendMethods } from '../types'; +import { + BaseRecommendClient, + RecommendationsQuery, + TrendingQuery, + WithRecommendMethods, +} from '../types'; type GetRecommendations = ( base: BaseRecommendClient ) => WithRecommendMethods['getRecommendations']; export const getRecommendations: GetRecommendations = base => { - return (queries: readonly RecommendationsQuery[], requestOptions) => { - const requests: readonly RecommendationsQuery[] = queries.map(query => ({ + return (queries: ReadonlyArray, requestOptions) => { + const requests: ReadonlyArray = queries.map(query => ({ ...query, // The `threshold` param is required by the endpoint to make it easier // to provide a default value later, so we default it in the client diff --git a/packages/recommend/src/methods/getTrendingFacets.ts b/packages/recommend/src/methods/getTrendingFacets.ts new file mode 100644 index 000000000..0bf3b279e --- /dev/null +++ b/packages/recommend/src/methods/getTrendingFacets.ts @@ -0,0 +1,32 @@ +import { MethodEnum } from '@algolia/requester-common'; + +import { BaseRecommendClient, TrendingFacetsQuery, WithRecommendMethods } from '../types'; + +type GetTrendingFacets = ( + base: BaseRecommendClient +) => WithRecommendMethods['getTrendingFacets']; + +export const getTrendingFacets: GetTrendingFacets = base => { + return (queries: readonly TrendingFacetsQuery[], requestOptions) => { + const requests: readonly TrendingFacetsQuery[] = queries.map(query => ({ + ...query, + model: 'trending-facets', + // The `threshold` param is required by the endpoint to make it easier + // to provide a default value later, so we default it in the client + // so that users don't have to provide a value. + threshold: query.threshold || 0, + })); + + return base.transporter.read( + { + method: MethodEnum.Post, + path: '1/indexes/*/recommendations', + data: { + requests, + }, + cacheable: true, + }, + requestOptions + ); + }; +}; diff --git a/packages/recommend/src/methods/getTrendingItems.ts b/packages/recommend/src/methods/getTrendingItems.ts new file mode 100644 index 000000000..fb1610dc2 --- /dev/null +++ b/packages/recommend/src/methods/getTrendingItems.ts @@ -0,0 +1,32 @@ +import { MethodEnum } from '@algolia/requester-common'; + +import { BaseRecommendClient, TrendingItemsQuery, WithRecommendMethods } from '../types'; + +type GetTrendingItems = ( + base: BaseRecommendClient +) => WithRecommendMethods['getTrendingItems']; + +export const getTrendingItems: GetTrendingItems = base => { + return (queries: readonly TrendingItemsQuery[], requestOptions) => { + const requests: readonly TrendingItemsQuery[] = queries.map(query => ({ + ...query, + model: 'trending-items', + // The `threshold` param is required by the endpoint to make it easier + // to provide a default value later, so we default it in the client + // so that users don't have to provide a value. + threshold: query.threshold || 0, + })); + + return base.transporter.read( + { + method: MethodEnum.Post, + path: '1/indexes/*/recommendations', + data: { + requests, + }, + cacheable: true, + }, + requestOptions + ); + }; +}; diff --git a/packages/recommend/src/methods/index.ts b/packages/recommend/src/methods/index.ts index a53053aef..e30466cc0 100644 --- a/packages/recommend/src/methods/index.ts +++ b/packages/recommend/src/methods/index.ts @@ -5,3 +5,5 @@ export * from './getFrequentlyBoughtTogether'; export * from './getRecommendations'; export * from './getRelatedProducts'; +export * from './getTrendingFacets'; +export * from './getTrendingItems'; diff --git a/packages/recommend/src/types/RecommendModel.ts b/packages/recommend/src/types/RecommendModel.ts index 8e3cdfcdf..de574598f 100644 --- a/packages/recommend/src/types/RecommendModel.ts +++ b/packages/recommend/src/types/RecommendModel.ts @@ -1 +1,2 @@ -export type RecommendModel = 'related-products' | 'bought-together'; +export type RecommendModel = 'related-products' | 'bought-together' | TrendingModel; +export type TrendingModel = 'trending-items' | 'trending-facets'; diff --git a/packages/recommend/src/types/TrendingFacetsQuery.ts b/packages/recommend/src/types/TrendingFacetsQuery.ts new file mode 100644 index 000000000..1c25ec4b8 --- /dev/null +++ b/packages/recommend/src/types/TrendingFacetsQuery.ts @@ -0,0 +1,3 @@ +import { TrendingQuery } from './TrendingQuery'; + +export type TrendingFacetsQuery = Omit; diff --git a/packages/recommend/src/types/TrendingItemsQuery.ts b/packages/recommend/src/types/TrendingItemsQuery.ts new file mode 100644 index 000000000..fe012604e --- /dev/null +++ b/packages/recommend/src/types/TrendingItemsQuery.ts @@ -0,0 +1,3 @@ +import { TrendingQuery } from './TrendingQuery'; + +export type TrendingItemsQuery = Omit; diff --git a/packages/recommend/src/types/TrendingQuery.ts b/packages/recommend/src/types/TrendingQuery.ts new file mode 100644 index 000000000..bce65655e --- /dev/null +++ b/packages/recommend/src/types/TrendingQuery.ts @@ -0,0 +1,46 @@ +import { TrendingModel } from './RecommendModel'; +import { RecommendSearchOptions } from './RecommendSearchOptions'; + +export type TrendingQuery = { + /** + * The name of the target index. + */ + readonly indexName: string; + + /** + * The name of the Recommendation model to use. + */ + readonly model: TrendingModel; + + /** + * Threshold for the recommendations confidence score (between 0 and 100). Only recommendations with a greater score are returned. + */ + readonly threshold?: number; + + /** + * How many recommendations to retrieve. + */ + readonly maxRecommendations?: number; + + /** + * List of [search parameters](https://www.algolia.com/doc/api-reference/search-api-parameters/) to send. + */ + readonly queryParameters?: RecommendSearchOptions; + + /** + * List of [search parameters](https://www.algolia.com/doc/api-reference/search-api-parameters/) to send. + * + * Additional filters to use as fallback when there aren’t enough recommendations. + */ + readonly fallbackParameters?: RecommendSearchOptions; + + /** + * Used for trending model + */ + readonly facetName?: string; + + /** + * Used for trending model + */ + readonly facetValue?: string; +}; diff --git a/packages/recommend/src/types/WithRecommendMethods.ts b/packages/recommend/src/types/WithRecommendMethods.ts index cb673b4bc..25320a195 100644 --- a/packages/recommend/src/types/WithRecommendMethods.ts +++ b/packages/recommend/src/types/WithRecommendMethods.ts @@ -4,6 +4,8 @@ import { RequestOptions } from '@algolia/transporter'; import { FrequentlyBoughtTogetherQuery } from './FrequentlyBoughtTogetherQuery'; import { RecommendationsQuery } from './RecommendationsQuery'; import { RelatedProductsQuery } from './RelatedProductsQuery'; +import { TrendingFacetsQuery } from './TrendingFacetsQuery'; +import { TrendingItemsQuery } from './TrendingItemsQuery'; export type WithRecommendMethods = TType & { /** @@ -29,4 +31,20 @@ export type WithRecommendMethods = TType & { queries: readonly FrequentlyBoughtTogetherQuery[], requestOptions?: RequestOptions & SearchOptions ) => Readonly>>; + + /** + * Returns trending items + */ + readonly getTrendingItems: ( + queries: readonly TrendingItemsQuery[], + requestOptions?: RequestOptions & SearchOptions + ) => Readonly>>; + + /** + * Returns trending items per facet + */ + readonly getTrendingFacets: ( + queries: readonly TrendingFacetsQuery[], + requestOptions?: RequestOptions & SearchOptions + ) => Readonly>>; }; diff --git a/packages/recommend/src/types/index.ts b/packages/recommend/src/types/index.ts index 98035bb8f..19b2cd086 100644 --- a/packages/recommend/src/types/index.ts +++ b/packages/recommend/src/types/index.ts @@ -10,4 +10,7 @@ export * from './RecommendOptions'; export * from './RecommendSearchOptions'; export * from './RecommendationsQuery'; export * from './RelatedProductsQuery'; +export * from './TrendingFacetsQuery'; +export * from './TrendingQuery'; +export * from './TrendingItemsQuery'; export * from './WithRecommendMethods';