This is an automated email from the ASF dual-hosted git repository. ppawar pushed a commit to branch ATLAS-5132 in repository https://gitbox.apache.org/repos/asf/atlas.git
commit b36d705866e68b733c05795d6e2e0702dbeca0ec Author: Prasad Pawar <[email protected]> AuthorDate: Thu Sep 25 12:36:02 2025 +0530 ATLAS-5132: [REACT UI] Unique Advanced/Search queries do not work as expected --- dashboard/src/components/Table/TableLayout.tsx | 50 +++++++- dashboard/src/components/muiComponents.tsx | 26 ++-- dashboard/src/views/SearchResult/SearchResult.tsx | 134 +++++++++++++++++---- .../public/js/views/graph/LineageLayoutView.js | 4 +- .../public/js/views/graph/TypeSystemTreeView.js | 7 +- .../public/js/views/graph/LineageLayoutView.js | 4 +- .../public/js/views/graph/TypeSystemTreeView.js | 7 +- 7 files changed, 186 insertions(+), 46 deletions(-) diff --git a/dashboard/src/components/Table/TableLayout.tsx b/dashboard/src/components/Table/TableLayout.tsx index f58d150b0..931bda7ac 100644 --- a/dashboard/src/components/Table/TableLayout.tsx +++ b/dashboard/src/components/Table/TableLayout.tsx @@ -374,14 +374,40 @@ const TableLayout: FC<TableProps> = ({ const [goToPageVal, setGoToPageVal] = useState<any>(""); const [rowSelection, setRowSelection] = useState({}); - const [sorting, setSorting] = useState<SortingState>( - !isEmpty(defaultSortCol) ? defaultSortCol : [] + const existingColumnIds = useMemo( + () => + (!isEmpty(memoizedColumns) + ? memoizedColumns.map((c: any) => c.id || c.accessorKey) + : []) as string[], + [memoizedColumns] ); + + const sanitizeSorting = (sortingState: SortingState) => { + if (isEmpty(sortingState)) return [] as SortingState; + return sortingState.filter( + (s: any) => s && existingColumnIds.includes(s.id) + ); + }; + + const [sorting, setSorting] = useState<SortingState>(() => []); + const [columnOrder, setColumnOrder] = useState<any>(() => !isEmpty(memoizedColumns) - ? memoizedColumns.map((c: any) => c.accessorKey) + ? memoizedColumns + .map((c: any) => c.id || c.accessorKey) + .filter(Boolean) : [] ); + + useEffect(() => { + setColumnOrder( + !isEmpty(memoizedColumns) + ? memoizedColumns + .map((c: any) => c.id || c.accessorKey) + .filter(Boolean) + : [] + ) + }, [memoizedColumns]) const [columnVisibility, setColumnVisibility] = useState(defaultHideColumns); const [tagModal, setTagModal] = useState<boolean>(false); let currentParams = searchParams; @@ -426,7 +452,7 @@ const TableLayout: FC<TableProps> = ({ columnVisibility: columnVisibilityParams ? defaultHideColumns : columnVisibility, - sorting, + sorting: sanitizeSorting(sorting), pagination, rowSelection, columnOrder @@ -437,6 +463,17 @@ const TableLayout: FC<TableProps> = ({ autoResetPageIndex: false }); + useEffect(() => { + setSorting((prev) => sanitizeSorting(prev)); + setColumnOrder( + !isEmpty(memoizedColumns) + ? memoizedColumns + .map((c: any) => c.id || c.accessorKey) + .filter(Boolean) + : [] + ); + }, [existingColumnIds]); + useEffect(() => { if (typeof fetchData === "function") { fetchData({ @@ -474,7 +511,8 @@ const TableLayout: FC<TableProps> = ({ resetSorting(true); resetRowSelection(true); setRowSelection({}); - setSorting(!isEmpty(defaultSortCol) ? defaultSortCol : []); + const candidate = !isEmpty(defaultSortCol) ? defaultSortCol : [] + setSorting(sanitizeSorting(candidate)); }, [typeParam, defaultSortCol, setSearchParams]); const handleCloseTagModal = () => { @@ -619,7 +657,7 @@ const TableLayout: FC<TableProps> = ({ items={columnOrder} strategy={horizontalListSortingStrategy} > - {" "} + {headerGroup.headers.map((header) => header.isPlaceholder ? null : ( <DraggableTableHeader diff --git a/dashboard/src/components/muiComponents.tsx b/dashboard/src/components/muiComponents.tsx index fc4492ef1..cf96d2c6d 100644 --- a/dashboard/src/components/muiComponents.tsx +++ b/dashboard/src/components/muiComponents.tsx @@ -104,18 +104,20 @@ const CustomButton = ({ let mergedStyle = { ...defaultStyles, ...customStyles }; return ( - <Button - variant={variant} - color={color} - sx={mergedStyle} - onClick={onClick} - size={size} - endIcon={endIcon} - startIcon={startIcon} - disabled={disabled} - > - {children} - </Button> + <Box component="span" sx={{ display: 'inline-flex' }}> + <Button + variant={variant} + color={color} + sx={mergedStyle} + onClick={onClick} + size={size} + endIcon={endIcon} + startIcon={startIcon} + disabled={disabled} + > + {children} + </Button> + </Box> ); }; diff --git a/dashboard/src/views/SearchResult/SearchResult.tsx b/dashboard/src/views/SearchResult/SearchResult.tsx index aa1cd0921..c181aa877 100644 --- a/dashboard/src/views/SearchResult/SearchResult.tsx +++ b/dashboard/src/views/SearchResult/SearchResult.tsx @@ -207,8 +207,17 @@ const SearchResult = ({ classificationParams, glossaryTypeParams }: any) => { globalSearchParams.basicParams = params; globalSearchParams.dslParams = dslParams; - let searchTypeParams = - searchParams.get("searchType") == "dsl" ? dslParams : params; + + const qParam = searchParams.get("query"); + if (qParam) { + try { + (globalSearchParams as any).basicParams.query = qParam; + (globalSearchParams as any).dslParams.query = qParam; + } catch (_e) {} + } + + const isDslSearch = searchParams.get("searchType") == "dsl"; + let searchTypeParams = isDslSearch ? dslParams : params; try { const searchResp = await getBasicSearchResult( { @@ -221,21 +230,43 @@ const SearchResult = ({ classificationParams, glossaryTypeParams }: any) => { searchParams.get("searchType") || "basic" ); const { data = {} } = searchResp || {}; - const { approximateCount, entities } = data || {}; - let totalCount = approximateCount; - let dataLength; - if (entities) { - dataLength = entities?.length; + + const hasEntities = Array.isArray((data as any)?.entities); + const hasDslAttributes = + Array.isArray((data as any)?.attributes) || + Array.isArray((data as any)?.attributes?.name); + const hasDslValues = + Array.isArray((data as any)?.attributes?.values) || + Array.isArray((data as any)?.values); + + if (isDslSearch && !hasEntities && !hasDslAttributes) { + setSearchData({ entities: [] }); + setIsEmptyData(true); + setLoader(false); + return; + } + + let dataLength = 0; + let totalCountLocal = 0; + if (isDslSearch && !hasEntities && hasDslAttributes && hasDslValues) { + const vals = (data as any).attributes?.values || (data as any).values; + dataLength = (vals as any[])?.length || 0; + totalCountLocal = dataLength; } else { - dataLength = searchResp?.data?.length; + const { approximateCount, entities } = data as any; + dataLength = Array.isArray(entities) ? entities.length : 0; + totalCountLocal = approximateCount ?? dataLength; } + if (!dataLength) { setIsEmptyData(true); setLoader(false); } else { setSearchData(searchResp.data); - setTotalCount(totalCount || 0); - setPageCount(Math.ceil(totalCount / pagination.pageSize)); + setTotalCount(totalCountLocal || 0); + setPageCount( + Math.ceil((totalCountLocal || dataLength) / (pagination.pageSize || pageSize)) + ); setLoader(false); } } catch (error: any) { @@ -745,10 +776,40 @@ const SearchResult = ({ classificationParams, glossaryTypeParams }: any) => { } ); + const isDslSearchMode = searchParams.get("searchType") === "dsl"; + const getDslAttributeNames = () => { + const src: any = Array.isArray(searchData) ? searchData[0] : searchData; + if (!src) return [] as string[]; + if (Array.isArray(src?.attributes)) { + return src.attributes + .map((a: any) => (typeof a === 'string' ? a : a?.name)) + .filter(Boolean); + } + if (src?.attributes && Array.isArray(src?.attributes?.name)) { + return src.attributes.name as string[]; + } + return [] as string[]; + }; + const sanitize = (name: string) => + String(name).replace(/[^A-Za-z0-9_]/g, "_").replace(/_{2,}/g, "_"); + const dslAttrNames = isDslSearchMode ? getDslAttributeNames() : []; + const dslColumns = dslAttrNames.map((label: string, idx: number) => { + const key = `dsl_${sanitize(label)}` || `dsl_${idx}` + return { + id: key, + accessorKey: key, + header: label, + cell: (info: any) => <span>{info.getValue()}</span>, + show: true + } + }); + let allColumns = - isEmpty(searchParams.get("type")) && - isEmpty(searchParams.get("tag")) && - !isEmpty(searchParams.get("term")) + isDslSearchMode && dslColumns.length > 0 + ? dslColumns + : isEmpty(searchParams.get("type")) && + isEmpty(searchParams.get("tag")) && + !isEmpty(searchParams.get("term")) ? removeDuplicateObjects([...defaultColumns]) : removeDuplicateObjects([ ...defaultColumns, @@ -756,6 +817,8 @@ const SearchResult = ({ classificationParams, glossaryTypeParams }: any) => { ...defaultHideColumns ]); + const isDslAggregate = isDslSearchMode && dslColumns.length > 0; + const defaultColumnVisibility: any = (columns: any) => { let columnsParams: any = searchParams.get("attributes"); let hideColumns: any = {}; @@ -778,7 +841,12 @@ const SearchResult = ({ classificationParams, glossaryTypeParams }: any) => { return hideColumns; }; - const getDefaultSort = useMemo(() => [{ id: "name", asc: true }], []); + const getDefaultSort = useMemo(() => { + if (isDslAggregate) { + return [] as any[]; // no default sorting for DSL aggregates + } + return [{ id: "name", asc: true }]; + }, [isDslAggregate]); return ( <Stack position="relative" gap={"1rem"}> @@ -833,27 +901,49 @@ const SearchResult = ({ classificationParams, glossaryTypeParams }: any) => { <TableLayout fetchData={fetchSearchResult} - data={searchData.entities || []} + data={ + isDslSearchMode && dslColumns.length > 0 + ? (() => { + const src: any = Array.isArray(searchData) + ? searchData[0] + : searchData; + const values: any[] = Array.isArray(src?.values) + ? src.values + : Array.isArray(src?.attributes?.values) + ? src.attributes.values + : []; + return values.map((row: any[], idx: number) => { + const obj: any = { id: `dsl-row-${idx}` }; + dslAttrNames.forEach((n: string, i: number) => { + const colKey = `dsl_${sanitize(n)}` + obj[colKey] = Array.isArray(row) ? row[i] : row + }); + return obj; + }); + })() + : searchData.entities || [] + } columns={allColumns.filter( (value: {}) => Object.keys(value).length !== 0 )} emptyText="No Records found!" isFetching={loader} pageCount={pageCount} - columnVisibilityParams={true} - defaultColumnVisibility={defaultColumnVisibility(allColumns)} + // columnVisibilityParams={true} + columnVisibilityParams={!isDslAggregate} + defaultColumnVisibility={isDslAggregate ? {} : defaultColumnVisibility(allColumns)} defaultColumnParams={defaultColumnsName.join(",")} - columnVisibility={true} + columnVisibility={!isDslAggregate} + // columnVisibility={true} refreshTable={refreshTable} defaultSortCol={getDefaultSort} clientSideSorting={true} isClientSidePagination={false} columnSort={true} showPagination={true} - showRowSelection={true} - tableFilters={ - isEmpty(classificationParams || glossaryTypeParams) ? true : false - } + showRowSelection={!isDslAggregate} + // showRowSelection={true} + tableFilters={true} assignFilters={ !isEmpty(classificationParams || glossaryTypeParams) ? { diff --git a/dashboardv2/public/js/views/graph/LineageLayoutView.js b/dashboardv2/public/js/views/graph/LineageLayoutView.js index ce7692535..15f7d775f 100644 --- a/dashboardv2/public/js/views/graph/LineageLayoutView.js +++ b/dashboardv2/public/js/views/graph/LineageLayoutView.js @@ -576,7 +576,9 @@ define(['require', if ((that.filterObj.isProcessHideCheck && obj && obj.isProcess) || (that.filterObj.isDeletedEntityHideCheck && obj && obj.isDeleted) || (Globals.isLineageOnDemandEnabled && obj && _.contains(["Input", "Output"], obj.btnType))) { return; } - typeStr += '<option value="' + obj.guid + '">' + obj.displayText + '</option>'; + var label = _.escape(obj.displayText); + var value = _.escape(obj.guid); + typeStr += '<option value="' + value + '">' + label + '</option>'; }); } that.ui.lineageTypeSearch.html(typeStr); diff --git a/dashboardv2/public/js/views/graph/TypeSystemTreeView.js b/dashboardv2/public/js/views/graph/TypeSystemTreeView.js index 935cae001..a75139443 100644 --- a/dashboardv2/public/js/views/graph/TypeSystemTreeView.js +++ b/dashboardv2/public/js/views/graph/TypeSystemTreeView.js @@ -460,10 +460,13 @@ define([ var nodes = that.LineageHelperRef.getNodes(); if (!_.isEmpty(nodes)) { _.each(nodes, function(obj) { - searchStr += '<option value="' + obj.guid + '">' + obj.name + "</option>"; + var label = _.escape(obj.name) + var value = _.escape(obj.guid) + searchStr += '<option value="' + value + '">' + label + "</option>"; if (obj.serviceType && !tempFilteMap[obj.serviceType]) { tempFilteMap[obj.serviceType] = obj.serviceType; - filterStr += '<option value="' + obj.serviceType + '">' + obj.serviceType + "</option>"; + var svc = _.escape(obj.serviceType) + filterStr += '<option value="' + svc + '">' + svc + "</option>"; } }); } diff --git a/dashboardv3/public/js/views/graph/LineageLayoutView.js b/dashboardv3/public/js/views/graph/LineageLayoutView.js index ce7692535..15f7d775f 100644 --- a/dashboardv3/public/js/views/graph/LineageLayoutView.js +++ b/dashboardv3/public/js/views/graph/LineageLayoutView.js @@ -576,7 +576,9 @@ define(['require', if ((that.filterObj.isProcessHideCheck && obj && obj.isProcess) || (that.filterObj.isDeletedEntityHideCheck && obj && obj.isDeleted) || (Globals.isLineageOnDemandEnabled && obj && _.contains(["Input", "Output"], obj.btnType))) { return; } - typeStr += '<option value="' + obj.guid + '">' + obj.displayText + '</option>'; + var label = _.escape(obj.displayText); + var value = _.escape(obj.guid); + typeStr += '<option value="' + value + '">' + label + '</option>'; }); } that.ui.lineageTypeSearch.html(typeStr); diff --git a/dashboardv3/public/js/views/graph/TypeSystemTreeView.js b/dashboardv3/public/js/views/graph/TypeSystemTreeView.js index 935cae001..a75139443 100644 --- a/dashboardv3/public/js/views/graph/TypeSystemTreeView.js +++ b/dashboardv3/public/js/views/graph/TypeSystemTreeView.js @@ -460,10 +460,13 @@ define([ var nodes = that.LineageHelperRef.getNodes(); if (!_.isEmpty(nodes)) { _.each(nodes, function(obj) { - searchStr += '<option value="' + obj.guid + '">' + obj.name + "</option>"; + var label = _.escape(obj.name) + var value = _.escape(obj.guid) + searchStr += '<option value="' + value + '">' + label + "</option>"; if (obj.serviceType && !tempFilteMap[obj.serviceType]) { tempFilteMap[obj.serviceType] = obj.serviceType; - filterStr += '<option value="' + obj.serviceType + '">' + obj.serviceType + "</option>"; + var svc = _.escape(obj.serviceType) + filterStr += '<option value="' + svc + '">' + svc + "</option>"; } }); }
