diff --git a/redisinsight/api/src/modules/browser/controllers/redisearch/redisearch.controller.ts b/redisinsight/api/src/modules/browser/controllers/redisearch/redisearch.controller.ts index c9eb60f990..70a01a5fa7 100644 --- a/redisinsight/api/src/modules/browser/controllers/redisearch/redisearch.controller.ts +++ b/redisinsight/api/src/modules/browser/controllers/redisearch/redisearch.controller.ts @@ -3,17 +3,22 @@ import { Controller, Get, HttpCode, Param, - Post, Query, + Post, } from '@nestjs/common'; import { ApiBody, + ApiOkResponse, ApiOperation, ApiTags, } from '@nestjs/swagger'; import { ApiRedisParams } from 'src/decorators/api-redis-params.decorator'; import { ApiQueryRedisStringEncoding } from 'src/common/decorators'; import { BaseController } from 'src/modules/browser/controllers/base.controller'; -import { CreateRedisearchIndexDto, SearchRedisearchDto } from 'src/modules/browser/dto/redisearch'; +import { + CreateRedisearchIndexDto, + ListRedisearchIndexesResponse, + SearchRedisearchDto, +} from 'src/modules/browser/dto/redisearch'; import { RedisearchService } from 'src/modules/browser/services/redisearch/redisearch.service'; import { GetKeysWithDetailsResponse } from 'src/modules/browser/dto'; @@ -26,12 +31,13 @@ export class RedisearchController extends BaseController { @Get('') @ApiOperation({ description: 'Get list of available indexes' }) + @ApiOkResponse({ type: ListRedisearchIndexesResponse }) @ApiRedisParams() @ApiQueryRedisStringEncoding() async list( @Param('dbInstance') dbInstance: string, - ): Promise { - return await this.service.list( + ): Promise { + return this.service.list( { instanceId: dbInstance, }, @@ -55,13 +61,14 @@ export class RedisearchController extends BaseController { ); } - @Get('search') + @Post('search') @ApiOperation({ description: 'Search for keys in index' }) + @ApiOkResponse({ type: GetKeysWithDetailsResponse }) @ApiRedisParams() @ApiQueryRedisStringEncoding() async search( @Param('dbInstance') dbInstance: string, - @Query() dto: SearchRedisearchDto, + @Body() dto: SearchRedisearchDto, ): Promise { return await this.service.search( { diff --git a/redisinsight/api/src/modules/browser/dto/redisearch.ts b/redisinsight/api/src/modules/browser/dto/redisearch.ts index 979ccbbc02..b444682e63 100644 --- a/redisinsight/api/src/modules/browser/dto/redisearch.ts +++ b/redisinsight/api/src/modules/browser/dto/redisearch.ts @@ -1,8 +1,10 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { - ArrayMinSize, IsDefined, IsEnum, IsInt, IsNotEmpty, IsString, ValidateNested + ArrayMinSize, IsDefined, IsEnum, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; +import { RedisString } from 'src/common/constants'; +import { IsRedisString, RedisStringType } from 'src/common/decorators'; export enum RedisearchIndexKeyType { HASH = 'hash', @@ -17,14 +19,24 @@ export enum RedisearchIndexDataType { VECTOR = 'vector', } +export class ListRedisearchIndexesResponse { + @ApiProperty({ + description: 'Indexes names', + type: String, + }) + @RedisStringType({ each: true }) + indexes: RedisString[]; +} + export class CreateRedisearchIndexFieldDto { @ApiProperty({ description: 'Name of field to be indexed', type: String, }) @IsDefined() - @IsString() - name: string; + @RedisStringType() + @IsRedisString() + name: RedisString; @ApiProperty({ description: 'Type of how data must be indexed', @@ -41,8 +53,9 @@ export class CreateRedisearchIndexDto { type: String, }) @IsDefined() - @IsString() - index: string; + @RedisStringType() + @IsRedisString() + index: RedisString; @ApiProperty({ description: 'Type of keys to index', @@ -52,14 +65,15 @@ export class CreateRedisearchIndexDto { @IsEnum(RedisearchIndexKeyType) type: RedisearchIndexKeyType; - @ApiProperty({ + @ApiPropertyOptional({ description: 'Keys prefixes to find keys for index', isArray: true, type: String, }) - @IsString({ each: true }) - @IsNotEmpty({ each: true }) - prefixes: string[]; + @IsOptional() + @RedisStringType({ each: true }) + @IsRedisString({ each: true }) + prefixes?: RedisString[]; @ApiProperty({ description: 'Fields to index', @@ -78,8 +92,9 @@ export class SearchRedisearchDto { type: String, }) @IsDefined() - @IsString() - index: string; + @RedisStringType() + @IsRedisString() + index: RedisString; @ApiProperty({ description: 'Query to search inside data fields', @@ -95,7 +110,6 @@ export class SearchRedisearchDto { }) @IsDefined() @IsInt() - @Type(() => Number) limit: number = 500; // todo use @Default from another PR @ApiProperty({ @@ -104,6 +118,5 @@ export class SearchRedisearchDto { }) @IsDefined() @IsInt() - @Type(() => Number) offset: number = 0; // todo use @Default from another PR } diff --git a/redisinsight/api/src/modules/browser/services/redisearch/redisearch.service.ts b/redisinsight/api/src/modules/browser/services/redisearch/redisearch.service.ts index 32d9f62e2f..73a691b207 100644 --- a/redisinsight/api/src/modules/browser/services/redisearch/redisearch.service.ts +++ b/redisinsight/api/src/modules/browser/services/redisearch/redisearch.service.ts @@ -7,7 +7,11 @@ import { import ERROR_MESSAGES from 'src/constants/error-messages'; import { catchAclError } from 'src/utils'; import { IFindRedisClientInstanceByOptions } from 'src/modules/core/services/redis/redis.service'; -import { CreateRedisearchIndexDto, SearchRedisearchDto } from 'src/modules/browser/dto/redisearch'; +import { + CreateRedisearchIndexDto, + ListRedisearchIndexesResponse, + SearchRedisearchDto, +} from 'src/modules/browser/dto/redisearch'; import { GetKeysWithDetailsResponse } from 'src/modules/browser/dto'; import { plainToClass } from 'class-transformer'; import { BrowserToolService } from '../browser-tool/browser-tool.service'; @@ -24,7 +28,7 @@ export class RedisearchService { * Get list of all available redisearch indexes * @param clientOptions */ - public async list(clientOptions: IFindRedisClientInstanceByOptions): Promise { + public async list(clientOptions: IFindRedisClientInstanceByOptions): Promise { this.logger.log('Getting all redisearch indexes.'); try { @@ -33,10 +37,12 @@ export class RedisearchService { const nodes = this.getShards(client); const res = await Promise.all(nodes.map(async (node) => node.sendCommand( - new Command('FT._LIST', [], { replyEncoding: 'utf8' }), + new Command('FT._LIST'), ))); - return [].concat(...res); + return plainToClass(ListRedisearchIndexesResponse, { + indexes: [].concat(...res), + }); } catch (e) { this.logger.error('Failed to get redisearch indexes', e); @@ -81,11 +87,18 @@ export class RedisearchService { const nodes = this.getShards(client); - const commandArgs = [ + const commandArgs: any[] = [ index, 'ON', type, - 'PREFIX', prefixes.length, ...prefixes, - 'SCHEMA', ...[].concat(...fields.map((field) => ([field.name, field.type]))), ]; + + if (prefixes && prefixes.length) { + commandArgs.push('PREFIX', prefixes.length, ...prefixes); + } + + commandArgs.push( + 'SCHEMA', ...[].concat(...fields.map((field) => ([field.name, field.type]))), + ); + const command = new Command('FT.CREATE', commandArgs, { replyEncoding: 'utf8', });