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>";
                     }
                 });
             }

Reply via email to