This is an automated email from the ASF dual-hosted git repository.
ppawar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/master by this push:
new 15f6082f1 ATLAS-5132: [REACT UI] Unique Advanced/Search queries do not
work as expected (#457)
15f6082f1 is described below
commit 15f6082f1c5973722e31b443dc4395f7961a9d1c
Author: Prasad Pawar <[email protected]>
AuthorDate: Fri Sep 26 10:53:01 2025 +0530
ATLAS-5132: [REACT UI] Unique Advanced/Search queries do not work as
expected (#457)
---
dashboard/src/components/Table/TableLayout.tsx | 50 +++++++-
dashboard/src/components/muiComponents.tsx | 26 ++--
dashboard/src/views/SearchResult/SearchResult.tsx | 132 +++++++++++++++++----
.../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, 184 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 a6574e1d2..e2e4cca9f 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,14 +230,34 @@ 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);
setSearchData({ entities: [], referredEntities: {} });
@@ -238,9 +267,9 @@ const SearchResult = ({ classificationParams,
glossaryTypeParams }: any) => {
} else {
setIsEmptyData(false);
setSearchData(searchResp.data);
- setTotalCount(totalCount || 0);
+ setTotalCount(totalCountLocal || 0);
setPageCount(
- Math.ceil((totalCount || 0) / ((pagination && pagination.pageSize)
|| 1))
+ Math.ceil((totalCountLocal || dataLength) / (pagination.pageSize
|| pageSize))
);
setLoader(false);
}
@@ -751,10 +780,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,
@@ -762,6 +821,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 = {};
@@ -784,7 +845,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"}>
@@ -839,27 +905,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>";
}
});
}