From 16950d079030a58090d96efd4d3a1f11bf177b92 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 9 Oct 2024 19:45:57 +0300 Subject: [PATCH] update ft.info to actually work + with newest options --- packages/search/lib/commands/INFO.spec.ts | 91 +++++- packages/search/lib/commands/INFO.ts | 360 +++++++++++++--------- 2 files changed, 295 insertions(+), 156 deletions(-) diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index a2e9b4d5cd..9f526bfc1f 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -15,9 +15,92 @@ describe('INFO', () => { await client.ft.create('index', { field: SCHEMA_FIELD_TYPE.TEXT }); - const reply = await client.ft.info('index'); - assert.deepEqual(reply.indexName, 'index'); - assert.deepEqual(reply.indexOptions, []); - assert.deepEqual(reply.numDocs, '0'); + assert.deepEqual( + await client.ft.info('index'), + { + indexName: 'index', + indexOptions: [], + indexDefinition: Object.create(null, { + default_score: { + value: '1', + configurable: true, + enumerable: true + }, + key_type: { + value: 'HASH', + configurable: true, + enumerable: true + }, + prefixes: { + value: [''], + configurable: true, + enumerable: true + } + }), + attributes: [Object.create(null, { + identifier: { + value: 'field', + configurable: true, + enumerable: true + }, + attribute: { + value: 'field', + configurable: true, + enumerable: true + }, + type: { + value: 'TEXT', + configurable: true, + enumerable: true + }, + WEIGHT: { + value: '1', + configurable: true, + enumerable: true + } + })], + numDocs: 0, + maxDocId: 0, + numTerms: 0, + numRecords: 0, + invertedSzMb: 0, + vectorIndexSzMb: 0, + totalInvertedIndexBlocks: 0, + offsetVectorsSzMb: 0, + docTableSizeMb: 0, + sortableValuesSizeMb: 0, + keyTableSizeMb: 0, + recordsPerDocAvg: NaN, + bytesPerRecordAvg: NaN, + cleaning: 0, + offsetsPerTermAvg: NaN, + offsetBitsPerRecordAvg: NaN, + geoshapeSizeMb: 0, + hashIndexingFailures: 0, + indexing: 0, + percentIndexed: 1, + numberOfUses: 1, + tagOverheadSizeMb: 0, + textOverheadSizeMb: 0, + totalIndexMemorySizeMb: 0, + totalIndexingTime: 0, + gcStats: { + bytesCollected: 0, + totalMsRun: 0, + totalCycles: 0, + averageCycleTimeMs: NaN, + lastRunTimeMs: 0, + gcNumericTreesMissed: 0, + gcBlocksDenied: 0 + }, + cursorStats: { + globalIdle: 0, + globalTotal: 0, + indexCapacity: 128, + indexTotal: 0 + }, + stopWords: undefined + } + ); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index 3c4914e086..b984998ffe 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -1,6 +1,6 @@ import { RedisArgument } from "@redis/client"; -import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, ReplyUnion, TypeMapping } from "@redis/client/dist/lib/RESP/types"; -import { createTransformTuplesReplyFunc } from "@redis/client/dist/lib/commands/generic-transformers"; +import { ArrayReply, BlobStringReply, Command, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; +import { createTransformTuplesReplyFunc, transformDoubleReply } from "@redis/client/dist/lib/commands/generic-transformers"; export default { FIRST_KEY_INDEX: undefined, @@ -15,165 +15,221 @@ export default { unstableResp3: true } as const satisfies Command; -type InfoRawReply = [ - 'index_name', - BlobStringReply, - 'index_options', - ArrayReply, - 'index_definition', - ArrayReply, - 'attributes', - Array>, - 'num_docs', - BlobStringReply, - 'max_doc_id', - BlobStringReply, - 'num_terms', - BlobStringReply, - 'num_records', - BlobStringReply, - 'inverted_sz_mb', - BlobStringReply, - 'vector_index_sz_mb', - BlobStringReply, - 'total_inverted_index_blocks', - BlobStringReply, - 'offset_vectors_sz_mb', - BlobStringReply, - 'doc_table_size_mb', - BlobStringReply, - 'sortable_values_size_mb', - BlobStringReply, - 'key_table_size_mb', - BlobStringReply, - 'records_per_doc_avg', - BlobStringReply, - 'bytes_per_record_avg', - BlobStringReply, - 'offsets_per_term_avg', - BlobStringReply, - 'offset_bits_per_record_avg', - BlobStringReply, - 'hash_indexing_failures', - BlobStringReply, - 'indexing', - BlobStringReply, - 'percent_indexed', - BlobStringReply, - 'gc_stats', - [ - 'bytes_collected', - BlobStringReply, - 'total_ms_run', - BlobStringReply, - 'total_cycles', - BlobStringReply, - 'average_cycle_time_ms', - BlobStringReply, - 'last_run_time_ms', - BlobStringReply, - 'gc_numeric_trees_missed', - BlobStringReply, - 'gc_blocks_denied', - BlobStringReply - ], - 'cursor_stats', - [ - 'global_idle', - NumberReply, - 'global_total', - NumberReply, - 'index_capacity', - NumberReply, - 'index_total', - NumberReply, - ], - 'stopwords_list'?, - ArrayReply? -]; - export interface InfoReply { - indexName: BlobStringReply; - indexOptions: ArrayReply; - indexDefinition: MapReply; - attributes: Array>; - numDocs: BlobStringReply - maxDocId: BlobStringReply; - numTerms: BlobStringReply; - numRecords: BlobStringReply; - invertedSzMb: BlobStringReply; - vectorIndexSzMb: BlobStringReply; - totalInvertedIndexBlocks: BlobStringReply; - offsetVectorsSzMb: BlobStringReply; - docTableSizeMb: BlobStringReply; - sortableValuesSizeMb: BlobStringReply; - keyTableSizeMb: BlobStringReply; - recordsPerDocAvg: BlobStringReply; - bytesPerRecordAvg: BlobStringReply; - offsetsPerTermAvg: BlobStringReply; - offsetBitsPerRecordAvg: BlobStringReply; - hashIndexingFailures: BlobStringReply; - indexing: BlobStringReply; - percentIndexed: BlobStringReply; + indexName: SimpleStringReply; + indexOptions: ArrayReply; + indexDefinition: MapReply; + attributes: Array>; + numDocs: NumberReply + maxDocId: NumberReply; + numTerms: NumberReply; + numRecords: NumberReply; + invertedSzMb: DoubleReply; + vectorIndexSzMb: DoubleReply; + totalInvertedIndexBlocks: NumberReply; + offsetVectorsSzMb: DoubleReply; + docTableSizeMb: DoubleReply; + sortableValuesSizeMb: DoubleReply; + keyTableSizeMb: DoubleReply; + tagOverheadSizeMb: DoubleReply; + textOverheadSizeMb: DoubleReply; + totalIndexMemorySizeMb: DoubleReply; + geoshapeSizeMb: DoubleReply; + recordsPerDocAvg: DoubleReply; + bytesPerRecordAvg: DoubleReply; + offsetsPerTermAvg: DoubleReply; + offsetBitsPerRecordAvg: DoubleReply; + hashIndexingFailures: NumberReply; + totalIndexingTime: DoubleReply; + indexing: NumberReply; + percentIndexed: DoubleReply; + numberOfUses: NumberReply; + cleaning: NumberReply; gcStats: { - bytesCollected: BlobStringReply; - totalMsRun: BlobStringReply; - totalCycles: BlobStringReply; - averageCycleTimeMs: BlobStringReply; - lastRunTimeMs: BlobStringReply; - gcNumericTreesMissed: BlobStringReply; - gcBlocksDenied: BlobStringReply; + bytesCollected: DoubleReply; + totalMsRun: DoubleReply; + totalCycles: DoubleReply; + averageCycleTimeMs: DoubleReply; + lastRunTimeMs: DoubleReply; + gcNumericTreesMissed: DoubleReply; + gcBlocksDenied: DoubleReply; }; cursorStats: { globalIdle: NumberReply; globalTotal: NumberReply; indexCapacity: NumberReply; - idnexTotal: NumberReply; + indexTotal: NumberReply; }; - stopWords: ArrayReply | undefined; + stopWords: ArrayReply | undefined; } -function transformV2Reply(rawReply: InfoRawReply, preserve?: any, typeMapping?: TypeMapping): InfoReply { - const myTransformFunc = createTransformTuplesReplyFunc(preserve, typeMapping); +function transformV2Reply(reply: Array, preserve?: any, typeMapping?: TypeMapping): InfoReply { + const myTransformFunc = createTransformTuplesReplyFunc(preserve, typeMapping); - return { - indexName: rawReply[1], - indexOptions: rawReply[3], - indexDefinition: myTransformFunc(rawReply[5]), - attributes: rawReply[7].map(attribute => myTransformFunc(attribute)), - numDocs: rawReply[9], - maxDocId: rawReply[11], - numTerms: rawReply[13], - numRecords: rawReply[15], - invertedSzMb: rawReply[17], - vectorIndexSzMb: rawReply[19], - totalInvertedIndexBlocks: rawReply[21], - offsetVectorsSzMb: rawReply[23], - docTableSizeMb: rawReply[25], - sortableValuesSizeMb: rawReply[27], - keyTableSizeMb: rawReply[29], - recordsPerDocAvg: rawReply[31], - bytesPerRecordAvg: rawReply[33], - offsetsPerTermAvg: rawReply[35], - offsetBitsPerRecordAvg: rawReply[37], - hashIndexingFailures: rawReply[39], - indexing: rawReply[41], - percentIndexed: rawReply[43], - gcStats: { - bytesCollected: rawReply[45][1], - totalMsRun: rawReply[45][3], - totalCycles: rawReply[45][5], - averageCycleTimeMs: rawReply[45][7], - lastRunTimeMs: rawReply[45][9], - gcNumericTreesMissed: rawReply[45][11], - gcBlocksDenied: rawReply[45][13] - }, - cursorStats: { - globalIdle: rawReply[47][1], - globalTotal: rawReply[47][3], - indexCapacity: rawReply[47][5], - idnexTotal: rawReply[47][7] - }, - stopWords: rawReply[49] - }; + const ret = {} as unknown as InfoReply; + + ret.stopWords = undefined; + + for (let i=0; i < reply.length; i += 2) { + const key = reply[i].toString(); + + switch (key) { + case 'index_name': + ret.indexName = reply[i+1]; + break; + case 'index_options': + ret.indexOptions = reply[i+1]; + break; + case 'index_definition': + ret.indexDefinition = myTransformFunc(reply[i+1]); + break; + case 'attributes': + ret.attributes = (reply[i+1] as Array>).map(attribute => myTransformFunc(attribute)); + break; + case 'num_docs': + ret.numDocs = reply[i+1]; + break; + case 'max_doc_id': + ret.maxDocId = reply[i+1]; + break; + case 'num_terms': + ret.numTerms = reply[i+1]; + break; + case 'num_records': + ret.numRecords = reply[i+1]; + break; + case 'inverted_sz_mb': + ret.invertedSzMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'vector_index_sz_mb': + ret.vectorIndexSzMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'total_inverted_index_blocks': + ret.totalInvertedIndexBlocks = reply[i+1]; + break; + case 'offset_vectors_sz_mb': + ret.offsetVectorsSzMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'doc_table_size_mb': + ret.docTableSizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'sortable_values_size_mb': + ret.sortableValuesSizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'key_table_size_mb': + ret.keyTableSizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'tag_overhead_sz_mb': + ret.tagOverheadSizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'text_overhead_sz_mb': + ret.textOverheadSizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'total_index_memory_sz_mb': + ret.totalIndexMemorySizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'geoshapes_sz_mb': + ret.geoshapeSizeMb = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'records_per_doc_avg': + ret.recordsPerDocAvg = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'bytes_per_record_avg': + ret.bytesPerRecordAvg = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'offsets_per_term_avg': + ret.offsetsPerTermAvg = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'offset_bits_per_record_avg': + ret.offsetBitsPerRecordAvg = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'hash_indexing_failures': + ret.hashIndexingFailures = reply[i+1]; + break; + case 'total_indexing_time': + ret.totalIndexingTime = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'indexing': + ret.indexing = reply[i+1]; + break; + case 'percent_indexed': + ret.percentIndexed = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'number_of_uses': + ret.numberOfUses = reply[i+1]; + break; + case 'cleaning': + ret.cleaning = reply[i+1]; + break; + case 'gc_stats': { + const func = (array: Array) => { + const innerRet = {} as unknown as InfoReply['gcStats']; + + for (let i=0; i < array.length; i += 2) { + const innerKey = array[i].toString(); + + switch (innerKey) { + case 'bytes_collected': + innerRet.bytesCollected = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'total_ms_run': + innerRet.totalMsRun = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'total_cycles': + innerRet.totalCycles = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'average_cycle_time_ms': + innerRet.averageCycleTimeMs = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'last_run_time_ms': + innerRet.lastRunTimeMs = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'gc_numeric_trees_missed': + innerRet.gcNumericTreesMissed = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'gc_blocks_denied': + innerRet.gcBlocksDenied = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + } + } + + return innerRet; + } + ret.gcStats = func(reply[i+1]); + break; + } + case 'cursor_stats': { + const func = (array: Array) => { + const innerRet = {} as unknown as InfoReply['cursorStats']; + + for (let i=0; i < array.length; i += 2) { + const innerKey = array[i].toString(); + + switch (innerKey) { + case 'global_idle': + innerRet.globalIdle = array[i+1]; + break; + case 'global_total': + innerRet.globalTotal = array[i+1]; + break; + case 'index_capacity': + innerRet.indexCapacity = array[i+1]; + break; + case 'index_total': + innerRet.indexTotal = array[i+1]; + break; + } + } + + return innerRet; + } + ret.cursorStats = func(reply[i+1]); + break; + } + case 'stopwords_list': + ret.stopWords = reply[i+1]; + } + } + + return ret; }