mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-13 17:32:53 +00:00
persist es query value (#18360)
(cherry picked from commit 3c244da0516efcff150ddf5abfb8f556f3b8b642)
This commit is contained in:
parent
540ecefdd0
commit
4af077fbd6
@ -21,6 +21,7 @@ import {
|
|||||||
Builder,
|
Builder,
|
||||||
Config,
|
Config,
|
||||||
ImmutableTree,
|
ImmutableTree,
|
||||||
|
JsonTree,
|
||||||
Query,
|
Query,
|
||||||
Utils as QbUtils,
|
Utils as QbUtils,
|
||||||
} from 'react-awesome-query-builder';
|
} from 'react-awesome-query-builder';
|
||||||
@ -28,6 +29,7 @@ import { getExplorePath } from '../../../../../../constants/constants';
|
|||||||
import { EntityType } from '../../../../../../enums/entity.enum';
|
import { EntityType } from '../../../../../../enums/entity.enum';
|
||||||
import { SearchIndex } from '../../../../../../enums/search.enum';
|
import { SearchIndex } from '../../../../../../enums/search.enum';
|
||||||
import { searchQuery } from '../../../../../../rest/searchAPI';
|
import { searchQuery } from '../../../../../../rest/searchAPI';
|
||||||
|
import { getJsonTreeFromQueryFilter } from '../../../../../../utils/QueryBuilderUtils';
|
||||||
import searchClassBase from '../../../../../../utils/SearchClassBase';
|
import searchClassBase from '../../../../../../utils/SearchClassBase';
|
||||||
import { withAdvanceSearch } from '../../../../../AppRouter/withAdvanceSearch';
|
import { withAdvanceSearch } from '../../../../../AppRouter/withAdvanceSearch';
|
||||||
import { useAdvanceSearch } from '../../../../../Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
|
import { useAdvanceSearch } from '../../../../../Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
|
||||||
@ -106,6 +108,21 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
onChangeSearchIndex(searchIndex);
|
onChangeSearchIndex(searchIndex);
|
||||||
}, [searchIndex]);
|
}, [searchIndex]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!isEmpty(value) &&
|
||||||
|
outputType === QueryBuilderOutputType.ELASTICSEARCH
|
||||||
|
) {
|
||||||
|
const tree = QbUtils.checkTree(
|
||||||
|
QbUtils.loadTree(
|
||||||
|
getJsonTreeFromQueryFilter(JSON.parse(value || '')) as JsonTree
|
||||||
|
),
|
||||||
|
config
|
||||||
|
);
|
||||||
|
onTreeUpdate(tree, config);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="query-builder-form-field"
|
className="query-builder-form-field"
|
||||||
|
|||||||
@ -38,6 +38,12 @@ export interface EsExistsQuery {
|
|||||||
field: string;
|
field: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EsWildCard {
|
||||||
|
wildcard: {
|
||||||
|
[key: string]: { value: string };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface EsBoolQuery {
|
export interface EsBoolQuery {
|
||||||
filter?: QueryFieldInterface | QueryFieldInterface[];
|
filter?: QueryFieldInterface | QueryFieldInterface[];
|
||||||
must?: QueryFieldInterface | QueryFieldInterface[];
|
must?: QueryFieldInterface | QueryFieldInterface[];
|
||||||
@ -56,6 +62,12 @@ export interface QueryFilterInterface {
|
|||||||
query: QueryFieldInterface;
|
query: QueryFieldInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EsTerm {
|
||||||
|
term: {
|
||||||
|
[key: string]: string | boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export enum ExploreSidebarTab {
|
export enum ExploreSidebarTab {
|
||||||
ASSETS = 'assets',
|
ASSETS = 'assets',
|
||||||
TREE = 'tree',
|
TREE = 'tree',
|
||||||
|
|||||||
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 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 { QueryFilterInterface } from '../pages/ExplorePage/ExplorePage.interface';
|
||||||
|
import { getJsonTreeFromQueryFilter } from './QueryBuilderUtils';
|
||||||
|
|
||||||
|
jest.mock('./StringsUtils', () => ({
|
||||||
|
generateUUID: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('getJsonTreeFromQueryFilter', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a valid JSON tree structure for a given query filter', () => {
|
||||||
|
const mockUUIDs = ['uuid1', 'uuid2', 'uuid3', 'uuid4'];
|
||||||
|
(
|
||||||
|
jest.requireMock('./StringsUtils').generateUUID as jest.Mock
|
||||||
|
).mockImplementation(() => mockUUIDs.shift());
|
||||||
|
const queryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
bool: {
|
||||||
|
must: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
field1: 'value1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getJsonTreeFromQueryFilter(queryFilter);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'group',
|
||||||
|
properties: { conjunction: 'AND', not: false },
|
||||||
|
children1: {
|
||||||
|
uuid2: {
|
||||||
|
type: 'group',
|
||||||
|
properties: { conjunction: 'AND', not: false },
|
||||||
|
children1: {
|
||||||
|
uuid3: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: 'field1',
|
||||||
|
operator: 'select_equals',
|
||||||
|
value: ['value1'],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
operatorOptions: null,
|
||||||
|
valueType: ['select'],
|
||||||
|
asyncListValues: [
|
||||||
|
{
|
||||||
|
key: 'value1',
|
||||||
|
value: 'value1',
|
||||||
|
children: 'value1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
id: 'uuid3',
|
||||||
|
path: ['uuid1', 'uuid2', 'uuid3'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: 'uuid2',
|
||||||
|
path: ['uuid1', 'uuid2'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: 'uuid1',
|
||||||
|
path: ['uuid1'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty object if an error occurs', () => {
|
||||||
|
const queryFilter: QueryFilterInterface = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
must: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getJsonTreeFromQueryFilter(queryFilter);
|
||||||
|
|
||||||
|
expect(result).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 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 { isUndefined } from 'lodash';
|
||||||
|
import {
|
||||||
|
EsBoolQuery,
|
||||||
|
EsExistsQuery,
|
||||||
|
EsTerm,
|
||||||
|
EsWildCard,
|
||||||
|
QueryFieldInterface,
|
||||||
|
QueryFilterInterface,
|
||||||
|
} from '../pages/ExplorePage/ExplorePage.interface';
|
||||||
|
import { generateUUID } from './StringsUtils';
|
||||||
|
|
||||||
|
export const getSelectEqualsNotEqualsProperties = (
|
||||||
|
parentPath: Array<string>,
|
||||||
|
field: string,
|
||||||
|
value: string,
|
||||||
|
operator: string
|
||||||
|
) => {
|
||||||
|
const id = generateUUID();
|
||||||
|
|
||||||
|
return {
|
||||||
|
[id]: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: field,
|
||||||
|
operator,
|
||||||
|
value: [value],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
operatorOptions: null,
|
||||||
|
valueType: ['select'],
|
||||||
|
asyncListValues: [
|
||||||
|
{
|
||||||
|
key: value,
|
||||||
|
value,
|
||||||
|
children: value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
path: [...parentPath, id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSelectAnyInProperties = (
|
||||||
|
parentPath: Array<string>,
|
||||||
|
termObjects: Array<EsTerm>
|
||||||
|
) => {
|
||||||
|
const values = termObjects.map(
|
||||||
|
(termObject) => Object.values(termObject.term)[0]
|
||||||
|
);
|
||||||
|
const id = generateUUID();
|
||||||
|
|
||||||
|
return {
|
||||||
|
[id]: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: Object.keys(termObjects[0].term)[0],
|
||||||
|
operator: 'select_any_in',
|
||||||
|
value: [values],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
operatorOptions: null,
|
||||||
|
valueType: ['multiselect'],
|
||||||
|
asyncListValues: values.map((value) => ({
|
||||||
|
key: value,
|
||||||
|
value,
|
||||||
|
children: value,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
path: [...parentPath, id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSelectNotAnyInProperties = (
|
||||||
|
parentPath: Array<string>,
|
||||||
|
termObjects: QueryFieldInterface[]
|
||||||
|
) => {
|
||||||
|
const values = termObjects.map(
|
||||||
|
(termObject) =>
|
||||||
|
Object.values((termObject?.bool?.must_not as EsTerm)?.term)[0]
|
||||||
|
);
|
||||||
|
const id = generateUUID();
|
||||||
|
|
||||||
|
return {
|
||||||
|
[id]: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: Object.keys((termObjects[0].bool?.must_not as EsTerm).term)[0],
|
||||||
|
operator: 'select_not_any_in',
|
||||||
|
value: [values],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
operatorOptions: null,
|
||||||
|
valueType: ['multiselect'],
|
||||||
|
asyncListValues: values.map((value) => ({
|
||||||
|
key: value,
|
||||||
|
value,
|
||||||
|
children: value,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
path: [...parentPath, id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCommonFieldProperties = (
|
||||||
|
parentPath: Array<string>,
|
||||||
|
field: string,
|
||||||
|
operator: string,
|
||||||
|
value?: string
|
||||||
|
) => {
|
||||||
|
const id = generateUUID();
|
||||||
|
|
||||||
|
return {
|
||||||
|
[id]: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field,
|
||||||
|
operator,
|
||||||
|
value: isUndefined(value) ? [] : [value.replaceAll(/(^\*)|(\*$)/g, '')],
|
||||||
|
valueSrc: isUndefined(value) ? [] : ['value'],
|
||||||
|
operatorOptions: null,
|
||||||
|
valueType: isUndefined(value) ? [] : ['text'],
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
path: [...parentPath, id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEqualFieldProperties = (
|
||||||
|
parentPath: Array<string>,
|
||||||
|
value: boolean
|
||||||
|
) => {
|
||||||
|
const id = generateUUID();
|
||||||
|
|
||||||
|
return {
|
||||||
|
[id]: {
|
||||||
|
type: 'rule',
|
||||||
|
properties: {
|
||||||
|
field: 'deleted',
|
||||||
|
operator: 'equal',
|
||||||
|
value: [value],
|
||||||
|
valueSrc: ['value'],
|
||||||
|
operatorOptions: null,
|
||||||
|
valueType: ['boolean'],
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
path: [...parentPath, id],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getJsonTreePropertyFromQueryFilter = (
|
||||||
|
parentPath: Array<string>,
|
||||||
|
queryFilter: QueryFieldInterface[]
|
||||||
|
) => {
|
||||||
|
const convertedObj = queryFilter.reduce(
|
||||||
|
(acc, curr: QueryFieldInterface): Record<string, any> => {
|
||||||
|
if (!isUndefined(curr.term?.deleted)) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getEqualFieldProperties(parentPath, curr.term?.deleted as boolean),
|
||||||
|
};
|
||||||
|
} else if (!isUndefined(curr.term)) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getSelectEqualsNotEqualsProperties(
|
||||||
|
parentPath,
|
||||||
|
Object.keys(curr.term)[0],
|
||||||
|
Object.values(curr.term)[0] as string,
|
||||||
|
'select_equals'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
!isUndefined((curr.bool?.must_not as QueryFieldInterface).term)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getSelectEqualsNotEqualsProperties(
|
||||||
|
parentPath,
|
||||||
|
Object.keys((curr.bool?.must_not as EsTerm)?.term)[0],
|
||||||
|
Object.values((curr.bool?.must_not as EsTerm)?.term)[0] as string,
|
||||||
|
'select_not_equals'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
!isUndefined(
|
||||||
|
((curr.bool?.should as QueryFieldInterface[])?.[0] as EsTerm)?.term
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getSelectAnyInProperties(
|
||||||
|
parentPath,
|
||||||
|
curr?.bool?.should as EsTerm[]
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
!isUndefined(
|
||||||
|
(
|
||||||
|
(curr.bool?.should as QueryFieldInterface[])?.[0]?.bool
|
||||||
|
?.must_not as EsTerm
|
||||||
|
)?.term
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getSelectNotAnyInProperties(
|
||||||
|
parentPath,
|
||||||
|
curr?.bool?.should as QueryFieldInterface[]
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
!isUndefined(
|
||||||
|
(curr.bool?.must_not as QueryFieldInterface)?.exists?.field
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getCommonFieldProperties(
|
||||||
|
parentPath,
|
||||||
|
(curr.bool?.must_not as QueryFieldInterface)?.exists
|
||||||
|
?.field as string,
|
||||||
|
'is_null'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (!isUndefined(curr.exists?.field)) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getCommonFieldProperties(
|
||||||
|
parentPath,
|
||||||
|
(curr.exists as EsExistsQuery).field,
|
||||||
|
'is_not_null'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (!isUndefined((curr as EsWildCard).wildcard)) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getCommonFieldProperties(
|
||||||
|
parentPath,
|
||||||
|
Object.keys((curr as EsWildCard).wildcard)[0],
|
||||||
|
'like',
|
||||||
|
Object.values((curr as EsWildCard).wildcard)[0]?.value
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (!isUndefined((curr.bool?.must_not as EsWildCard)?.wildcard)) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...getCommonFieldProperties(
|
||||||
|
parentPath,
|
||||||
|
Object.keys(
|
||||||
|
(curr.bool?.must_not as EsWildCard)?.wildcard
|
||||||
|
)[0] as string,
|
||||||
|
'not_like',
|
||||||
|
Object.values((curr.bool?.must_not as EsWildCard)?.wildcard)[0]
|
||||||
|
?.value
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, any>
|
||||||
|
);
|
||||||
|
|
||||||
|
return convertedObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getJsonTreeFromQueryFilter = (
|
||||||
|
queryFilter: QueryFilterInterface
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const id1 = generateUUID();
|
||||||
|
const id2 = generateUUID();
|
||||||
|
const mustFilters = queryFilter?.query?.bool?.must as QueryFieldInterface[];
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'group',
|
||||||
|
properties: { conjunction: 'AND', not: false },
|
||||||
|
children1: {
|
||||||
|
[id2]: {
|
||||||
|
type: 'group',
|
||||||
|
properties: { conjunction: 'AND', not: false },
|
||||||
|
children1: getJsonTreePropertyFromQueryFilter(
|
||||||
|
[id1, id2],
|
||||||
|
(mustFilters?.[0]?.bool as EsBoolQuery)
|
||||||
|
.must as QueryFieldInterface[]
|
||||||
|
),
|
||||||
|
id: id2,
|
||||||
|
path: [id1, id2],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: id1,
|
||||||
|
path: [id1],
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user