Browse Source

fix: search page refresh error

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 1 week ago
parent
commit
7cca991e82

+ 9 - 2
client/src/components/advancedSearch/Filter.tsx

@@ -1,4 +1,4 @@
-import { forwardRef, useState, useEffect, useImperativeHandle } from 'react';
+import { forwardRef, useState, useEffect, useImperativeHandle, useRef } from 'react';
 import Chip from '@mui/material/Chip';
 import Chip from '@mui/material/Chip';
 import Tooltip from '@mui/material/Tooltip';
 import Tooltip from '@mui/material/Tooltip';
 import { Box } from '@mui/material';
 import { Box } from '@mui/material';
@@ -30,6 +30,9 @@ const Filter = forwardRef((props: FilterProps, ref) => {
   const [initConditions, setInitConditions] = useState<any[]>([]);
   const [initConditions, setInitConditions] = useState<any[]>([]);
   const [isConditionsLegal, setIsConditionsLegal] = useState(false);
   const [isConditionsLegal, setIsConditionsLegal] = useState(false);
   const [filterExpression, setFilterExpression] = useState('');
   const [filterExpression, setFilterExpression] = useState('');
+  
+  // Use ref to track previous expression to prevent unnecessary updates
+  const prevExpressionRef = useRef<string>('');
 
 
   const FilterIcon = icons.filter;
   const FilterIcon = icons.filter;
 
 
@@ -57,7 +60,11 @@ const Filter = forwardRef((props: FilterProps, ref) => {
       }
       }
     }
     }
     setIsConditionsLegal(true);
     setIsConditionsLegal(true);
-    generateExpression(flatConditions, setFilterExpression);
+    
+    // Only generate expression if conditions are legal
+    if (flatConditions.length > 0) {
+      generateExpression(flatConditions, setFilterExpression);
+    }
   }, [flatConditions]);
   }, [flatConditions]);
 
 
   const setFilteredFlatConditions = (conditions: any[]) => {
   const setFilteredFlatConditions = (conditions: any[]) => {

+ 26 - 8
client/src/pages/databases/collections/data/OptimizedInput.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useState, useEffect } from 'react';
+import { useCallback, useState, useEffect, useRef } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import CustomInput from '@/components/customInput/CustomInput';
 import CustomInput from '@/components/customInput/CustomInput';
 import Filter from '@/components/advancedSearch';
 import Filter from '@/components/advancedSearch';
@@ -24,6 +24,24 @@ const OptimizedInput = ({
   const { t: collectionTrans } = useTranslation('collection');
   const { t: collectionTrans } = useTranslation('collection');
   const [localValue, setLocalValue] = useState(value);
   const [localValue, setLocalValue] = useState(value);
 
 
+  // Use refs to stabilize function calls
+  const onChangeRef = useRef(onChange);
+  const onSubmitRef = useRef(onSubmit);
+  const onKeyDownRef = useRef(onKeyDown);
+
+  // Update refs when props change
+  useEffect(() => {
+    onChangeRef.current = onChange;
+  }, [onChange]);
+
+  useEffect(() => {
+    onSubmitRef.current = onSubmit;
+  }, [onSubmit]);
+
+  useEffect(() => {
+    onKeyDownRef.current = onKeyDown;
+  }, [onKeyDown]);
+
   useEffect(() => {
   useEffect(() => {
     setLocalValue(value);
     setLocalValue(value);
   }, [value]);
   }, [value]);
@@ -31,25 +49,25 @@ const OptimizedInput = ({
   const handleChange = useCallback(
   const handleChange = useCallback(
     (newValue: string) => {
     (newValue: string) => {
       setLocalValue(newValue);
       setLocalValue(newValue);
-      onChange(newValue);
+      onChangeRef.current(newValue);
     },
     },
-    [onChange]
+    [] // Remove dependencies since we use refs
   );
   );
 
 
   const handleKeyDown = useCallback(
   const handleKeyDown = useCallback(
     (e: any) => {
     (e: any) => {
-      onKeyDown(e);
+      onKeyDownRef.current(e);
     },
     },
-    [onKeyDown]
+    [] // Remove dependencies since we use refs
   );
   );
 
 
   const handleFilterSubmit = useCallback(
   const handleFilterSubmit = useCallback(
     (expression: string) => {
     (expression: string) => {
       setLocalValue(expression);
       setLocalValue(expression);
-      onChange(expression);
-      onSubmit(expression);
+      onChangeRef.current(expression);
+      onSubmitRef.current(expression);
     },
     },
-    [onChange, onSubmit]
+    [] // Remove dependencies since we use refs
   );
   );
 
 
   return (
   return (

+ 126 - 140
client/src/pages/databases/collections/search/Search.tsx

@@ -1,17 +1,10 @@
-import {
-  useState,
-  useMemo,
-  ChangeEvent,
-  useCallback,
-  useEffect,
-  useRef,
-  useContext,
-} from 'react';
+import { useState, useMemo, ChangeEvent, useCallback, useContext } from 'react';
 import { Typography, AccordionSummary, Checkbox } from '@mui/material';
 import { Typography, AccordionSummary, Checkbox } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { DataService, CollectionService } from '@/http';
 import { DataService, CollectionService } from '@/http';
 import Icons from '@/components/icons/Icons';
 import Icons from '@/components/icons/Icons';
 import AttuGrid from '@/components/grid/Grid';
 import AttuGrid from '@/components/grid/Grid';
+import Filter from '@/components/advancedSearch';
 import EmptyCard from '@/components/cards/EmptyCard';
 import EmptyCard from '@/components/cards/EmptyCard';
 import CustomButton from '@/components/customButton/CustomButton';
 import CustomButton from '@/components/customButton/CustomButton';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
@@ -20,7 +13,7 @@ import SearchGlobalParams from './SearchGlobalParams';
 import VectorInputBox from './SearchInputBox';
 import VectorInputBox from './SearchInputBox';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
 import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
-import OptimizedInput from '../data/OptimizedInput';
+import CustomInput from '@/components/customInput/CustomInput';
 import PartitionsSelector from './PartitionsSelector';
 import PartitionsSelector from './PartitionsSelector';
 import {
 import {
   formatFieldType,
   formatFieldType,
@@ -28,6 +21,7 @@ import {
   generateVectorsByField,
   generateVectorsByField,
   saveCsvAs,
   saveCsvAs,
   buildSearchParams,
   buildSearchParams,
+  getColumnWidth,
 } from '@/utils';
 } from '@/utils';
 import SearchParams from './SearchParams';
 import SearchParams from './SearchParams';
 import DataExplorer, { formatMilvusData } from './DataExplorer';
 import DataExplorer, { formatMilvusData } from './DataExplorer';
@@ -59,7 +53,7 @@ import {
   CheckboxRow,
   CheckboxRow,
   LeftSection,
   LeftSection,
 } from './StyledComponents';
 } from './StyledComponents';
-import { authContext } from '@/context/Auth';
+import { authContext } from '@/context';
 
 
 export interface CollectionDataProps {
 export interface CollectionDataProps {
   collectionName: string;
   collectionName: string;
@@ -74,7 +68,7 @@ const emptyExplorerData: GraphData = {
 };
 };
 
 
 const Search = (props: CollectionDataProps) => {
 const Search = (props: CollectionDataProps) => {
-  // context
+  // auth context
   const { isManaged } = useContext(authContext);
   const { isManaged } = useContext(authContext);
 
 
   // props
   // props
@@ -87,45 +81,15 @@ const Search = (props: CollectionDataProps) => {
   const [tableLoading, setTableLoading] = useState<boolean>();
   const [tableLoading, setTableLoading] = useState<boolean>();
   const [highlightField, setHighlightField] = useState<string>('');
   const [highlightField, setHighlightField] = useState<string>('');
   const [explorerOpen, setExplorerOpen] = useState<boolean>(false);
   const [explorerOpen, setExplorerOpen] = useState<boolean>(false);
-  const [localFilterValue, setLocalFilterValue] = useState<string>('');
-
-  // Use ref to track searchParams changes without causing re-renders
-  const searchParamsRef = useRef(searchParams);
-  searchParamsRef.current = searchParams;
-
-  // Memoize collection to avoid unnecessary re-renders
-  const memoizedCollection = useMemo(
-    () => collection,
-    [collection?.collection_name]
-  );
-
-  // Memoize selected fields to avoid recalculation
-  const selectedFields = useMemo(
-    () => searchParams.searchParams.filter(s => s.selected),
-    [searchParams.searchParams]
-  );
-
-  // Memoize enablePartitionsFilter
-  const enablePartitionsFilter = useMemo(
-    () => !collection.schema.enablePartitionKey,
-    [collection.schema.enablePartitionKey]
-  );
 
 
   // translations
   // translations
   const { t: searchTrans } = useTranslation('search');
   const { t: searchTrans } = useTranslation('search');
   const { t: btnTrans } = useTranslation('btn');
   const { t: btnTrans } = useTranslation('btn');
 
 
-  // Sync local filter value with searchParams on mount and when searchParams change
-  useEffect(() => {
-    if (searchParams?.globalParams?.filter !== undefined) {
-      setLocalFilterValue(searchParams.globalParams.filter);
-    }
-  }, [searchParams?.globalParams?.filter]);
-
-  // UI functions - optimized with better dependencies
+  // UI functions
   const handleExpand = useCallback(
   const handleExpand = useCallback(
     (panel: string) => (event: ChangeEvent<{}>, expanded: boolean) => {
     (panel: string) => (event: ChangeEvent<{}>, expanded: boolean) => {
-      const s = cloneObj(searchParamsRef.current);
+      const s = cloneObj(searchParams);
       const target = s.searchParams.find((sp: SearchSingleParams) => {
       const target = s.searchParams.find((sp: SearchSingleParams) => {
         return sp.field.name === panel;
         return sp.field.name === panel;
       });
       });
@@ -135,54 +99,54 @@ const Search = (props: CollectionDataProps) => {
         setSearchParams({ ...s });
         setSearchParams({ ...s });
       }
       }
     },
     },
-    [setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
   const handleSelect = useCallback(
   const handleSelect = useCallback(
     (panel: string) => (event: ChangeEvent<{}>) => {
     (panel: string) => (event: ChangeEvent<{}>) => {
-      const s = cloneObj(searchParamsRef.current) as SearchParamsType;
+      const s = cloneObj(searchParams) as SearchParamsType;
       const target = s.searchParams.find(sp => {
       const target = s.searchParams.find(sp => {
         return sp.field.name === panel;
         return sp.field.name === panel;
       });
       });
       if (target) {
       if (target) {
         target.selected = !target.selected;
         target.selected = !target.selected;
+
         setSearchParams({ ...s });
         setSearchParams({ ...s });
       }
       }
     },
     },
-    [setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
   // update search params
   // update search params
   const updateSearchParamCallback = useCallback(
   const updateSearchParamCallback = useCallback(
     (updates: SearchSingleParams, index: number) => {
     (updates: SearchSingleParams, index: number) => {
-      const currentParams = searchParamsRef.current;
       if (
       if (
         JSON.stringify(updates) !==
         JSON.stringify(updates) !==
-        JSON.stringify(currentParams.searchParams[index].params)
+        JSON.stringify(searchParams.searchParams[index].params)
       ) {
       ) {
-        const s = cloneObj(currentParams);
+        const s = cloneObj(searchParams);
         // update the searchParams
         // update the searchParams
         s.searchParams[index].params = updates;
         s.searchParams[index].params = updates;
         setSearchParams({ ...s });
         setSearchParams({ ...s });
       }
       }
     },
     },
-    [setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
   // generate random vectors
   // generate random vectors
   const genRandomVectors = useCallback(() => {
   const genRandomVectors = useCallback(() => {
-    const s = cloneObj(searchParamsRef.current) as SearchParamsType;
+    const s = cloneObj(searchParams) as SearchParamsType;
     s.searchParams.forEach((sp: SearchSingleParams) => {
     s.searchParams.forEach((sp: SearchSingleParams) => {
       sp.data = generateVectorsByField(sp.field) as any;
       sp.data = generateVectorsByField(sp.field) as any;
     });
     });
 
 
     setSearchParams({ ...s });
     setSearchParams({ ...s });
-  }, [setSearchParams]);
+  }, [JSON.stringify(searchParams)]);
 
 
   // on vector input change, update the search params
   // on vector input change, update the search params
   const onSearchInputChange = useCallback(
   const onSearchInputChange = useCallback(
     (anns_field: string, value: string) => {
     (anns_field: string, value: string) => {
-      const s = cloneObj(searchParamsRef.current) as SearchParamsType;
+      const s = cloneObj(searchParams) as SearchParamsType;
       const target = s.searchParams.find((sp: SearchSingleParams) => {
       const target = s.searchParams.find((sp: SearchSingleParams) => {
         return sp.anns_field === anns_field;
         return sp.anns_field === anns_field;
       });
       });
@@ -192,29 +156,36 @@ const Search = (props: CollectionDataProps) => {
         setSearchParams({ ...s });
         setSearchParams({ ...s });
       }
       }
     },
     },
-    [setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
-  // on filter change - only update local state, not searchParams
-  const onFilterChange = useCallback((value: string) => {
-    setLocalFilterValue(value);
-  }, []);
+  // on filter change
+  const onFilterChange = useCallback(
+    (value: string) => {
+      const s = cloneObj(searchParams) as SearchParamsType;
+      s.globalParams.filter = value;
+      setSearchParams({ ...s });
+
+      return s;
+    },
+    [JSON.stringify(searchParams)]
+  );
 
 
   // on partitions change
   // on partitions change
   const onPartitionsChange = useCallback(
   const onPartitionsChange = useCallback(
     (value: any) => {
     (value: any) => {
-      const s = cloneObj(searchParamsRef.current) as SearchParamsType;
+      const s = cloneObj(searchParams) as SearchParamsType;
       s.partitions = value;
       s.partitions = value;
       setSearchParams({ ...s });
       setSearchParams({ ...s });
     },
     },
-    [setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
   // set search result
   // set search result
   const setSearchResult = useCallback(
   const setSearchResult = useCallback(
     (props: VectorSearchResults) => {
     (props: VectorSearchResults) => {
       const { results, latency } = props;
       const { results, latency } = props;
-      const s = cloneObj(searchParamsRef.current) as SearchParamsType;
+      const s = cloneObj(searchParams) as SearchParamsType;
       s.searchResult = results;
       s.searchResult = results;
       s.searchLatency = latency;
       s.searchLatency = latency;
       const newGraphData = formatMilvusData(
       const newGraphData = formatMilvusData(
@@ -226,25 +197,18 @@ const Search = (props: CollectionDataProps) => {
 
 
       setSearchParams({ ...s });
       setSearchParams({ ...s });
     },
     },
-    [setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
   // execute search
   // execute search
   const onSearchClicked = useCallback(async () => {
   const onSearchClicked = useCallback(async () => {
-    // Create a copy of current searchParams and update with current filter value
-    const s = cloneObj(searchParamsRef.current) as SearchParamsType;
-    s.globalParams.filter = localFilterValue;
-
-    // Update searchParams for future use
-    setSearchParams({ ...s });
-
-    const params = buildSearchParams(s);
+    const params = buildSearchParams(searchParams);
     setExplorerOpen(false);
     setExplorerOpen(false);
 
 
     setTableLoading(true);
     setTableLoading(true);
     try {
     try {
       const res = await DataService.vectorSearchData(
       const res = await DataService.vectorSearchData(
-        s.collection.collection_name,
+        searchParams.collection.collection_name,
         params
         params
       );
       );
 
 
@@ -253,12 +217,12 @@ const Search = (props: CollectionDataProps) => {
     } catch (err) {
     } catch (err) {
       setTableLoading(false);
       setTableLoading(false);
     }
     }
-  }, [localFilterValue, setSearchParams, setSearchResult]);
+  }, [JSON.stringify(searchParams)]);
 
 
   // execute explore
   // execute explore
-  const onExploreClicked = useCallback(() => {
+  const onExploreClicked = () => {
     setExplorerOpen(explorerOpen => !explorerOpen);
     setExplorerOpen(explorerOpen => !explorerOpen);
-  }, []);
+  };
 
 
   const onNodeClicked = useCallback(
   const onNodeClicked = useCallback(
     async (node: GraphNode) => {
     async (node: GraphNode) => {
@@ -268,8 +232,8 @@ const Search = (props: CollectionDataProps) => {
       setExplorerOpen(true);
       setExplorerOpen(true);
       setTableLoading(false);
       setTableLoading(false);
 
 
-      const s = cloneObj(searchParamsRef.current);
-      const params = cloneObj(buildSearchParams(searchParamsRef.current));
+      const s = cloneObj(searchParams);
+      const params = cloneObj(buildSearchParams(searchParams));
 
 
       try {
       try {
         const query = await CollectionService.queryData(collectionName, {
         const query = await CollectionService.queryData(collectionName, {
@@ -297,7 +261,7 @@ const Search = (props: CollectionDataProps) => {
         console.log('err', err);
         console.log('err', err);
       }
       }
     },
     },
-    [collectionName, setSearchParams]
+    [JSON.stringify(searchParams)]
   );
   );
 
 
   const searchResultMemo = useSearchResult(
   const searchResultMemo = useSearchResult(
@@ -321,20 +285,18 @@ const Search = (props: CollectionDataProps) => {
 
 
   // reset
   // reset
   const onResetClicked = useCallback(() => {
   const onResetClicked = useCallback(() => {
-    const s = cloneObj(searchParamsRef.current) as SearchParamsType;
+    const s = cloneObj(searchParams) as SearchParamsType;
     s.searchResult = null;
     s.searchResult = null;
 
 
     setSearchParams({ ...s });
     setSearchParams({ ...s });
     setCurrentPage(0);
     setCurrentPage(0);
-    setLocalFilterValue(''); // Also reset local filter value
-  }, [setSearchParams, setCurrentPage]);
+  }, [JSON.stringify(searchParams), setCurrentPage]);
 
 
   const onExplorerResetClicked = useCallback(() => {
   const onExplorerResetClicked = useCallback(() => {
     onSearchClicked();
     onSearchClicked();
     onExploreClicked();
     onExploreClicked();
-  }, [onSearchClicked, onExploreClicked]);
+  }, [onSearchClicked, onSearchClicked]);
 
 
-  // Memoize colDefinitions with better dependencies
   const colDefinitions: ColDefinitionsType[] = useMemo(() => {
   const colDefinitions: ColDefinitionsType[] = useMemo(() => {
     if (!searchParams || !searchParams.collection) {
     if (!searchParams || !searchParams.collection) {
       return [];
       return [];
@@ -369,15 +331,10 @@ const Search = (props: CollectionDataProps) => {
               label: key === DYNAMIC_FIELD ? searchTrans('dynamicFields') : key,
               label: key === DYNAMIC_FIELD ? searchTrans('dynamicFields') : key,
               needCopy: key !== 'score',
               needCopy: key !== 'score',
               headerFormatter: v => {
               headerFormatter: v => {
-                return (
-                  <CollectionColHeader
-                    def={v}
-                    collection={memoizedCollection}
-                  />
-                );
+                return <CollectionColHeader def={v} collection={collection} />;
               },
               },
               formatter(_: any, cellData: any) {
               formatter(_: any, cellData: any) {
-                const field = memoizedCollection.schema.fields.find(
+                const field = collection.schema.fields.find(
                   f => f.name === key
                   f => f.name === key
                 );
                 );
 
 
@@ -388,42 +345,30 @@ const Search = (props: CollectionDataProps) => {
                   />
                   />
                 );
                 );
               },
               },
+              getStyle: d => {
+                const field = collection.schema.fields.find(
+                  f => f.name === key
+                );
+                if (!d || !field) {
+                  return {};
+                }
+                return {
+                  minWidth: getColumnWidth(field),
+                };
+              },
             };
             };
           })
           })
       : [];
       : [];
   }, [
   }, [
-    searchParams?.searchResult,
-    searchParams?.globalParams?.output_fields,
-    searchTrans,
-    memoizedCollection,
+    JSON.stringify({
+      searchParams,
+    }),
   ]);
   ]);
 
 
-  // Memoize disable search logic
-  const { disableSearch, disableSearchTooltip } = useMemo(() => {
-    let disableSearch = false;
-    let disableSearchTooltip = '';
-
-    if (selectedFields.length === 0) {
-      disableSearch = true;
-      disableSearchTooltip = searchTrans('noSelectedVectorField');
-    }
-
-    const noDataInSelected = selectedFields.some(s => s.data === '');
-    if (noDataInSelected) {
-      disableSearch = true;
-      disableSearchTooltip = searchTrans('noVectorToSearch');
-    }
-
-    return { disableSearch, disableSearchTooltip };
-  }, [selectedFields, searchTrans]);
-
   // methods
   // methods
-  const handlePageChange = useCallback(
-    (e: any, page: number) => {
-      handleCurrentPage(page);
-    },
-    [handleCurrentPage]
-  );
+  const handlePageChange = (e: any, page: number) => {
+    handleCurrentPage(page);
+  };
 
 
   // collection is not found or collection full object is not ready
   // collection is not found or collection full object is not ready
   if (
   if (
@@ -447,6 +392,27 @@ const Search = (props: CollectionDataProps) => {
     );
     );
   }
   }
 
 
+  // disable search button
+  let disableSearch = false;
+  let disableSearchTooltip = '';
+  // has selected vector fields
+  const selectedFields = searchParams.searchParams.filter(s => s.selected);
+
+  if (selectedFields.length === 0) {
+    disableSearch = true;
+    disableSearchTooltip = searchTrans('noSelectedVectorField');
+  }
+  // has vector data to search
+  const noDataInSelected = selectedFields.some(s => s.data === '');
+
+  if (noDataInSelected) {
+    disableSearch = true;
+    disableSearchTooltip = searchTrans('noVectorToSearch');
+  }
+
+  // enable partition filter
+  const enablePartitionsFilter = !collection.schema.enablePartitionKey;
+
   return (
   return (
     <SearchRoot>
     <SearchRoot>
       {collection && (
       {collection && (
@@ -499,7 +465,7 @@ const Search = (props: CollectionDataProps) => {
                       <VectorInputBox
                       <VectorInputBox
                         searchParams={s}
                         searchParams={s}
                         onChange={onSearchInputChange}
                         onChange={onSearchInputChange}
-                        collection={memoizedCollection}
+                        collection={collection}
                         type={field.is_function_output ? 'text' : 'vector'}
                         type={field.is_function_output ? 'text' : 'vector'}
                       />
                       />
 
 
@@ -538,9 +504,8 @@ const Search = (props: CollectionDataProps) => {
                 }}
                 }}
                 searchParams={searchParams}
                 searchParams={searchParams}
                 handleFormChange={(params: any) => {
                 handleFormChange={(params: any) => {
-                  const s = cloneObj(searchParamsRef.current);
-                  s.globalParams = params;
-                  setSearchParams({ ...s });
+                  searchParams.globalParams = params;
+                  setSearchParams({ ...searchParams });
                 }}
                 }}
               />
               />
 
 
@@ -578,26 +543,47 @@ const Search = (props: CollectionDataProps) => {
           <SearchResults>
           <SearchResults>
             <Toolbar>
             <Toolbar>
               <div className="left">
               <div className="left">
-                <OptimizedInput
-                  value={localFilterValue}
-                  onChange={onFilterChange}
-                  onKeyDown={(e: any) => {
-                    if (e.key === 'Enter') {
-                      e.preventDefault();
-                      onSearchClicked();
-                    }
-                  }}
-                  disabled={explorerOpen}
-                  fields={memoizedCollection.schema.scalarFields}
-                  onSubmit={(expression: string) => {
-                    setLocalFilterValue(expression);
-                    // Don't immediately update searchParams, wait for search
+                <CustomInput
+                  type="text"
+                  textConfig={{
+                    label: searchTrans('filterExpr'),
+                    key: 'advFilter',
+                    className: 'textarea',
+                    onChange: onFilterChange,
+                    value: searchParams.globalParams.filter,
+                    disabled: explorerOpen,
+                    variant: 'filled',
+                    required: false,
+                    InputLabelProps: { shrink: true },
+                    InputProps: {
+                      endAdornment: (
+                        <Filter
+                          title={''}
+                          showTitle={false}
+                          fields={collection.schema.scalarFields}
+                          filterDisabled={explorerOpen}
+                          onSubmit={(value: string) => {
+                            onFilterChange(value);
+                          }}
+                          showTooltip={false}
+                        />
+                      ),
+                    },
+                    onKeyDown: (e: any) => {
+                      if (e.key === 'Enter') {
+                        e.preventDefault();
+                        onSearchClicked();
+                      }
+                    },
                   }}
                   }}
+                  checkValid={() => true}
                 />
                 />
               </div>
               </div>
               <div className="right">
               <div className="right">
                 <CustomButton
                 <CustomButton
-                  onClick={onExploreClicked}
+                  onClick={() => {
+                    onExploreClicked();
+                  }}
                   size="small"
                   size="small"
                   disabled={
                   disabled={
                     !searchParams.searchResult ||
                     !searchParams.searchResult ||
@@ -664,8 +650,8 @@ const Search = (props: CollectionDataProps) => {
                 rowCount={total}
                 rowCount={total}
                 primaryKey="rank"
                 primaryKey="rank"
                 page={currentPage}
                 page={currentPage}
-                tableHeaderHeight={45}
-                rowHeight={42}
+                tableHeaderHeight={46}
+                rowHeight={41}
                 openCheckBox={false}
                 openCheckBox={false}
                 onPageChange={handlePageChange}
                 onPageChange={handlePageChange}
                 rowsPerPage={pageSize}
                 rowsPerPage={pageSize}