UNPKG

autosql

Version:

An auto-parser of JSON into SQL.

136 lines (135 loc) 6.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.predictIndexes = predictIndexes; const defaults_1 = require("../config/defaults"); const groupings_1 = require("../config/groupings"); const utilities_1 = require("../helpers/utilities"); function predictIndexes(meta_data, maxKeyLengthInput, primaryKey, data) { try { const headers = JSON.parse(JSON.stringify(meta_data)); // Deep copy to avoid mutation const maxKeyLength = maxKeyLengthInput || defaults_1.defaults.maxKeyLength; let primaryKeyFound = false; let requiredPrimaryKeys = []; let potentialPrimaryKeys = []; let potentialCompositeKeys = []; let NullablePseudoUniqueColumns = []; // ✅ Step 1: Predict indexes for date-related, unique, and pseudo-unique columns for (const [columnName, column] of Object.entries(headers)) { const columnType = column.type ?? "varchar"; const columnLength = column.length ?? 255; const isNumeric = groupings_1.groupings.intGroup.includes(columnType) || groupings_1.groupings.specialIntGroup.includes(columnType); const isDecimal = (column.decimal !== 0 && column.decimal !== undefined) || column.type == 'decimal' || groupings_1.groupings.specialIntGroup.includes(columnType); // Identify decimal columns const isText = groupings_1.groupings.textGroup.includes(columnType) && columnType !== "varchar"; const isDate = groupings_1.groupings.dateGroup.includes(columnType); // Exclude long text fields from indexing if (isText) continue; // Exclude any field longer than max key length if (columnLength >= maxKeyLength) continue; // Exclude decimals from indexes if (isDecimal) continue; // Include dates, unique values and pseudouniques as indexes if (isDate || column.unique || column.pseudounique) { column.index = true; } // If an explicit primary key is defined, set it if (primaryKey && primaryKey.includes(columnName)) { column.primary = true; headers[columnName].primary = true; if (column.unique && !column.allowNull) { potentialPrimaryKeys.push(columnName); primaryKeyFound = true; } else { requiredPrimaryKeys.push(columnName); } } else if (column.unique && !column.allowNull) { // ✅ Only consider unique columns that do NOT allow nulls as a primary key candidate potentialPrimaryKeys.push(columnName); } else if ((column.pseudounique || column.categorical) && !column.allowNull) { potentialCompositeKeys.push(columnName); } else if (column.pseudounique) { NullablePseudoUniqueColumns.push(columnName); } } if (!primaryKeyFound) { let selectedPrimaryKey = null; if (potentialPrimaryKeys && potentialPrimaryKeys.length > 0) { let idLikeKey = null; let numericKey = null; let shortestKey = potentialPrimaryKeys[0]; for (const key of potentialPrimaryKeys) { const type = headers[key]?.type ?? ""; // Prefer key ending in 'id' or '_id' if (!idLikeKey && /(_id|id)$/i.test(key)) { idLikeKey = key; } // Prefer numeric type if (!numericKey && groupings_1.groupings.intGroup.includes(type)) { numericKey = key; } // Track shortest as fallback if (key.length < shortestKey.length) { shortestKey = key; } } // Pick in order of priority selectedPrimaryKey = idLikeKey ? [idLikeKey] : numericKey ? [numericKey] : shortestKey ? [shortestKey] : null; } // ✅ If no unique column exists, try pseudo-unique combinations using data let foundUniqueCombination = false; const dateColumns = Object.keys(headers).filter(col => groupings_1.groupings.dateGroup.includes(headers[col].type ?? "") && headers[col].allowNull !== true); if (!selectedPrimaryKey && data && data.length > 0) { // Find the smallest set of pseudo-unique columns that together are unique for (let i = 1; i <= potentialCompositeKeys.length; i++) { const combinations = (0, utilities_1.generateCombinations)(potentialCompositeKeys, i); for (const combo of combinations) { const fullCombo = Array.from(new Set([...requiredPrimaryKeys, ...combo])); if ((0, utilities_1.isCombinationUnique)(data, fullCombo)) { selectedPrimaryKey = fullCombo; foundUniqueCombination = true; break; } } if (foundUniqueCombination) break; } } if (!selectedPrimaryKey && !foundUniqueCombination && data && data.length > 0) { const extendedColumns = [...potentialCompositeKeys, ...dateColumns]; for (let i = 1; i <= extendedColumns.length; i++) { const combinations = (0, utilities_1.generateCombinations)(extendedColumns, i); for (const combo of combinations) { const fullCombo = Array.from(new Set([...requiredPrimaryKeys, ...combo])); if ((0, utilities_1.isCombinationUnique)(data, fullCombo)) { selectedPrimaryKey = fullCombo; // ✅ Assign combo with date column foundUniqueCombination = true; break; } } if (foundUniqueCombination) break; } } if (selectedPrimaryKey) { for (const key of selectedPrimaryKey) { headers[key].primary = true; } } } return headers; } catch (error) { throw new Error(`Error in predictIndexes: ${error.message}`); } }