@hotmeshio/hotmesh
Version:
Permanent-Memory Workflows & AI Agents
190 lines (189 loc) • 7.12 kB
JavaScript
;
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;