diff --git a/src/context/diagram-filter-context/diagram-filter-provider.tsx b/src/context/diagram-filter-context/diagram-filter-provider.tsx index d7b66f3a..b59d73dc 100644 --- a/src/context/diagram-filter-context/diagram-filter-provider.tsx +++ b/src/context/diagram-filter-context/diagram-filter-provider.tsx @@ -18,7 +18,7 @@ import { import { useStorage } from '@/hooks/use-storage'; import { useChartDB } from '@/hooks/use-chartdb'; import { filterTable } from '@/lib/domain/diagram-filter/filter'; -import { schemaNameToSchemaId } from '@/lib/domain'; +import { databasesWithSchemas, schemaNameToSchemaId } from '@/lib/domain'; import { defaultSchemas } from '@/lib/data/default-schemas'; import type { ChartDBEvent } from '../chartdb-context/chartdb-context'; @@ -246,7 +246,7 @@ export const DiagramFilterProvider: React.FC = ({ const toggleTableFilter: DiagramFilterContext['toggleTableFilter'] = useCallback( (tableId: string) => { - if (!defaultSchemas[databaseType]) { + if (!databasesWithSchemas.includes(databaseType)) { // No schemas, toggle table filter without schema context toggleTableFilterForNoSchema(tableId); return; diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 743fbe81..6db1b6c1 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -149,6 +149,7 @@ export const ar: LanguageTranslation = { title: 'خصائص الفهرس', name: 'الإسم', unique: 'فريد', + index_type: 'نوع الفهرس', delete_index: 'حذف الفهرس', }, table_actions: { diff --git a/src/i18n/locales/bn.ts b/src/i18n/locales/bn.ts index 666e11e9..f1bb5e50 100644 --- a/src/i18n/locales/bn.ts +++ b/src/i18n/locales/bn.ts @@ -151,6 +151,7 @@ export const bn: LanguageTranslation = { title: 'ইনডেক্স কর্ম', name: 'নাম', unique: 'অদ্বিতীয়', + index_type: 'ইনডেক্স ধরন', delete_index: 'ইনডেক্স মুছুন', }, table_actions: { diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index f459e555..45226950 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -152,6 +152,7 @@ export const de: LanguageTranslation = { title: 'Indexattribute', name: 'Name', unique: 'Eindeutig', + index_type: 'Indextyp', delete_index: 'Index löschen', }, table_actions: { diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 02233933..4b12192e 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -145,6 +145,7 @@ export const en = { title: 'Index Attributes', name: 'Name', unique: 'Unique', + index_type: 'Index Type', delete_index: 'Delete Index', }, table_actions: { diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index 14898cf5..03c06df3 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -150,6 +150,7 @@ export const es: LanguageTranslation = { title: 'Atributos del Índice', name: 'Nombre', unique: 'Único', + index_type: 'Tipo de Índice', delete_index: 'Eliminar Índice', }, table_actions: { diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 18918d17..6234b673 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -148,6 +148,7 @@ export const fr: LanguageTranslation = { title: "Attributs de l'Index", name: 'Nom', unique: 'Unique', + index_type: "Type d'index", delete_index: "Supprimer l'Index", }, table_actions: { diff --git a/src/i18n/locales/gu.ts b/src/i18n/locales/gu.ts index 2ce080ec..2a022e64 100644 --- a/src/i18n/locales/gu.ts +++ b/src/i18n/locales/gu.ts @@ -152,6 +152,7 @@ export const gu: LanguageTranslation = { title: 'ઇન્ડેક્સ લક્ષણો', name: 'નામ', unique: 'અદ્વિતીય', + index_type: 'ઇન્ડેક્સ પ્રકાર', delete_index: 'ઇન્ડેક્સ કાઢી નાખો', }, table_actions: { diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts index f7880182..fb8611e2 100644 --- a/src/i18n/locales/hi.ts +++ b/src/i18n/locales/hi.ts @@ -151,6 +151,7 @@ export const hi: LanguageTranslation = { title: 'सूचकांक विशेषताएँ', name: 'नाम', unique: 'अद्वितीय', + index_type: 'इंडेक्स प्रकार', delete_index: 'सूचकांक हटाएँ', }, table_actions: { diff --git a/src/i18n/locales/hr.ts b/src/i18n/locales/hr.ts index 03cbee90..fbd01ad0 100644 --- a/src/i18n/locales/hr.ts +++ b/src/i18n/locales/hr.ts @@ -146,6 +146,7 @@ export const hr: LanguageTranslation = { title: 'Atributi indeksa', name: 'Naziv', unique: 'Jedinstven', + index_type: 'Vrsta indeksa', delete_index: 'Izbriši indeks', }, table_actions: { diff --git a/src/i18n/locales/id_ID.ts b/src/i18n/locales/id_ID.ts index b54c1944..3b15c8c9 100644 --- a/src/i18n/locales/id_ID.ts +++ b/src/i18n/locales/id_ID.ts @@ -150,6 +150,7 @@ export const id_ID: LanguageTranslation = { title: 'Atribut Indeks', name: 'Nama', unique: 'Unik', + index_type: 'Tipe Indeks', delete_index: 'Hapus Indeks', }, table_actions: { diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index d7e62e31..d3e67cd0 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -154,6 +154,7 @@ export const ja: LanguageTranslation = { title: 'インデックス属性', name: '名前', unique: 'ユニーク', + index_type: 'インデックスタイプ', delete_index: 'インデックスを削除', }, table_actions: { diff --git a/src/i18n/locales/ko_KR.ts b/src/i18n/locales/ko_KR.ts index 3d3e1dae..2463837c 100644 --- a/src/i18n/locales/ko_KR.ts +++ b/src/i18n/locales/ko_KR.ts @@ -150,6 +150,7 @@ export const ko_KR: LanguageTranslation = { title: '인덱스 속성', name: '인덱스 명', unique: '유니크 여부', + index_type: '인덱스 타입', delete_index: '인덱스 삭제', }, table_actions: { diff --git a/src/i18n/locales/mr.ts b/src/i18n/locales/mr.ts index 699cd666..c37b5cc2 100644 --- a/src/i18n/locales/mr.ts +++ b/src/i18n/locales/mr.ts @@ -153,6 +153,7 @@ export const mr: LanguageTranslation = { title: 'इंडेक्स गुणधर्म', name: 'नाव', unique: 'युनिक', + index_type: 'इंडेक्स प्रकार', delete_index: 'इंडेक्स हटवा', }, table_actions: { diff --git a/src/i18n/locales/ne.ts b/src/i18n/locales/ne.ts index c2f7da22..fb805cc9 100644 --- a/src/i18n/locales/ne.ts +++ b/src/i18n/locales/ne.ts @@ -151,6 +151,7 @@ export const ne: LanguageTranslation = { title: 'सूचक विशेषताहरू', name: 'नाम', unique: 'अनन्य', + index_type: 'इन्डेक्स प्रकार', delete_index: 'सूचक हटाउनुहोस्', }, table_actions: { diff --git a/src/i18n/locales/pt_BR.ts b/src/i18n/locales/pt_BR.ts index 3b96c34d..9e1f2082 100644 --- a/src/i18n/locales/pt_BR.ts +++ b/src/i18n/locales/pt_BR.ts @@ -151,6 +151,7 @@ export const pt_BR: LanguageTranslation = { title: 'Atributos do Índice', name: 'Nome', unique: 'Único', + index_type: 'Tipo de Índice', delete_index: 'Excluir Índice', }, table_actions: { diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index 75a1b0cb..6664744f 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -147,6 +147,7 @@ export const ru: LanguageTranslation = { title: 'Атрибуты индекса', name: 'Имя', unique: 'Уникальный', + index_type: 'Тип индекса', delete_index: 'Удалить индекс', }, table_actions: { diff --git a/src/i18n/locales/te.ts b/src/i18n/locales/te.ts index de434249..b557883f 100644 --- a/src/i18n/locales/te.ts +++ b/src/i18n/locales/te.ts @@ -151,6 +151,7 @@ export const te: LanguageTranslation = { title: 'ఇండెక్స్ గుణాలు', name: 'పేరు', unique: 'అద్వితీయ', + index_type: 'ఇండెక్స్ రకం', delete_index: 'ఇండెక్స్ తొలగించు', }, table_actions: { diff --git a/src/i18n/locales/tr.ts b/src/i18n/locales/tr.ts index f9d0481a..c7832ab2 100644 --- a/src/i18n/locales/tr.ts +++ b/src/i18n/locales/tr.ts @@ -150,6 +150,7 @@ export const tr: LanguageTranslation = { title: 'İndeks Özellikleri', name: 'Ad', unique: 'Tekil', + index_type: 'İndeks Türü', delete_index: 'İndeksi Sil', }, table_actions: { diff --git a/src/i18n/locales/uk.ts b/src/i18n/locales/uk.ts index aa83b812..373c3f8f 100644 --- a/src/i18n/locales/uk.ts +++ b/src/i18n/locales/uk.ts @@ -149,6 +149,7 @@ export const uk: LanguageTranslation = { title: 'Атрибути індексу', name: 'Назва індекса', unique: 'Унікальний', + index_type: 'Тип індексу', delete_index: 'Видалити індекс', }, table_actions: { diff --git a/src/i18n/locales/vi.ts b/src/i18n/locales/vi.ts index bba64051..ca0e0c6f 100644 --- a/src/i18n/locales/vi.ts +++ b/src/i18n/locales/vi.ts @@ -150,6 +150,7 @@ export const vi: LanguageTranslation = { title: 'Thuộc tính chỉ mục', name: 'Tên', unique: 'Giá trị duy nhất', + index_type: 'Loại chỉ mục', delete_index: 'Xóa chỉ mục', }, table_actions: { diff --git a/src/i18n/locales/zh_CN.ts b/src/i18n/locales/zh_CN.ts index caa80920..efe87d11 100644 --- a/src/i18n/locales/zh_CN.ts +++ b/src/i18n/locales/zh_CN.ts @@ -147,6 +147,7 @@ export const zh_CN: LanguageTranslation = { title: '索引属性', name: '名称', unique: '唯一', + index_type: '索引类型', delete_index: '删除索引', }, table_actions: { diff --git a/src/i18n/locales/zh_TW.ts b/src/i18n/locales/zh_TW.ts index 34fbd30c..76464cb4 100644 --- a/src/i18n/locales/zh_TW.ts +++ b/src/i18n/locales/zh_TW.ts @@ -147,6 +147,7 @@ export const zh_TW: LanguageTranslation = { title: '索引屬性', name: '名稱', unique: '唯一', + index_type: '索引類型', delete_index: '刪除索引', }, table_actions: { diff --git a/src/lib/data/export-metadata/export-per-type/postgresql.ts b/src/lib/data/export-metadata/export-per-type/postgresql.ts index 45163bfc..a745bd5d 100644 --- a/src/lib/data/export-metadata/export-per-type/postgresql.ts +++ b/src/lib/data/export-metadata/export-per-type/postgresql.ts @@ -405,7 +405,7 @@ export function exportPostgreSQL({ .filter(Boolean); return indexFieldNames.length > 0 - ? `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${indexName} ON ${tableName} (${indexFieldNames.join(', ')});` + ? `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${indexName} ON ${tableName}${index.type && index.type !== 'btree' ? ` USING ${index.type.toUpperCase()}` : ''} (${indexFieldNames.join(', ')});` : ''; }) .filter(Boolean); diff --git a/src/lib/domain/db-index.ts b/src/lib/domain/db-index.ts index ca2b98d2..b8b27e92 100644 --- a/src/lib/domain/db-index.ts +++ b/src/lib/domain/db-index.ts @@ -2,6 +2,17 @@ import { z } from 'zod'; import type { AggregatedIndexInfo } from '../data/import-metadata/metadata-types/index-info'; import { generateId } from '../utils'; import type { DBField } from './db-field'; +import { DatabaseType } from './database-type'; + +export const INDEX_TYPES = [ + 'btree', + 'hash', + 'gist', + 'gin', + 'spgist', + 'brin', +] as const; +export type IndexType = (typeof INDEX_TYPES)[number]; export interface DBIndex { id: string; @@ -9,6 +20,7 @@ export interface DBIndex { unique: boolean; fieldIds: string[]; createdAt: number; + type?: IndexType | null; } export const dbIndexSchema: z.ZodType = z.object({ @@ -17,6 +29,7 @@ export const dbIndexSchema: z.ZodType = z.object({ unique: z.boolean(), fieldIds: z.array(z.string()), createdAt: z.number(), + type: z.enum(INDEX_TYPES).optional(), }); export const createIndexesFromMetadata = ({ @@ -36,5 +49,10 @@ export const createIndexesFromMetadata = ({ .map((c) => fields.find((f) => f.name === c.name)?.id) .filter((id): id is string => id !== undefined), createdAt: Date.now(), + type: idx.index_type?.toLowerCase() as IndexType, }) ); + +export const databaseIndexTypes: { [key in DatabaseType]?: IndexType[] } = { + [DatabaseType.POSTGRESQL]: ['btree', 'hash'], +}; diff --git a/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx b/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx index 01b2b875..234e4918 100644 --- a/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx +++ b/src/pages/editor-page/canvas/canvas-filter/canvas-filter.tsx @@ -16,7 +16,6 @@ import type { TreeNode } from '@/components/tree-view/tree'; import { ScrollArea } from '@/components/scroll-area/scroll-area'; import { useDiagramFilter } from '@/context/diagram-filter-context/use-diagram-filter'; import { ToggleGroup, ToggleGroupItem } from '@/components/toggle/toggle-group'; -import { defaultSchemas } from '@/lib/data/default-schemas'; import type { GroupingMode, NodeContext, @@ -26,6 +25,7 @@ import type { } from './types'; import { generateTreeDataByAreas, generateTreeDataBySchemas } from './utils'; import { FilterItemActions } from './filter-item-actions'; +import { databasesWithSchemas } from '@/lib/domain'; export interface CanvasFilterProps { onClose: () => void; @@ -63,7 +63,7 @@ export const CanvasFilter: React.FC = ({ onClose }) => { ); const databaseWithSchemas = useMemo( - () => !!defaultSchemas[databaseType], + () => databasesWithSchemas.includes(databaseType), [databaseType] ); diff --git a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-index/table-index.tsx b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-index/table-index.tsx index 16676bc7..5d006b22 100644 --- a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-index/table-index.tsx +++ b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-index/table-index.tsx @@ -1,7 +1,11 @@ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Ellipsis, Trash2 } from 'lucide-react'; import { Button } from '@/components/button/button'; -import type { DBIndex } from '@/lib/domain/db-index'; +import { + databaseIndexTypes, + type DBIndex, + type IndexType, +} from '@/lib/domain/db-index'; import type { DBField } from '@/lib/domain/db-field'; import { Popover, @@ -20,6 +24,7 @@ import { TooltipContent, TooltipTrigger, } from '@/components/tooltip/tooltip'; +import { useChartDB } from '@/hooks/use-chartdb'; export interface TableIndexProps { index: DBIndex; @@ -28,6 +33,11 @@ export interface TableIndexProps { fields: DBField[]; } +const allIndexTypeOptions: { label: string; value: IndexType }[] = [ + { label: 'B-tree (default)', value: 'btree' }, + { label: 'Hash', value: 'hash' }, +]; + export const TableIndex: React.FC = ({ fields, index, @@ -35,14 +45,51 @@ export const TableIndex: React.FC = ({ removeIndex, }) => { const { t } = useTranslation(); + const { databaseType } = useChartDB(); const fieldOptions = fields.map((field) => ({ label: field.name, value: field.id, })); - const updateIndexFields = (fieldIds: string | string[]) => { - const ids = Array.isArray(fieldIds) ? fieldIds : [fieldIds]; - updateIndex({ fieldIds: ids }); - }; + const updateIndexFields = useCallback( + (fieldIds: string | string[]) => { + const ids = Array.isArray(fieldIds) ? fieldIds : [fieldIds]; + + // For hash indexes, only keep the last selected field + if (index.type === 'hash' && ids.length > 0) { + updateIndex({ fieldIds: [ids[ids.length - 1]] }); + } else { + updateIndex({ fieldIds: ids }); + } + }, + [index.type, updateIndex] + ); + + const indexTypeOptions = useMemo( + () => + allIndexTypeOptions.filter((option) => + databaseIndexTypes[databaseType]?.includes(option.value) + ), + [databaseType] + ); + + const updateIndexType = useCallback( + (value: string | string[]) => { + { + const newType = value as IndexType; + // If switching to hash and multiple fields are selected, keep only the first + if (newType === 'hash' && index.fieldIds.length > 1) { + updateIndex({ + type: newType, + fieldIds: [index.fieldIds[0]], + }); + } else { + updateIndex({ type: newType }); + } + } + }, + [updateIndex, index.fieldIds] + ); + return (
= ({ } />
+ {indexTypeOptions.length > 0 ? ( +
+ + +
+ ) : null}