Browse Source

reduce data query requests

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>
ruiyi.jiang 1 year ago
parent
commit
2f94ef77a7

+ 19 - 39
client/src/hooks/Query.ts

@@ -4,15 +4,24 @@ import { CollectionService } from '@/http';
 import { CollectionFullObject, FieldObject } from '@server/types';
 
 export const useQuery = (params: {
+  collection: CollectionFullObject;
+  fields: FieldObject[];
+  consistencyLevel?: string;
   onQueryStart: Function;
   onQueryEnd?: Function;
   onQueryFinally?: Function;
-  collectionName: string;
 }) => {
-  // state
-  const [collection, setCollection] = useState<CollectionFullObject>();
-  const [fields, setFields] = useState<FieldObject[]>([]);
-  const [consistencyLevel, setConsistencyLevel] = useState<string>('Bounded');
+  // params
+  const {
+    collection,
+    fields,
+    onQueryStart,
+    onQueryEnd,
+    onQueryFinally,
+    consistencyLevel,
+  } = params;
+
+  // states
   const [currentPage, setCurrentPage] = useState<number>(0);
   const [pageSize, setPageSize] = useState<number>(0);
   const [total, setTotal] = useState<number>(0);
@@ -62,7 +71,7 @@ export const useQuery = (params: {
   ) => {
     const _expr = getPageExpr(page);
     // console.log('query expr', _expr);
-    params.onQueryStart(_expr);
+    onQueryStart(_expr);
 
     try {
       const queryParams = {
@@ -77,7 +86,7 @@ export const useQuery = (params: {
       lastQuery.current = queryParams;
       // execute query
       const res = await CollectionService.queryData(
-        params.collectionName,
+        collection.collection_name,
         queryParams
       );
 
@@ -105,30 +114,17 @@ export const useQuery = (params: {
       // update query result
       setQueryResult(res);
 
-      params.onQueryEnd?.(res);
+      onQueryEnd?.(res);
     } catch (e: any) {
       reset();
     } finally {
-      params.onQueryFinally?.();
+      onQueryFinally?.();
     }
   };
 
-  // get collection info
-  const prepare = async (collectionName: string) => {
-    const collection = await CollectionService.getCollection(
-      collectionName
-    );
-    setFields([
-      ...collection.schema.fields,
-      ...collection.schema.dynamicFields,
-    ]);
-    setConsistencyLevel(collection.consistency_level);
-    setCollection(collection);
-  };
-
   const count = async (consistency_level = consistencyLevel) => {
     const count = 'count(*)';
-    const res = await CollectionService.queryData(params.collectionName, {
+    const res = await CollectionService.queryData(collection.collection_name, {
       expr: expr,
       output_fields: [count],
       consistency_level,
@@ -144,14 +140,6 @@ export const useQuery = (params: {
     pageCache.current.clear();
   };
 
-  // Get fields at first or collection name changed.
-  useEffect(() => {
-    // reset
-    reset();
-    // get collection info
-    params.collectionName && prepare(params.collectionName);
-  }, [params.collectionName]);
-
   // query if expr is changed
   useEffect(() => {
     if (!collection || !collection.loaded) {
@@ -179,20 +167,12 @@ export const useQuery = (params: {
   }, [collection]);
 
   return {
-    // collection info(primaryKey, consistency level, fields)
-    collection,
-    // fields,
-    fields,
     // total query count
     total,
     // page size
     pageSize,
     // update page size
     setPageSize,
-    // consistency level
-    consistencyLevel,
-    // update consistency level
-    setConsistencyLevel,
     // current page
     currentPage,
     // query current data page

+ 2 - 1
client/src/i18n/cn/collection.ts

@@ -82,7 +82,8 @@ const collectionTrans = {
 
   // collection tabs
   partitionTab: '分区',
-  schemaTab: '概览',
+  overviewTab: '概览',
+  schemaTab: 'Schema',
   dataTab: '数据',
   previewTab: '数据预览',
   segmentsTab: '数据段(Segments)',

+ 2 - 1
client/src/i18n/en/collection.ts

@@ -84,7 +84,8 @@ const collectionTrans = {
 
   // collection tabs
   partitionTab: 'Partitions',
-  schemaTab: 'Overview',
+  overviewTab: 'Overview',
+  schemaTab: 'Schema',
   dataTab: 'Data',
   previewTab: 'Data Preview',
   segmentsTab: 'Segments',

+ 78 - 42
client/src/pages/databases/Databases.tsx

@@ -15,6 +15,7 @@ import { dataContext, authContext } from '@/context';
 import Collections from './collections/Collections';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import RefreshButton from './RefreshButton';
+import { CollectionObject } from '@server/types';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -39,9 +40,9 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
 }));
 
+// Databases page(tree and tabs)
 const Databases = () => {
   // context
-  const { isManaged } = useContext(authContext);
   const { database, collections, loading, fetchCollection } =
     useContext(dataContext);
 
@@ -53,11 +54,6 @@ const Databases = () => {
     collectionPage = '',
   } = params;
 
-  // refresh collection
-  const refreshCollection = async () => {
-    await fetchCollection(collectionName);
-  };
-
   // get style
   const classes = useStyles();
 
@@ -65,10 +61,51 @@ const Databases = () => {
   useNavigationHook(ALL_ROUTER_TYPES.DATABASES, {
     collectionName,
     databaseName,
-    extra: <RefreshButton onClick={refreshCollection} />,
+    extra: (
+      <RefreshButton
+        onClick={async () => {
+          await fetchCollection(collectionName);
+        }}
+      />
+    ),
   });
 
-  // i18n
+  // render
+  return (
+    <section className={`page-wrapper ${classes.wrapper}`}>
+      <section className={classes.tree}>
+        {loading ? (
+          <StatusIcon type={LoadingType.CREATING} />
+        ) : (
+          <DatabaseTree
+            key="collections"
+            collections={collections}
+            database={database}
+            params={params}
+          />
+        )}
+      </section>
+      {!collectionName && (
+        <DatabasesTab databaseName={databaseName} tabClass={classes.tab} />
+      )}
+      {collectionName && (
+        <CollectionTabs
+          collectionPage={collectionPage}
+          collectionName={collectionName}
+          tabClass={classes.tab}
+          collections={collections}
+        />
+      )}
+    </section>
+  );
+};
+
+// Database tab pages
+const DatabasesTab = (props: {
+  databaseName: string;
+  tabClass: string; // tab class
+}) => {
+  const { databaseName, tabClass } = props;
   const { t: collectionTrans } = useTranslation('collection');
 
   const dbTab: ITab[] = [
@@ -79,17 +116,40 @@ const Databases = () => {
     },
   ];
   const actionDbTab = dbTab.findIndex(t => t.path === databaseName);
+  return (
+    <RouteTabList
+      tabs={dbTab}
+      wrapperClass={tabClass}
+      activeIndex={actionDbTab !== -1 ? actionDbTab : 0}
+    />
+  );
+};
 
+// Collection tab pages
+const CollectionTabs = (props: {
+  collectionPage: string; // current collection page
+  collectionName: string; // current collection name
+  tabClass: string; // tab class
+  collections: CollectionObject[]; // collections
+}) => {
+  // props
+  const { collectionPage, collectionName, tabClass, collections } = props;
+  // context
+  const { isManaged } = useContext(authContext);
+  // i18n
+  const { t: collectionTrans } = useTranslation('collection');
   // collection tabs
   const collectionTabs: ITab[] = [
     {
-      label: collectionTrans('schemaTab'),
+      label: collectionTrans('overviewTab'),
       component: <Overview />,
       path: `overview`,
     },
     {
       label: collectionTrans('dataTab'),
-      component: <Data />,
+      component: (
+        <Data collections={collections} collectionName={collectionName} />
+      ),
       path: `data`,
     },
     {
@@ -99,6 +159,9 @@ const Databases = () => {
     },
   ];
 
+  // get active collection tab
+  const activeColTab = collectionTabs.findIndex(t => t.path === collectionPage);
+
   if (!isManaged) {
     collectionTabs.push({
       label: collectionTrans('segmentsTab'),
@@ -107,39 +170,12 @@ const Databases = () => {
     });
   }
 
-  // get active collection tab
-  const activeColTab = collectionTabs.findIndex(t => t.path === collectionPage);
-
-  // render
   return (
-    <section className={`page-wrapper ${classes.wrapper}`}>
-      <section className={classes.tree}>
-        {loading ? (
-          <StatusIcon type={LoadingType.CREATING} />
-        ) : (
-          <DatabaseTree
-            key="collections"
-            collections={collections}
-            database={database}
-            params={params}
-          />
-        )}
-      </section>
-      {!collectionName && (
-        <RouteTabList
-          tabs={dbTab}
-          wrapperClass={classes.tab}
-          activeIndex={actionDbTab !== -1 ? actionDbTab : 0}
-        />
-      )}
-      {collectionName && (
-        <RouteTabList
-          tabs={collectionTabs}
-          wrapperClass={classes.tab}
-          activeIndex={activeColTab !== -1 ? activeColTab : 0}
-        />
-      )}
-    </section>
+    <RouteTabList
+      tabs={collectionTabs}
+      wrapperClass={tabClass}
+      activeIndex={activeColTab !== -1 ? activeColTab : 0}
+    />
   );
 };
 

+ 35 - 13
client/src/pages/databases/collections/data/CollectionData.tsx

@@ -1,7 +1,6 @@
 import { useState, useEffect, useRef, useContext } from 'react';
 import { TextField, Typography } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
-import { useParams } from 'react-router-dom';
 import { rootContext } from '@/context';
 import { DataService } from '@/http';
 import { useQuery } from '@/hooks';
@@ -26,14 +25,40 @@ import CustomSelector from '@/components/customSelector/CustomSelector';
 import EmptyDataDialog from '@/pages/dialogs/EmptyDataDialog';
 import ImportSampleDialog from '@/pages/dialogs/ImportSampleDialog';
 import { detectItemType } from '@/utils';
+import { CollectionObject, CollectionFullObject } from '@server/types';
+import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
+
+export interface CollectionDataProps {
+  collectionName: string;
+  collections: CollectionObject[];
+}
+
+const CollectionData = (props: CollectionDataProps) => {
+  // props
+  const { collections } = props;
+  const collection = collections.find(
+    i => i.collection_name === props.collectionName
+  ) as CollectionFullObject;
+
+  // collection is not found or collection full object is not ready
+  if (!collection || !collection.consistency_level) {
+    return <StatusIcon type={LoadingType.CREATING} />;
+  }
 
-const Data = () => {
-  // get collection name from url
-  const { collectionName = '' } = useParams<{ collectionName: string }>();
   // UI state
   const [tableLoading, setTableLoading] = useState<boolean>();
   const [selectedData, setSelectedData] = useState<any[]>([]);
   const [expression, setExpression] = useState<string>('');
+  const [consistencyLevel, setConsistencyLevel] = useState<string>(
+    collection.consistency_level
+  );
+
+  // collection fields, combine static and dynamic fields
+  const fields = [
+    ...collection.schema.fields,
+    ...collection.schema.dynamicFields,
+  ];
+
   // UI functions
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
@@ -89,7 +114,7 @@ const Data = () => {
   };
   const handleDelete = async () => {
     // call delete api
-    await DataService.deleteEntities(collectionName, {
+    await DataService.deleteEntities(collection.collection_name, {
       expr: `${collection!.schema.primaryField.name} in [${selectedData
         .map(v =>
           collection!.schema.primaryField.data_type ===
@@ -107,23 +132,20 @@ const Data = () => {
 
   // Query hook
   const {
-    collection,
-    fields,
     currentPage,
     total,
     pageSize,
     expr,
     queryResult,
     setPageSize,
-    consistencyLevel,
-    setConsistencyLevel,
     setCurrentPage,
     setExpr,
     query,
     reset,
     count,
   } = useQuery({
-    collectionName,
+    collection,
+    fields,
     onQueryStart: (expr: string = '') => {
       setTableLoading(true);
       if (expr === '') {
@@ -152,7 +174,7 @@ const Data = () => {
           params: {
             component: (
               <InsertDialog
-                defaultSelectedCollection={collectionName}
+                defaultSelectedCollection={collection.collection_name}
                 // user can't select partition on collection page, so default value is ''
                 defaultSelectedPartition={''}
                 collections={[collection!]}
@@ -215,7 +237,7 @@ const Data = () => {
       type: 'button',
       btnVariant: 'text',
       onClick: () => {
-        saveCsvAs(selectedData, `${collectionName}.query.csv`);
+        saveCsvAs(selectedData, `${collection.collection_name}.query.csv`);
       },
       label: btnTrans('export'),
       icon: 'download',
@@ -420,4 +442,4 @@ const Data = () => {
   );
 };
 
-export default Data;
+export default CollectionData;