import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { CodeLanguageEnum, CodeViewData } from '@/components/code/Types'; import DialogTemplate from '@/components/customDialog/DialogTemplate'; import CustomSwitch from '@/components/customSwitch/CustomSwitch'; import { DEFAULT_SEFMENT_FILE_SIZE, DEFAULT_VECTORS, INDEX_CONFIG, INDEX_OPTIONS_MAP, METRIC_TYPES_VALUES, } from '../../consts/Milvus'; import { useFormValidation } from '@/hooks/Form'; import { getCreateIndexJSCode } from '@/utils/code/Js'; import { getCreateIndexPYCode } from '@/utils/code/Py'; import { getCreateIndexJavaCode } from '@/utils/code/Java'; import { formatForm, getMetricOptions } from '@/utils/Form'; import { computMilvusRecommonds, formatSize } from '@/utils/SizingTool'; import { DataTypeEnum, DataTypeStringEnum } from '../collections/Types'; import CreateForm from './CreateForm'; import SizingInfo from './SizingInfo'; import { IndexType, IndexExtraParam, INDEX_TYPES_ENUM } from './Types'; const CreateIndex = (props: { collectionName: string; fieldType: DataTypeStringEnum; handleCreate: (params: IndexExtraParam, index_name: string) => void; handleCancel: () => void; // used for code mode fieldName: string; // used for sizing info dimension: number; }) => { const { collectionName, fieldType, handleCreate, handleCancel, fieldName, dimension, } = props; const { t: indexTrans } = useTranslation('index'); const { t: dialogTrans } = useTranslation('dialog'); const { t: btnTrans } = useTranslation('btn'); const { t: commonTrans } = useTranslation(); const defaultIndexType = useMemo(() => { switch (fieldType) { case DataTypeStringEnum.BinaryVector: return INDEX_TYPES_ENUM.BIN_IVF_FLAT; case DataTypeStringEnum.FloatVector: return INDEX_TYPES_ENUM.IVF_FLAT; case DataTypeStringEnum.VarChar: return INDEX_TYPES_ENUM.MARISA_TRIE; default: return INDEX_TYPES_ENUM.SORT; } }, [fieldType]); const defaultMetricType = useMemo(() => { switch (fieldType) { case DataTypeStringEnum.BinaryVector: return METRIC_TYPES_VALUES.HAMMING; case DataTypeStringEnum.FloatVector: return METRIC_TYPES_VALUES.L2; default: return ''; } }, [fieldType]); const [indexSetting, setIndexSetting] = useState<{ index_type: IndexType; [x: string]: string; }>({ index_name: '', index_type: defaultIndexType, metric_type: defaultMetricType, M: '', m: '4', efConstruction: '', nlist: '', nbits: '', n_trees: '', outDegree: '', candidatePoolSize: '', searchLength: '', knng: '', }); // control whether show code mode const [showCode, setShowCode] = useState(false); const indexCreateParams = useMemo(() => { if (!INDEX_CONFIG[indexSetting.index_type]) { return []; } return INDEX_CONFIG[indexSetting.index_type].create; }, [indexSetting.index_type]); const metricOptions = useMemo(() => { const vectorType = [ DataTypeStringEnum.BinaryVector, DataTypeStringEnum.FloatVector, ]; return vectorType.includes(fieldType) ? getMetricOptions(indexSetting.index_type, fieldType) : []; }, [indexSetting.index_type, fieldType]); const extraParams = useMemo(() => { const params: { [x: string]: string } = {}; indexCreateParams.forEach(v => { params[v] = indexSetting[v]; }); const { index_type, metric_type } = indexSetting; const extraParams: IndexExtraParam = { index_type, metric_type, params: JSON.stringify(params), }; return extraParams; }, [indexCreateParams, indexSetting]); const indexOptions = useMemo(() => { switch (fieldType) { case DataTypeStringEnum.BinaryVector: return INDEX_OPTIONS_MAP[DataTypeEnum.BinaryVector]; case DataTypeStringEnum.FloatVector: return INDEX_OPTIONS_MAP[DataTypeEnum.FloatVector]; case DataTypeStringEnum.VarChar: return INDEX_OPTIONS_MAP[DataTypeEnum.VarChar]; default: return [{ label: 'Ascending', value: 'sort' }]; } }, [fieldType]); const checkedForm = useMemo(() => { if ( fieldType !== DataTypeStringEnum.BinaryVector && fieldType !== DataTypeStringEnum.FloatVector ) { return []; } const paramsForm: any = { metric_type: indexSetting.metric_type }; indexCreateParams.forEach(v => { paramsForm[v] = indexSetting[v]; }); const form = formatForm(paramsForm); return form; }, [indexSetting, indexCreateParams, fieldType]); // sizing info needed param const sizingInfo = useMemo(() => { const { index_type } = indexSetting; const { nlist, m } = indexSetting; const floatTypes = [ INDEX_TYPES_ENUM.IVF_FLAT, INDEX_TYPES_ENUM.IVF_PQ, INDEX_TYPES_ENUM.IVF_SQ8, INDEX_TYPES_ENUM.IVF_SQ8_HYBRID, INDEX_TYPES_ENUM.FLAT, ]; const bytesTyps = [ INDEX_TYPES_ENUM.BIN_FLAT, INDEX_TYPES_ENUM.BIN_IVF_FLAT, ]; const supportedTypes = [...floatTypes, ...bytesTyps]; // check param validation if (!supportedTypes.includes(index_type)) { return null; } if (!nlist) { return null; } if (index_type === INDEX_TYPES_ENUM.IVF_PQ && !m) { return null; } // vector 100000, segment file size 1024 as default value const milvusRecommends = computMilvusRecommonds( DEFAULT_VECTORS, dimension, Number(nlist), Number(m), DEFAULT_SEFMENT_FILE_SIZE * 1024 * 1024 ); let memoryType = 'byteMemorySize'; let diskType = 'byteDiskSize'; if (floatTypes.includes(index_type)) { memoryType = 'memorySize'; diskType = 'diskSize'; } const memorySize = milvusRecommends[memoryType][index_type]; const diskSize = milvusRecommends[diskType][index_type]; return { memory: formatSize(memorySize), disk: formatSize(diskSize), }; }, [dimension, indexSetting]); /** * create index code mode */ const codeBlockData: CodeViewData[] = useMemo(() => { const vectorTypes = [ DataTypeStringEnum.BinaryVector, DataTypeStringEnum.FloatVector, ]; const isScalarField = !vectorTypes.includes(fieldType); const getCodeParams = { collectionName, fieldName, extraParams, isScalarField, indexName: indexSetting.index_name, metricType: indexSetting.metric_type, indexType: indexSetting.index_type, }; return [ { label: commonTrans('py'), language: CodeLanguageEnum.python, code: getCreateIndexPYCode(getCodeParams), }, { label: commonTrans('java'), language: CodeLanguageEnum.java, code: getCreateIndexJavaCode(getCodeParams), }, { label: commonTrans('js'), language: CodeLanguageEnum.javascript, code: getCreateIndexJSCode(getCodeParams), }, ]; }, [ commonTrans, extraParams, collectionName, fieldName, indexSetting.index_name, fieldType, ]); const { validation, checkIsValid, disabled, setDisabled, resetValidation } = useFormValidation(checkedForm); // reset index params useEffect(() => { // no need // setDisabled(true); setIndexSetting(v => ({ ...v, index_name: v.index_name, metric_type: defaultMetricType, M: '', m: '4', efConstruction: '', nlist: '', nbits: '8', // 8 by default n_trees: '', out_degree: '', candidate_pool_size: '', search_length: '', knng: '', })); }, [indexCreateParams, setDisabled, defaultMetricType]); const updateStepTwoForm = (type: string, value: string) => { setIndexSetting(v => ({ ...v, [type]: value })); }; const onIndexTypeChange = (type: string) => { let paramsForm: { [key in string]: string } = {}; // m is selector not input (INDEX_CONFIG[type].create || []) .filter(t => t !== 'm') .forEach(item => { paramsForm[item] = ''; }); // if no other params, the form should be valid. setDisabled((INDEX_CONFIG[type].create || []).length === 0 ? false : true); const form = formatForm(paramsForm); resetValidation(form); }; const handleCreateIndex = () => { handleCreate(extraParams, indexSetting.index_name); }; const handleShowCode = (event: React.ChangeEvent<{ checked: boolean }>) => { const isChecked = event.target.checked; setShowCode(isChecked); }; return ( } showCode={showCode} codeBlocksData={codeBlockData} > <> ); }; export default CreateIndex;