Skip to content

feat(recommend): Add trending types and methods #1396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 14, 2022
Merged
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
},
{
"path": "packages/recommend/dist/recommend.umd.js",
"maxSize": "4.1KB"
"maxSize": "4.2KB"
}
]
}
46 changes: 46 additions & 0 deletions packages/recommend/src/__tests__/getTrendingFacets.test.ts
Original file line number Diff line number Diff line change
@@ -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',
},
{}
);
});
});
44 changes: 44 additions & 0 deletions packages/recommend/src/__tests__/getTrendingGlobalItems.test.ts
Original file line number Diff line number Diff line change
@@ -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('getTrendingGlobalItems', () => {
test('builds the request', async () => {
const client = createMockedClient();

await client.getTrendingGlobalItems(
[
{
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',
},
{}
);
});
});
48 changes: 48 additions & 0 deletions packages/recommend/src/__tests__/getTrendingItemsForFacet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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('getTrendingItemsForFacet', () => {
test('builds the request', async () => {
const client = createMockedClient();

await client.getTrendingItemsForFacet(
[
{
indexName: 'products',
facetName: 'company',
facetValue: 'tesla',
},
],
{}
);

expect(client.transporter.read).toHaveBeenCalledTimes(1);
expect(client.transporter.read).toHaveBeenCalledWith(
{
cacheable: true,
data: {
requests: [
{
indexName: 'products',
model: 'trending-items',
facetName: 'company',
facetValue: 'tesla',
threshold: 0,
},
],
},
method: 'POST',
path: '1/indexes/*/recommendations',
},
{}
);
});
});
12 changes: 11 additions & 1 deletion packages/recommend/src/builds/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ 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,
getTrendingGlobalItems,
getTrendingItemsForFacet,
} from '../methods';
import { BaseRecommendClient, RecommendOptions, WithRecommendMethods } from '../types';

export default function recommend(
Expand Down Expand Up @@ -47,6 +54,9 @@ export default function recommend(
getFrequentlyBoughtTogether,
getRecommendations,
getRelatedProducts,
getTrendingFacets,
getTrendingGlobalItems,
getTrendingItemsForFacet,
},
});
}
Expand Down
12 changes: 11 additions & 1 deletion packages/recommend/src/builds/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ 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,
getTrendingGlobalItems,
getTrendingItemsForFacet,
} from '../methods';
import { BaseRecommendClient, RecommendOptions, WithRecommendMethods } from '../types';

export default function recommend(
Expand Down Expand Up @@ -41,6 +48,9 @@ export default function recommend(
getFrequentlyBoughtTogether,
getRecommendations,
getRelatedProducts,
getTrendingFacets,
getTrendingGlobalItems,
getTrendingItemsForFacet,
},
});
}
Expand Down
32 changes: 32 additions & 0 deletions packages/recommend/src/methods/getTrendingFacets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { MethodEnum } from '@algolia/requester-common';

import { BaseRecommendClient, TrendingFacetsQuery, WithRecommendMethods } from '../types';

type GetTrendingFacets = (
base: BaseRecommendClient
) => WithRecommendMethods<BaseRecommendClient>['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
);
};
};
32 changes: 32 additions & 0 deletions packages/recommend/src/methods/getTrendingGlobalItems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { MethodEnum } from '@algolia/requester-common';

import { BaseRecommendClient, TrendingGlobalItemsQuery, WithRecommendMethods } from '../types';

type GetTrendingGlobalItems = (
base: BaseRecommendClient
) => WithRecommendMethods<BaseRecommendClient>['getTrendingGlobalItems'];

export const getTrendingGlobalItems: GetTrendingGlobalItems = base => {
return (queries: readonly TrendingGlobalItemsQuery[], requestOptions) => {
const requests: readonly TrendingGlobalItemsQuery[] = 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
);
};
};
17 changes: 17 additions & 0 deletions packages/recommend/src/methods/getTrendingItemsForFacet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BaseRecommendClient, TrendingItemsForFacetQuery, WithRecommendMethods } from '../types';
import { getTrendingGlobalItems } from '.';

type GetTrendingItemsForFacet = (
base: BaseRecommendClient
) => WithRecommendMethods<BaseRecommendClient>['getTrendingItemsForFacet'];

export const getTrendingItemsForFacet: GetTrendingItemsForFacet = base => {
return (queries: readonly TrendingItemsForFacetQuery[], requestOptions) => {
return getTrendingGlobalItems(base)(
queries.map(query => ({
...query,
})),
requestOptions
);
};
};
3 changes: 3 additions & 0 deletions packages/recommend/src/methods/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
export * from './getFrequentlyBoughtTogether';
export * from './getRecommendations';
export * from './getRelatedProducts';
export * from './getTrendingFacets';
export * from './getTrendingGlobalItems';
export * from './getTrendingItemsForFacet';
1 change: 1 addition & 0 deletions packages/recommend/src/types/RecommendModel.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export type RecommendModel = 'related-products' | 'bought-together';
export type TrendingModel = 'trending-items' | 'trending-facets';
3 changes: 3 additions & 0 deletions packages/recommend/src/types/TrendingFacetsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { TrendingQuery } from './TrendingQuery';

export type TrendingFacetsQuery = Omit<TrendingQuery, 'model' | 'facetValue'>;
3 changes: 3 additions & 0 deletions packages/recommend/src/types/TrendingGlobalItemsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { TrendingQuery } from './TrendingQuery';

export type TrendingGlobalItemsQuery = Omit<TrendingQuery, 'model' | 'facetName' | 'facetValue'>;
3 changes: 3 additions & 0 deletions packages/recommend/src/types/TrendingItemsForFacetQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { TrendingQuery } from './TrendingQuery';

export type TrendingItemsForFacetQuery = Omit<TrendingQuery, 'model'>;
46 changes: 46 additions & 0 deletions packages/recommend/src/types/TrendingQuery.ts
Original file line number Diff line number Diff line change
@@ -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;
};
27 changes: 27 additions & 0 deletions packages/recommend/src/types/WithRecommendMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { RequestOptions } from '@algolia/transporter';
import { FrequentlyBoughtTogetherQuery } from './FrequentlyBoughtTogetherQuery';
import { RecommendationsQuery } from './RecommendationsQuery';
import { RelatedProductsQuery } from './RelatedProductsQuery';
import { TrendingFacetsQuery } from './TrendingFacetsQuery';
import { TrendingGlobalItemsQuery } from './TrendingGlobalItemsQuery';
import { TrendingItemsForFacetQuery } from './TrendingItemsForFacetQuery';

export type WithRecommendMethods<TType> = TType & {
/**
Expand All @@ -29,4 +32,28 @@ export type WithRecommendMethods<TType> = TType & {
queries: readonly FrequentlyBoughtTogetherQuery[],
requestOptions?: RequestOptions & SearchOptions
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;

/**
* Returns trending items
*/
readonly getTrendingGlobalItems: <TObject>(
queries: readonly TrendingGlobalItemsQuery[],
requestOptions?: RequestOptions & SearchOptions
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;

/**
* Returns trending items per facet
*/
readonly getTrendingItemsForFacet: <TObject>(
queries: readonly TrendingItemsForFacetQuery[],
requestOptions?: RequestOptions & SearchOptions
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;

/**
* Returns trending items per facet
*/
readonly getTrendingFacets: <TObject>(
queries: readonly TrendingFacetsQuery[],
requestOptions?: RequestOptions & SearchOptions
) => Readonly<Promise<MultipleQueriesResponse<TObject>>>;
};
Loading