@yihuangdb/storage-object
Version:
A Node.js storage object layer library using Redis OM
541 lines • 18 kB
JavaScript
/**
* Redis Key Patterns Documentation
*
* This module documents all Redis key patterns used by the StorageObject library.
* It serves as the single source of truth for Redis key naming conventions.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PatternValidator = exports.RedisPatternUtils = exports.PATTERN_DOCUMENTATION = exports.PatternCategory = exports.REDIS_KEY_PATTERNS = void 0;
exports.getAllPatterns = getAllPatterns;
/**
* Core Redis key patterns used by StorageObject
*/
exports.REDIS_KEY_PATTERNS = {
// ====================
// ENTITY DATA PATTERNS
// ====================
/**
* Main entity storage patterns
* Format: {entityName}:{entityId}
* Example: User:123, Product:abc-def
* Type: JSON or Hash
*/
ENTITY_DATA: '{entityName}:{entityId}',
/**
* Entity with custom prefix
* Format: {prefix}:{entityName}:{entityId}
* Example: app:User:123, tenant1:Product:abc
*/
ENTITY_DATA_WITH_PREFIX: '{prefix}:{entityName}:{entityId}',
// ====================
// INDEX PATTERNS
// ====================
/**
* RediSearch index definition
* Format: idx:{entityName}
* Example: idx:User, idx:Product
* Type: RediSearch Index
*/
INDEX_DEFINITION: 'idx:{entityName}',
/**
* RediSearch index data
* Format: idx:{entityName}:{field}
* Example: idx:User:email, idx:Product:category
* Type: Various (Set, Sorted Set, etc.)
*/
INDEX_DATA: 'idx:{entityName}:{field}',
/**
* Field-level indexes (Redis OM specific)
* Format: {entityName}#{field}:{value}
* Example: User#email:john@example.com
* Type: String (points to entity ID)
*/
FIELD_INDEX: '{entityName}#{field}:{value}',
// ====================
// FULL-TEXT SEARCH PATTERNS
// ====================
/**
* Full-text search index
* Format: ft:{entityName}
* Example: ft:Article, ft:Product
* Type: RediSearch FT Index
*/
FT_INDEX: 'ft:{entityName}',
/**
* Full-text search data
* Format: ft:{entityName}:{component}
* Example: ft:Article:doc, ft:Product:terms
*/
FT_INDEX_DATA: 'ft:{entityName}:{component}',
/**
* Alternative FT format (legacy)
* Format: ft.{entityName}
* Example: ft.Article, ft.Product
*/
FT_INDEX_ALT: 'ft.{entityName}',
// ====================
// SCHEMA VERSIONING PATTERNS
// ====================
/**
* Schema metadata hash
* Format: schema:{entityName}
* Example: schema:User, schema:Product
* Type: Hash
* Fields: version, fields, indexes, dataStructure, entityCount, etc.
*/
SCHEMA_METADATA: 'schema:{entityName}',
/**
* HyperLogLog for entity counting
* Format: schema:{entityName}:hll
* Example: schema:User:hll
* Type: HyperLogLog
* Purpose: Efficient approximate counting
*/
SCHEMA_HLL: 'schema:{entityName}:hll',
/**
* Version history sorted set
* Format: schema:{entityName}:versions
* Example: schema:User:versions
* Type: Sorted Set
* Score: Timestamp, Value: v{version}:{reason}
*/
SCHEMA_VERSIONS: 'schema:{entityName}:versions',
// ====================
// METADATA PATTERNS
// ====================
/**
* Redis OM metadata
* Format: RedisOM:{entityName}:{type}
* Example: RedisOM:User:Schema, RedisOM:User:DataStructure
* Type: String/Hash
*/
REDIS_OM_METADATA: 'RedisOM:{entityName}:{type}',
/**
* Custom metadata
* Format: _meta:{entityName}:{key}
* Example: _meta:User:config, _meta:Product:settings
*/
CUSTOM_METADATA: '_meta:{entityName}:{key}',
// ====================
// REGISTRY PATTERNS
// ====================
/**
* Schema registry
* Format: registry:schemas
* Type: Hash
* Fields: {schemaName} -> version
*/
REGISTRY_SCHEMAS: 'registry:schemas',
/**
* Registry metadata
* Format: registry:metadata
* Type: Hash
*/
REGISTRY_METADATA: 'registry:metadata',
// ====================
// TEMPORARY PATTERNS
// ====================
/**
* Temporary keys
* Format: _tmp:{entityName}:{id}
* Example: _tmp:User:import-123
* Type: Various (auto-expire)
*/
TEMPORARY: '_tmp:{entityName}:{id}',
/**
* Lock keys (not currently used - using WATCH/MULTI/EXEC instead)
* Format: _lock:{entityName}:{resource}
* Example: _lock:User:schema-update
*/
LOCK: '_lock:{entityName}:{resource}',
// ====================
// BATCH OPERATION PATTERNS
// ====================
/**
* Batch operation tracking
* Format: batch:{operationId}
* Example: batch:import-2024-01-01
*/
BATCH_OPERATION: 'batch:{operationId}',
/**
* Rollback data
* Format: rollback:{operationId}:{entityId}
* Example: rollback:update-123:user-456
*/
ROLLBACK_DATA: 'rollback:{operationId}:{entityId}',
};
/**
* Pattern categories for documentation
*/
var PatternCategory;
(function (PatternCategory) {
PatternCategory["ENTITY"] = "Entity Data";
PatternCategory["INDEX"] = "Indexes";
PatternCategory["SEARCH"] = "Full-Text Search";
PatternCategory["SCHEMA"] = "Schema Versioning";
PatternCategory["METADATA"] = "Metadata";
PatternCategory["REGISTRY"] = "Registry";
PatternCategory["TEMPORARY"] = "Temporary";
PatternCategory["BATCH"] = "Batch Operations";
})(PatternCategory || (exports.PatternCategory = PatternCategory = {}));
/**
* Complete pattern documentation
*/
exports.PATTERN_DOCUMENTATION = [
// Entity Data
{
pattern: exports.REDIS_KEY_PATTERNS.ENTITY_DATA,
category: PatternCategory.ENTITY,
description: 'Main entity storage',
example: 'User:123',
redisType: 'JSON or Hash',
notes: 'Primary data storage for entities'
},
{
pattern: exports.REDIS_KEY_PATTERNS.ENTITY_DATA_WITH_PREFIX,
category: PatternCategory.ENTITY,
description: 'Entity with custom prefix',
example: 'app:User:123',
redisType: 'JSON or Hash',
notes: 'Used for multi-tenant or namespaced data'
},
// Indexes
{
pattern: exports.REDIS_KEY_PATTERNS.INDEX_DEFINITION,
category: PatternCategory.INDEX,
description: 'RediSearch index definition',
example: 'idx:User',
redisType: 'RediSearch Index',
notes: 'Stores index structure and configuration'
},
{
pattern: exports.REDIS_KEY_PATTERNS.INDEX_DATA,
category: PatternCategory.INDEX,
description: 'Index data for specific fields',
example: 'idx:User:email',
redisType: 'Set/Sorted Set',
notes: 'Actual index data for queries'
},
{
pattern: exports.REDIS_KEY_PATTERNS.FIELD_INDEX,
category: PatternCategory.INDEX,
description: 'Field-level unique indexes',
example: 'User#email:john@example.com',
redisType: 'String',
notes: 'Points to entity ID for unique constraints'
},
// Full-Text Search
{
pattern: exports.REDIS_KEY_PATTERNS.FT_INDEX,
category: PatternCategory.SEARCH,
description: 'Full-text search index',
example: 'ft:Article',
redisType: 'RediSearch FT',
notes: 'Full-text search capabilities'
},
// Schema Versioning
{
pattern: exports.REDIS_KEY_PATTERNS.SCHEMA_METADATA,
category: PatternCategory.SCHEMA,
description: 'Schema metadata and configuration',
example: 'schema:User',
redisType: 'Hash',
notes: 'Stores version, fields, indexes, stats'
},
{
pattern: exports.REDIS_KEY_PATTERNS.SCHEMA_HLL,
category: PatternCategory.SCHEMA,
description: 'HyperLogLog for entity counting',
example: 'schema:User:hll',
redisType: 'HyperLogLog',
notes: 'Constant 12KB memory for counting'
},
{
pattern: exports.REDIS_KEY_PATTERNS.SCHEMA_VERSIONS,
category: PatternCategory.SCHEMA,
description: 'Version history tracking',
example: 'schema:User:versions',
redisType: 'Sorted Set',
notes: 'Keeps last 100 versions with timestamps'
},
// Metadata
{
pattern: exports.REDIS_KEY_PATTERNS.REDIS_OM_METADATA,
category: PatternCategory.METADATA,
description: 'Redis OM framework metadata',
example: 'RedisOM:User:Schema',
redisType: 'String/Hash',
notes: 'Framework-specific metadata'
},
// Registry
{
pattern: exports.REDIS_KEY_PATTERNS.REGISTRY_SCHEMAS,
category: PatternCategory.REGISTRY,
description: 'Central schema registry',
example: 'registry:schemas',
redisType: 'Hash',
notes: 'Maps schema names to versions'
},
// Temporary
{
pattern: exports.REDIS_KEY_PATTERNS.TEMPORARY,
category: PatternCategory.TEMPORARY,
description: 'Temporary processing keys',
example: '_tmp:User:import-123',
redisType: 'Various',
notes: 'Should have TTL for auto-cleanup'
}
];
/**
* Utility class for pattern operations
*/
class RedisPatternUtils {
/**
* Generate actual key from pattern
*/
static generateKey(pattern, params) {
let key = pattern;
Object.entries(params).forEach(([param, value]) => {
key = key.replace(`{${param}}`, value);
});
return key;
}
/**
* Extract parameters from key using pattern
*/
static extractParams(key, pattern) {
const params = {};
const patternRegex = pattern.replace(/{(\w+)}/g, '(?<$1>[^:]+)');
const regex = new RegExp(`^${patternRegex}$`);
const match = key.match(regex);
if (!match || !match.groups) {
return null;
}
return match.groups;
}
/**
* Check if key matches pattern
*/
static matchesPattern(key, pattern) {
const patternRegex = pattern.replace(/{(\w+)}/g, '[^:]+');
const regex = new RegExp(`^${patternRegex}$`);
return regex.test(key);
}
/**
* Get all patterns for an entity
*/
static getEntityPatterns(entityName, prefix) {
const patterns = [];
// Entity data (with storage prefix)
patterns.push(`storage:${entityName}:*`);
patterns.push(`${entityName}:*`); // Legacy pattern for backward compatibility
if (prefix) {
patterns.push(`${prefix}:${entityName}:*`);
}
// Indexes
patterns.push(`idx:${entityName}`);
patterns.push(`idx:${entityName}:*`);
patterns.push(`${entityName}#*`);
// Full-text search
patterns.push(`ft:${entityName}`);
patterns.push(`ft:${entityName}:*`);
patterns.push(`ft.${entityName}`);
// Schema versioning
patterns.push(`schema:${entityName}`);
patterns.push(`schema:${entityName}:hll`);
patterns.push(`schema:${entityName}:versions`);
// Metadata
patterns.push(`RedisOM:${entityName}:*`);
patterns.push(`_meta:${entityName}:*`);
// Temporary
patterns.push(`_tmp:${entityName}:*`);
patterns.push(`_lock:${entityName}:*`);
return patterns;
}
/**
* Categorize a key
*/
static categorizeKey(key) {
if (key.startsWith('idx:') || key.includes('#')) {
return PatternCategory.INDEX;
}
if (key.startsWith('ft:') || key.startsWith('ft.')) {
return PatternCategory.SEARCH;
}
if (key.startsWith('schema:')) {
return PatternCategory.SCHEMA;
}
if (key.startsWith('RedisOM:') || key.startsWith('_meta:')) {
return PatternCategory.METADATA;
}
if (key.startsWith('registry:')) {
return PatternCategory.REGISTRY;
}
if (key.startsWith('_tmp:') || key.startsWith('_lock:')) {
return PatternCategory.TEMPORARY;
}
if (key.startsWith('batch:') || key.startsWith('rollback:')) {
return PatternCategory.BATCH;
}
// Check for entity pattern
if (key.includes(':') && !key.startsWith('_')) {
return PatternCategory.ENTITY;
}
return null;
}
/**
* Generate pattern documentation as markdown
*/
static generateDocumentation() {
let doc = '# Redis Key Patterns\n\n';
// Group by category
const byCategory = new Map();
exports.PATTERN_DOCUMENTATION.forEach(pattern => {
const list = byCategory.get(pattern.category) || [];
list.push(pattern);
byCategory.set(pattern.category, list);
});
// Generate documentation
byCategory.forEach((patterns, category) => {
doc += `## ${category}\n\n`;
patterns.forEach(pattern => {
doc += `### ${pattern.pattern}\n`;
doc += `- **Description**: ${pattern.description}\n`;
doc += `- **Example**: \`${pattern.example}\`\n`;
doc += `- **Redis Type**: ${pattern.redisType}\n`;
if (pattern.notes) {
doc += `- **Notes**: ${pattern.notes}\n`;
}
doc += '\n';
});
});
return doc;
}
}
exports.RedisPatternUtils = RedisPatternUtils;
/**
* Pattern validator for runtime checks
*/
class PatternValidator {
/**
* Validate that a key follows naming conventions
*/
static validateKey(key) {
// Check for valid characters (allow @ for email patterns)
if (!/^[a-zA-Z0-9:_#.\-@]+$/.test(key)) {
return {
valid: false,
error: 'Key contains invalid characters. Use only alphanumeric, :, _, #, ., -, @'
};
}
// Special patterns first (most specific)
// Field index pattern: {entity}#{field}:{value}
if (/^[A-Z][a-zA-Z0-9]+#[a-z]+:[^:]+$/.test(key)) {
return { valid: true, pattern: 'FIELD_INDEX' };
}
// Schema patterns (allow flexible entity names including lowercase, hyphens, numbers)
if (key.startsWith('schema:')) {
if (key.endsWith(':hll')) {
return { valid: true, pattern: 'SCHEMA_HLL' };
}
if (key.endsWith(':versions')) {
return { valid: true, pattern: 'SCHEMA_VERSIONS' };
}
// Allow any valid entity name after schema:
if (/^schema:[a-zA-Z0-9\-_]+$/.test(key)) {
return { valid: true, pattern: 'SCHEMA_METADATA' };
}
}
// Index patterns (allow flexible entity names)
if (key.startsWith('idx:')) {
if (/^idx:[a-zA-Z0-9\-_]+:[a-zA-Z0-9\-_]+$/.test(key)) {
return { valid: true, pattern: 'INDEX_DATA' };
}
if (/^idx:[a-zA-Z0-9\-_]+$/.test(key)) {
return { valid: true, pattern: 'INDEX_DEFINITION' };
}
}
// Full-text search patterns
if (key.startsWith('ft:') || key.startsWith('ft.')) {
return { valid: true, pattern: 'FT_INDEX' };
}
// RedisOM metadata
if (key.startsWith('RedisOM:')) {
return { valid: true, pattern: 'REDIS_OM_METADATA' };
}
// Custom metadata patterns
if (key.startsWith('_meta:')) {
return { valid: true, pattern: 'CUSTOM_METADATA' };
}
// Registry patterns
if (key.startsWith('registry:')) {
return { valid: true, pattern: 'REGISTRY_SCHEMAS' };
}
// Temporary patterns
if (key.startsWith('_tmp:')) {
return { valid: true, pattern: 'TEMPORARY' };
}
// Lock patterns
if (key.startsWith('_lock:')) {
return { valid: true, pattern: 'LOCK' };
}
// Entity keys with IDs
if (/^[A-Z][a-zA-Z0-9]+:[a-zA-Z0-9\-]+$/.test(key)) {
return { valid: true, pattern: 'ENTITY_DATA' };
}
// Prefixed entity keys
if (/^[a-z]+:[A-Z][a-zA-Z0-9]+:[a-zA-Z0-9\-]+$/.test(key)) {
return { valid: true, pattern: 'ENTITY_DATA_WITH_PREFIX' };
}
return {
valid: false,
error: 'Key does not match any known pattern'
};
}
/**
* Validate a set of keys
*/
static validateKeys(keys) {
const valid = [];
const invalid = [];
const stats = {};
keys.forEach(key => {
const result = this.validateKey(key);
if (result.valid) {
valid.push(key);
stats[result.pattern] = (stats[result.pattern] || 0) + 1;
}
else {
invalid.push({ key, error: result.error });
}
});
return { valid, invalid, stats };
}
}
exports.PatternValidator = PatternValidator;
/**
* Export all patterns as a flat list for scanning
*/
function getAllPatterns(entityName) {
if (entityName) {
return RedisPatternUtils.getEntityPatterns(entityName);
}
// Return generic patterns
return [
'*:*', // All entity-like keys
'idx:*', // All indexes
'ft:*', // All full-text
'ft.*', // Alternative FT
'schema:*', // All schema keys
'RedisOM:*', // All RedisOM metadata
'_meta:*', // All custom metadata
'registry:*', // All registry keys
'_tmp:*', // All temporary
'_lock:*', // All locks
'batch:*', // All batch operations
'rollback:*', // All rollback data
'*#*:*' // All field indexes
];
}
//# sourceMappingURL=redis-patterns.js.map
;