Browse Source

Merge pull request #258 from nameczz/dev

support bool field
nameczz 3 years ago
parent
commit
6087d90b39

+ 19 - 37
client/src/components/advancedSearch/Condition.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect, FC } from 'react';
+import React, { useState, useEffect, FC, useMemo } from 'react';
 import {
   makeStyles,
   Theme,
@@ -9,39 +9,7 @@ import {
 import CloseIcon from '@material-ui/icons/Close';
 import { ConditionProps, Field } from './Types';
 import CustomSelector from '../customSelector/CustomSelector';
-
-// Todo: Move to corrsponding Constant file.
-// Static logical operators.
-const LogicalOperators = [
-  {
-    value: '<',
-    label: '<',
-  },
-  {
-    value: '<=',
-    label: '<=',
-  },
-  {
-    value: '>',
-    label: '>',
-  },
-  {
-    value: '>=',
-    label: '>=',
-  },
-  {
-    value: '==',
-    label: '==',
-  },
-  {
-    value: '!=',
-    label: '!=',
-  },
-  {
-    value: 'in',
-    label: 'in',
-  },
-];
+import { LOGICAL_OPERATORS } from '../../consts/Util';
 
 const Condition: FC<ConditionProps> = props => {
   const {
@@ -54,9 +22,9 @@ const Condition: FC<ConditionProps> = props => {
     ...others
   } = props;
   const [operator, setOperator] = useState(
-    initData?.op || LogicalOperators[0].value
+    initData?.op || LOGICAL_OPERATORS[0].value
   );
-  const [conditionField, setConditionField] = useState<Field | any>(
+  const [conditionField, setConditionField] = useState<Field>(
     initData?.field || fields[0] || {}
   );
   const [conditionValue, setConditionValue] = useState(initData?.value || '');
@@ -90,6 +58,10 @@ const Condition: FC<ConditionProps> = props => {
           ? regFloatInterval.test(conditionValueWithNoSpace)
           : regFloat.test(conditionValueWithNoSpace);
         break;
+      case 'bool':
+        const legalValues = ['false', 'true'];
+        isLegal = legalValues.includes(conditionValueWithNoSpace);
+        break;
       default:
         isLegal = false;
         break;
@@ -108,6 +80,16 @@ const Condition: FC<ConditionProps> = props => {
 
   const classes = useStyles();
 
+  const logicalOperators = useMemo(() => {
+    if (conditionField.type === 'bool') {
+      const data = LOGICAL_OPERATORS.filter(v => v.value === '==');
+      setOperator(data[0].value);
+      // bool only support ==
+      return data;
+    }
+    return LOGICAL_OPERATORS;
+  }, [conditionField]);
+
   // Logic operator input change.
   const handleOpChange = (event: React.ChangeEvent<{ value: unknown }>) => {
     setOperator(event.target.value);
@@ -140,7 +122,7 @@ const Condition: FC<ConditionProps> = props => {
         label="Logic"
         value={operator}
         onChange={handleOpChange}
-        options={LogicalOperators}
+        options={logicalOperators}
         variant="filled"
         wrapperClass={classes.logic}
       />

+ 1 - 1
client/src/components/advancedSearch/Types.ts

@@ -12,7 +12,7 @@ export interface ConditionProps {
 
 export interface Field {
   name: string;
-  type: 'int' | 'float';
+  type: 'int' | 'float' | 'bool';
 }
 
 export interface TriggerChangeData {

+ 31 - 0
client/src/consts/Util.ts

@@ -4,3 +4,34 @@ export const BYTE_UNITS: { [x: string]: number } = {
   m: 1024 * 1024,
   g: 1024 * 1024 * 1024,
 };
+
+export const LOGICAL_OPERATORS = [
+  {
+    value: '<',
+    label: '<',
+  },
+  {
+    value: '<=',
+    label: '<=',
+  },
+  {
+    value: '>',
+    label: '>',
+  },
+  {
+    value: '>=',
+    label: '>=',
+  },
+  {
+    value: '==',
+    label: '==',
+  },
+  {
+    value: '!=',
+    label: '!=',
+  },
+  {
+    value: 'in',
+    label: 'in',
+  },
+];

+ 2 - 2
client/src/http/Field.ts

@@ -1,9 +1,9 @@
-import { DataType } from '../pages/collections/Types';
+import { DataTypeStringEnum } from '../pages/collections/Types';
 import { FieldData } from '../pages/schema/Types';
 import BaseModel from './BaseModel';
 
 export class FieldHttp extends BaseModel implements FieldData {
-  data_type!: DataType;
+  data_type!: DataTypeStringEnum;
   fieldID!: string;
   type_params!: { key: string; value: string }[];
   is_primary_key!: true;

+ 25 - 24
client/src/pages/collections/Collections.tsx

@@ -36,7 +36,7 @@ import { LOADING_STATE } from '../../consts/Milvus';
 import { webSokcetContext } from '../../context/WebSocket';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '../../consts/Http';
 import { checkIndexBuilding, checkLoading } from '../../utils/Validation';
-import CreateAlias from './CreateAlias';
+// import CreateAlias from './CreateAlias';
 
 const useStyles = makeStyles((theme: Theme) => ({
   emptyWrapper: {
@@ -309,29 +309,30 @@ const Collections = () => {
       disabledTooltip: collectionTrans('deleteTooltip'),
       disabled: data => data.length === 0,
     },
-    {
-      type: 'iconBtn',
-      onClick: () => {
-        setDialog({
-          open: true,
-          type: 'custom',
-          params: {
-            component: (
-              <CreateAlias
-                collectionName={selectedCollections[0]._name}
-                cb={() => {
-                  setSelectedCollections([]);
-                }}
-              />
-            ),
-          },
-        });
-      },
-      label: collectionTrans('alias'),
-      icon: 'alias',
-      disabledTooltip: collectionTrans('aliasTooltip'),
-      disabled: data => data.length !== 1,
-    },
+    // Todo: hide alias after we can get all alias from milvus.
+    // {
+    //   type: 'iconBtn',
+    //   onClick: () => {
+    //     setDialog({
+    //       open: true,
+    //       type: 'custom',
+    //       params: {
+    //         component: (
+    //           <CreateAlias
+    //             collectionName={selectedCollections[0]._name}
+    //             cb={() => {
+    //               setSelectedCollections([]);
+    //             }}
+    //           />
+    //         ),
+    //       },
+    //     });
+    //   },
+    //   label: collectionTrans('alias'),
+    //   icon: 'alias',
+    //   disabledTooltip: collectionTrans('aliasTooltip'),
+    //   disabled: data => data.length !== 1,
+    // },
     {
       label: 'Search',
       icon: 'search',

+ 5 - 0
client/src/pages/collections/Constants.ts

@@ -38,6 +38,11 @@ export const ALL_OPTIONS: KeyValuePair[] = [
     label: 'Double',
     value: DataTypeEnum.Double,
   },
+
+  {
+    label: 'Boolean',
+    value: DataTypeEnum.Bool,
+  },
 ];
 
 export const AUTO_ID_OPTIONS: KeyValuePair[] = [

+ 12 - 10
client/src/pages/collections/Types.ts

@@ -32,6 +32,7 @@ export interface CollectionCreateParam {
 }
 
 export enum DataTypeEnum {
+  Bool = 1,
   Int8 = 2,
   Int16 = 3,
   Int32 = 4,
@@ -41,16 +42,17 @@ export enum DataTypeEnum {
   BinaryVector = 100,
   FloatVector = 101,
 }
-
-export type DataType =
-  | 'Int8'
-  | 'Int16'
-  | 'Int32'
-  | 'Int64'
-  | 'Float'
-  | 'Double'
-  | 'BinaryVector'
-  | 'FloatVector';
+export enum DataTypeStringEnum {
+  Bool = 'Bool',
+  Int8 = 'Int8',
+  Int16 = 'Int16',
+  Int32 = 'Int32',
+  Int64 = 'Int64',
+  Float = 'Float',
+  Double = 'Double',
+  BinaryVector = 'BinaryVector',
+  FloatVector = 'FloatVector',
+}
 
 export interface Field {
   name: string | null;

+ 3 - 3
client/src/pages/schema/Create.tsx

@@ -14,13 +14,13 @@ import { getCreateIndexJSCode } from '../../utils/code/Js';
 import { getCreateIndexPYCode } from '../../utils/code/Py';
 import { formatForm, getMetricOptions } from '../../utils/Form';
 import { getEmbeddingType } from '../../utils/search';
-import { DataType } from '../collections/Types';
+import { DataTypeStringEnum } from '../collections/Types';
 import CreateForm from './CreateForm';
 import { IndexType, IndexExtraParam, INDEX_TYPES_ENUM } from './Types';
 
 const CreateIndex = (props: {
   collectionName: string;
-  fieldType: DataType;
+  fieldType: DataTypeStringEnum;
   handleCreate: (params: IndexExtraParam) => void;
   handleCancel: () => void;
 
@@ -84,7 +84,7 @@ const CreateIndex = (props: {
     });
 
     const { index_type, metric_type } = indexSetting;
-  
+
     const extraParams: IndexExtraParam = {
       index_type,
       metric_type,

+ 5 - 2
client/src/pages/schema/Schema.tsx

@@ -9,7 +9,7 @@ import CustomToolTip from '../../components/customToolTip/CustomToolTip';
 import { FieldHttp } from '../../http/Field';
 import { FieldView } from './Types';
 import IndexTypeElement from './IndexTypeElement';
-import { DataType } from '../collections/Types';
+import { DataTypeStringEnum } from '../collections/Types';
 import { IndexHttp } from '../../http/Index';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -77,7 +77,10 @@ const Schema: FC<{
   const fetchSchemaListWithIndex = async (
     collectionName: string
   ): Promise<FieldView[]> => {
-    const vectorTypes: DataType[] = ['BinaryVector', 'FloatVector'];
+    const vectorTypes: DataTypeStringEnum[] = [
+      DataTypeStringEnum.BinaryVector,
+      DataTypeStringEnum.FloatVector,
+    ];
     const indexList = await IndexHttp.getIndexInfo(collectionName);
     const schemaList = await FieldHttp.getFields(collectionName);
     let fields: FieldView[] = [];

+ 3 - 3
client/src/pages/schema/Types.ts

@@ -1,6 +1,6 @@
 import { ReactElement } from 'react';
 import { MetricType } from '../../consts/Milvus';
-import { DataType } from '../collections/Types';
+import { DataTypeStringEnum } from '../collections/Types';
 
 export enum INDEX_TYPES_ENUM {
   IVF_FLAT = 'IVF_FLAT',
@@ -16,7 +16,7 @@ export enum INDEX_TYPES_ENUM {
 }
 
 export interface Field {
-  data_type: DataType;
+  data_type: DataTypeStringEnum;
   fieldID: string;
   type_params: { key: string; value: string }[];
   is_primary_key: true;
@@ -30,7 +30,7 @@ export interface FieldData {
   _isAutoId: boolean;
   _fieldName: string;
   _fieldNameElement?: ReactElement;
-  _fieldType: DataType;
+  _fieldType: DataTypeStringEnum;
   _dimension: string;
   _desc: string;
 }

+ 6 - 3
client/src/plugins/search/Types.ts

@@ -1,6 +1,9 @@
 import { Option } from '../../components/customSelector/Types';
 import { searchKeywordsType } from '../../consts/Milvus';
-import { DataType, DataTypeEnum } from 'insight_src/pages/collections/Types';
+import {
+  DataTypeEnum,
+  DataTypeStringEnum,
+} from 'insight_src/pages/collections/Types';
 import { IndexView } from 'insight_src/pages/schema/Types';
 
 export interface SearchParamsProps {
@@ -30,7 +33,7 @@ export interface SearchResultView {
 }
 
 export interface FieldOption extends Option {
-  fieldType: DataType;
+  fieldType: DataTypeStringEnum;
   // used to get metric type, index type and index params for search params
   // if user doesn't create index, default value is null
   indexInfo: IndexView | null;
@@ -60,7 +63,7 @@ export interface VectorSearchParam {
   };
   vectors: any;
   output_fields: string[];
-  vector_type: number | DataTypeEnum;
+  vector_type: DataTypeEnum;
   travel_timestamp?: string;
 }
 

+ 3 - 3
client/src/types/SearchTypes.ts

@@ -1,6 +1,6 @@
 import { Option } from '../components/customSelector/Types';
 import { searchKeywordsType } from '../consts/Milvus';
-import { DataType, DataTypeEnum } from '../pages/collections/Types';
+import { DataTypeEnum, DataTypeStringEnum } from '../pages/collections/Types';
 import { IndexView } from '../pages/schema/Types';
 
 export interface SearchParamsProps {
@@ -30,7 +30,7 @@ export interface SearchResultView {
 }
 
 export interface FieldOption extends Option {
-  fieldType: DataType;
+  fieldType: DataTypeStringEnum;
   // used to get metric type, index type and index params for search params
   // if user doesn't create index, default value is null
   indexInfo: IndexView | null;
@@ -60,7 +60,7 @@ export interface VectorSearchParam {
   };
   vectors: any;
   output_fields: string[];
-  vector_type: number | DataTypeEnum;
+  vector_type: DataTypeEnum;
 }
 
 export interface SearchResult {

+ 2 - 2
client/src/utils/Form.ts

@@ -1,7 +1,7 @@
 import { Option } from '../components/customSelector/Types';
 import { METRIC_TYPES_VALUES } from '../consts/Milvus';
 import { IForm } from '../hooks/Form';
-import { DataType } from '../pages/collections/Types';
+import { DataTypeStringEnum } from '../pages/collections/Types';
 import { IndexType } from '../pages/schema/Types';
 
 interface IInfo {
@@ -22,7 +22,7 @@ export const formatForm = (info: IInfo): IForm[] => {
 
 export const getMetricOptions = (
   indexType: IndexType,
-  fieldType: DataType
+  fieldType: DataTypeStringEnum
 ): Option[] => {
   const baseFloatOptions = [
     {

+ 30 - 10
client/src/utils/search.ts

@@ -1,5 +1,5 @@
 import { Field } from '../components/advancedSearch/Types';
-import { DataType, DataTypeEnum } from '../pages/collections/Types';
+import { DataTypeEnum, DataTypeStringEnum } from '../pages/collections/Types';
 import {
   FieldData,
   IndexType,
@@ -21,11 +21,19 @@ import {
 export const transferSearchResult = (
   result: SearchResult[]
 ): SearchResultView[] => {
-  const resultView = result.map((r, index) => ({
-    rank: index + 1,
-    ...r,
-    distance: r.score,
-  }));
+  const resultView = result.map((r, index) => {
+    const { rank, distance, ...others } = r;
+    const data: any = {
+      rank: index + 1,
+      distance: r.score,
+    };
+    // When value is boolean ,table will not render bool value.
+    // So we need to use toString() here.
+    Object.keys(others).forEach(v => {
+      data[v] = others[v].toString();
+    });
+    return data;
+  });
 
   return resultView;
 };
@@ -35,7 +43,7 @@ export const transferSearchResult = (
  * @param fieldType only vector type fields: 'BinaryVector' or 'FloatVector'
  */
 export const getEmbeddingType = (
-  fieldType: DataType
+  fieldType: DataTypeStringEnum
 ): DataTypeEnum.BinaryVector | DataTypeEnum.FloatVector => {
   const type =
     fieldType === 'BinaryVector'
@@ -64,7 +72,10 @@ export const getDefaultIndexType = (embeddingType: DataTypeEnum): IndexType => {
 export const classifyFields = (
   fields: FieldData[]
 ): { vectorFields: FieldData[]; nonVectorFields: FieldData[] } => {
-  const vectorTypes: DataType[] = ['BinaryVector', 'FloatVector'];
+  const vectorTypes: DataTypeStringEnum[] = [
+    DataTypeStringEnum.BinaryVector,
+    DataTypeStringEnum.FloatVector,
+  ];
   return fields.reduce(
     (result, cur) => {
       const changedFieldType = vectorTypes.includes(cur._fieldType)
@@ -101,9 +112,18 @@ export const getVectorFieldOptions = (
 };
 
 export const getNonVectorFieldsForFilter = (fields: FieldData[]): Field[] => {
-  const intTypes: DataType[] = ['Int8', 'Int16', 'Int32', 'Int64'];
+  const intTypes: DataTypeStringEnum[] = [
+    DataTypeStringEnum.Int8,
+    DataTypeStringEnum.Int16,
+    DataTypeStringEnum.Int32,
+    DataTypeStringEnum.Int64,
+  ];
   return fields.map(f => ({
     name: f._fieldName,
-    type: intTypes.includes(f._fieldType) ? 'int' : 'float',
+    type: intTypes.includes(f._fieldType)
+      ? 'int'
+      : f._fieldType === 'Bool'
+      ? 'bool'
+      : 'float',
   }));
 };

+ 5 - 2
server/generate-csv.ts

@@ -3,7 +3,10 @@ import { createObjectCsvWriter as createCsvWriter } from 'csv-writer';
 // use to test vector insert
 const csvWriter = createCsvWriter({
   path: './vectors.csv',
-  header: [{ id: 'vector', title: 'vector' }],
+  header: [
+    { id: 'vector', title: 'vector' },
+    { id: 'bool', title: 'bool' },
+  ],
 });
 
 const records = [];
@@ -20,7 +23,7 @@ const generateVector = (dimension) => {
 
 while (records.length < 50000) {
   const value = generateVector(4);
-  records.push({ vector: value });
+  records.push({ vector: value, bool: records.length % 2 === 0 });
 }
 
 csvWriter