From 24cdd0fd37fb1b326fd1c9965785160780113481 Mon Sep 17 00:00:00 2001 From: Danny Gauthier Date: Mon, 19 Aug 2024 11:20:32 -0400 Subject: [PATCH] fix(answerApi): search context trigger new request --- .../src/api/knowledge/stream-answer-api.ts | 85 +- .../tests/stream-answer-api-state-mock.ts | 1396 +++++++++++++++++ .../knowledge/tests/stream-answer-api.test.ts | 37 + .../features/search/legacy/search-request.ts | 55 +- .../src/features/search/search-mappings.ts | 4 +- packages/headless/src/utils/facet-utils.ts | 60 + 6 files changed, 1570 insertions(+), 67 deletions(-) create mode 100644 packages/headless/src/api/knowledge/tests/stream-answer-api-state-mock.ts create mode 100644 packages/headless/src/api/knowledge/tests/stream-answer-api.test.ts diff --git a/packages/headless/src/api/knowledge/stream-answer-api.ts b/packages/headless/src/api/knowledge/stream-answer-api.ts index 0fc50000021..6e63e42a321 100644 --- a/packages/headless/src/api/knowledge/stream-answer-api.ts +++ b/packages/headless/src/api/knowledge/stream-answer-api.ts @@ -8,16 +8,20 @@ import { GeneratedAnswerStyle, GeneratedContentFormat, } from '../../features/generated-answer/generated-response-format'; +import {maximumNumberOfResultsFromIndex} from '../../features/pagination/pagination-constants'; import {selectPipeline} from '../../features/pipeline/select-pipeline'; import {selectQuery} from '../../features/query/query-selectors'; import {selectSearchHub} from '../../features/search-hub/search-hub-selectors'; +import { + initialSearchMappings, + mapFacetRequest, +} from '../../features/search/search-mappings'; +import {SearchAppState} from '../../state/search-app-state'; import { ConfigurationSection, - DebugSection, GeneratedAnswerSection, - QuerySection, - SearchSection, } from '../../state/state-sections'; +import {getFacets} from '../../utils/facet-utils'; import {GeneratedAnswerCitation} from '../generated-answer/generated-answer-event-payload'; import {SearchRequest} from '../search/search/search-request'; import {answerSlice} from './answer-slice'; @@ -27,9 +31,7 @@ export type StateNeededByAnswerAPI = { pipeline: string; answer: ReturnType; } & ConfigurationSection & - QuerySection & - SearchSection & - DebugSection & + Partial & GeneratedAnswerSection; export interface GeneratedAnswerStream { @@ -226,12 +228,47 @@ export const selectAnswerTriggerParams = createSelector( (q, requestId) => ({q, requestId}) ); -const constructAnswerQueryParams = (state: StateNeededByAnswerAPI) => { +let generateFacetParams: Record> = {}; + +const getGeneratedFacetParams = (q: string, state: StateNeededByAnswerAPI) => ({ + ...generateFacetParams, + [q]: getFacets(state) + ?.map((facetRequest) => + mapFacetRequest(facetRequest, initialSearchMappings()) + ) + .sort((a, b) => + a.facetId > b.facetId ? 1 : b.facetId > a.facetId ? -1 : 0 + ), +}); + +const getNumberOfResultsWithinIndexLimit = (state: StateNeededByAnswerAPI) => { + if (!state.pagination) { + return undefined; + } + + const isOverIndexLimit = + state.pagination.firstResult + state.pagination.numberOfResults > + maximumNumberOfResultsFromIndex; + + if (isOverIndexLimit) { + return maximumNumberOfResultsFromIndex - state.pagination.firstResult; + } + return state.pagination.numberOfResults; +}; + +export const constructAnswerQueryParams = ( + state: StateNeededByAnswerAPI, + usage: 'fetch' | 'select' +) => { const q = selectQuery(state)?.q; const searchHub = selectSearchHub(state); const pipeline = selectPipeline(state); const citationsFieldToInclude = selectFieldsToIncludeInCitation(state) ?? []; + if (q && usage === 'fetch') { + generateFacetParams = getGeneratedFacetParams(q, state); + } + return { q, pipelineRuleParameters: { @@ -242,13 +279,39 @@ const constructAnswerQueryParams = (state: StateNeededByAnswerAPI) => { }, ...(searchHub?.length && {searchHub}), ...(pipeline?.length && {pipeline}), + ...(generateFacetParams[q!]?.length && { + facets: generateFacetParams[q!], + }), + ...(state.fields && {fieldsToInclude: state.fields.fieldsToInclude}), + ...(state.didYouMean && { + queryCorrection: { + enabled: + state.didYouMean.enableDidYouMean && + state.didYouMean.queryCorrectionMode === 'next', + options: { + automaticallyCorrect: state.didYouMean.automaticallyCorrectQuery + ? ('whenNoResults' as const) + : ('never' as const), + }, + }, + enableDidYouMean: + state.didYouMean.enableDidYouMean && + state.didYouMean.queryCorrectionMode === 'legacy', + }), + ...(state.pagination && { + numberOfResults: getNumberOfResultsWithinIndexLimit(state), + firstResult: state.pagination.firstResult, + }), + tab: state.configuration.analytics.originLevel2, }; }; export const fetchAnswer = (state: StateNeededByAnswerAPI) => - answerApi.endpoints.getAnswer.initiate(constructAnswerQueryParams(state)); + answerApi.endpoints.getAnswer.initiate( + constructAnswerQueryParams(state, 'fetch') + ); export const selectAnswer = (state: StateNeededByAnswerAPI) => - answerApi.endpoints.getAnswer.select(constructAnswerQueryParams(state))( - state - ); + answerApi.endpoints.getAnswer.select( + constructAnswerQueryParams(state, 'select') + )(state); diff --git a/packages/headless/src/api/knowledge/tests/stream-answer-api-state-mock.ts b/packages/headless/src/api/knowledge/tests/stream-answer-api-state-mock.ts new file mode 100644 index 00000000000..a20f27cd452 --- /dev/null +++ b/packages/headless/src/api/knowledge/tests/stream-answer-api-state-mock.ts @@ -0,0 +1,1396 @@ +/* eslint-disable @cspell/spellchecker */ +import {StateNeededByAnswerAPI} from '../stream-answer-api'; + +export const streamAnswerAPIStateMock: StateNeededByAnswerAPI = { + configuration: { + organizationId: 'lbergeronsfdevt1z2624x', + accessToken: 'x7d408c4b-5b56-40b0-9cc5-eb6e9bf7a7f8', + platformUrl: 'https://lbergeronsfdevt1z2624x.orgdev.coveo.com', + search: { + apiBaseUrl: + 'https://lbergeronsfdevt1z2624x.orgdev.coveo.com/rest/search/v2', + locale: 'en', + timezone: 'America/New_York', + authenticationProviders: [], + }, + analytics: { + enabled: false, + apiBaseUrl: 'https://lbergeronsfdevt1z2624x.analytics.orgdev.coveo.com', + nextApiBaseUrl: + 'https://lbergeronsfdevt1z2624x.analytics.orgdev.coveo.com/rest/organizations/lbergeronsfdevt1z2624x/events/v1', + originContext: 'Search', + originLevel2: 'default', + originLevel3: 'http://localhost:3333/examples/genqa.html', + anonymous: false, + deviceId: '', + userDisplayName: '', + documentLocation: + 'http://localhost:3333/examples/genqa.html#q=what%20is%20the%20hardest%20wood', + trackingId: '', + analyticsMode: 'legacy', + source: { + '@coveo/atomic': '2.77.0', + }, + }, + knowledge: { + answerConfigurationId: '', + }, + }, + version: '2.77.0', + debug: false, + pipeline: '', + searchHub: 'jstpierre2 test - Woods test', + search: { + response: { + results: [], + searchUid: '', + totalCountFiltered: 0, + facets: [], + generateAutomaticFacets: { + facets: [], + }, + queryCorrections: [], + triggers: [], + questionAnswer: { + answerSnippet: '', + documentId: { + contentIdKey: '', + contentIdValue: '', + }, + question: '', + relatedQuestions: [], + score: 0, + }, + pipeline: '', + splitTestRun: '', + termsToHighlight: {}, + phrasesToHighlight: {}, + extendedResults: {}, + }, + duration: 0, + queryExecuted: '', + error: null, + automaticallyCorrected: false, + isLoading: true, + results: [], + searchResponseId: '', + requestId: 'uyb6Ti2pkk5whKHzOiX58', + questionAnswer: { + answerSnippet: '', + documentId: { + contentIdKey: '', + contentIdValue: '', + }, + question: '', + relatedQuestions: [], + score: 0, + }, + extendedResults: {}, + }, + fields: { + fieldsToInclude: [ + 'author', + 'language', + 'urihash', + 'objecttype', + 'collection', + 'source', + 'permanentid', + 'date', + 'filetype', + 'parents', + 'ec_price', + 'ec_name', + 'ec_description', + 'ec_brand', + 'ec_category', + 'ec_item_group_id', + 'ec_shortdesc', + 'ec_thumbnails', + 'ec_images', + 'ec_promo_price', + 'ec_in_stock', + 'ec_rating', + 'snrating', + 'sncost', + ], + fetchAllFields: false, + fieldsDescription: [], + }, + facetOptions: { + freezeFacetOrder: false, + facets: { + ytviewcount_input_range: { + enabled: true, + }, + ytviewcount: { + enabled: false, + }, + ytviewcount_input: { + enabled: true, + }, + sncost: { + enabled: true, + }, + ytlikecount: { + enabled: false, + }, + geographicalhierarchy: { + enabled: true, + }, + filetype: { + enabled: true, + }, + author: { + enabled: true, + }, + source: { + enabled: true, + }, + year: { + enabled: true, + }, + snrating_range: { + enabled: true, + }, + snrating: { + enabled: true, + }, + date: { + enabled: true, + }, + date_input_range: { + enabled: true, + }, + date_input: { + enabled: true, + }, + }, + }, + pagination: { + firstResult: 0, + defaultNumberOfResults: 10, + numberOfResults: 10, + totalCountFiltered: 0, + }, + triggers: { + redirectTo: '', + query: '', + executions: [], + notifications: [], + queryModification: { + originalQuery: '', + newQuery: '', + queryToIgnore: '', + }, + }, + query: { + q: 'what is the hardest wood', + enableQuerySyntax: false, + }, + querySuggest: {}, + querySet: { + 'atomic-search-box-ie7ah': 'what is the hardest wood', + }, + facetSet: { + filetype: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 6, + sortCriteria: 'occurrences', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'filetype', + field: 'filetype', + }, + hasBreadcrumbs: true, + }, + author: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'author', + field: 'author', + }, + hasBreadcrumbs: true, + }, + source: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'source', + field: 'source', + }, + hasBreadcrumbs: true, + }, + year: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'year', + field: 'year', + }, + hasBreadcrumbs: true, + }, + }, + numericFacetSet: { + ytviewcount_input_range: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 1, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + generateAutomaticRanges: true, + facetId: 'ytviewcount_input_range', + field: 'ytviewcount', + }, + }, + ytviewcount: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'ytviewcount', + field: 'ytviewcount', + generateAutomaticRanges: true, + }, + }, + ytviewcount_input: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 0, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'ytviewcount_input', + field: 'ytviewcount', + generateAutomaticRanges: false, + }, + }, + sncost: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'sncost', + field: 'sncost', + generateAutomaticRanges: true, + }, + }, + ytlikecount: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 4, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: false, + state: 'idle', + start: 0, + end: 1000, + }, + { + endInclusive: false, + state: 'idle', + start: 1000, + end: 8000, + }, + { + endInclusive: false, + state: 'idle', + start: 8000, + end: 100000, + }, + { + endInclusive: false, + state: 'idle', + start: 100000, + end: 999999999, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'ytlikecount', + field: 'ytlikecount', + generateAutomaticRanges: false, + }, + }, + snrating_range: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 5, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: true, + state: 'idle', + start: 1, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 2, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 3, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 4, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 5, + end: 5, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'snrating_range', + field: 'snrating', + generateAutomaticRanges: false, + }, + }, + snrating: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 5, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: false, + state: 'idle', + start: 1, + end: 2, + }, + { + endInclusive: false, + state: 'idle', + start: 2, + end: 3, + }, + { + endInclusive: false, + state: 'idle', + start: 3, + end: 4, + }, + { + endInclusive: false, + state: 'idle', + start: 4, + end: 5, + }, + { + endInclusive: false, + state: 'idle', + start: 5, + end: 6, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'snrating', + field: 'snrating', + generateAutomaticRanges: false, + }, + }, + }, + dateFacetSet: { + date: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 7, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + start: 'past-1-hour', + end: 'now', + endInclusive: false, + state: 'idle', + }, + { + start: 'past-1-day', + end: 'now', + endInclusive: false, + state: 'idle', + }, + { + start: 'past-1-week', + end: 'now', + endInclusive: false, + state: 'idle', + }, + { + start: 'past-1-month', + end: 'now', + endInclusive: false, + state: 'idle', + }, + { + start: 'past-1-quarter', + end: 'now', + endInclusive: false, + state: 'idle', + }, + { + start: 'past-1-year', + end: 'now', + endInclusive: false, + state: 'idle', + }, + { + start: 'now', + end: 'next-10-year', + endInclusive: false, + state: 'idle', + }, + ], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date', + field: 'date', + generateAutomaticRanges: false, + }, + }, + date_input_range: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 1, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date_input_range', + generateAutomaticRanges: true, + field: 'date', + }, + }, + date_input: { + request: { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 0, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date_input', + field: 'date', + generateAutomaticRanges: false, + }, + }, + }, + categoryFacetSet: { + geographicalhierarchy: { + request: { + delimitingCharacter: ';', + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'occurrences', + basePath: [], + filterByBasePath: true, + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'hierarchical', + facetId: 'geographicalhierarchy', + field: 'geographicalhierarchy', + }, + initialNumberOfValues: 8, + }, + }, + tabSet: {}, + categoryFacetSearchSet: { + geographicalhierarchy: { + options: { + captions: {}, + numberOfValues: 8, + query: '', + }, + isLoading: false, + response: { + moreValuesAvailable: false, + values: [], + }, + initialNumberOfValues: 8, + requestId: '', + }, + }, + facetSearchSet: { + filetype: { + options: { + captions: {}, + numberOfValues: 6, + query: '', + }, + isLoading: false, + response: { + moreValuesAvailable: false, + values: [], + }, + initialNumberOfValues: 6, + requestId: '', + }, + author: { + options: { + captions: {}, + numberOfValues: 8, + query: '', + }, + isLoading: false, + response: { + moreValuesAvailable: false, + values: [], + }, + initialNumberOfValues: 8, + requestId: '', + }, + source: { + options: { + captions: {}, + numberOfValues: 8, + query: '', + }, + isLoading: false, + response: { + moreValuesAvailable: false, + values: [], + }, + initialNumberOfValues: 8, + requestId: '', + }, + year: { + options: { + captions: {}, + numberOfValues: 8, + query: '', + }, + isLoading: false, + response: { + moreValuesAvailable: false, + values: [], + }, + initialNumberOfValues: 8, + requestId: '', + }, + }, + history: { + past: [], + future: [], + }, + facetOrder: [], + sortCriteria: 'relevancy', + didYouMean: { + enableDidYouMean: true, + wasCorrectedTo: '', + wasAutomaticallyCorrected: false, + queryCorrection: { + correctedQuery: '', + wordCorrections: [], + }, + originalQuery: '', + automaticallyCorrectQuery: true, + queryCorrectionMode: 'legacy', + }, + answer: { + queries: { + 'getAnswer({"enableDidYouMean":true,"facets":[{"currentValues":[],"facetId":"author","field":"author","filterFacetCount":true,"freezeCurrentValues":false,"injectionDepth":1000,"isFieldExpanded":false,"numberOfValues":8,"preventAutoSelect":false,"resultsMustMatch":"atLeastOneValue","sortCriteria":"automatic","type":"specific"},{"currentValues":[{"end":"2024/08/16@14:55:59","endInclusive":false,"start":"2024/08/16@13:55:59","state":"idle"},{"end":"2024/08/16@14:55:59","endInclusive":false,"start":"2024/08/15@14:55:59","state":"idle"},{"end":"2024/08/16@14:55:59","endInclusive":false,"start":"2024/08/09@14:55:59","state":"idle"},{"end":"2024/08/16@14:55:59","endInclusive":false,"start":"2024/07/16@14:55:59","state":"idle"},{"end":"2024/08/16@14:55:59","endInclusive":false,"start":"2024/05/16@14:55:59","state":"idle"},{"end":"2024/08/16@14:55:59","endInclusive":false,"start":"2023/08/16@14:55:59","state":"idle"},{"end":"2034/08/16@14:55:59","endInclusive":false,"start":"2024/08/16@14:55:59","state":"idle"}],"facetId":"date","field":"date","filterFacetCount":true,"generateAutomaticRanges":false,"injectionDepth":1000,"numberOfValues":7,"preventAutoSelect":false,"rangeAlgorithm":"even","resultsMustMatch":"atLeastOneValue","sortCriteria":"descending","type":"dateRange"},{"currentValues":[],"facetId":"date_input","field":"date","filterFacetCount":true,"generateAutomaticRanges":false,"injectionDepth":1000,"numberOfValues":0,"preventAutoSelect":false,"rangeAlgorithm":"even","resultsMustMatch":"atLeastOneValue","sortCriteria":"ascending","type":"dateRange"},{"currentValues":[],"facetId":"date_input_range","field":"date","filterFacetCount":true,"generateAutomaticRanges":true,"injectionDepth":1000,"numberOfValues":1,"preventAutoSelect":false,"rangeAlgorithm":"even","resultsMustMatch":"atLeastOneValue","sortCriteria":"ascending","type":"dateRange"},{"currentValues":[],"facetId":"filetype","field":"filetype","filterFacetCount":true,"freezeCurrentValues":false,"injectionDepth":1000,"isFieldExpanded":false,"numberOfValues":6,"preventAutoSelect":false,"resultsMustMatch":"atLeastOneValue","sortCriteria":"occurrences","type":"specific"},{"basePath":[],"currentValues":[],"delimitingCharacter":";","facetId":"geographicalhierarchy","field":"geographicalhierarchy","filterByBasePath":true,"filterFacetCount":true,"injectionDepth":1000,"numberOfValues":8,"preventAutoSelect":false,"resultsMustMatch":"atLeastOneValue","sortCriteria":"occurrences","type":"hierarchical"},{"currentValues":[],"facetId":"sncost","field":"sncost","filterFacetCount":true,"generateAutomaticRanges":true,"injectionDepth":1000,"numberOfValues":8,"preventAutoSelect":false,"rangeAlgorithm":"equiprobable","resultsMustMatch":"atLeastOneValue","sortCriteria":"ascending","type":"numericalRange"},{"currentValues":[{"end":2,"endInclusive":false,"start":1,"state":"idle"},{"end":3,"endInclusive":false,"start":2,"state":"idle"},{"end":4,"endInclusive":false,"start":3,"state":"idle"},{"end":5,"endInclusive":false,"start":4,"state":"idle"},{"end":6,"endInclusive":false,"start":5,"state":"idle"}],"facetId":"snrating","field":"snrating","filterFacetCount":true,"generateAutomaticRanges":false,"injectionDepth":1000,"numberOfValues":5,"preventAutoSelect":false,"rangeAlgorithm":"even","resultsMustMatch":"atLeastOneValue","sortCriteria":"descending","type":"numericalRange"},{"currentValues":[{"end":5,"endInclusive":true,"start":1,"state":"idle"},{"end":5,"endInclusive":true,"start":2,"state":"idle"},{"end":5,"endInclusive":true,"start":3,"state":"idle"},{"end":5,"endInclusive":true,"start":4,"state":"idle"},{"end":5,"endInclusive":true,"start":5,"state":"idle"}],"facetId":"snrating_range","field":"snrating","filterFacetCount":true,"generateAutomaticRanges":false,"injectionDepth":1000,"numberOfValues":5,"preventAutoSelect":false,"rangeAlgorithm":"even","resultsMustMatch":"atLeastOneValue","sortCriteria":"descending","type":"numericalRange"},{"currentValues":[],"facetId":"source","field":"source","filterFacetCount":true,"freezeCurrentValues":false,"injectionDepth":1000,"isFieldExpanded":false,"numberOfValues":8,"preventAutoSelect":false,"resultsMustMatch":"atLeastOneValue","sortCriteria":"automatic","type":"specific"},{"currentValues":[],"facetId":"year","field":"year","filterFacetCount":true,"freezeCurrentValues":false,"injectionDepth":1000,"isFieldExpanded":false,"numberOfValues":8,"preventAutoSelect":false,"resultsMustMatch":"atLeastOneValue","sortCriteria":"automatic","type":"specific"},{"currentValues":[],"facetId":"ytviewcount_input","field":"ytviewcount","filterFacetCount":true,"generateAutomaticRanges":false,"injectionDepth":1000,"numberOfValues":0,"preventAutoSelect":false,"rangeAlgorithm":"even","resultsMustMatch":"atLeastOneValue","sortCriteria":"ascending","type":"numericalRange"},{"currentValues":[],"facetId":"ytviewcount_input_range","field":"ytviewcount","filterFacetCount":true,"generateAutomaticRanges":true,"injectionDepth":1000,"numberOfValues":1,"preventAutoSelect":false,"rangeAlgorithm":"equiprobable","resultsMustMatch":"atLeastOneValue","sortCriteria":"ascending","type":"numericalRange"}],"fieldsToInclude":["author","language","urihash","objecttype","collection","source","permanentid","date","filetype","parents","ec_price","ec_name","ec_description","ec_brand","ec_category","ec_item_group_id","ec_shortdesc","ec_thumbnails","ec_images","ec_promo_price","ec_in_stock","ec_rating","snrating","sncost"],"firstResult":0,"numberOfResults":10,"pipelineRuleParameters":{"mlGenerativeQuestionAnswering":{"citationsFieldToInclude":[],"responseFormat":{"answerStyle":"default","contentFormat":["text/markdown","text/plain"]}}},"q":"what is the hardest wood","queryCorrection":{"enabled":false,"options":{"automaticallyCorrect":"whenNoResults"}},"searchHub":"jstpierre2 test - Woods test","tab":"default"})': + { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + status: 'pending' as any, + endpointName: 'getAnswer', + requestId: 'ePS46iWmVlz23hfyR8TVQ', + originalArgs: { + q: 'what is the hardest wood', + pipelineRuleParameters: { + mlGenerativeQuestionAnswering: { + responseFormat: { + answerStyle: 'default', + contentFormat: ['text/markdown', 'text/plain'], + }, + citationsFieldToInclude: [], + }, + }, + searchHub: 'jstpierre2 test - Woods test', + facets: [ + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'author', + field: 'author', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 7, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + start: '2024/08/16@13:55:59', + end: '2024/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + { + start: '2024/08/15@14:55:59', + end: '2024/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + { + start: '2024/08/09@14:55:59', + end: '2024/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + { + start: '2024/07/16@14:55:59', + end: '2024/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + { + start: '2024/05/16@14:55:59', + end: '2024/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + { + start: '2023/08/16@14:55:59', + end: '2024/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + { + start: '2024/08/16@14:55:59', + end: '2034/08/16@14:55:59', + endInclusive: false, + state: 'idle', + }, + ], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date', + field: 'date', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 0, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date_input', + field: 'date', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 1, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date_input_range', + generateAutomaticRanges: true, + field: 'date', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 6, + sortCriteria: 'occurrences', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'filetype', + field: 'filetype', + }, + { + delimitingCharacter: ';', + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'occurrences', + basePath: [], + filterByBasePath: true, + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'hierarchical', + facetId: 'geographicalhierarchy', + field: 'geographicalhierarchy', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'sncost', + field: 'sncost', + generateAutomaticRanges: true, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 5, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: false, + state: 'idle', + start: 1, + end: 2, + }, + { + endInclusive: false, + state: 'idle', + start: 2, + end: 3, + }, + { + endInclusive: false, + state: 'idle', + start: 3, + end: 4, + }, + { + endInclusive: false, + state: 'idle', + start: 4, + end: 5, + }, + { + endInclusive: false, + state: 'idle', + start: 5, + end: 6, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'snrating', + field: 'snrating', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 5, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: true, + state: 'idle', + start: 1, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 2, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 3, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 4, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 5, + end: 5, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'snrating_range', + field: 'snrating', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'source', + field: 'source', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'year', + field: 'year', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 0, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'ytviewcount_input', + field: 'ytviewcount', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 1, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + generateAutomaticRanges: true, + facetId: 'ytviewcount_input_range', + field: 'ytviewcount', + }, + ], + fieldsToInclude: [ + 'author', + 'language', + 'urihash', + 'objecttype', + 'collection', + 'source', + 'permanentid', + 'date', + 'filetype', + 'parents', + 'ec_price', + 'ec_name', + 'ec_description', + 'ec_brand', + 'ec_category', + 'ec_item_group_id', + 'ec_shortdesc', + 'ec_thumbnails', + 'ec_images', + 'ec_promo_price', + 'ec_in_stock', + 'ec_rating', + 'snrating', + 'sncost', + ], + queryCorrection: { + enabled: false, + options: { + automaticallyCorrect: 'whenNoResults', + }, + }, + enableDidYouMean: true, + numberOfResults: 10, + firstResult: 0, + tab: 'default', + }, + startedTimeStamp: 1723834559240, + }, + }, + mutations: {}, + provided: {}, + subscriptions: {}, + config: { + online: true, + focused: true, + middlewareRegistered: false, + refetchOnFocus: false, + refetchOnReconnect: false, + refetchOnMountOrArgChange: false, + keepUnusedDataFor: 60, + reducerPath: 'answer', + invalidationBehavior: 'delayed', + }, + }, + generatedAnswer: { + id: '', + isVisible: true, + isLoading: false, + isStreaming: false, + citations: [], + liked: false, + disliked: false, + responseFormat: { + answerStyle: 'default', + contentFormat: ['text/markdown', 'text/plain'], + }, + feedbackModalOpen: false, + feedbackSubmitted: false, + fieldsToIncludeInCitations: [], + isAnswerGenerated: false, + expanded: false, + answerConfigurationId: 'c36fd994-3eb7-4aaf-bfce-2dad4b15a622', + }, +}; + +export const expectedStreamAnswerAPIParam = { + q: 'what is the hardest wood', + pipelineRuleParameters: { + mlGenerativeQuestionAnswering: { + responseFormat: { + answerStyle: 'default', + contentFormat: ['text/markdown', 'text/plain'], + }, + citationsFieldToInclude: [], + }, + }, + searchHub: 'jstpierre2 test - Woods test', + facets: [ + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'author', + field: 'author', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 7, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + start: '2020/01/01@07:45:00', + end: '2020/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + { + start: '2019/12/31@08:45:00', + end: '2020/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + { + start: '2019/12/25@08:45:00', + end: '2020/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + { + start: '2019/12/01@08:45:00', + end: '2020/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + { + start: '2019/10/01@08:45:00', + end: '2020/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + { + start: '2019/01/01@08:45:00', + end: '2020/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + { + start: '2020/01/01@08:45:00', + end: '2030/01/01@08:45:00', + endInclusive: false, + state: 'idle', + }, + ], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date', + field: 'date', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 0, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date_input', + field: 'date', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 1, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'dateRange', + facetId: 'date_input_range', + generateAutomaticRanges: true, + field: 'date', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 6, + sortCriteria: 'occurrences', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'filetype', + field: 'filetype', + }, + { + delimitingCharacter: ';', + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'occurrences', + basePath: [], + filterByBasePath: true, + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'hierarchical', + facetId: 'geographicalhierarchy', + field: 'geographicalhierarchy', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'sncost', + field: 'sncost', + generateAutomaticRanges: true, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 5, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: false, + state: 'idle', + start: 1, + end: 2, + }, + { + endInclusive: false, + state: 'idle', + start: 2, + end: 3, + }, + { + endInclusive: false, + state: 'idle', + start: 3, + end: 4, + }, + { + endInclusive: false, + state: 'idle', + start: 4, + end: 5, + }, + { + endInclusive: false, + state: 'idle', + start: 5, + end: 6, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'snrating', + field: 'snrating', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 5, + sortCriteria: 'descending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [ + { + endInclusive: true, + state: 'idle', + start: 1, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 2, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 3, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 4, + end: 5, + }, + { + endInclusive: true, + state: 'idle', + start: 5, + end: 5, + }, + ], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'snrating_range', + field: 'snrating', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'source', + field: 'source', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 8, + sortCriteria: 'automatic', + resultsMustMatch: 'atLeastOneValue', + type: 'specific', + currentValues: [], + freezeCurrentValues: false, + isFieldExpanded: false, + preventAutoSelect: false, + facetId: 'year', + field: 'year', + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 0, + sortCriteria: 'ascending', + rangeAlgorithm: 'even', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + facetId: 'ytviewcount_input', + field: 'ytviewcount', + generateAutomaticRanges: false, + }, + { + filterFacetCount: true, + injectionDepth: 1000, + numberOfValues: 1, + sortCriteria: 'ascending', + rangeAlgorithm: 'equiprobable', + resultsMustMatch: 'atLeastOneValue', + currentValues: [], + preventAutoSelect: false, + type: 'numericalRange', + generateAutomaticRanges: true, + facetId: 'ytviewcount_input_range', + field: 'ytviewcount', + }, + ], + fieldsToInclude: [ + 'author', + 'language', + 'urihash', + 'objecttype', + 'collection', + 'source', + 'permanentid', + 'date', + 'filetype', + 'parents', + 'ec_price', + 'ec_name', + 'ec_description', + 'ec_brand', + 'ec_category', + 'ec_item_group_id', + 'ec_shortdesc', + 'ec_thumbnails', + 'ec_images', + 'ec_promo_price', + 'ec_in_stock', + 'ec_rating', + 'snrating', + 'sncost', + ], + queryCorrection: { + enabled: false, + options: { + automaticallyCorrect: 'whenNoResults', + }, + }, + enableDidYouMean: true, + numberOfResults: 10, + firstResult: 0, + tab: 'default', +}; diff --git a/packages/headless/src/api/knowledge/tests/stream-answer-api.test.ts b/packages/headless/src/api/knowledge/tests/stream-answer-api.test.ts new file mode 100644 index 00000000000..051df860fac --- /dev/null +++ b/packages/headless/src/api/knowledge/tests/stream-answer-api.test.ts @@ -0,0 +1,37 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import {constructAnswerQueryParams} from '../stream-answer-api'; +import { + expectedStreamAnswerAPIParam, + streamAnswerAPIStateMock, +} from './stream-answer-api-state-mock'; + +describe('#streamAnswerApi', () => { + describe('constructAnswerQueryParams', () => { + beforeEach(() => { + jest.useFakeTimers().setSystemTime(new Date('2020-01-01')); + }); + afterAll(() => { + jest.useRealTimers(); + }); + it('returns the correct query params with fetch usage', () => { + const queryParams = constructAnswerQueryParams( + streamAnswerAPIStateMock as any, + 'fetch' + ); + + expect(queryParams).toEqual(expectedStreamAnswerAPIParam); + }); + + it('will create the right selector with select usage', () => { + constructAnswerQueryParams(streamAnswerAPIStateMock as any, 'select'); + + jest.useFakeTimers().setSystemTime(new Date('2024-01-01')); + const queryParams = constructAnswerQueryParams( + streamAnswerAPIStateMock as any, + 'select' + ); + + expect(queryParams).toEqual(expectedStreamAnswerAPIParam); + }); + }); +}); diff --git a/packages/headless/src/features/search/legacy/search-request.ts b/packages/headless/src/features/search/legacy/search-request.ts index 4a4d7d6742f..58da0faf5ca 100644 --- a/packages/headless/src/features/search/legacy/search-request.ts +++ b/packages/headless/src/features/search/legacy/search-request.ts @@ -1,13 +1,9 @@ import {EventDescription} from 'coveo.analytics'; import {SearchAppState} from '../../../state/search-app-state'; import {ConfigurationSection} from '../../../state/state-sections'; -import {sortFacets} from '../../../utils/facet-utils'; +import {getFacets} from '../../../utils/facet-utils'; import {AutomaticFacetRequest} from '../../facets/automatic-facet-set/interfaces/request'; import {AutomaticFacetResponse} from '../../facets/automatic-facet-set/interfaces/response'; -import {FacetSetState} from '../../facets/facet-set/facet-set-state'; -import {getFacetRequests} from '../../facets/generic/interfaces/generic-facet-request'; -import {AnyFacetValue} from '../../facets/generic/interfaces/generic-facet-response'; -import {RangeFacetSetState} from '../../facets/range-facets/generic/interfaces/range-facet'; import {maximumNumberOfResultsFromIndex} from '../../pagination/pagination-constants'; import {buildSearchAndFoldingLoadCollectionRequest} from '../../search-and-folding/legacy/search-and-folding-request'; import {mapSearchRequest} from '../search-mappings'; @@ -94,10 +90,6 @@ export const buildSearchRequest = async ( }); }; -function getFacets(state: StateNeededBySearchRequest) { - return sortFacets(getAllEnabledFacets(state), state.facetOrder ?? []); -} - function getAutomaticFacets(state: StateNeededBySearchRequest) { const facets = state.automaticFacetSet?.set; @@ -120,51 +112,6 @@ function responseToAutomaticFacetRequest( currentValues: selectedValues, }; } -function getAllEnabledFacets(state: StateNeededBySearchRequest) { - return getAllFacets(state).filter( - ({facetId}) => state.facetOptions?.facets[facetId]?.enabled ?? true - ); -} - -function getAllFacets(state: StateNeededBySearchRequest) { - return [ - ...getSpecificFacetRequests(state.facetSet ?? {}), - ...getRangeFacetRequests(state.numericFacetSet ?? {}), - ...getRangeFacetRequests(state.dateFacetSet ?? {}), - ...getFacetRequests(state.categoryFacetSet ?? {}), - ]; -} - -function getSpecificFacetRequests(state: T) { - return getFacetRequests(state).map((request) => { - /* The Search API does not support 'alphanumericDescending' as a string value and instead relies on a new sort mechanism to specify sort order. - At the moment, this is only supported for alphanumeric sorting, but will likely transition to this pattern for other types in the future. */ - if (request.sortCriteria === 'alphanumericDescending') { - return { - ...request, - sortCriteria: { - type: 'alphanumeric', - order: 'descending', - }, - }; - } - - return request; - }); -} - -function getRangeFacetRequests(state: T) { - return getFacetRequests(state).map((request) => { - const currentValues = request.currentValues as AnyFacetValue[]; - const hasActiveValues = currentValues.some(({state}) => state !== 'idle'); - - if (request.generateAutomaticRanges && !hasActiveValues) { - return {...request, currentValues: []}; - } - - return request; - }); -} function buildConstantQuery(state: StateNeededBySearchRequest) { const cq = state.advancedSearchQueries?.cq.trim() || ''; diff --git a/packages/headless/src/features/search/search-mappings.ts b/packages/headless/src/features/search/search-mappings.ts index 430fc774fda..7002dc0e52f 100644 --- a/packages/headless/src/features/search/search-mappings.ts +++ b/packages/headless/src/features/search/search-mappings.ts @@ -30,7 +30,7 @@ export interface SearchMappings { dateFacetValueMap: Record>; } -const initialSearchMappings: () => SearchMappings = () => ({ +export const initialSearchMappings: () => SearchMappings = () => ({ dateFacetValueMap: {}, }); @@ -54,7 +54,7 @@ function mapDateRangeRequest( return {...value, start, end}; } -function mapFacetRequest( +export function mapFacetRequest( facetRequest: AnyFacetRequest, mappings: SearchMappings ) { diff --git a/packages/headless/src/utils/facet-utils.ts b/packages/headless/src/utils/facet-utils.ts index 91284819f28..7ba6a26e911 100644 --- a/packages/headless/src/utils/facet-utils.ts +++ b/packages/headless/src/utils/facet-utils.ts @@ -1,3 +1,13 @@ +import {FacetSetState} from '../features/facets/facet-set/facet-set-state'; +import {getFacetRequests} from '../features/facets/generic/interfaces/generic-facet-request'; +import {AnyFacetValue} from '../features/facets/generic/interfaces/generic-facet-response'; +import {RangeFacetSetState} from '../features/facets/range-facets/generic/interfaces/range-facet'; +import {SearchAppState} from '../state/search-app-state'; +import {ConfigurationSection} from '../state/state-sections'; + +type StateNeededBySearchRequest = ConfigurationSection & + Partial; + export function sortFacets( facets: T[], sortOrder: string[] @@ -16,3 +26,53 @@ export function sortFacets( return [...sortedFacets, ...remainingFacets]; } + +function getRangeFacetRequests(state: T) { + return getFacetRequests(state).map((request) => { + const currentValues = request.currentValues as AnyFacetValue[]; + const hasActiveValues = currentValues.some(({state}) => state !== 'idle'); + + if (request.generateAutomaticRanges && !hasActiveValues) { + return {...request, currentValues: []}; + } + + return request; + }); +} + +function getSpecificFacetRequests(state: T) { + return getFacetRequests(state).map((request) => { + /* The Search API does not support 'alphanumericDescending' as a string value and instead relies on a new sort mechanism to specify sort order. + At the moment, this is only supported for alphanumeric sorting, but will likely transition to this pattern for other types in the future. */ + if (request.sortCriteria === 'alphanumericDescending') { + return { + ...request, + sortCriteria: { + type: 'alphanumeric', + order: 'descending', + }, + }; + } + + return request; + }); +} + +function getAllFacets(state: StateNeededBySearchRequest) { + return [ + ...getSpecificFacetRequests(state.facetSet ?? {}), + ...getRangeFacetRequests(state.numericFacetSet ?? {}), + ...getRangeFacetRequests(state.dateFacetSet ?? {}), + ...getFacetRequests(state.categoryFacetSet ?? {}), + ]; +} + +function getAllEnabledFacets(state: StateNeededBySearchRequest) { + return getAllFacets(state).filter( + ({facetId}) => state.facetOptions?.facets[facetId]?.enabled ?? true + ); +} + +export function getFacets(state: StateNeededBySearchRequest) { + return sortFacets(getAllEnabledFacets(state), state.facetOrder ?? []); +}