UNPKG

@decaf-ts/for-couchdb

Version:
215 lines 8.56 kB
import { OrderDirection, PersistenceKeys } from "@decaf-ts/core"; import { CouchDBKeys } from "./../constants.js"; import { DefaultSeparator } from "@decaf-ts/db-decorators"; import { Model } from "@decaf-ts/decorator-validation"; import { CouchDBOperator } from "./../query/constants.js"; import { generateViewIndexes } from "./../views/index.js"; /** * @description Generates a name for a CouchDB index * @summary Creates a standardized name for a CouchDB index by combining name parts, compositions, and direction * @param {string[]} name - Array of name parts for the index * @param {OrderDirection} [direction] - Optional sort direction for the index * @param {string[]} [compositions] - Optional additional attributes to include in the index name * @param {string} [separator=DefaultSeparator] - The separator to use between parts of the index name * @return {string} The generated index name * @memberOf module:for-couchdb */ export function generateIndexName(name, direction, compositions, separator = DefaultSeparator) { return [ ...name.map((n) => (n === CouchDBKeys.TABLE ? "table" : n)), ...(compositions || []), ...(direction ? [direction] : []), CouchDBKeys.INDEX, ].join(separator); } export function generateModelIndexName(tableName, fieldKey, compositions = [], direction, separator = DefaultSeparator) { return [ tableName, fieldKey, ...compositions, ...(direction ? [direction] : []), PersistenceKeys.INDEX, ].join(separator); } /** * @description Generates CouchDB index configurations for models * @summary Creates a set of CouchDB index configurations based on the metadata of the provided models * @template M - The model type that extends Model * @param models - Array of model constructors to generate indexes for * @return {CreateIndexRequest[]} Array of CouchDB index configurations * @function generateIndexes * @memberOf module:for-couchdb * @mermaid * sequenceDiagram * participant Caller * participant generateIndexes * participant generateIndexName * participant Repository * * Caller->>generateIndexes: models * * Note over generateIndexes: Create base table index * generateIndexes->>generateIndexName: [CouchDBKeys.TABLE] * generateIndexName-->>generateIndexes: tableName * generateIndexes->>generateIndexes: Create table index config * * loop For each model * generateIndexes->>Repository: Get indexes metadata * Repository-->>generateIndexes: index metadata * * loop For each index in metadata * Note over generateIndexes: Extract index properties * generateIndexes->>Repository: Get table name * Repository-->>generateIndexes: tableName * * Note over generateIndexes: Define nested generate function * * generateIndexes->>generateIndexes: Call generate() for default order * Note over generateIndexes: Create index name and config * * alt Has directions * loop For each direction * generateIndexes->>generateIndexes: Call generate(direction) * Note over generateIndexes: Create ordered index config * end * end * end * end * * generateIndexes-->>Caller: Array of index configurations */ export function generateIndexes(models) { const tableName = generateIndexName([CouchDBKeys.TABLE]); const indexes = {}; indexes[tableName] = { index: { fields: [CouchDBKeys.TABLE], }, name: tableName, ddoc: tableName, type: "json", }; models.forEach((m) => { const modelTableName = Model.tableName(m); let defaultQueryAttrs = []; try { defaultQueryAttrs = Model.defaultQueryAttributes(m); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (err) { defaultQueryAttrs = []; } defaultQueryAttrs.forEach((attr) => { const baseName = generateIndexName([ modelTableName, attr, "defaultQuery", ]); const defaultFilter = { [CouchDBKeys.TABLE]: { [CouchDBOperator.EQUAL]: modelTableName, }, }; indexes[baseName] = { index: { fields: [CouchDBKeys.TABLE, attr], }, name: baseName, ddoc: baseName, type: "json", }; indexes[baseName].index.partial_filter_selector = defaultFilter; [OrderDirection.ASC, OrderDirection.DSC].forEach((direction) => { const sortedName = generateIndexName([modelTableName, attr, "defaultQuery"], direction); indexes[sortedName] = { index: { fields: [ { [CouchDBKeys.TABLE]: direction, }, { [attr]: direction, }, ], }, name: sortedName, ddoc: sortedName, type: "json", }; }); }); const ind = Model.indexes(m); Object.entries(ind).forEach(([key, value]) => { const metadataEntries = []; Object.entries(value || {}).forEach(([, metadataValue]) => { if (!metadataValue) return; const candidate = metadataValue; if (candidate.directions !== undefined || candidate.compositions !== undefined) { metadataEntries.push([key, candidate]); return; } if (typeof metadataValue === "object" && !Array.isArray(metadataValue)) { const nested = metadataValue; Object.entries(nested).forEach(([field, meta]) => { if (meta) metadataEntries.push([field, meta]); }); } }); metadataEntries.forEach(([fieldKey, meta]) => { if (!meta) return; // eslint-disable-next-line prefer-const let { directions, compositions } = meta; compositions = compositions || []; const fieldKeys = [fieldKey, ...compositions]; const tableFilter = { [CouchDBKeys.TABLE]: { [CouchDBOperator.EQUAL]: modelTableName, }, }; function generate(sort, suffix) { const name = generateModelIndexName(modelTableName, fieldKey, compositions, suffix); const baseFields = [CouchDBKeys.TABLE, ...fieldKeys]; const fields = sort ? [ { [CouchDBKeys.TABLE]: sort, }, ...fieldKeys.map((sortField) => ({ [sortField]: sort, })), ] : baseFields; indexes[name] = { index: { fields, }, name, ddoc: name, type: "json", }; if (!sort) { indexes[name].index.partial_filter_selector = tableFilter; } } generate(); const normalizedDirections = Array.from(new Set((directions || [OrderDirection.ASC]).map((dir) => String(dir).toLowerCase()))); const validDirections = normalizedDirections.filter((dir) => dir === OrderDirection.ASC || dir === OrderDirection.DSC); validDirections.forEach((direction) => { generate(direction, direction); }); }); }); }); generateViewIndexes(models).forEach((index) => { if (!index.name) return; indexes[index.name] = index; }); return Object.values(indexes); } //# sourceMappingURL=generator.js.map