Skip to content

Commit

Permalink
Merge pull request #86 from Relewise/feat/highlighting
Browse files Browse the repository at this point in the history
Feat: Add support for search highlighting
  • Loading branch information
SWH-Relewise authored Dec 5, 2024
2 parents 5ea6dea + 0da21e6 commit 3aaf8b9
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ For more information about how to use the SDK via CDN - go to our [docs site](ht

## Running integration tests

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

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@relewise/client",
"version": "2.0.0",
"version": "2.1.0",
"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.",
"repository": {
"type": "git",
Expand Down
50 changes: 50 additions & 0 deletions packages/client/src/builders/search/contentHighlightingBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ContentHighlightProps, ContentSearchSettingsHighlightSettings, HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits, HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape } from '../../models/data-contracts';

export class ContentHighlightingBuilder {
private enabledState: boolean = true;
private highlightable: ContentHighlightProps = {
$type: 'Relewise.Client.Requests.Shared.Highlighting.ContentHighlightProps, Relewise.Client',
displayName: false
};
private limit: HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits = {};
private shape: HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape = {
includeOffsets: false
};

public enabled(enabled: boolean): this {
this.enabledState = enabled;

return this;
}

public setHighlightable(highlightable: { displayName?: boolean, dataKeys?: string[] | null }): this {
this.highlightable.displayName = highlightable.displayName ?? false;
this.highlightable.dataKeys = highlightable.dataKeys;

return this;
}

public setLimit(limit: { maxEntryLimit?: number | null; maxSnippetsPerEntry?: number | null; maxSnippetsPerField?: number | null; }): this {
this.limit.maxEntryLimit = limit.maxEntryLimit;
this.limit.maxSnippetsPerEntry = limit.maxSnippetsPerEntry;
this.limit.maxSnippetsPerField = limit.maxSnippetsPerField;

return this;
}

public setShape(shape: { includeOffsets: boolean }): this {
this.shape.includeOffsets = shape.includeOffsets;

return this;
}

public build(): ContentSearchSettingsHighlightSettings {
return {
$type: 'Relewise.Client.Requests.Search.Settings.ContentSearchSettings+HighlightSettings, Relewise.Client',
enabled: this.enabledState,
highlightable: this.highlightable,
limit: this.limit,
shape: this.shape
};
}
}
10 changes: 10 additions & 0 deletions packages/client/src/builders/search/contentSearchBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ContentSearchRequest, ContentSearchSettings, RecommendationSettings, SelectedContentPropertiesSettings } from '../../models/data-contracts';
import { PaginationBuilder } from '../paginationBuilder';
import { Settings } from '../settings';
import { ContentHighlightingBuilder } from './contentHighlightingBuilder';
import { ContentSortingBuilder } from './contentSortingBuilder';
import { FacetBuilder } from './facetBuilder';
import { SearchBuilder } from './searchBuilder';
Expand All @@ -11,6 +12,7 @@ export class ContentSearchBuilder extends SearchRequestBuilder implements Search
private paginationBuilder: PaginationBuilder = new PaginationBuilder();
private sortingBuilder: ContentSortingBuilder = new ContentSortingBuilder();
private term: string | null | undefined;
private highlightingBuilder = new ContentHighlightingBuilder();

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

public highlighting(highlightingBuilder: (highlightingBuilder: ContentHighlightingBuilder) => void): this {
highlightingBuilder(this.highlightingBuilder);

this.searchSettings.highlight = this.highlightingBuilder.build();

return this;
}

public build(): ContentSearchRequest {
const { take, skip } = this.paginationBuilder.build();
return {
Expand Down
4 changes: 3 additions & 1 deletion packages/client/src/builders/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ export * from './dataObjectValueSelectorBuilder';
export * from './getProductFacet';
export * from './getContentFacet';
export * from './getProductCategoryFacet';
export * from './searchConstraintBuilder';
export * from './searchConstraintBuilder';
export * from './productHighlightingBuilder';
export * from './contentHighlightingBuilder';
50 changes: 50 additions & 0 deletions packages/client/src/builders/search/productHighlightingBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits, HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape, ProductHighlightProps, ProductSearchSettingsHighlightSettings } from '../../models/data-contracts';

export class ProductHighlightingBuilder {
private enabledState: boolean = true;
private highlightable: ProductHighlightProps = {
$type: 'Relewise.Client.Requests.Shared.Highlighting.ProductHighlightProps, Relewise.Client',
displayName: false
};
private limit: HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits = {};
private shape: HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape = {
includeOffsets: false
};

public enabled(enabled: boolean): this {
this.enabledState = enabled;

return this;
}

public setHighlightable(highlightable: { displayName?: boolean, dataKeys?: string[] | null }): this {
this.highlightable.displayName = highlightable.displayName ?? false;
this.highlightable.dataKeys = highlightable.dataKeys;

return this;
}

public setLimit(limit: { maxEntryLimit?: number | null; maxSnippetsPerEntry?: number | null; maxSnippetsPerField?: number | null; }): this {
this.limit.maxEntryLimit = limit.maxEntryLimit;
this.limit.maxSnippetsPerEntry = limit.maxSnippetsPerEntry;
this.limit.maxSnippetsPerField = limit.maxSnippetsPerField;

return this;
}

public setShape(shape: { includeOffsets: boolean }): this {
this.shape.includeOffsets = shape.includeOffsets;

return this;
}

public build(): ProductSearchSettingsHighlightSettings {
return {
$type: 'Relewise.Client.Requests.Search.Settings.ProductSearchSettings+HighlightSettings, Relewise.Client',
enabled: this.enabledState,
highlightable: this.highlightable,
limit: this.limit,
shape: this.shape
};
}
}
12 changes: 10 additions & 2 deletions packages/client/src/builders/search/productSearchBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ProductSearchRequest, ProductSearchSettings, RecommendationSettings, Re
import { PaginationBuilder } from '../paginationBuilder';
import { Settings } from '../settings';
import { FacetBuilder } from './facetBuilder';
import { ProductHighlightingBuilder } from './productHighlightingBuilder';
import { ProductSortingBuilder } from './productSortingBuilder';
import { SearchBuilder } from './searchBuilder';
import { SearchConstraintBuilder } from './searchConstraintBuilder';
Expand All @@ -14,6 +15,7 @@ export class ProductSearchBuilder extends SearchRequestBuilder implements Search
private sortingBuilder: ProductSortingBuilder = new ProductSortingBuilder();
private searchConstraintBuilder: SearchConstraintBuilder = new SearchConstraintBuilder();
private term: string | null | undefined;
private highlightingBuilder = new ProductHighlightingBuilder();

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

public highlighting(highlightingBuilder: (highlightingBuilder: ProductHighlightingBuilder) => void): this {
highlightingBuilder(this.highlightingBuilder);

this.searchSettings.highlight = this.highlightingBuilder.build();

return this;
}

public build(): ProductSearchRequest {
const { take, skip } = this.paginationBuilder.build();
return {
$type: 'Relewise.Client.Requests.Search.ProductSearchRequest, Relewise.Client',
...this.baseBuild(),
take,
skip,

term: this.term,

facets: this.facetBuilder.build(),
settings: this.searchSettings,
sorting: this.sortingBuilder.build(),
Expand Down
87 changes: 87 additions & 0 deletions packages/client/src/models/data-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,14 @@ export type ContentCategoryView = Trackable & {
channel?: Channel | null;
};

export interface ContentContentHighlightPropsHighlightSettings {
$type: string;
enabled: boolean;
limit: HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits;
highlightable: ContentHighlightProps;
shape: HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape;
}

export type ContentDataBooleanValueFacet = BooleanContentDataValueFacet;

export type ContentDataBooleanValueFacetResult = BooleanContentDataValueFacetResult;
Expand Down Expand Up @@ -1265,6 +1273,14 @@ export interface ContentFacetResult {

export type ContentHasCategoriesFilter = Filter;

export interface ContentHighlightProperties {
$type: string;
displayName: boolean;
dataKeys?: string[] | null;
}

export type ContentHighlightProps = ContentHighlightProperties;

export type ContentIdFilter = Filter & {
contentIds: string[];
};
Expand Down Expand Up @@ -1359,6 +1375,7 @@ export interface ContentResult {
categoryPaths?: CategoryPathResult[] | null;
viewedByUser?: ViewedByUserInfo | null;
custom?: Record<string, string | null>;
highlight?: HighlightResult | null;
}

export interface ContentResultDetails {
Expand Down Expand Up @@ -1397,8 +1414,11 @@ export type ContentSearchResponse = PaginatedSearchResponse & {
export type ContentSearchSettings = SearchSettings & {
selectedContentProperties?: SelectedContentPropertiesSettings | null;
recommendations: RecommendationSettings;
highlight?: ContentSearchSettingsHighlightSettings | null;
};

export type ContentSearchSettingsHighlightSettings = ContentContentHighlightPropsHighlightSettings;

export interface ContentSortBySpecification {
value?: ContentAttributeSorting | ContentDataSorting | ContentPopularitySorting | ContentRelevanceSorting | null;
}
Expand Down Expand Up @@ -2323,6 +2343,41 @@ export type HasRecentlyReceivedTriggerCondition = UserCondition & {

export type HasValueCondition = ValueCondition;

export interface HighlightResult {
offsets?: HighlightResultOffset | null;
}

export interface HighlightResultOffset {
displayName: Int32Range[];
data: StringRange1ArrayKeyValuePair[];
}

export interface HighlightSettings2ContentContentHighlightPropsHighlightSettings2Limits {
/** @format int32 */
maxEntryLimit?: number | null;
/** @format int32 */
maxSnippetsPerEntry?: number | null;
/** @format int32 */
maxSnippetsPerField?: number | null;
}

export interface HighlightSettings2ContentContentHighlightPropsHighlightSettings2ResponseShape {
includeOffsets: boolean;
}

export interface HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits {
/** @format int32 */
maxEntryLimit?: number | null;
/** @format int32 */
maxSnippetsPerEntry?: number | null;
/** @format int32 */
maxSnippetsPerField?: number | null;
}

export interface HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape {
includeOffsets: boolean;
}

export type HtmlParser = Parser;

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

export interface Int32Range {
/** @format int32 */
lowerBoundInclusive: number;
/** @format int32 */
upperBoundInclusive: number;
}

export interface KeyMultiplier {
key?: string | null;
/** @format double */
Expand Down Expand Up @@ -3652,6 +3714,14 @@ export type ProductHasVariantsFilter = Filter & {
numberOfVariants: Int32NullableRange;
};

export interface ProductHighlightProperties {
$type: string;
displayName: boolean;
dataKeys?: string[] | null;
}

export type ProductHighlightProps = ProductHighlightProperties;

export type ProductIdFilter = Filter & {
productIds: string[];
};
Expand Down Expand Up @@ -3820,6 +3890,14 @@ export interface ProductPerformanceResultViewsMetrics {

export type ProductPopularitySorting = ProductSorting;

export interface ProductProductHighlightPropsHighlightSettings {
$type: string;
enabled: boolean;
limit: HighlightSettings2ProductProductHighlightPropsHighlightSettings2Limits;
highlightable: ProductHighlightProps;
shape: HighlightSettings2ProductProductHighlightPropsHighlightSettings2ResponseShape;
}

export type ProductPromotion = Promotion & {
filters?: FilterCollection | null;
};
Expand Down Expand Up @@ -4070,6 +4148,7 @@ export interface ProductResult {
purchasedByUserCompany?: PurchasedByUserCompanyInfo | null;
viewedByUserCompany?: ViewedByUserCompanyInfo | null;
filteredVariants?: VariantResult[] | null;
highlight?: HighlightResult | null;
}

export interface ProductResultDetails {
Expand Down Expand Up @@ -4149,8 +4228,11 @@ export type ProductSearchSettings = SearchSettings & {
selectedBrandProperties?: SelectedBrandPropertiesSettings | null;
variantSettings?: VariantSearchSettings | null;
resultConstraint?: ResultMustHaveVariantConstraint | null;
highlight?: ProductSearchSettingsHighlightSettings | null;
};

export type ProductSearchSettingsHighlightSettings = ProductProductHighlightPropsHighlightSettings;

export interface ProductSortBySpecification {
value?:
| ProductAttributeSorting
Expand Down Expand Up @@ -5206,6 +5288,11 @@ export interface StringProductDataValueFacetResult {
field: "Category" | "Assortment" | "ListPrice" | "SalesPrice" | "Brand" | "Data" | "VariantSpecification" | "User";
}

export interface StringRange1ArrayKeyValuePair {
key: string;
value: Int32Range[];
}

export interface StringStringKeyValuePair {
key: string;
value: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,19 @@ test('Facet result', async() => {
}

expect(result?.hits).toBeGreaterThan(0);
});
});

test('Highlighting', async() => {
const request: ContentSearchRequest = baseContentBuilder()
.setTerm('highlighted')
.highlighting(h => {
h.setHighlightable({ dataKeys: ['Description'] })
// You have to specify to include the offset.
// Currently offset is the only way to get a result, so if not set, you won't get a result.
h.setShape({ includeOffsets: true })
}).build();

const result = await searcher.searchContents(request);

expect(result?.results![0].highlight?.offsets?.data[0].value.length).toBeGreaterThan(0);
})
Loading

0 comments on commit 3aaf8b9

Please sign in to comment.