From 971225dbceeeb5c401621895febc8584f0fc91db Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:35:45 +0530 Subject: [PATCH] Fix #21414: Custom properties display issue in advanced search (#21873) * fix: remove recursive extraction of nested fields for entityReference and entityReferenceList type custom properties * get displayName field along with other fields in /customProperties api * show display name instead of name for custom props * show display name instead of name for custom props * fix extension type and add tests * fix tests * fix data model tests --------- Co-authored-by: karanh37 Co-authored-by: Karan Hotchandani <33024356+karanh37@users.noreply.github.com> --- .../service/util/SchemaFieldExtractor.java | 84 ++++++----- .../ui/playwright/constant/customProperty.ts | 2 +- .../AdvanceSearchCustomProperty.spec.ts | 6 + .../CustomPropertyAdvanceSeach.spec.ts | 21 ++- .../e2e/Pages/Customproperties-part1.spec.ts | 13 ++ .../e2e/Pages/Customproperties-part2.spec.ts | 73 ++++++++++ .../ui/playwright/utils/customProperty.ts | 40 +++++- .../AdvanceSearchProvider.component.tsx | 17 ++- .../Explore/advanced-search-modal.less | 9 ++ .../ui/src/utils/AdvancedSearchClassBase.ts | 132 +++++++++--------- .../QueryBuilderElasticsearchFormatUtils.js | 41 +++++- 11 files changed, 323 insertions(+), 115 deletions(-) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/SchemaFieldExtractor.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/SchemaFieldExtractor.java index 6ed4801e0b1..f52af1b87dc 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/util/SchemaFieldExtractor.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/SchemaFieldExtractor.java @@ -18,7 +18,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.extern.slf4j.Slf4j; -import org.everit.json.schema.*; +import org.everit.json.schema.ArraySchema; +import org.everit.json.schema.BooleanSchema; +import org.everit.json.schema.NullSchema; +import org.everit.json.schema.NumberSchema; +import org.everit.json.schema.ObjectSchema; +import org.everit.json.schema.ReferenceSchema; +import org.everit.json.schema.Schema; +import org.everit.json.schema.StringSchema; import org.everit.json.schema.loader.SchemaClient; import org.everit.json.schema.loader.SchemaLoader; import org.json.JSONObject; @@ -207,7 +214,7 @@ public class SchemaFieldExtractor { } else { String fieldType = mapSchemaTypeToSimpleType(fieldSchema); fieldTypesMap.putIfAbsent( - fullFieldName, new FieldDefinition(fullFieldName, fieldType, null)); + fullFieldName, FieldDefinition.of(fullFieldName, fullFieldName, fieldType, null)); processedFields.add(fullFieldName); LOG.debug("Added field '{}', Type: '{}'", fullFieldName, fieldType); // Recursively process nested objects or arrays @@ -221,7 +228,8 @@ public class SchemaFieldExtractor { handleArraySchema(arraySchema, parentPath, fieldTypesMap, processingStack, processedFields); } else { String fieldType = mapSchemaTypeToSimpleType(schema); - fieldTypesMap.putIfAbsent(parentPath, new FieldDefinition(parentPath, fieldType, null)); + fieldTypesMap.putIfAbsent( + parentPath, FieldDefinition.of(parentPath, parentPath, fieldType, null)); LOG.debug("Added field '{}', Type: '{}'", parentPath, fieldType); } } finally { @@ -241,7 +249,7 @@ public class SchemaFieldExtractor { if (referenceType != null) { fieldTypesMap.putIfAbsent( - fullFieldName, new FieldDefinition(fullFieldName, referenceType, null)); + fullFieldName, FieldDefinition.of(fullFieldName, fullFieldName, referenceType, null)); processedFields.add(fullFieldName); LOG.debug("Added field '{}', Type: '{}'", fullFieldName, referenceType); if (referenceType.startsWith("array<") && referenceType.endsWith(">")) { @@ -257,7 +265,8 @@ public class SchemaFieldExtractor { referredSchema, fullFieldName, fieldTypesMap, processingStack, processedFields); } } else { - fieldTypesMap.putIfAbsent(fullFieldName, new FieldDefinition(fullFieldName, "object", null)); + fieldTypesMap.putIfAbsent( + fullFieldName, FieldDefinition.of(fullFieldName, fullFieldName, "object", null)); processedFields.add(fullFieldName); LOG.debug("Added field '{}', Type: 'object'", fullFieldName); extractFieldsFromSchema( @@ -285,7 +294,7 @@ public class SchemaFieldExtractor { if (itemsReferenceType != null) { String arrayFieldType = "array<" + itemsReferenceType + ">"; fieldTypesMap.putIfAbsent( - fullFieldName, new FieldDefinition(fullFieldName, arrayFieldType, null)); + fullFieldName, FieldDefinition.of(fullFieldName, fullFieldName, arrayFieldType, null)); processedFields.add(fullFieldName); LOG.debug("Added field '{}', Type: '{}'", fullFieldName, arrayFieldType); Schema referredItemsSchema = itemsReferenceSchema.getReferredSchema(); @@ -296,7 +305,8 @@ public class SchemaFieldExtractor { } String arrayType = mapSchemaTypeToSimpleType(itemsSchema); fieldTypesMap.putIfAbsent( - fullFieldName, new FieldDefinition(fullFieldName, "array<" + arrayType + ">", null)); + fullFieldName, + FieldDefinition.of(fullFieldName, fullFieldName, "array<" + arrayType + ">", null)); processedFields.add(fullFieldName); LOG.debug("Added field '{}', Type: 'array<{}>'", fullFieldName, arrayType); @@ -321,49 +331,31 @@ public class SchemaFieldExtractor { String propertyName = customProperty.getName(); String propertyType = customProperty.getPropertyType().getName(); String fullFieldName = propertyName; // No parent path for custom properties - + String displayName = customProperty.getDisplayName(); LOG.debug("Processing custom property '{}'", fullFieldName); Object customPropertyConfigObj = customProperty.getCustomPropertyConfig(); if (isEntityReferenceList(propertyType)) { String referenceType = "array"; - FieldDefinition referenceFieldDefinition = - new FieldDefinition(fullFieldName, referenceType, customPropertyConfigObj); - fieldTypesMap.putIfAbsent(fullFieldName, referenceFieldDefinition); + FieldDefinition fieldDef = + FieldDefinition.of(fullFieldName, displayName, referenceType, customPropertyConfigObj); + fieldTypesMap.putIfAbsent(fullFieldName, fieldDef); processedFields.add(fullFieldName); LOG.debug("Added custom property '{}', Type: '{}'", fullFieldName, referenceType); - Schema itemSchema = resolveSchemaByType("entityReference", schemaUri, schemaClient); - if (itemSchema != null) { - extractFieldsFromSchema( - itemSchema, fullFieldName, fieldTypesMap, processingStack, processedFields); - } else { - LOG.warn( - "Schema for type 'entityReference' not found. Skipping nested field extraction for '{}'.", - fullFieldName); - } } else if (isEntityReference(propertyType)) { String referenceType = "entityReference"; - FieldDefinition referenceFieldDefinition = - new FieldDefinition(fullFieldName, referenceType, customPropertyConfigObj); - fieldTypesMap.putIfAbsent(fullFieldName, referenceFieldDefinition); + FieldDefinition fieldDef = + FieldDefinition.of(fullFieldName, displayName, referenceType, customPropertyConfigObj); + fieldTypesMap.putIfAbsent(fullFieldName, fieldDef); processedFields.add(fullFieldName); LOG.debug("Added custom property '{}', Type: '{}'", fullFieldName, referenceType); - Schema referredSchema = resolveSchemaByType("entityReference", schemaUri, schemaClient); - if (referredSchema != null) { - extractFieldsFromSchema( - referredSchema, fullFieldName, fieldTypesMap, processingStack, processedFields); - } else { - LOG.warn( - "Schema for type 'entityReference' not found. Skipping nested field extraction for '{}'.", - fullFieldName); - } } else { - FieldDefinition entityFieldDefinition = - new FieldDefinition(fullFieldName, propertyType, customPropertyConfigObj); - fieldTypesMap.putIfAbsent(fullFieldName, entityFieldDefinition); + FieldDefinition fieldDef = + FieldDefinition.of(fullFieldName, displayName, propertyType, customPropertyConfigObj); + fieldTypesMap.putIfAbsent(fullFieldName, fieldDef); processedFields.add(fullFieldName); LOG.debug("Added custom property '{}', Type: '{}'", fullFieldName, propertyType); } @@ -375,8 +367,11 @@ public class SchemaFieldExtractor { for (Map.Entry entry : fieldTypesMap.entrySet()) { FieldDefinition fieldDef = entry.getValue(); fieldsList.add( - new FieldDefinition( - fieldDef.getName(), fieldDef.getType(), fieldDef.getCustomPropertyConfig())); + FieldDefinition.of( + fieldDef.getName(), + fieldDef.getDisplayName(), + fieldDef.getType(), + fieldDef.getCustomPropertyConfig())); } return fieldsList; } @@ -638,13 +633,28 @@ public class SchemaFieldExtractor { @lombok.Setter public static class FieldDefinition { private String name; + private String displayName; private String type; private Object customPropertyConfig; public FieldDefinition(String name, String type, Object customPropertyConfig) { this.name = name; + this.displayName = name; this.type = type; this.customPropertyConfig = customPropertyConfig; } + + public FieldDefinition( + String name, String displayName, String type, Object customPropertyConfig) { + this.name = name; + this.displayName = displayName; + this.type = type; + this.customPropertyConfig = customPropertyConfig; + } + + public static FieldDefinition of( + String name, String displayName, String type, Object customPropertyConfig) { + return new FieldDefinition(name, displayName, type, customPropertyConfig); + } } } diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts index 51081f66fe1..8f0b11b0ec9 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts @@ -351,7 +351,7 @@ export const CUSTOM_PROPERTIES_ENTITIES = { }, }, entity_dashboardDataModel: { - name: 'dataModel', + name: 'dashboardDataModel', description: 'This is Data Model custom property', integerValue: '23', stringValue: 'This is string propery', diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts index 176fe349918..6c9c22db4eb 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts @@ -100,6 +100,12 @@ test.describe('Advanced Search Custom Property', () => { 'Custom Properties' ); + await selectOption( + page, + ruleLocator.locator('.rule--field .ant-select'), + 'Table' + ); + // Perform click on custom property type to filter await selectOption( page, diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts index eb3a908344d..438d99880db 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts @@ -15,6 +15,7 @@ import { CUSTOM_PROPERTIES_ENTITIES } from '../../../constant/customProperty'; import { GlobalSettingOptions } from '../../../constant/settings'; import { SidebarItem } from '../../../constant/sidebar'; import { DashboardClass } from '../../../support/entity/DashboardClass'; +import { selectOption } from '../../../utils/advancedSearch'; import { createNewPage, redirectToHomePage, uuid } from '../../../utils/common'; import { addCustomPropertiesForEntity, @@ -122,8 +123,16 @@ test('CustomProperty Dashboard Filter', async ({ page }) => { .getByText('Owner') .click(); + const ruleLocator = page.locator('.rule').nth(0); + await page.getByTitle('Custom Properties').click(); + await selectOption( + page, + ruleLocator.locator('.rule--field .ant-select'), + 'Dashboard' + ); + // Select Custom Property Field when we want filter await page .locator( @@ -145,13 +154,15 @@ test('CustomProperty Dashboard Filter', async ({ page }) => { // Validate if filter dashboard appeared - expect(page.getByTestId('advance-search-filter-text')).toContainText( - `extension.${propertyName} = '${propertyValue}'` + await expect( + page.getByTestId('advance-search-filter-text') + ).toContainText( + `extension.dashboard.${propertyName} = '${propertyValue}'` ); - expect(page.getByTestId('entity-header-display-name')).toContainText( - dashboardEntity.entity.displayName - ); + await expect( + page.getByTestId('entity-header-display-name') + ).toContainText(dashboardEntity.entity.displayName); } ); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part1.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part1.spec.ts index 8f3f7a4534b..5427dea3e39 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part1.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part1.spec.ts @@ -17,6 +17,7 @@ import { addCustomPropertiesForEntity, deleteCreatedProperty, editCreatedProperty, + verifyCustomPropertyInAdvancedSearch, } from '../../utils/customProperty'; import { settingClick, SettingOptionsType } from '../../utils/sidebar'; @@ -65,6 +66,18 @@ test.describe('Custom properties without custom property config', () => { await editCreatedProperty(page, propertyName); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part2.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part2.spec.ts index f90d5d69246..a2e87e8be39 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part2.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Customproperties-part2.spec.ts @@ -17,6 +17,7 @@ import { addCustomPropertiesForEntity, deleteCreatedProperty, editCreatedProperty, + verifyCustomPropertyInAdvancedSearch, } from '../../utils/customProperty'; import { settingClick, SettingOptionsType } from '../../utils/sidebar'; @@ -51,6 +52,18 @@ test.describe('Custom properties with custom property config', () => { await editCreatedProperty(page, propertyName, 'Enum'); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); @@ -79,6 +92,18 @@ test.describe('Custom properties with custom property config', () => { await editCreatedProperty(page, propertyName, 'Table'); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); @@ -111,6 +136,18 @@ test.describe('Custom properties with custom property config', () => { await editCreatedProperty(page, propertyName, 'Entity Reference'); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); @@ -148,6 +185,18 @@ test.describe('Custom properties with custom property config', () => { 'Entity Reference List' ); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); @@ -205,6 +254,18 @@ test.describe('Custom properties with custom property config', () => { await editCreatedProperty(page, propertyName); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); @@ -235,6 +296,18 @@ test.describe('Custom properties with custom property config', () => { await editCreatedProperty(page, propertyName); + await verifyCustomPropertyInAdvancedSearch( + page, + propertyName.toUpperCase(), // displayName is in uppercase + entity.name.charAt(0).toUpperCase() + entity.name.slice(1) + ); + + await settingClick( + page, + entity.entityApiType as SettingOptionsType, + true + ); + await deleteCreatedProperty(page, propertyName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index cf47a893e2b..5883998b454 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -16,17 +16,20 @@ import { CUSTOM_PROPERTY_NAME_VALIDATION_ERROR, ENTITY_REFERENCE_PROPERTIES, } from '../constant/customProperty'; +import { SidebarItem } from '../constant/sidebar'; import { EntityTypeEndpoint, ENTITY_PATH, } from '../support/entity/Entity.interface'; import { UserClass } from '../support/user/UserClass'; +import { selectOption, showAdvancedSearchDialog } from './advancedSearch'; import { clickOutside, descriptionBox, descriptionBoxReadOnly, uuid, } from './common'; +import { sidebarClick } from './sidebar'; export enum CustomPropertyType { STRING = 'String', @@ -732,7 +735,7 @@ export const editCreatedProperty = async ( // displayName await page.fill('[data-testid="display-name"]', ''); - await page.fill('[data-testid="display-name"]', propertyName); + await page.fill('[data-testid="display-name"]', propertyName.toUpperCase()); await page.locator(descriptionBox).fill(''); await page.locator(descriptionBox).fill('This is new description'); @@ -807,3 +810,38 @@ export const deleteCreatedProperty = async ( await page.locator('[data-testid="save-button"]').click(); }; + +export const verifyCustomPropertyInAdvancedSearch = async ( + page: Page, + propertyName: string, + entityType: string +) => { + await sidebarClick(page, SidebarItem.EXPLORE); + await page.waitForLoadState('networkidle'); + + // Open advanced search dialog + await showAdvancedSearchDialog(page); + + const ruleLocator = page.locator('.rule').nth(0); + + // Select "Custom Properties" from the field dropdown + await selectOption( + page, + ruleLocator.locator('.rule--field .ant-select'), + 'Custom Properties' + ); + + await selectOption( + page, + ruleLocator.locator('.rule--field .ant-select'), + entityType + ); + + await selectOption( + page, + ruleLocator.locator('.rule--field .ant-select'), + propertyName + ); + + await page.getByTestId('cancel-btn').click(); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component.tsx index bf03040a2c0..40e60f0f99a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component.tsx @@ -224,18 +224,31 @@ export const AdvanceSearchProvider = ({ try { const res = await getAllCustomProperties(); - Object.entries(res).forEach(([_, fields]) => { + Object.entries(res).forEach(([entityType, fields]) => { if (Array.isArray(fields) && fields.length > 0) { + // Create nested subfields for each entity type (e.g., table, database, etc.) + const entitySubfields: Record = {}; + fields.forEach((field) => { if (field.name && field.type) { const { subfieldsKey, dataObject } = advancedSearchClassBase.getCustomPropertiesSubFields(field); - subfields[subfieldsKey] = { + + entitySubfields[subfieldsKey] = { ...dataObject, valueSources: dataObject.valueSources as ValueSource[], }; } }); + + // Only create the entity type field if it has custom properties + if (!isEmpty(entitySubfields)) { + subfields[entityType] = { + label: entityType.charAt(0).toUpperCase() + entityType.slice(1), + type: '!group', + subfields: entitySubfields, + } as Field; + } } }); } catch (error) { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/advanced-search-modal.less b/openmetadata-ui/src/main/resources/ui/src/components/Explore/advanced-search-modal.less index cbebaa8f531..8b6ac087c4b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/advanced-search-modal.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/advanced-search-modal.less @@ -15,6 +15,7 @@ .group.rule_group { border: none !important; padding: 0; + .group--children { padding-top: 0; padding-bottom: 0; @@ -24,12 +25,20 @@ .group--field { width: 180px; + .ant-select { width: 100% !important; } + label { font-weight: normal; margin-bottom: 6px; } } + + .rule_group { + .group--field { + align-self: flex-start; + } + } } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts index 99b37c5fd15..31a02987547 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts @@ -44,6 +44,7 @@ import { getCustomPropertyAdvanceSearchEnumOptions, renderAdvanceSearchButtons, } from './AdvancedSearchUtils'; +import { getEntityName } from './EntityUtils'; import { getCombinedQueryFilterObject } from './ExplorePage/ExplorePageUtils'; import { renderQueryBuilderFilterButtons } from './QueryBuilderUtils'; import { parseBucketsData } from './SearchUtils'; @@ -955,76 +956,77 @@ class AdvancedSearchClassBase { }; public getCustomPropertiesSubFields(field: CustomPropertySummary) { - { - switch (field.type) { - case 'array': - case 'entityReference': - return { - subfieldsKey: field.name + `.displayName`, - dataObject: { - type: 'select', - label: field.name, - fieldSettings: { - asyncFetch: this.autocomplete({ - searchIndex: ( - (field.customPropertyConfig?.config ?? []) as string[] - ).join(',') as SearchIndex, - entityField: EntityFields.DISPLAY_NAME_KEYWORD, - }), - useAsyncSearch: true, - }, + const label = getEntityName(field); + switch (field.type) { + case 'array': + case 'entityReference': + return { + subfieldsKey: field.name + `.displayName`, + dataObject: { + type: 'select', + label, + fieldSettings: { + asyncFetch: this.autocomplete({ + searchIndex: ( + (field.customPropertyConfig?.config ?? []) as string[] + ).join(',') as SearchIndex, + entityField: EntityFields.DISPLAY_NAME_KEYWORD, + }), + useAsyncSearch: true, }, - }; + }, + }; - case 'enum': - return { - subfieldsKey: field.name, - dataObject: { - type: 'select', - operators: LIST_VALUE_OPERATORS, - fieldSettings: { - listValues: getCustomPropertyAdvanceSearchEnumOptions( - ( - field.customPropertyConfig - ?.config as CustomPropertyEnumConfig - ).values - ), - }, + case 'enum': + return { + subfieldsKey: field.name, + dataObject: { + type: 'select', + label, + operators: LIST_VALUE_OPERATORS, + fieldSettings: { + listValues: getCustomPropertyAdvanceSearchEnumOptions( + (field.customPropertyConfig?.config as CustomPropertyEnumConfig) + .values + ), }, - }; + }, + }; - case 'date-cp': { - return { - subfieldsKey: field.name, - dataObject: { - type: 'date', - operators: RANGE_FIELD_OPERATORS, - }, - }; - } - - case 'timestamp': - case 'integer': - case 'number': { - return { - subfieldsKey: field.name, - dataObject: { - type: 'number', - operators: RANGE_FIELD_OPERATORS, - }, - }; - } - - default: - return { - subfieldsKey: field.name, - dataObject: { - type: 'text', - valueSources: ['value'], - operators: TEXT_FIELD_OPERATORS, - }, - }; + case 'date-cp': { + return { + subfieldsKey: field.name, + dataObject: { + type: 'date', + label, + operators: RANGE_FIELD_OPERATORS, + }, + }; } + + case 'timestamp': + case 'integer': + case 'number': { + return { + subfieldsKey: field.name, + dataObject: { + type: 'number', + label, + operators: RANGE_FIELD_OPERATORS, + }, + }; + } + + default: + return { + subfieldsKey: field.name, + dataObject: { + type: 'text', + label, + valueSources: ['value'], + operators: TEXT_FIELD_OPERATORS, + }, + }; } } } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderElasticsearchFormatUtils.js b/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderElasticsearchFormatUtils.js index a2de5a564fb..1d8d8bcd030 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderElasticsearchFormatUtils.js +++ b/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderElasticsearchFormatUtils.js @@ -244,6 +244,19 @@ function buildParameters(queryType, value, operator, fieldName, config) { */ function buildEsRule(fieldName, value, operator, config, valueSrc) { if (!fieldName || !operator || value == undefined) return undefined; // rule is not fully entered + + // Check if field has custom elasticsearch field mapping or handle extension fields + let actualFieldName = fieldName; + let isNestedExtensionField = false; + let entityType = null; + + if (fieldName.startsWith('extension.') && fieldName.split('.').length >= 3) { + const parts = fieldName.split('.'); + entityType = parts[1]; + actualFieldName = `${parts[0]}.${parts.slice(2).join('.')}`; + isNestedExtensionField = true; + } + let op = operator; let opConfig = config.operators[op]; if (!opConfig) return undefined; // unknown operator @@ -286,15 +299,17 @@ function buildEsRule(fieldName, value, operator, config, valueSrc) { queryType, value, op, - fieldName, + actualFieldName, config ); } else { - parameters = buildParameters(queryType, value, op, fieldName, config); + parameters = buildParameters(queryType, value, op, actualFieldName, config); } + // Build the main query + let mainQuery; if (not) { - return { + mainQuery = { bool: { must_not: { [queryType]: { ...parameters }, @@ -302,10 +317,28 @@ function buildEsRule(fieldName, value, operator, config, valueSrc) { }, }; } else { - return { + mainQuery = { [queryType]: { ...parameters }, }; } + + // For nested extension fields, combine with entityType filter + if (isNestedExtensionField && entityType) { + return { + bool: { + must: [ + mainQuery, + { + term: { + entityType: entityType, + }, + }, + ], + }, + }; + } + + return mainQuery; } /**