Browse Source

feat: duplicate field name validation (#862)

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 2 months ago
parent
commit
16c4186aa6

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

@@ -133,6 +133,7 @@ const collectionTrans = {
 
   // duplicate dialog
   duplicateNameExist: 'Collection 已经存在。',
+  fieldNameExist: '字段名称已存在。',
 
   // segment
   segments: 'Segment',

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

@@ -142,6 +142,7 @@ const collectionTrans = {
 
   // duplicate dialog
   duplicateNameExist: 'A collection with this name already exists.',
+  fieldNameExist: 'field name is already exist',
 
   // segment
   segments: 'Segments',

+ 13 - 2
client/src/pages/dialogs/create/CreateFields.tsx

@@ -168,11 +168,19 @@ const CreateFields: FC<CreateFieldsProps> = ({
   const handleAddNewField = (index: number, type = DataTypeEnum.Int16) => {
     const id = generateId();
     const shortType = getShortTypeName(type);
-    const sameTypeCount = fields.filter(
+
+    let sameTypeCount = fields.filter(
       f => getShortTypeName(f.data_type) === shortType
     ).length;
-    const name =
+    let name =
       sameTypeCount === 0 ? shortType : `${shortType}_${sameTypeCount + 1}`;
+
+    const existingNames = new Set(fields.map(f => f.name));
+    while (existingNames.has(name)) {
+      sameTypeCount += 1;
+      name = `${shortType}_${sameTypeCount}`;
+    }
+
     const newDefaultItem: FieldType = {
       id,
       name,
@@ -214,6 +222,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       return (
         <PrimaryKeyFieldRow
           field={field}
+          fields={fields}
           autoID={autoID}
           onFieldChange={changeFields}
           setAutoID={setAutoID}
@@ -225,6 +234,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       return (
         <VectorFieldRow
           field={field}
+          fields={fields}
           index={index}
           requiredFields={requiredFields}
           onFieldChange={changeFields}
@@ -239,6 +249,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     return (
       <VectorFieldRow
         field={field}
+        fields={fields}
         index={index}
         requiredFields={requiredFields}
         onFieldChange={changeFields}

+ 8 - 2
client/src/pages/dialogs/create/NameField.tsx

@@ -7,6 +7,7 @@ import type { FieldType } from '../../databases/collections/Types';
 
 interface NameFieldProps {
   field: FieldType;
+  fields: FieldType[];
   onChange: (id: string, name: string, isValid: boolean) => void;
   label?: string;
   isReadOnly?: boolean;
@@ -14,6 +15,7 @@ interface NameFieldProps {
 
 const NameField: FC<NameFieldProps> = ({
   field,
+  fields,
   onChange,
   label,
   isReadOnly = false,
@@ -37,7 +39,10 @@ const NameField: FC<NameFieldProps> = ({
   // Common validation function
   const validateField = (name: string) => {
     const isValid = checkEmptyValid(name);
-    return { isValid };
+    const isDuplicate =
+      fields.filter((f: FieldType) => f.name === name && f.id !== field.id)
+        .length > 0;
+    return { isValid: isValid && !isDuplicate, isDuplicate };
   };
 
   const handleChange = (newValue: string) => {
@@ -60,7 +65,8 @@ const NameField: FC<NameFieldProps> = ({
 
   const getHelperText = (name: string) => {
     if (!touched) return ' ';
-    const { isValid } = validateField(name);
+    const { isValid, isDuplicate } = validateField(name);
+    if (isDuplicate) return collectionTrans('fieldNameExist');
     return isValid ? ' ' : warningTrans('requiredOnly');
   };
 

+ 1 - 0
client/src/pages/dialogs/create/rows/FunctionFieldRow.tsx

@@ -86,6 +86,7 @@ const FunctionFieldRow: FC<FunctionFieldRowProps> = ({
     <Box sx={rowStyles}>
       <NameField
         field={field}
+        fields={fields}
         onChange={(id, name, isValid) =>
           onFieldChange(field.id!, { name }, isValid)
         }

+ 3 - 0
client/src/pages/dialogs/create/rows/PrimaryKeyFieldRow.tsx

@@ -12,6 +12,7 @@ import { rowStyles } from './styles';
 
 interface PrimaryKeyFieldRowProps {
   field: FieldType;
+  fields: FieldType[];
   autoID: boolean;
   onFieldChange: (
     id: string,
@@ -23,6 +24,7 @@ interface PrimaryKeyFieldRowProps {
 
 const PrimaryKeyFieldRow: FC<PrimaryKeyFieldRowProps> = ({
   field,
+  fields,
   autoID,
   onFieldChange,
   setAutoID,
@@ -40,6 +42,7 @@ const PrimaryKeyFieldRow: FC<PrimaryKeyFieldRowProps> = ({
     <Box sx={rowStyles}>
       <NameField
         field={field}
+        fields={fields}
         onChange={(id, name, isValid) => onFieldChange(id, { name }, isValid)}
         label={collectionTrans('idFieldName')}
       />

+ 1 - 0
client/src/pages/dialogs/create/rows/ScalarFieldRow.tsx

@@ -95,6 +95,7 @@ const ScalarFieldRow: FC<ScalarFieldRowProps> = ({
     <Box sx={rowStyles}>
       <NameField
         field={field}
+        fields={fields}
         onChange={(id, name, isValid) => onFieldChange(id, { name }, isValid)}
       />
 

+ 3 - 0
client/src/pages/dialogs/create/rows/VectorFieldRow.tsx

@@ -11,6 +11,7 @@ import { rowStyles } from './styles';
 
 interface VectorFieldRowProps {
   field: FieldType;
+  fields: FieldType[];
   index: number;
   requiredFields?: FieldType[];
   onFieldChange: (
@@ -25,6 +26,7 @@ interface VectorFieldRowProps {
 
 const VectorFieldRow: FC<VectorFieldRowProps> = ({
   field,
+  fields,
   index,
   requiredFields = [],
   onFieldChange,
@@ -42,6 +44,7 @@ const VectorFieldRow: FC<VectorFieldRowProps> = ({
     <Box sx={rowStyles}>
       <NameField
         field={field}
+        fields={fields}
         onChange={(id, name, isValid) =>
           onFieldChange(field.id!, { name }, isValid)
         }