mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[App Search][Nested Fields / Object fields]Use field capabilities (#135951)
This commit is contained in:
parent
daec70ba1c
commit
188b9e094d
8 changed files with 91 additions and 47 deletions
|
@ -15,11 +15,23 @@ describe('buildSearchUIConfig', () => {
|
|||
it('builds a configuration object for Search UI', () => {
|
||||
const connector = {};
|
||||
const schema = {
|
||||
foo: SchemaType.Text,
|
||||
bar: SchemaType.Number,
|
||||
foo: {
|
||||
type: SchemaType.Text,
|
||||
capabilities: {
|
||||
snippet: true,
|
||||
facet: true,
|
||||
},
|
||||
},
|
||||
bar: {
|
||||
type: SchemaType.Number,
|
||||
capabilities: {
|
||||
snippet: false,
|
||||
facet: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
const fields = {
|
||||
filterFields: ['fieldA', 'fieldB'],
|
||||
filterFields: ['foo', 'bar'],
|
||||
sortFields: [],
|
||||
};
|
||||
|
||||
|
@ -32,13 +44,9 @@ describe('buildSearchUIConfig', () => {
|
|||
sortField: 'id',
|
||||
},
|
||||
searchQuery: {
|
||||
disjunctiveFacets: ['fieldA', 'fieldB'],
|
||||
disjunctiveFacets: ['foo'],
|
||||
facets: {
|
||||
fieldA: {
|
||||
size: 30,
|
||||
type: 'value',
|
||||
},
|
||||
fieldB: {
|
||||
foo: {
|
||||
size: 30,
|
||||
type: 'value',
|
||||
},
|
||||
|
@ -50,7 +58,6 @@ describe('buildSearchUIConfig', () => {
|
|||
foo: {
|
||||
raw: {},
|
||||
snippet: {
|
||||
fallback: true,
|
||||
size: 300,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -7,26 +7,34 @@
|
|||
|
||||
import type { APIConnector, SortDirection } from '@elastic/search-ui';
|
||||
|
||||
import { Schema } from '../../../../shared/schema/types';
|
||||
import { SchemaType, AdvancedSchema } from '../../../../shared/schema/types';
|
||||
|
||||
import { Fields } from './types';
|
||||
|
||||
export const buildSearchUIConfig = (
|
||||
apiConnector: APIConnector,
|
||||
schema: Schema,
|
||||
schema: AdvancedSchema,
|
||||
fields: Fields,
|
||||
initialState = { sortDirection: 'desc' as SortDirection, sortField: 'id' }
|
||||
) => {
|
||||
const facets = fields.filterFields.reduce((facetsConfig, fieldName) => {
|
||||
// Geolocation fields do not support value facets https://www.elastic.co/guide/en/app-search/current/facets.html
|
||||
if (schema[fieldName] === 'geolocation') {
|
||||
return facetsConfig;
|
||||
}
|
||||
return {
|
||||
...facetsConfig,
|
||||
[fieldName]: { type: 'value', size: 30 },
|
||||
};
|
||||
}, {});
|
||||
const facets = fields.filterFields
|
||||
.filter((fieldName) => !!schema[fieldName] && schema[fieldName].type !== SchemaType.Geolocation)
|
||||
.filter((fieldName) => !!schema[fieldName].capabilities.facet)
|
||||
.reduce((facetsConfig, fieldName) => {
|
||||
return {
|
||||
...facetsConfig,
|
||||
[fieldName]: { type: 'value', size: 30 },
|
||||
};
|
||||
}, {});
|
||||
|
||||
const resultFields = Object.entries(schema)
|
||||
.filter(([, schemaField]) => schemaField.type !== SchemaType.Nested)
|
||||
.reduce((acc, [fieldName, schemaField]) => {
|
||||
if (schemaField.capabilities.snippet) {
|
||||
return { ...acc, [fieldName]: { raw: {}, snippet: { size: 300 } } };
|
||||
}
|
||||
return { ...acc, [fieldName]: { raw: {} } };
|
||||
}, {});
|
||||
|
||||
return {
|
||||
alwaysSearchOnInitialLoad: true,
|
||||
|
@ -34,25 +42,9 @@ export const buildSearchUIConfig = (
|
|||
trackUrlState: false,
|
||||
initialState,
|
||||
searchQuery: {
|
||||
disjunctiveFacets: fields.filterFields,
|
||||
disjunctiveFacets: Object.keys(facets),
|
||||
facets,
|
||||
result_fields: Object.keys(schema).reduce((acc: { [key: string]: object }, key: string) => {
|
||||
if (schema[key] === 'text') {
|
||||
// Only text fields support snippets
|
||||
acc[key] = {
|
||||
snippet: {
|
||||
size: 300,
|
||||
fallback: true,
|
||||
},
|
||||
raw: {},
|
||||
};
|
||||
} else {
|
||||
acc[key] = {
|
||||
raw: {},
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
result_fields: resultFields,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -52,14 +52,20 @@ export const CustomizationModal: React.FC<Props> = ({
|
|||
sortFields.map(fieldNameToComboBoxOption)
|
||||
);
|
||||
|
||||
const engineSchema = engine.schema || {};
|
||||
const schema = engine.advancedSchema || {};
|
||||
const selectableFilterFields = useMemo(
|
||||
() => Object.keys(engineSchema).map(fieldNameToComboBoxOption),
|
||||
[engineSchema]
|
||||
() =>
|
||||
Object.keys(schema)
|
||||
.filter((fieldName) => schema[fieldName].capabilities.filter)
|
||||
.map(fieldNameToComboBoxOption),
|
||||
[schema]
|
||||
);
|
||||
const selectableSortFields = useMemo(
|
||||
() => Object.keys(engineSchema).map(fieldNameToComboBoxOption),
|
||||
[engineSchema]
|
||||
() =>
|
||||
Object.keys(schema)
|
||||
.filter((fieldName) => schema[fieldName].capabilities.sort)
|
||||
.map(fieldNameToComboBoxOption),
|
||||
[schema]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -100,7 +100,7 @@ export const SearchExperience: React.FC = () => {
|
|||
|
||||
const searchProviderConfig = buildSearchUIConfig(
|
||||
connector,
|
||||
engine.schema || {},
|
||||
engine.advancedSchema || {},
|
||||
fields,
|
||||
initialState
|
||||
);
|
||||
|
|
|
@ -41,6 +41,19 @@ describe('EngineLogic', () => {
|
|||
isMeta: false,
|
||||
invalidBoosts: false,
|
||||
schema: { test: SchemaType.Text },
|
||||
advancedSchema: {
|
||||
test: {
|
||||
type: SchemaType.Text,
|
||||
capabilities: {
|
||||
fulltext: true,
|
||||
filter: true,
|
||||
facet: true,
|
||||
sort: true,
|
||||
snippet: true,
|
||||
boost: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
apiTokens: [],
|
||||
apiKey: 'some-key',
|
||||
adaptive_relevance_suggestions_active: true,
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Schema, SchemaConflicts, IIndexingStatus } from '../../../shared/schema/types';
|
||||
import {
|
||||
Schema,
|
||||
SchemaConflicts,
|
||||
IIndexingStatus,
|
||||
AdvancedSchema,
|
||||
} from '../../../shared/schema/types';
|
||||
import { ApiToken } from '../credentials/types';
|
||||
|
||||
export enum EngineTypes {
|
||||
|
@ -47,6 +52,7 @@ export interface EngineDetails extends Engine {
|
|||
apiKey: string;
|
||||
elasticsearchIndexName?: string;
|
||||
schema: Schema;
|
||||
advancedSchema: AdvancedSchema;
|
||||
schemaConflicts?: SchemaConflicts;
|
||||
unconfirmedFields?: string[];
|
||||
activeReindexJob?: IIndexingStatus;
|
||||
|
|
|
@ -26,6 +26,7 @@ const fieldTypeToTokenMap = {
|
|||
[InternalSchemaType.Location]: 'tokenGeo',
|
||||
[SchemaType.Date]: 'tokenDate',
|
||||
[InternalSchemaType.Date]: 'tokenDate',
|
||||
[InternalSchemaType.Nested]: 'tokenNested',
|
||||
};
|
||||
|
||||
export const ResultToken: React.FC<Props> = ({ fieldType }) => {
|
||||
|
|
|
@ -14,6 +14,7 @@ export enum SchemaType {
|
|||
Number = 'number',
|
||||
Geolocation = 'geolocation',
|
||||
Date = 'date',
|
||||
Nested = 'nested',
|
||||
}
|
||||
// Certain API endpoints will use these internal type names, which map to the external names above
|
||||
export enum InternalSchemaType {
|
||||
|
@ -21,6 +22,7 @@ export enum InternalSchemaType {
|
|||
Float = 'float',
|
||||
Location = 'location',
|
||||
Date = 'date',
|
||||
Nested = 'nested',
|
||||
}
|
||||
|
||||
export type Schema = Record<string, SchemaType>;
|
||||
|
@ -62,3 +64,20 @@ export interface FieldCoercionError {
|
|||
error: string;
|
||||
}
|
||||
export type FieldCoercionErrors = Record<string, FieldCoercionError[]>;
|
||||
|
||||
export interface SchemaFieldCapabilities {
|
||||
fulltext?: boolean;
|
||||
filter?: boolean;
|
||||
facet?: boolean;
|
||||
sort?: boolean;
|
||||
snippet?: boolean;
|
||||
boost?: boolean;
|
||||
}
|
||||
|
||||
export interface AdvancedSchemaField {
|
||||
type: SchemaType;
|
||||
nestedPath?: string;
|
||||
capabilities: SchemaFieldCapabilities;
|
||||
}
|
||||
|
||||
export type AdvancedSchema = Record<string, AdvancedSchemaField>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue