From 69dc6b4c38de1764fce178f33296dc6665ad6345 Mon Sep 17 00:00:00 2001
From: Karan Hotchandani <33024356+karanh37@users.noreply.github.com>
Date: Thu, 24 Oct 2024 15:47:11 +0530
Subject: [PATCH] add query builder widget ui improvements (#18389)
* add query builder improvements
* fix alert flicker
(cherry picked from commit c8e2ed0653ac8eaf44f3fbfc788a091aceeefc10)
---
.../AppRouter/withAdvanceSearch.tsx | 8 +-
.../QueryBuilderWidget/QueryBuilderWidget.tsx | 142 +++++++++++-------
.../query-builder-widget.less | 137 +++++++++++++++++
.../resources/ui/src/styles/variables.less | 7 +-
.../ui/src/utils/AdvancedSearchClassBase.ts | 5 +-
...yBuilderUtils.ts => QueryBuilderUtils.tsx} | 40 ++++-
6 files changed, 276 insertions(+), 63 deletions(-)
rename openmetadata-ui/src/main/resources/ui/src/utils/{QueryBuilderUtils.ts => QueryBuilderUtils.tsx} (89%)
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withAdvanceSearch.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withAdvanceSearch.tsx
index 19b05f55ec2..db88f2c9330 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withAdvanceSearch.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withAdvanceSearch.tsx
@@ -12,12 +12,16 @@
*/
import React, { FC } from 'react';
import { AdvanceSearchProvider } from '../../components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
+import { AdvanceSearchProviderProps } from '../Explore/AdvanceSearchProvider/AdvanceSearchProvider.interface';
export const withAdvanceSearch =
-
>(Component: FC
) =>
+
>(
+ Component: FC
,
+ providerProps?: Omit
+ ) =>
(props: P) => {
return (
-
+
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
index 42b703adff2..21d181da82b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
@@ -12,7 +12,7 @@
*/
import { InfoCircleOutlined } from '@ant-design/icons';
import { WidgetProps } from '@rjsf/utils';
-import { Alert, Button, Col, Typography } from 'antd';
+import { Alert, Button, Card, Col, Row, Skeleton, Typography } from 'antd';
import { t } from 'i18next';
import { debounce, isEmpty, isUndefined } from 'lodash';
import Qs from 'qs';
@@ -29,6 +29,7 @@ import { getExplorePath } from '../../../../../../constants/constants';
import { EntityType } from '../../../../../../enums/entity.enum';
import { SearchIndex } from '../../../../../../enums/search.enum';
import { searchQuery } from '../../../../../../rest/searchAPI';
+import { elasticSearchFormat } from '../../../../../../utils/QueryBuilderElasticsearchFormatUtils';
import { getJsonTreeFromQueryFilter } from '../../../../../../utils/QueryBuilderUtils';
import searchClassBase from '../../../../../../utils/SearchClassBase';
import { withAdvanceSearch } from '../../../../../AppRouter/withAdvanceSearch';
@@ -44,7 +45,8 @@ const QueryBuilderWidget: FC = ({
}: WidgetProps) => {
const { config, treeInternal, onTreeUpdate, onChangeSearchIndex } =
useAdvanceSearch();
- const [searchResults, setSearchResults] = useState(0);
+ const [searchResults, setSearchResults] = useState();
+ const [isCountLoading, setIsCountLoading] = useState(false);
const entityType =
(props.formContext?.entityType ?? schema?.entityType) || EntityType.ALL;
const searchIndexMapping = searchClassBase.getEntityTypeSearchIndexMapping();
@@ -54,6 +56,7 @@ const QueryBuilderWidget: FC = ({
const fetchEntityCount = useCallback(
async (queryFilter: Record) => {
try {
+ setIsCountLoading(true);
const res = await searchQuery({
query: '',
pageNumber: 0,
@@ -67,6 +70,8 @@ const QueryBuilderWidget: FC = ({
setSearchResults(res.hits.total.value ?? 0);
} catch (_) {
// silent fail
+ } finally {
+ setIsCountLoading(false);
}
},
[]
@@ -85,11 +90,20 @@ const QueryBuilderWidget: FC = ({
return `${getExplorePath({})}${queryFilterString}`;
}, [treeInternal]);
+ const showFilteredResourceCount = useMemo(
+ () =>
+ outputType === QueryBuilderOutputType.ELASTICSEARCH &&
+ !isUndefined(value) &&
+ searchResults !== undefined &&
+ !isCountLoading,
+ [outputType, value, isCountLoading]
+ );
+
const handleChange = (nTree: ImmutableTree, nConfig: Config) => {
onTreeUpdate(nTree, nConfig);
if (outputType === QueryBuilderOutputType.ELASTICSEARCH) {
- const data = QbUtils.elasticSearchFormat(nTree, config) ?? {};
+ const data = elasticSearchFormat(nTree, config) ?? {};
const qFilter = {
query: data,
};
@@ -109,17 +123,21 @@ const QueryBuilderWidget: FC = ({
}, [searchIndex]);
useEffect(() => {
- if (
- !isEmpty(value) &&
- outputType === QueryBuilderOutputType.ELASTICSEARCH
- ) {
- const tree = QbUtils.checkTree(
- QbUtils.loadTree(
- getJsonTreeFromQueryFilter(JSON.parse(value || '')) as JsonTree
- ),
- config
- );
- onTreeUpdate(tree, config);
+ if (!isEmpty(value)) {
+ if (outputType === QueryBuilderOutputType.ELASTICSEARCH) {
+ const tree = QbUtils.checkTree(
+ QbUtils.loadTree(
+ getJsonTreeFromQueryFilter(JSON.parse(value || '')) as JsonTree
+ ),
+ config
+ );
+ onTreeUpdate(tree, config);
+ } else {
+ const tree = QbUtils.loadFromJsonLogic(JSON.parse(value || ''), config);
+ if (tree) {
+ onTreeUpdate(tree, config);
+ }
+ }
}
}, []);
@@ -127,50 +145,66 @@ const QueryBuilderWidget: FC = ({
-
(
-
-
-
- )}
- value={treeInternal}
- onChange={handleChange}
- />
- {outputType === QueryBuilderOutputType.ELASTICSEARCH &&
- !isUndefined(value) && (
-
-
+ )}
+
+ {showFilteredResourceCount && (
+
+
+
+ )}
- )}
+
+
);
};
-export default withAdvanceSearch(QueryBuilderWidget);
+export default withAdvanceSearch(QueryBuilderWidget, { isExplorePage: false });
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/query-builder-widget.less b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/query-builder-widget.less
index 43d768729e2..f7b038a2c95 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/query-builder-widget.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/query-builder-widget.less
@@ -10,6 +10,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+@import (reference) url('../../../../../../styles/variables.less');
+
+.query-builder-card {
+ background-color: @grey-6;
+
+ .ant-alert-info {
+ background-color: @blue-8;
+ border-color: @blue-7;
+
+ .ant-alert-icon {
+ color: @blue-7;
+ }
+ }
+}
+
.query-builder-form-field {
.hide--line.one--child {
margin-top: 0;
@@ -39,4 +55,125 @@
margin-bottom: 6px;
}
}
+
+ .query-builder-container {
+ .group-or-rule-container.rule-container {
+ padding: 0px;
+
+ .rule.group-or-rule {
+ .rule--header {
+ .ant-btn-group {
+ margin: 0px;
+ align-self: flex-start;
+ }
+ }
+ }
+ }
+
+ .group-or-rule-container.group-container {
+ padding: 0px;
+
+ .group.rule_group {
+ .group--field {
+ margin: 0px;
+ flex: 0 1 25%;
+
+ .ant-select {
+ min-width: 100% !important; // override the inline min-width style of the select provided by antd
+ }
+ }
+ }
+
+ & > .group.group-or-rule {
+ display: flex;
+ flex-direction: column;
+
+ & > .group--header {
+ order: 9999;
+
+ .group--actions.group--actions--tr {
+ justify-content: flex-start;
+ margin: 0px;
+ }
+
+ .action.action--ADD-GROUP {
+ display: none;
+ }
+
+ .rule-container .ant-btn-group {
+ visibility: visible;
+ }
+
+ .action.action--ADD-RULE {
+ position: static;
+ margin-top: 8px;
+ }
+ }
+
+ .rule--body--wrapper {
+ .rule--body {
+ margin: 0px;
+ display: flex;
+ gap: 16px;
+
+ .group--field,
+ .rule--field,
+ .rule--operator,
+ .rule--value,
+ .widget--widget {
+ margin: 0px;
+ flex: 1;
+
+ .ant-col {
+ padding: 0px !important; // remove padding from ant-col inline styling by antd
+ }
+ }
+
+ .rule--operator,
+ .rule--value .rule--widget {
+ width: 100%;
+
+ .ant-select {
+ min-width: 100% !important; // override the inline min-width style of the select provided by antd
+ }
+ }
+ }
+ }
+ }
+
+ & > .group.group-or-rule.rule_group {
+ flex-direction: row;
+ align-items: center;
+ }
+ }
+
+ .group--children {
+ padding: 0px;
+ margin: 0px;
+
+ .group-or-rule-container.group-container,
+ .group-or-rule-container.rule-container {
+ .group.group-or-rule,
+ .rule.group-or-rule {
+ background: inherit;
+ padding: 0px;
+ border: none;
+
+ .group--header {
+ .group--conjunctions {
+ display: none;
+ }
+ }
+
+ &:not(:first-child) {
+ padding: 16px 8px;
+ }
+ }
+ }
+
+ .rule-container .ant-btn-group {
+ visibility: visible;
+ }
+ }
+ }
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/variables.less b/openmetadata-ui/src/main/resources/ui/src/styles/variables.less
index 7dca1705868..5a086f7893c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/styles/variables.less
+++ b/openmetadata-ui/src/main/resources/ui/src/styles/variables.less
@@ -52,6 +52,8 @@
@blue-4: #f1f9ff;
@blue-5: #f2f6fc;
@blue-6: #eff5ff;
+@blue-7: #3062d4;
+@blue-8: #f5f8ff;
@partial-success-1: #06a4a4;
@partial-success-2: #bdeeee;
@black: #000000;
@@ -112,9 +114,8 @@
// 172px - navbar height
@entity-details-tab-height: calc(100vh - 172px - @om-navbar-height);
-@users-page-tabs-height: calc(
- 100vh - @om-navbar-height - 58px
-); /* navbar+tab_height+padding = 64+46+12 */
+@users-page-tabs-height: calc(100vh - @om-navbar-height - 58px);
+/* navbar+tab_height+padding = 64+46+12 */
// 142px - navbar height
@glossary-page-height: calc(100vh - 142px - @om-navbar-height);
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 e5d2d2ee2ff..d71aad8dae6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts
@@ -27,6 +27,7 @@ import { SearchIndex } from '../enums/search.enum';
import { getAggregateFieldOptions } from '../rest/miscAPI';
import { renderAdvanceSearchButtons } from './AdvancedSearchUtils';
import { getCombinedQueryFilterObject } from './ExplorePage/ExplorePageUtils';
+import { renderQueryBuilderFilterButtons } from './QueryBuilderUtils';
class AdvancedSearchClassBase {
baseConfig = AntdConfig as BasicConfig;
@@ -408,7 +409,9 @@ class AdvancedSearchClassBase {
operatorLabel: t('label.condition') + ':',
showNot: false,
valueLabel: t('label.criteria') + ':',
- renderButton: renderAdvanceSearchButtons,
+ renderButton: isExplorePage
+ ? renderAdvanceSearchButtons
+ : renderQueryBuilderFilterButtons,
},
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx
similarity index 89%
rename from openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.ts
rename to openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx
index 1f53db69ac0..75149e12f5d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx
@@ -10,7 +10,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { CloseOutlined } from '@ant-design/icons';
+import { Button } from 'antd';
+import { t } from 'i18next';
import { isUndefined } from 'lodash';
+import React from 'react';
+import { RenderSettings } from 'react-awesome-query-builder';
import {
EsBoolQuery,
EsExistsQuery,
@@ -262,9 +267,7 @@ export const getJsonTreePropertyFromQueryFilter = (
...acc,
...getCommonFieldProperties(
parentPath,
- Object.keys(
- (curr.bool?.must_not as EsWildCard)?.wildcard
- )[0] as string,
+ Object.keys((curr.bool?.must_not as EsWildCard)?.wildcard)[0],
'not_like',
Object.values((curr.bool?.must_not as EsWildCard)?.wildcard)[0]
?.value
@@ -311,3 +314,34 @@ export const getJsonTreeFromQueryFilter = (
return {};
}
};
+
+export const renderQueryBuilderFilterButtons: RenderSettings['renderButton'] = (
+ props
+) => {
+ const type = props?.type;
+
+ if (type === 'delRule') {
+ return (
+ }
+ onClick={props?.onClick}
+ />
+ );
+ } else if (type === 'addRule') {
+ return (
+
+ );
+ }
+
+ return <>>;
+};