UNPKG

@yihuangdb/storage-object

Version:

A Node.js storage object layer library using Redis OM

541 lines 18 kB
"use strict"; /** * 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