@decaf-ts/for-couchdb
Version:
decaf-ts couchdb wrappers
215 lines • 8.56 kB
JavaScript
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