UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

190 lines (189 loc) 7.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IORedisSearchService = void 0; const index_1 = require("../../index"); class IORedisSearchService extends index_1.SearchService { constructor(searchClient, storeClient) { super(searchClient, storeClient); } async init(namespace, appId, logger) { this.namespace = namespace; this.appId = appId; this.logger = logger; } async createSearchIndex(indexName, prefixes, schema) { try { await this.searchClient.call('FT.CREATE', indexName, 'ON', 'HASH', 'PREFIX', prefixes.length.toString(), ...prefixes, 'SCHEMA', ...schema); } catch (error) { this.logger.info('Error creating search index', { error }); throw error; } } async listSearchIndexes() { try { const indexes = await this.searchClient.call('FT._LIST'); return indexes; } catch (error) { this.logger.info('Error listing search indexes', { error }); throw error; } } async updateContext(key, fields) { // Find replay ID if present (field with hyphen, not the @udata field) const replayId = Object.keys(fields).find((k) => k.includes('-') && !k.startsWith('@')); // Route based on @udata operation if ('@udata:set' in fields) { const udata = JSON.parse(fields['@udata:set']); const fieldsToSet = Array.isArray(udata) ? Object.fromEntries(Array.from({ length: udata.length / 2 }, (_, i) => [ udata[i * 2], udata[i * 2 + 1], ])) : udata; const result = await this.setFields(key, fieldsToSet); if (replayId) await this.searchClient.hset(key, { [replayId]: String(result) }); return result; } if ('@udata:get' in fields) { const result = await this.getField(key, fields['@udata:get']); if (replayId) await this.searchClient.hset(key, { [replayId]: result }); return result; } if ('@udata:mget' in fields) { const result = await this.getFields(key, JSON.parse(fields['@udata:mget'])); if (replayId) await this.searchClient.hset(key, { [replayId]: result.join('|||') }); return result; } if ('@udata:delete' in fields) { const result = await this.deleteFields(key, JSON.parse(fields['@udata:delete'])); if (replayId) await this.searchClient.hset(key, { [replayId]: String(result) }); return result; } if ('@udata:increment' in fields) { const { field, value } = JSON.parse(fields['@udata:increment']); const result = await this.incrementFieldByFloat(key, field, value); if (replayId) await this.searchClient.hset(key, { [replayId]: String(result) }); return result; } if ('@udata:multiply' in fields) { const { field, value } = JSON.parse(fields['@udata:multiply']); const result = await this.incrementFieldByFloat(key, field, Math.log(value)); if (replayId) await this.searchClient.hset(key, { [replayId]: String(result) }); return result; } if ('@udata:all' in fields) { const all = await this.getAllFields(key); const result = Object.fromEntries(Object.entries(all).filter(([k]) => k.startsWith('_'))); if (replayId) await this.searchClient.hset(key, { [replayId]: JSON.stringify(result) }); return result; } // Default: call setFields return await this.setFields(key, fields); } async setFields(key, fields) { try { const result = await this.searchClient.hset(key, fields); return Number(result); } catch (error) { this.logger.error(`Error setting fields for key: ${key}`, { error }); throw error; } } async getField(key, field) { try { return await this.searchClient.hget(key, field); } catch (error) { this.logger.error(`Error getting field ${field} for key: ${key}`, { error, }); throw error; } } async getFields(key, fields) { try { return await this.searchClient.hmget(key, [...fields]); } catch (error) { this.logger.error(`Error getting fields for key: ${key}`, { error }); throw error; } } async getAllFields(key) { try { return await this.searchClient.hgetall(key); } catch (error) { this.logger.error(`Error getting fields for key: ${key}`, { error }); throw error; } } async deleteFields(key, fields) { try { const result = await this.searchClient.hdel(key, ...fields); return Number(result); } catch (error) { this.logger.error(`Error deleting fields for key: ${key}`, { error }); throw error; } } async incrementFieldByFloat(key, field, increment) { try { const result = await this.searchClient.hincrbyfloat(key, field, increment); return Number(result); } catch (error) { this.logger.error(`Error incrementing field ${field} for key: ${key}`, { error, }); throw error; } } async sendQuery(...query) { try { return await this.searchClient.call(...query); } catch (error) { this.logger.error('Error executing query', { error }); throw error; } } async sendIndexedQuery(index, query) { try { if (query[0]?.startsWith('FT.')) { const [cmd, ...rest] = query; return (await this.searchClient.call(cmd, ...rest)); } return (await this.searchClient.call('FT.SEARCH', index, ...query)); } catch (error) { this.logger.error('Error executing query', { error }); throw error; } } // Entity methods - not implemented for Redis (postgres-specific JSONB operations) async findEntities() { throw new Error('Entity findEntities not supported in Redis - use PostgreSQL'); } async findEntityById() { throw new Error('Entity findEntityById not supported in Redis - use PostgreSQL'); } async findEntitiesByCondition() { throw new Error('Entity findEntitiesByCondition not supported in Redis - use PostgreSQL'); } async createEntityIndex() { throw new Error('Entity createEntityIndex not supported in Redis - use PostgreSQL'); } } exports.IORedisSearchService = IORedisSearchService;