Skip to content

Commit 3aaf8b9

Browse files
authored
Merge pull request #86 from Relewise/feat/highlighting
Feat: Add support for search highlighting
2 parents 5ea6dea + 0da21e6 commit 3aaf8b9

File tree

12 files changed

+312
-6
lines changed

12 files changed

+312
-6
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ For more information about how to use the SDK via CDN - go to our [docs site](ht
206206

207207
## Running integration tests
208208

209-
You can read about running the integration tests [here](/lib/dev.guide.md#testing).
209+
You can read about running the integration tests [here](/packages/client/dev.guide.md#testing).
210210

211211
## Contributing
212212

packages/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@relewise/client",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"description": "Relewise is a next generation personalization SaaS-platform, which offers functionality within product- and content recommendations and personalized search. This official SDK helps you interact with our API.",
55
"repository": {
66
"type": "git",
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ContentHighlightProps, ContentSearchSettingsHighlightSettings, HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits, HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape } from '../../models/data-contracts';
2+
3+
export class ContentHighlightingBuilder {
4+
private enabledState: boolean = true;
5+
private highlightable: ContentHighlightProps = {
6+
$type: 'Relewise.Client.Requests.Shared.Highlighting.ContentHighlightProps, Relewise.Client',
7+
displayName: false
8+
};
9+
private limit: HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits = {};
10+
private shape: HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape = {
11+
includeOffsets: false
12+
};
13+
14+
public enabled(enabled: boolean): this {
15+
this.enabledState = enabled;
16+
17+
return this;
18+
}
19+
20+
public setHighlightable(highlightable: { displayName?: boolean, dataKeys?: string[] | null }): this {
21+
this.highlightable.displayName = highlightable.displayName ?? false;
22+
this.highlightable.dataKeys = highlightable.dataKeys;
23+
24+
return this;
25+
}
26+
27+
public setLimit(limit: { maxEntryLimit?: number | null; maxSnippetsPerEntry?: number | null; maxSnippetsPerField?: number | null; }): this {
28+
this.limit.maxEntryLimit = limit.maxEntryLimit;
29+
this.limit.maxSnippetsPerEntry = limit.maxSnippetsPerEntry;
30+
this.limit.maxSnippetsPerField = limit.maxSnippetsPerField;
31+
32+
return this;
33+
}
34+
35+
public setShape(shape: { includeOffsets: boolean }): this {
36+
this.shape.includeOffsets = shape.includeOffsets;
37+
38+
return this;
39+
}
40+
41+
public build(): ContentSearchSettingsHighlightSettings {
42+
return {
43+
$type: 'Relewise.Client.Requests.Search.Settings.ContentSearchSettings+HighlightSettings, Relewise.Client',
44+
enabled: this.enabledState,
45+
highlightable: this.highlightable,
46+
limit: this.limit,
47+
shape: this.shape
48+
};
49+
}
50+
}

packages/client/src/builders/search/contentSearchBuilder.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ContentSearchRequest, ContentSearchSettings, RecommendationSettings, SelectedContentPropertiesSettings } from '../../models/data-contracts';
22
import { PaginationBuilder } from '../paginationBuilder';
33
import { Settings } from '../settings';
4+
import { ContentHighlightingBuilder } from './contentHighlightingBuilder';
45
import { ContentSortingBuilder } from './contentSortingBuilder';
56
import { FacetBuilder } from './facetBuilder';
67
import { SearchBuilder } from './searchBuilder';
@@ -11,6 +12,7 @@ export class ContentSearchBuilder extends SearchRequestBuilder implements Search
1112
private paginationBuilder: PaginationBuilder = new PaginationBuilder();
1213
private sortingBuilder: ContentSortingBuilder = new ContentSortingBuilder();
1314
private term: string | null | undefined;
15+
private highlightingBuilder = new ContentHighlightingBuilder();
1416

1517
private searchSettings: ContentSearchSettings = {
1618
$type: 'Relewise.Client.Requests.Search.Settings.ContentSearchSettings, Relewise.Client',
@@ -57,6 +59,14 @@ export class ContentSearchBuilder extends SearchRequestBuilder implements Search
5759
return this;
5860
}
5961

62+
public highlighting(highlightingBuilder: (highlightingBuilder: ContentHighlightingBuilder) => void): this {
63+
highlightingBuilder(this.highlightingBuilder);
64+
65+
this.searchSettings.highlight = this.highlightingBuilder.build();
66+
67+
return this;
68+
}
69+
6070
public build(): ContentSearchRequest {
6171
const { take, skip } = this.paginationBuilder.build();
6272
return {

packages/client/src/builders/search/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ export * from './dataObjectValueSelectorBuilder';
1414
export * from './getProductFacet';
1515
export * from './getContentFacet';
1616
export * from './getProductCategoryFacet';
17-
export * from './searchConstraintBuilder';
17+
export * from './searchConstraintBuilder';
18+
export * from './productHighlightingBuilder';
19+
export * from './contentHighlightingBuilder';
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits, HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape, ProductHighlightProps, ProductSearchSettingsHighlightSettings } from '../../models/data-contracts';
2+
3+
export class ProductHighlightingBuilder {
4+
private enabledState: boolean = true;
5+
private highlightable: ProductHighlightProps = {
6+
$type: 'Relewise.Client.Requests.Shared.Highlighting.ProductHighlightProps, Relewise.Client',
7+
displayName: false
8+
};
9+
private limit: HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits = {};
10+
private shape: HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape = {
11+
includeOffsets: false
12+
};
13+
14+
public enabled(enabled: boolean): this {
15+
this.enabledState = enabled;
16+
17+
return this;
18+
}
19+
20+
public setHighlightable(highlightable: { displayName?: boolean, dataKeys?: string[] | null }): this {
21+
this.highlightable.displayName = highlightable.displayName ?? false;
22+
this.highlightable.dataKeys = highlightable.dataKeys;
23+
24+
return this;
25+
}
26+
27+
public setLimit(limit: { maxEntryLimit?: number | null; maxSnippetsPerEntry?: number | null; maxSnippetsPerField?: number | null; }): this {
28+
this.limit.maxEntryLimit = limit.maxEntryLimit;
29+
this.limit.maxSnippetsPerEntry = limit.maxSnippetsPerEntry;
30+
this.limit.maxSnippetsPerField = limit.maxSnippetsPerField;
31+
32+
return this;
33+
}
34+
35+
public setShape(shape: { includeOffsets: boolean }): this {
36+
this.shape.includeOffsets = shape.includeOffsets;
37+
38+
return this;
39+
}
40+
41+
public build(): ProductSearchSettingsHighlightSettings {
42+
return {
43+
$type: 'Relewise.Client.Requests.Search.Settings.ProductSearchSettings+HighlightSettings, Relewise.Client',
44+
enabled: this.enabledState,
45+
highlightable: this.highlightable,
46+
limit: this.limit,
47+
shape: this.shape
48+
};
49+
}
50+
}

packages/client/src/builders/search/productSearchBuilder.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ProductSearchRequest, ProductSearchSettings, RecommendationSettings, Re
22
import { PaginationBuilder } from '../paginationBuilder';
33
import { Settings } from '../settings';
44
import { FacetBuilder } from './facetBuilder';
5+
import { ProductHighlightingBuilder } from './productHighlightingBuilder';
56
import { ProductSortingBuilder } from './productSortingBuilder';
67
import { SearchBuilder } from './searchBuilder';
78
import { SearchConstraintBuilder } from './searchConstraintBuilder';
@@ -14,6 +15,7 @@ export class ProductSearchBuilder extends SearchRequestBuilder implements Search
1415
private sortingBuilder: ProductSortingBuilder = new ProductSortingBuilder();
1516
private searchConstraintBuilder: SearchConstraintBuilder = new SearchConstraintBuilder();
1617
private term: string | null | undefined;
18+
private highlightingBuilder = new ProductHighlightingBuilder();
1719

1820
private searchSettings: ProductSearchSettings = {
1921
$type: 'Relewise.Client.Requests.Search.Settings.ProductSearchSettings, Relewise.Client',
@@ -114,16 +116,22 @@ export class ProductSearchBuilder extends SearchRequestBuilder implements Search
114116
return this;
115117
}
116118

119+
public highlighting(highlightingBuilder: (highlightingBuilder: ProductHighlightingBuilder) => void): this {
120+
highlightingBuilder(this.highlightingBuilder);
121+
122+
this.searchSettings.highlight = this.highlightingBuilder.build();
123+
124+
return this;
125+
}
126+
117127
public build(): ProductSearchRequest {
118128
const { take, skip } = this.paginationBuilder.build();
119129
return {
120130
$type: 'Relewise.Client.Requests.Search.ProductSearchRequest, Relewise.Client',
121131
...this.baseBuild(),
122132
take,
123133
skip,
124-
125134
term: this.term,
126-
127135
facets: this.facetBuilder.build(),
128136
settings: this.searchSettings,
129137
sorting: this.sortingBuilder.build(),

packages/client/src/models/data-contracts.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,14 @@ export type ContentCategoryView = Trackable & {
11301130
channel?: Channel | null;
11311131
};
11321132

1133+
export interface ContentContentHighlightPropsHighlightSettings {
1134+
$type: string;
1135+
enabled: boolean;
1136+
limit: HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits;
1137+
highlightable: ContentHighlightProps;
1138+
shape: HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape;
1139+
}
1140+
11331141
export type ContentDataBooleanValueFacet = BooleanContentDataValueFacet;
11341142

11351143
export type ContentDataBooleanValueFacetResult = BooleanContentDataValueFacetResult;
@@ -1265,6 +1273,14 @@ export interface ContentFacetResult {
12651273

12661274
export type ContentHasCategoriesFilter = Filter;
12671275

1276+
export interface ContentHighlightProperties {
1277+
$type: string;
1278+
displayName: boolean;
1279+
dataKeys?: string[] | null;
1280+
}
1281+
1282+
export type ContentHighlightProps = ContentHighlightProperties;
1283+
12681284
export type ContentIdFilter = Filter & {
12691285
contentIds: string[];
12701286
};
@@ -1359,6 +1375,7 @@ export interface ContentResult {
13591375
categoryPaths?: CategoryPathResult[] | null;
13601376
viewedByUser?: ViewedByUserInfo | null;
13611377
custom?: Record<string, string | null>;
1378+
highlight?: HighlightResult | null;
13621379
}
13631380

13641381
export interface ContentResultDetails {
@@ -1397,8 +1414,11 @@ export type ContentSearchResponse = PaginatedSearchResponse & {
13971414
export type ContentSearchSettings = SearchSettings & {
13981415
selectedContentProperties?: SelectedContentPropertiesSettings | null;
13991416
recommendations: RecommendationSettings;
1417+
highlight?: ContentSearchSettingsHighlightSettings | null;
14001418
};
14011419

1420+
export type ContentSearchSettingsHighlightSettings = ContentContentHighlightPropsHighlightSettings;
1421+
14021422
export interface ContentSortBySpecification {
14031423
value?: ContentAttributeSorting | ContentDataSorting | ContentPopularitySorting | ContentRelevanceSorting | null;
14041424
}
@@ -2323,6 +2343,41 @@ export type HasRecentlyReceivedTriggerCondition = UserCondition & {
23232343

23242344
export type HasValueCondition = ValueCondition;
23252345

2346+
export interface HighlightResult {
2347+
offsets?: HighlightResultOffset | null;
2348+
}
2349+
2350+
export interface HighlightResultOffset {
2351+
displayName: Int32Range[];
2352+
data: StringRange1ArrayKeyValuePair[];
2353+
}
2354+
2355+
export interface HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits {
2356+
/** @format int32 */
2357+
maxEntryLimit?: number | null;
2358+
/** @format int32 */
2359+
maxSnippetsPerEntry?: number | null;
2360+
/** @format int32 */
2361+
maxSnippetsPerField?: number | null;
2362+
}
2363+
2364+
export interface HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape {
2365+
includeOffsets: boolean;
2366+
}
2367+
2368+
export interface HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits {
2369+
/** @format int32 */
2370+
maxEntryLimit?: number | null;
2371+
/** @format int32 */
2372+
maxSnippetsPerEntry?: number | null;
2373+
/** @format int32 */
2374+
maxSnippetsPerField?: number | null;
2375+
}
2376+
2377+
export interface HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape {
2378+
includeOffsets: boolean;
2379+
}
2380+
23262381
export type HtmlParser = Parser;
23272382

23282383
export type IChange = object;
@@ -2398,6 +2453,13 @@ export interface Int32ProductDataValueFacetResult {
23982453
field: "Category" | "Assortment" | "ListPrice" | "SalesPrice" | "Brand" | "Data" | "VariantSpecification" | "User";
23992454
}
24002455

2456+
export interface Int32Range {
2457+
/** @format int32 */
2458+
lowerBoundInclusive: number;
2459+
/** @format int32 */
2460+
upperBoundInclusive: number;
2461+
}
2462+
24012463
export interface KeyMultiplier {
24022464
key?: string | null;
24032465
/** @format double */
@@ -3652,6 +3714,14 @@ export type ProductHasVariantsFilter = Filter & {
36523714
numberOfVariants: Int32NullableRange;
36533715
};
36543716

3717+
export interface ProductHighlightProperties {
3718+
$type: string;
3719+
displayName: boolean;
3720+
dataKeys?: string[] | null;
3721+
}
3722+
3723+
export type ProductHighlightProps = ProductHighlightProperties;
3724+
36553725
export type ProductIdFilter = Filter & {
36563726
productIds: string[];
36573727
};
@@ -3820,6 +3890,14 @@ export interface ProductPerformanceResultViewsMetrics {
38203890

38213891
export type ProductPopularitySorting = ProductSorting;
38223892

3893+
export interface ProductProductHighlightPropsHighlightSettings {
3894+
$type: string;
3895+
enabled: boolean;
3896+
limit: HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits;
3897+
highlightable: ProductHighlightProps;
3898+
shape: HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape;
3899+
}
3900+
38233901
export type ProductPromotion = Promotion & {
38243902
filters?: FilterCollection | null;
38253903
};
@@ -4070,6 +4148,7 @@ export interface ProductResult {
40704148
purchasedByUserCompany?: PurchasedByUserCompanyInfo | null;
40714149
viewedByUserCompany?: ViewedByUserCompanyInfo | null;
40724150
filteredVariants?: VariantResult[] | null;
4151+
highlight?: HighlightResult | null;
40734152
}
40744153

40754154
export interface ProductResultDetails {
@@ -4149,8 +4228,11 @@ export type ProductSearchSettings = SearchSettings & {
41494228
selectedBrandProperties?: SelectedBrandPropertiesSettings | null;
41504229
variantSettings?: VariantSearchSettings | null;
41514230
resultConstraint?: ResultMustHaveVariantConstraint | null;
4231+
highlight?: ProductSearchSettingsHighlightSettings | null;
41524232
};
41534233

4234+
export type ProductSearchSettingsHighlightSettings = ProductProductHighlightPropsHighlightSettings;
4235+
41544236
export interface ProductSortBySpecification {
41554237
value?:
41564238
| ProductAttributeSorting
@@ -5206,6 +5288,11 @@ export interface StringProductDataValueFacetResult {
52065288
field: "Category" | "Assortment" | "ListPrice" | "SalesPrice" | "Brand" | "Data" | "VariantSpecification" | "User";
52075289
}
52085290

5291+
export interface StringRange1ArrayKeyValuePair {
5292+
key: string;
5293+
value: Int32Range[];
5294+
}
5295+
52095296
export interface StringStringKeyValuePair {
52105297
key: string;
52115298
value: string;

packages/client/tests/integration-tests/contentSearch.integration.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,19 @@ test('Facet result', async() => {
3535
}
3636

3737
expect(result?.hits).toBeGreaterThan(0);
38-
});
38+
});
39+
40+
test('Highlighting', async() => {
41+
const request: ContentSearchRequest = baseContentBuilder()
42+
.setTerm('highlighted')
43+
.highlighting(h => {
44+
h.setHighlightable({ dataKeys: ['Description'] })
45+
// You have to specify to include the offset.
46+
// Currently offset is the only way to get a result, so if not set, you won't get a result.
47+
h.setShape({ includeOffsets: true })
48+
}).build();
49+
50+
const result = await searcher.searchContents(request);
51+
52+
expect(result?.results![0].highlight?.offsets?.data[0].value.length).toBeGreaterThan(0);
53+
})

0 commit comments

Comments
 (0)