mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-18 14:06:59 +00:00
fix the search for custom properties in advance search for some types (#19113)
* fix the search in advance search custom properties * modify v1/metadata/types/customProperties api to get customPropertyConfig along with name and type * added config list in the entity ref list * fix type of duration, timestamp and other types * minor ui changes * remove customProperty type which are not supported for now * added playwright test around the duration type property * fix flaky test --------- Co-authored-by: sonikashah <sonikashah94@gmail.com> (cherry picked from commit 2179b43232803a9ebb7465fb1d545b887d68252c)
This commit is contained in:
parent
9a63d776b5
commit
cb733a2ad8
@ -32,7 +32,7 @@ import org.openmetadata.service.jdbi3.TypeRepository;
|
||||
@Slf4j
|
||||
public class SchemaFieldExtractor {
|
||||
|
||||
private static final Map<String, Map<String, String>> entityFieldsCache =
|
||||
private static final Map<String, Map<String, FieldDefinition>> entityFieldsCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
public SchemaFieldExtractor() {
|
||||
@ -55,7 +55,7 @@ public class SchemaFieldExtractor {
|
||||
Schema mainSchema = loadMainSchema(schemaPath, entityType, schemaUri, schemaClient);
|
||||
|
||||
// Extract fields from the schema
|
||||
Map<String, String> fieldTypesMap = new LinkedHashMap<>();
|
||||
Map<String, FieldDefinition> fieldTypesMap = new LinkedHashMap<>();
|
||||
Deque<Schema> processingStack = new ArrayDeque<>();
|
||||
Set<String> processedFields = new HashSet<>();
|
||||
extractFieldsFromSchema(mainSchema, "", fieldTypesMap, processingStack, processedFields);
|
||||
@ -75,7 +75,7 @@ public class SchemaFieldExtractor {
|
||||
SchemaClient schemaClient = new CustomSchemaClient(schemaUri);
|
||||
Deque<Schema> processingStack = new ArrayDeque<>();
|
||||
Set<String> processedFields = new HashSet<>();
|
||||
Map<String, String> fieldTypesMap = entityFieldsCache.get(entityType);
|
||||
Map<String, FieldDefinition> fieldTypesMap = entityFieldsCache.get(entityType);
|
||||
addCustomProperties(
|
||||
typeEntity, schemaUri, schemaClient, fieldTypesMap, processingStack, processedFields);
|
||||
return convertMapToFieldList(fieldTypesMap);
|
||||
@ -90,7 +90,7 @@ public class SchemaFieldExtractor {
|
||||
SchemaClient schemaClient = new CustomSchemaClient(schemaUri);
|
||||
EntityUtil.Fields fieldsParam = new EntityUtil.Fields(Set.of("customProperties"));
|
||||
Type typeEntity = repository.getByName(uriInfo, entityType, fieldsParam, Include.ALL, false);
|
||||
Map<String, String> fieldTypesMap = new LinkedHashMap<>();
|
||||
Map<String, FieldDefinition> fieldTypesMap = new LinkedHashMap<>();
|
||||
Set<String> processedFields = new HashSet<>();
|
||||
Deque<Schema> processingStack = new ArrayDeque<>();
|
||||
addCustomProperties(
|
||||
@ -170,7 +170,7 @@ public class SchemaFieldExtractor {
|
||||
private static void extractFieldsFromSchema(
|
||||
Schema schema,
|
||||
String parentPath,
|
||||
Map<String, String> fieldTypesMap,
|
||||
Map<String, FieldDefinition> fieldTypesMap,
|
||||
Deque<Schema> processingStack,
|
||||
Set<String> processedFields) {
|
||||
if (processingStack.contains(schema)) {
|
||||
@ -206,7 +206,8 @@ public class SchemaFieldExtractor {
|
||||
arraySchema, fullFieldName, fieldTypesMap, processingStack, processedFields);
|
||||
} else {
|
||||
String fieldType = mapSchemaTypeToSimpleType(fieldSchema);
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, fieldType);
|
||||
fieldTypesMap.putIfAbsent(
|
||||
fullFieldName, new FieldDefinition(fullFieldName, fieldType, null));
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added field '{}', Type: '{}'", fullFieldName, fieldType);
|
||||
// Recursively process nested objects or arrays
|
||||
@ -220,7 +221,7 @@ public class SchemaFieldExtractor {
|
||||
handleArraySchema(arraySchema, parentPath, fieldTypesMap, processingStack, processedFields);
|
||||
} else {
|
||||
String fieldType = mapSchemaTypeToSimpleType(schema);
|
||||
fieldTypesMap.putIfAbsent(parentPath, fieldType);
|
||||
fieldTypesMap.putIfAbsent(parentPath, new FieldDefinition(parentPath, fieldType, null));
|
||||
LOG.debug("Added field '{}', Type: '{}'", parentPath, fieldType);
|
||||
}
|
||||
} finally {
|
||||
@ -231,7 +232,7 @@ public class SchemaFieldExtractor {
|
||||
private static void handleReferenceSchema(
|
||||
ReferenceSchema referenceSchema,
|
||||
String fullFieldName,
|
||||
Map<String, String> fieldTypesMap,
|
||||
Map<String, FieldDefinition> fieldTypesMap,
|
||||
Deque<Schema> processingStack,
|
||||
Set<String> processedFields) {
|
||||
|
||||
@ -239,7 +240,8 @@ public class SchemaFieldExtractor {
|
||||
String referenceType = determineReferenceType(refUri);
|
||||
|
||||
if (referenceType != null) {
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, referenceType);
|
||||
fieldTypesMap.putIfAbsent(
|
||||
fullFieldName, new FieldDefinition(fullFieldName, referenceType, null));
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added field '{}', Type: '{}'", fullFieldName, referenceType);
|
||||
if (referenceType.startsWith("array<") && referenceType.endsWith(">")) {
|
||||
@ -255,7 +257,7 @@ public class SchemaFieldExtractor {
|
||||
referredSchema, fullFieldName, fieldTypesMap, processingStack, processedFields);
|
||||
}
|
||||
} else {
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, "object");
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, new FieldDefinition(fullFieldName, "object", null));
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added field '{}', Type: 'object'", fullFieldName);
|
||||
extractFieldsFromSchema(
|
||||
@ -270,7 +272,7 @@ public class SchemaFieldExtractor {
|
||||
private static void handleArraySchema(
|
||||
ArraySchema arraySchema,
|
||||
String fullFieldName,
|
||||
Map<String, String> fieldTypesMap,
|
||||
Map<String, FieldDefinition> fieldTypesMap,
|
||||
Deque<Schema> processingStack,
|
||||
Set<String> processedFields) {
|
||||
|
||||
@ -282,7 +284,8 @@ public class SchemaFieldExtractor {
|
||||
|
||||
if (itemsReferenceType != null) {
|
||||
String arrayFieldType = "array<" + itemsReferenceType + ">";
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, arrayFieldType);
|
||||
fieldTypesMap.putIfAbsent(
|
||||
fullFieldName, new FieldDefinition(fullFieldName, arrayFieldType, null));
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added field '{}', Type: '{}'", fullFieldName, arrayFieldType);
|
||||
Schema referredItemsSchema = itemsReferenceSchema.getReferredSchema();
|
||||
@ -292,7 +295,8 @@ public class SchemaFieldExtractor {
|
||||
}
|
||||
}
|
||||
String arrayType = mapSchemaTypeToSimpleType(itemsSchema);
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, "array<" + arrayType + ">");
|
||||
fieldTypesMap.putIfAbsent(
|
||||
fullFieldName, new FieldDefinition(fullFieldName, "array<" + arrayType + ">", null));
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added field '{}', Type: 'array<{}>'", fullFieldName, arrayType);
|
||||
|
||||
@ -306,7 +310,7 @@ public class SchemaFieldExtractor {
|
||||
Type typeEntity,
|
||||
String schemaUri,
|
||||
SchemaClient schemaClient,
|
||||
Map<String, String> fieldTypesMap,
|
||||
Map<String, FieldDefinition> fieldTypesMap,
|
||||
Deque<Schema> processingStack,
|
||||
Set<String> processedFields) {
|
||||
if (typeEntity == null || typeEntity.getCustomProperties() == null) {
|
||||
@ -320,9 +324,13 @@ public class SchemaFieldExtractor {
|
||||
|
||||
LOG.debug("Processing custom property '{}'", fullFieldName);
|
||||
|
||||
Object customPropertyConfigObj = customProperty.getCustomPropertyConfig();
|
||||
|
||||
if (isEntityReferenceList(propertyType)) {
|
||||
String referenceType = "array<entityReference>";
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, referenceType);
|
||||
FieldDefinition referenceFieldDefinition =
|
||||
new FieldDefinition(fullFieldName, referenceType, customPropertyConfigObj);
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, referenceFieldDefinition);
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added custom property '{}', Type: '{}'", fullFieldName, referenceType);
|
||||
|
||||
@ -337,7 +345,9 @@ public class SchemaFieldExtractor {
|
||||
}
|
||||
} else if (isEntityReference(propertyType)) {
|
||||
String referenceType = "entityReference";
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, referenceType);
|
||||
FieldDefinition referenceFieldDefinition =
|
||||
new FieldDefinition(fullFieldName, referenceType, customPropertyConfigObj);
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, referenceFieldDefinition);
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added custom property '{}', Type: '{}'", fullFieldName, referenceType);
|
||||
|
||||
@ -351,17 +361,22 @@ public class SchemaFieldExtractor {
|
||||
fullFieldName);
|
||||
}
|
||||
} else {
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, propertyType);
|
||||
FieldDefinition entityFieldDefinition =
|
||||
new FieldDefinition(fullFieldName, propertyType, customPropertyConfigObj);
|
||||
fieldTypesMap.putIfAbsent(fullFieldName, entityFieldDefinition);
|
||||
processedFields.add(fullFieldName);
|
||||
LOG.debug("Added custom property '{}', Type: '{}'", fullFieldName, propertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<FieldDefinition> convertMapToFieldList(Map<String, String> fieldTypesMap) {
|
||||
private List<FieldDefinition> convertMapToFieldList(Map<String, FieldDefinition> fieldTypesMap) {
|
||||
List<FieldDefinition> fieldsList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : fieldTypesMap.entrySet()) {
|
||||
fieldsList.add(new FieldDefinition(entry.getKey(), entry.getValue()));
|
||||
for (Map.Entry<String, FieldDefinition> entry : fieldTypesMap.entrySet()) {
|
||||
FieldDefinition fieldDef = entry.getValue();
|
||||
fieldsList.add(
|
||||
new FieldDefinition(
|
||||
fieldDef.getName(), fieldDef.getType(), fieldDef.getCustomPropertyConfig()));
|
||||
}
|
||||
return fieldsList;
|
||||
}
|
||||
@ -622,10 +637,12 @@ public class SchemaFieldExtractor {
|
||||
public static class FieldDefinition {
|
||||
private String name;
|
||||
private String type;
|
||||
private Object customPropertyConfig;
|
||||
|
||||
public FieldDefinition(String name, String type) {
|
||||
public FieldDefinition(String name, String type, Object customPropertyConfig) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.customPropertyConfig = customPropertyConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { CUSTOM_PROPERTIES_ENTITIES } from '../../constant/customProperty';
|
||||
import { GlobalSettingOptions } from '../../constant/settings';
|
||||
import { SidebarItem } from '../../constant/sidebar';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import {
|
||||
selectOption,
|
||||
showAdvancedSearchDialog,
|
||||
} from '../../utils/advancedSearch';
|
||||
import { advanceSearchSaveFilter } from '../../utils/advancedSearchCustomProperty';
|
||||
import { createNewPage, redirectToHomePage, uuid } from '../../utils/common';
|
||||
import { addCustomPropertiesForEntity } from '../../utils/customProperty';
|
||||
import { settingClick, sidebarClick } from '../../utils/sidebar';
|
||||
|
||||
// use the admin user to login
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
|
||||
test.describe('Advanced Search Custom Property', () => {
|
||||
const table = new TableClass();
|
||||
const durationPropertyName = `pwCustomPropertyDurationTest${uuid()}`;
|
||||
const durationPropertyValue = 'PT1H30M';
|
||||
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await table.create(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await table.delete(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test('Create, Assign and Test Advance Search for Duration', async ({
|
||||
page,
|
||||
}) => {
|
||||
test.slow(true);
|
||||
|
||||
await redirectToHomePage(page);
|
||||
|
||||
await test.step('Create and Assign Custom Property Value', async () => {
|
||||
await settingClick(page, GlobalSettingOptions.TABLES, true);
|
||||
|
||||
await addCustomPropertiesForEntity({
|
||||
page,
|
||||
propertyName: durationPropertyName,
|
||||
customPropertyData: CUSTOM_PROPERTIES_ENTITIES['entity_table'],
|
||||
customType: 'Duration',
|
||||
});
|
||||
|
||||
await table.visitEntityPage(page);
|
||||
|
||||
await page.getByTestId('custom_properties').click(); // Tab Click
|
||||
|
||||
await page
|
||||
.getByTestId(`custom-property-${durationPropertyName}-card`)
|
||||
.locator('svg')
|
||||
.click(); // Add Custom Property Value
|
||||
|
||||
await page.getByTestId('duration-input').fill(durationPropertyValue);
|
||||
|
||||
const saveResponse = page.waitForResponse('/api/v1/tables/*');
|
||||
await page.getByTestId('inline-save-btn').click();
|
||||
await saveResponse;
|
||||
});
|
||||
|
||||
await test.step('Verify Duration Type in Advance Search ', async () => {
|
||||
await sidebarClick(page, SidebarItem.EXPLORE);
|
||||
|
||||
await showAdvancedSearchDialog(page);
|
||||
|
||||
const ruleLocator = page.locator('.rule').nth(0);
|
||||
|
||||
// Perform click on rule field
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--field .ant-select'),
|
||||
'Custom Properties'
|
||||
);
|
||||
|
||||
// Perform click on custom property type to filter
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--field .ant-select'),
|
||||
durationPropertyName
|
||||
);
|
||||
|
||||
const inputElement = ruleLocator.locator(
|
||||
'.rule--widget--TEXT input[type="text"]'
|
||||
);
|
||||
|
||||
await inputElement.fill(durationPropertyValue);
|
||||
|
||||
await advanceSearchSaveFilter(page, durationPropertyValue);
|
||||
|
||||
await expect(
|
||||
page.getByTestId(
|
||||
`table-data-card_${table.entityResponseData.fullyQualifiedName}`
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Check around the Partial Search Value
|
||||
const partialSearchValue = durationPropertyValue.slice(0, 3);
|
||||
|
||||
await page.getByTestId('advance-search-filter-btn').click();
|
||||
|
||||
await expect(page.locator('[role="dialog"].ant-modal')).toBeVisible();
|
||||
|
||||
// Perform click on operator
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--operator .ant-select'),
|
||||
'Contains'
|
||||
);
|
||||
|
||||
await inputElement.fill(partialSearchValue);
|
||||
|
||||
await advanceSearchSaveFilter(page, partialSearchValue);
|
||||
|
||||
await expect(
|
||||
page.getByTestId(
|
||||
`table-data-card_${table.entityResponseData.fullyQualifiedName}`
|
||||
)
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { expect, Page } from '@playwright/test';
|
||||
|
||||
export const advanceSearchSaveFilter = async (
|
||||
page: Page,
|
||||
propertyValue: string
|
||||
) => {
|
||||
const searchResponse = page.waitForResponse(
|
||||
'/api/v1/search/query?*index=dataAsset&from=0&size=10*'
|
||||
);
|
||||
await page.getByTestId('apply-btn').click();
|
||||
|
||||
const res = await searchResponse;
|
||||
const json = await res.json();
|
||||
|
||||
expect(JSON.stringify(json)).toContain(propertyValue);
|
||||
};
|
@ -27,16 +27,15 @@ import {
|
||||
JsonTree,
|
||||
Utils as QbUtils,
|
||||
ValueField,
|
||||
ValueSource,
|
||||
} from 'react-awesome-query-builder';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import {
|
||||
emptyJsonTree,
|
||||
TEXT_FIELD_OPERATORS,
|
||||
} from '../../../constants/AdvancedSearch.constants';
|
||||
import { emptyJsonTree } from '../../../constants/AdvancedSearch.constants';
|
||||
import { SearchIndex } from '../../../enums/search.enum';
|
||||
import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation';
|
||||
import { TabsInfoData } from '../../../pages/ExplorePage/ExplorePage.interface';
|
||||
import { getAllCustomProperties } from '../../../rest/metadataTypeAPI';
|
||||
import advancedSearchClassBase from '../../../utils/AdvancedSearchClassBase';
|
||||
import {
|
||||
getTierOptions,
|
||||
getTreeConfig,
|
||||
@ -225,15 +224,24 @@ export const AdvanceSearchProvider = ({
|
||||
|
||||
Object.entries(res).forEach(([_, fields]) => {
|
||||
if (Array.isArray(fields) && fields.length > 0) {
|
||||
fields.forEach((field: { name: string; type: string }) => {
|
||||
if (field.name && field.type) {
|
||||
subfields[field.name] = {
|
||||
type: 'text',
|
||||
valueSources: ['value'],
|
||||
operators: TEXT_FIELD_OPERATORS,
|
||||
fields.forEach(
|
||||
(field: {
|
||||
name: string;
|
||||
type: string;
|
||||
customPropertyConfig: {
|
||||
config: string | string[];
|
||||
};
|
||||
}) => {
|
||||
if (field.name && field.type) {
|
||||
const { subfieldsKey, dataObject } =
|
||||
advancedSearchClassBase.getCustomPropertiesSubFields(field);
|
||||
subfields[subfieldsKey] = {
|
||||
...dataObject,
|
||||
valueSources: dataObject.valueSources as ValueSource[],
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -51,3 +51,7 @@ export interface AdvanceSearchContext {
|
||||
}
|
||||
|
||||
export type FilterObject = Record<string, string[]>;
|
||||
export interface CustomPropertyEnumConfig {
|
||||
multiSelect: boolean;
|
||||
values: string[];
|
||||
}
|
||||
|
@ -905,7 +905,7 @@ export const PropertyValue: FC<PropertyValueProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="d-flex justify-center flex-wrap gap-3"
|
||||
className="d-flex justify-center flex-wrap gap-2 py-2"
|
||||
data-testid="time-interval-value">
|
||||
<div className="d-flex flex-column gap-2 items-center">
|
||||
<StartTimeIcon height={30} width={30} />
|
||||
|
@ -299,6 +299,16 @@ export const TEXT_FIELD_OPERATORS = [
|
||||
'is_null',
|
||||
'is_not_null',
|
||||
];
|
||||
|
||||
export const RANGE_FIELD_OPERATORS = ['between', 'not_between'];
|
||||
|
||||
export const LIST_VALUE_OPERATORS = [
|
||||
'select_equals',
|
||||
'select_not_equals',
|
||||
'is_null',
|
||||
'is_not_null',
|
||||
];
|
||||
|
||||
/**
|
||||
* Generates a query builder tree with a group containing an empty rule
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
@ -10,9 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Airbyte Metadata Database Connection Config
|
||||
*/
|
||||
export interface AirbyteConnection {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
|
@ -255,6 +255,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.rule--widget--NUMBER {
|
||||
.widget--valuesrc {
|
||||
display: none;
|
||||
}
|
||||
.ant-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.rule--widget {
|
||||
min-width: 180px;
|
||||
.ant-select,
|
||||
|
@ -22,11 +22,20 @@ import {
|
||||
SelectFieldSettings,
|
||||
} from 'react-awesome-query-builder';
|
||||
import AntdConfig from 'react-awesome-query-builder/lib/config/antd';
|
||||
import { SEARCH_INDICES_WITH_COLUMNS_FIELD } from '../constants/AdvancedSearch.constants';
|
||||
import { CustomPropertyEnumConfig } from '../components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.interface';
|
||||
import {
|
||||
LIST_VALUE_OPERATORS,
|
||||
RANGE_FIELD_OPERATORS,
|
||||
SEARCH_INDICES_WITH_COLUMNS_FIELD,
|
||||
TEXT_FIELD_OPERATORS,
|
||||
} from '../constants/AdvancedSearch.constants';
|
||||
import { EntityFields, SuggestionField } from '../enums/AdvancedSearch.enum';
|
||||
import { SearchIndex } from '../enums/search.enum';
|
||||
import { getAggregateFieldOptions } from '../rest/miscAPI';
|
||||
import { renderAdvanceSearchButtons } from './AdvancedSearchUtils';
|
||||
import {
|
||||
getCustomPropertyAdvanceSearchEnumOptions,
|
||||
renderAdvanceSearchButtons,
|
||||
} from './AdvancedSearchUtils';
|
||||
import { getCombinedQueryFilterObject } from './ExplorePage/ExplorePageUtils';
|
||||
import { renderQueryBuilderFilterButtons } from './QueryBuilderUtils';
|
||||
|
||||
@ -208,12 +217,7 @@ class AdvancedSearchClassBase {
|
||||
[EntityFields.COLUMN_DESCRIPTION_STATUS]: {
|
||||
label: t('label.column-description'),
|
||||
type: 'select',
|
||||
operators: [
|
||||
'select_equals',
|
||||
'select_not_equals',
|
||||
'is_null',
|
||||
'is_not_null',
|
||||
],
|
||||
operators: LIST_VALUE_OPERATORS,
|
||||
mainWidgetProps: this.mainWidgetProps,
|
||||
valueSources: ['value'],
|
||||
fieldSettings: {
|
||||
@ -680,12 +684,7 @@ class AdvancedSearchClassBase {
|
||||
descriptionStatus: {
|
||||
label: t('label.description'),
|
||||
type: 'select',
|
||||
operators: [
|
||||
'select_equals',
|
||||
'select_not_equals',
|
||||
'is_null',
|
||||
'is_not_null',
|
||||
],
|
||||
operators: LIST_VALUE_OPERATORS,
|
||||
mainWidgetProps: this.mainWidgetProps,
|
||||
valueSources: ['value'],
|
||||
fieldSettings: {
|
||||
@ -899,6 +898,86 @@ class AdvancedSearchClassBase {
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
public getCustomPropertiesSubFields(field: {
|
||||
name: string;
|
||||
type: string;
|
||||
customPropertyConfig: {
|
||||
config: string | string[] | CustomPropertyEnumConfig;
|
||||
};
|
||||
}) {
|
||||
{
|
||||
switch (field.type) {
|
||||
case 'array<entityReference>':
|
||||
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,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case 'enum':
|
||||
return {
|
||||
subfieldsKey: field.name,
|
||||
dataObject: {
|
||||
type: 'select',
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const advancedSearchClassBase = new AdvancedSearchClassBase();
|
||||
|
@ -14,7 +14,7 @@
|
||||
import Icon, { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Checkbox, MenuProps, Space, Typography } from 'antd';
|
||||
import i18next from 'i18next';
|
||||
import { isArray, isEmpty } from 'lodash';
|
||||
import { isArray, isEmpty, toLower } from 'lodash';
|
||||
import React from 'react';
|
||||
import {
|
||||
AsyncFetchListValues,
|
||||
@ -452,3 +452,25 @@ export const getTreeConfig = ({
|
||||
? advancedSearchClassBase.getQbConfigs(tierOptions, index, isExplorePage)
|
||||
: jsonLogicSearchClassBase.getQbConfigs(tierOptions, index, isExplorePage);
|
||||
};
|
||||
|
||||
export const formatQueryValueBasedOnType = (
|
||||
value: string[],
|
||||
field: string,
|
||||
type: string
|
||||
) => {
|
||||
if (field.includes('extension') && type === 'text') {
|
||||
return value.map((item) => toLower(item));
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const getCustomPropertyAdvanceSearchEnumOptions = (
|
||||
enumValues: string[]
|
||||
) => {
|
||||
return enumValues.reduce((acc: Record<string, string>, value) => {
|
||||
acc[value] = value;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import { defaultConjunction } from 'react-awesome-query-builder/lib/utils/defaultUtils';
|
||||
import { getWidgetForFieldOp } from 'react-awesome-query-builder/lib/utils/ruleUtils';
|
||||
import { formatQueryValueBasedOnType } from './AdvancedSearchUtils';
|
||||
|
||||
/*
|
||||
* This script is a modified version of https://github.com/ukrbublik/react-awesome-query-builder/blob/5.1.2/modules/export/elasticSearch.js
|
||||
@ -372,7 +373,13 @@ export function elasticSearchFormat(tree, config) {
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return buildEsRule(field, value, operator, config, valueSrc);
|
||||
return buildEsRule(
|
||||
field,
|
||||
formatQueryValueBasedOnType(value, field, _valueType),
|
||||
operator,
|
||||
config,
|
||||
valueSrc
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user