1 #include "debug_commads.h"
2 #include "inverted_index.h"
3 #include "index.h"
4 #include "redis_index.h"
5 #include "tag_index.h"
6 #include "numeric_index.h"
7 #include "phonetic_manager.h"
8 #include "gc.h"
9 #include "module.h"
10
11 #define DUMP_PHONETIC_HASH "DUMP_PHONETIC_HASH"
12
13 #define DEBUG_COMMAND(name) static int name(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
14
15 #define GET_SEARCH_CTX(name) \
16 RedisSearchCtx *sctx = NewSearchCtx(ctx, name, true); \
17 if (!sctx) { \
18 RedisModule_ReplyWithError(ctx, "Can not create a search ctx"); \
19 return REDISMODULE_OK; \
20 }
21
22 #define REPLY_WITH_LONG_LONG(name, val, len) \
23 RedisModule_ReplyWithStringBuffer(ctx, name, strlen(name)); \
24 RedisModule_ReplyWithLongLong(ctx, val); \
25 len += 2;
26
27 #define REPLY_WITH_Str(name, val) \
28 RedisModule_ReplyWithStringBuffer(ctx, name, strlen(name)); \
29 RedisModule_ReplyWithStringBuffer(ctx, val, strlen(val)); \
30 bulkLen += 2;
31
ReplyReaderResults(IndexReader * reader,RedisModuleCtx * ctx)32 static void ReplyReaderResults(IndexReader *reader, RedisModuleCtx *ctx) {
33 IndexIterator *iter = NewReadIterator(reader);
34 RSIndexResult *r;
35 size_t resultSize = 0;
36 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
37 while (iter->Read(iter->ctx, &r) != INDEXREAD_EOF) {
38 RedisModule_ReplyWithLongLong(ctx, r->docId);
39 ++resultSize;
40 }
41 RedisModule_ReplySetArrayLength(ctx, resultSize);
42 ReadIterator_Free(iter);
43 }
44
getFieldKeyName(IndexSpec * spec,RedisModuleString * fieldNameRS,FieldType t)45 static RedisModuleString *getFieldKeyName(IndexSpec *spec, RedisModuleString *fieldNameRS,
46 FieldType t) {
47 const char *fieldName = RedisModule_StringPtrLen(fieldNameRS, NULL);
48 const FieldSpec *fieldSpec = IndexSpec_GetField(spec, fieldName, strlen(fieldName));
49 if (!fieldSpec) {
50 return NULL;
51 }
52 return IndexSpec_GetFormattedKey(spec, fieldSpec, t);
53 }
54
DEBUG_COMMAND(DumpTerms)55 DEBUG_COMMAND(DumpTerms) {
56 if (argc != 1) {
57 return RedisModule_WrongArity(ctx);
58 }
59 GET_SEARCH_CTX(argv[0])
60
61 rune *rstr = NULL;
62 t_len slen = 0;
63 float score = 0;
64 int dist = 0;
65 size_t termLen;
66
67 RedisModule_ReplyWithArray(ctx, sctx->spec->terms->size);
68
69 TrieIterator *it = Trie_Iterate(sctx->spec->terms, "", 0, 0, 1);
70 while (TrieIterator_Next(it, &rstr, &slen, NULL, &score, &dist)) {
71 char *res = runesToStr(rstr, slen, &termLen);
72 RedisModule_ReplyWithStringBuffer(ctx, res, termLen);
73 rm_free(res);
74 }
75 DFAFilter_Free(it->ctx);
76 rm_free(it->ctx);
77 TrieIterator_Free(it);
78
79 SearchCtx_Free(sctx);
80 return REDISMODULE_OK;
81 }
82
DEBUG_COMMAND(InvertedIndexSummary)83 DEBUG_COMMAND(InvertedIndexSummary) {
84 if (argc != 2) {
85 return RedisModule_WrongArity(ctx);
86 }
87 GET_SEARCH_CTX(argv[0])
88 RedisModuleKey *keyp = NULL;
89 size_t len;
90 const char *invIdxName = RedisModule_StringPtrLen(argv[1], &len);
91 InvertedIndex *invidx = Redis_OpenInvertedIndexEx(sctx, invIdxName, len, 0, &keyp);
92 if (!invidx) {
93 RedisModule_ReplyWithError(sctx->redisCtx, "Can not find the inverted index");
94 goto end;
95 }
96 size_t invIdxBulkLen = 0;
97 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
98
99 REPLY_WITH_LONG_LONG("numDocs", invidx->numDocs, invIdxBulkLen);
100 REPLY_WITH_LONG_LONG("lastId", invidx->lastId, invIdxBulkLen);
101 REPLY_WITH_LONG_LONG("flags", invidx->flags, invIdxBulkLen);
102 REPLY_WITH_LONG_LONG("numberOfBlocks", invidx->size, invIdxBulkLen);
103
104 RedisModule_ReplyWithStringBuffer(ctx, "blocks", strlen("blocks"));
105
106 for (uint32_t i = 0; i < invidx->size; ++i) {
107 size_t blockBulkLen = 0;
108 IndexBlock *block = invidx->blocks + i;
109 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
110
111 REPLY_WITH_LONG_LONG("firstId", block->firstId, blockBulkLen);
112 REPLY_WITH_LONG_LONG("lastId", block->lastId, blockBulkLen);
113 REPLY_WITH_LONG_LONG("numDocs", block->numDocs, blockBulkLen);
114
115 RedisModule_ReplySetArrayLength(ctx, blockBulkLen);
116 }
117
118 invIdxBulkLen += 2;
119
120 RedisModule_ReplySetArrayLength(ctx, invIdxBulkLen);
121
122 end:
123 if (keyp) {
124 RedisModule_CloseKey(keyp);
125 }
126 SearchCtx_Free(sctx);
127 return REDISMODULE_OK;
128 }
129
DEBUG_COMMAND(DumpInvertedIndex)130 DEBUG_COMMAND(DumpInvertedIndex) {
131 if (argc != 2) {
132 return RedisModule_WrongArity(ctx);
133 }
134 GET_SEARCH_CTX(argv[0])
135 RedisModuleKey *keyp = NULL;
136 size_t len;
137 const char *invIdxName = RedisModule_StringPtrLen(argv[1], &len);
138 InvertedIndex *invidx = Redis_OpenInvertedIndexEx(sctx, invIdxName, len, 0, &keyp);
139 if (!invidx) {
140 RedisModule_ReplyWithError(sctx->redisCtx, "Can not find the inverted index");
141 goto end;
142 }
143 IndexReader *reader = NewTermIndexReader(invidx, NULL, RS_FIELDMASK_ALL, NULL, 1);
144 ReplyReaderResults(reader, sctx->redisCtx);
145 end:
146 if (keyp) {
147 RedisModule_CloseKey(keyp);
148 }
149 SearchCtx_Free(sctx);
150 return REDISMODULE_OK;
151 }
152
DEBUG_COMMAND(NumericIndexSummary)153 DEBUG_COMMAND(NumericIndexSummary) {
154 if (argc != 2) {
155 return RedisModule_WrongArity(ctx);
156 }
157 GET_SEARCH_CTX(argv[0])
158 RedisModuleKey *keyp = NULL;
159 RedisModuleString *keyName = getFieldKeyName(sctx->spec, argv[1], INDEXFLD_T_NUMERIC);
160 if (!keyName) {
161 RedisModule_ReplyWithError(sctx->redisCtx, "Could not find given field in index spec");
162 goto end;
163 }
164 NumericRangeTree *rt = OpenNumericIndex(sctx, keyName, &keyp);
165 if (!rt) {
166 RedisModule_ReplyWithError(sctx->redisCtx, "can not open numeric field");
167 goto end;
168 }
169
170 size_t invIdxBulkLen = 0;
171 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
172
173 REPLY_WITH_LONG_LONG("numRanges", rt->numRanges, invIdxBulkLen);
174 REPLY_WITH_LONG_LONG("numEntries", rt->numEntries, invIdxBulkLen);
175 REPLY_WITH_LONG_LONG("lastDocId", rt->lastDocId, invIdxBulkLen);
176 REPLY_WITH_LONG_LONG("revisionId", rt->revisionId, invIdxBulkLen);
177
178 RedisModule_ReplySetArrayLength(ctx, invIdxBulkLen);
179
180 end:
181 if (keyp) {
182 RedisModule_CloseKey(keyp);
183 }
184 SearchCtx_Free(sctx);
185 return REDISMODULE_OK;
186 }
187
DEBUG_COMMAND(DumpNumericIndex)188 DEBUG_COMMAND(DumpNumericIndex) {
189 if (argc != 2) {
190 return RedisModule_WrongArity(ctx);
191 }
192 GET_SEARCH_CTX(argv[0])
193 RedisModuleKey *keyp = NULL;
194 RedisModuleString *keyName = getFieldKeyName(sctx->spec, argv[1], INDEXFLD_T_NUMERIC);
195 if (!keyName) {
196 RedisModule_ReplyWithError(sctx->redisCtx, "Could not find given field in index spec");
197 goto end;
198 }
199 NumericRangeTree *rt = OpenNumericIndex(sctx, keyName, &keyp);
200 if (!rt) {
201 RedisModule_ReplyWithError(sctx->redisCtx, "can not open numeric field");
202 goto end;
203 }
204 NumericRangeNode *currNode;
205 NumericRangeTreeIterator *iter = NumericRangeTreeIterator_New(rt);
206 size_t resultSize = 0;
207 RedisModule_ReplyWithArray(sctx->redisCtx, REDISMODULE_POSTPONED_ARRAY_LEN);
208 while ((currNode = NumericRangeTreeIterator_Next(iter))) {
209 NumericRange *range = currNode->range;
210 if (range) {
211 IndexReader *reader = NewNumericReader(NULL, range->entries, NULL, range->minVal, range->maxVal);
212 ReplyReaderResults(reader, sctx->redisCtx);
213 ++resultSize;
214 }
215 }
216 RedisModule_ReplySetArrayLength(sctx->redisCtx, resultSize);
217 NumericRangeTreeIterator_Free(iter);
218 end:
219 if (keyp) {
220 RedisModule_CloseKey(keyp);
221 }
222 SearchCtx_Free(sctx);
223 return REDISMODULE_OK;
224 }
225
DEBUG_COMMAND(DumpTagIndex)226 DEBUG_COMMAND(DumpTagIndex) {
227 if (argc != 2) {
228 return RedisModule_WrongArity(ctx);
229 }
230 GET_SEARCH_CTX(argv[0])
231 RedisModuleKey *keyp = NULL;
232 RedisModuleString *keyName = getFieldKeyName(sctx->spec, argv[1], INDEXFLD_T_TAG);
233 if (!keyName) {
234 RedisModule_ReplyWithError(sctx->redisCtx, "Could not find given field in index spec");
235 goto end;
236 }
237 TagIndex *tagIndex = TagIndex_Open(sctx, keyName, false, &keyp);
238 if (!tagIndex) {
239 RedisModule_ReplyWithError(sctx->redisCtx, "can not open tag field");
240 goto end;
241 }
242
243 TrieMapIterator *iter = TrieMap_Iterate(tagIndex->values, "", 0);
244
245 char *tag;
246 tm_len_t len;
247 InvertedIndex *iv;
248
249 size_t resultSize = 0;
250 RedisModule_ReplyWithArray(sctx->redisCtx, REDISMODULE_POSTPONED_ARRAY_LEN);
251 while (TrieMapIterator_Next(iter, &tag, &len, (void **)&iv)) {
252 RedisModule_ReplyWithArray(sctx->redisCtx, 2);
253 RedisModule_ReplyWithStringBuffer(sctx->redisCtx, tag, len);
254 IndexReader *reader = NewTermIndexReader(iv, NULL, RS_FIELDMASK_ALL, NULL, 1);
255 ReplyReaderResults(reader, sctx->redisCtx);
256 ++resultSize;
257 }
258 RedisModule_ReplySetArrayLength(sctx->redisCtx, resultSize);
259 TrieMapIterator_Free(iter);
260
261 end:
262 if (keyp) {
263 RedisModule_CloseKey(keyp);
264 }
265 SearchCtx_Free(sctx);
266 return REDISMODULE_OK;
267 }
268
DEBUG_COMMAND(IdToDocId)269 DEBUG_COMMAND(IdToDocId) {
270 if (argc != 2) {
271 return RedisModule_WrongArity(ctx);
272 }
273 GET_SEARCH_CTX(argv[0])
274 long long id;
275 if (RedisModule_StringToLongLong(argv[1], &id) != REDISMODULE_OK) {
276 RedisModule_ReplyWithError(sctx->redisCtx, "bad id given");
277 goto end;
278 }
279 RSDocumentMetadata *doc = DocTable_Get(&sctx->spec->docs, id);
280 if (!doc || (doc->flags & Document_Deleted)) {
281 RedisModule_ReplyWithError(sctx->redisCtx, "document was removed");
282 } else {
283 RedisModule_ReplyWithStringBuffer(sctx->redisCtx, doc->keyPtr, strlen(doc->keyPtr));
284 }
285 end:
286 SearchCtx_Free(sctx);
287 return REDISMODULE_OK;
288 }
289
DEBUG_COMMAND(DocIdToId)290 DEBUG_COMMAND(DocIdToId) {
291 if (argc != 2) {
292 return RedisModule_WrongArity(ctx);
293 }
294 GET_SEARCH_CTX(argv[0])
295 size_t n;
296 const char *key = RedisModule_StringPtrLen(argv[1], &n);
297 t_docId id = DocTable_GetId(&sctx->spec->docs, key, n);
298 RedisModule_ReplyWithLongLong(sctx->redisCtx, id);
299 SearchCtx_Free(sctx);
300 return REDISMODULE_OK;
301 }
302
DEBUG_COMMAND(DumpPhoneticHash)303 DEBUG_COMMAND(DumpPhoneticHash) {
304 if (argc != 1) {
305 return RedisModule_WrongArity(ctx);
306 }
307 size_t len;
308 const char *term_c = RedisModule_StringPtrLen(argv[0], &len);
309
310 char *primary = NULL;
311 char *secondary = NULL;
312
313 PhoneticManager_ExpandPhonetics(NULL, term_c, len, &primary, &secondary);
314
315 RedisModule_ReplyWithArray(ctx, 2);
316 RedisModule_ReplyWithStringBuffer(ctx, primary, strlen(primary));
317 RedisModule_ReplyWithStringBuffer(ctx, secondary, strlen(secondary));
318
319 rm_free(primary);
320 rm_free(secondary);
321 return REDISMODULE_OK;
322 }
323
GCForceInvokeReply(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)324 static int GCForceInvokeReply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
325 #define REPLY "DONE"
326 RedisModule_ReplyWithStringBuffer(ctx, REPLY, strlen(REPLY));
327 return REDISMODULE_OK;
328 }
329
GCForceInvokeReplyTimeout(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)330 static int GCForceInvokeReplyTimeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
331 #define ERROR_REPLY "INVOCATION FAILED"
332 RedisModule_ReplyWithError(ctx, ERROR_REPLY);
333 return REDISMODULE_OK;
334 }
335
DEBUG_COMMAND(GCForceInvoke)336 DEBUG_COMMAND(GCForceInvoke) {
337 #define INVOKATION_TIMEOUT 30000 // gc invocation timeout ms
338 if (argc < 1) {
339 return RedisModule_WrongArity(ctx);
340 }
341 IndexSpec *sp = IndexSpec_Load(ctx, RedisModule_StringPtrLen(argv[0], NULL), 0);
342 if (!sp) {
343 RedisModule_ReplyWithError(ctx, "Unknown index name");
344 return REDISMODULE_OK;
345 }
346 RedisModuleBlockedClient *bc = RedisModule_BlockClient(
347 ctx, GCForceInvokeReply, GCForceInvokeReplyTimeout, NULL, INVOKATION_TIMEOUT);
348 GCContext_ForceInvoke(sp->gc, bc);
349 return REDISMODULE_OK;
350 }
351
DEBUG_COMMAND(GCForceBGInvoke)352 DEBUG_COMMAND(GCForceBGInvoke) {
353 if (argc < 1) {
354 return RedisModule_WrongArity(ctx);
355 }
356 IndexSpec *sp = IndexSpec_Load(ctx, RedisModule_StringPtrLen(argv[0], NULL), 0);
357 if (!sp) {
358 RedisModule_ReplyWithError(ctx, "Unknown index name");
359 return REDISMODULE_OK;
360 }
361 GCContext_ForceBGInvoke(sp->gc);
362 RedisModule_ReplyWithSimpleString(ctx, "OK");
363 return REDISMODULE_OK;
364 }
365
DEBUG_COMMAND(ttl)366 DEBUG_COMMAND(ttl) {
367 if (argc < 1) {
368 return RedisModule_WrongArity(ctx);
369 }
370 IndexLoadOptions lopts = {.flags = INDEXSPEC_LOAD_NOTIMERUPDATE,
371 .name = {.cstring = RedisModule_StringPtrLen(argv[0], NULL)}};
372 lopts.flags |= INDEXSPEC_LOAD_KEYLESS;
373 IndexSpec *sp = IndexSpec_LoadEx(ctx, &lopts);
374
375 if (!sp) {
376 RedisModule_ReplyWithError(ctx, "Unknown index name");
377 return REDISMODULE_OK;
378 }
379
380 if (!(sp->flags & Index_Temporary)) {
381 RedisModule_ReplyWithError(ctx, "Index is not temporary");
382 return REDISMODULE_OK;
383 }
384
385 uint64_t remaining = 0;
386 if (RedisModule_GetTimerInfo(RSDummyContext, sp->timerId, &remaining, NULL) != REDISMODULE_OK) {
387 RedisModule_ReplyWithLongLong(ctx, 0); // timer was called but free operation is async so its
388 // gone be free each moment. lets return 0 timeout.
389 return REDISMODULE_OK;
390 }
391 RedisModule_ReplyWithLongLong(ctx, remaining / 1000); // return the results in seconds
392 return REDISMODULE_OK;
393 }
394
DEBUG_COMMAND(GitSha)395 DEBUG_COMMAND(GitSha) {
396 #ifdef RS_GIT_SHA
397 RedisModule_ReplyWithStringBuffer(ctx, RS_GIT_SHA, strlen(RS_GIT_SHA));
398 #else
399 RedisModule_ReplyWithError(ctx, "GIT SHA was not defined on compilation");
400 #endif
401 return REDISMODULE_OK;
402 }
403
404 typedef struct {
405 // Whether to enumerate the number of docids per entry
406 int countValueEntries;
407
408 // Whether to enumerate the *actual* document IDs in the entry
409 int dumpIdEntries;
410
411 // offset and limit for the tag entry
412 unsigned offset, limit;
413
414 // only inspect this value
415 const char *prefix;
416 } DumpOptions;
417
seekTagIterator(TrieMapIterator * it,size_t offset)418 static void seekTagIterator(TrieMapIterator *it, size_t offset) {
419 char *tag;
420 tm_len_t len;
421 InvertedIndex *iv;
422
423 for (size_t n = 0; n < offset; n++) {
424 if (!TrieMapIterator_Next(it, &tag, &len, (void **)&iv)) {
425 break;
426 }
427 }
428 }
429
430 /**
431 * INFO_TAGIDX <index> <field> [OPTIONS...]
432 */
DEBUG_COMMAND(InfoTagIndex)433 DEBUG_COMMAND(InfoTagIndex) {
434 if (argc < 2) {
435 return RedisModule_WrongArity(ctx);
436 }
437 GET_SEARCH_CTX(argv[0]);
438 DumpOptions options = {0};
439 ACArgSpec argspecs[] = {
440 {.name = "count_value_entries",
441 .type = AC_ARGTYPE_BOOLFLAG,
442 .target = &options.countValueEntries},
443 {.name = "dump_id_entries", .type = AC_ARGTYPE_BOOLFLAG, .target = &options.dumpIdEntries},
444 {.name = "prefix", .type = AC_ARGTYPE_STRING, .target = &options.prefix},
445 {.name = "offset", .type = AC_ARGTYPE_UINT, .target = &options.offset},
446 {.name = "limit", .type = AC_ARGTYPE_UINT, .target = &options.limit},
447 {NULL}};
448 RedisModuleKey *keyp = NULL;
449 ArgsCursor ac = {0};
450 ACArgSpec *errSpec = NULL;
451 ArgsCursor_InitRString(&ac, argv + 2, argc - 2);
452 int rv = AC_ParseArgSpec(&ac, argspecs, &errSpec);
453 if (rv != AC_OK) {
454 RedisModule_ReplyWithError(ctx, "Could not parse argument (argspec fixme)");
455 goto end;
456 }
457
458 RedisModuleString *keyName = getFieldKeyName(sctx->spec, argv[1], INDEXFLD_T_TAG);
459 if (!keyName) {
460 RedisModule_ReplyWithError(sctx->redisCtx, "Could not find given field in index spec");
461 goto end;
462 }
463
464 const TagIndex *idx = TagIndex_Open(sctx, keyName, false, &keyp);
465 if (!idx) {
466 RedisModule_ReplyWithError(sctx->redisCtx, "can not open tag field");
467 goto end;
468 }
469
470 size_t nelem = 0;
471 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
472 RedisModule_ReplyWithSimpleString(ctx, "num_values");
473 RedisModule_ReplyWithLongLong(ctx, idx->values->cardinality);
474 nelem += 2;
475
476 if (options.dumpIdEntries) {
477 options.countValueEntries = 1;
478 }
479 int shouldDescend = options.countValueEntries || options.dumpIdEntries;
480 if (!shouldDescend) {
481 goto reply_done;
482 }
483
484 size_t limit = options.limit ? options.limit : 0;
485 TrieMapIterator *iter = TrieMap_Iterate(idx->values, "", 0);
486 char *tag;
487 tm_len_t len;
488 InvertedIndex *iv;
489
490 nelem += 2;
491 RedisModule_ReplyWithSimpleString(ctx, "values");
492 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
493
494 seekTagIterator(iter, options.offset);
495 size_t nvalues = 0;
496 while (nvalues++ < limit && TrieMapIterator_Next(iter, &tag, &len, (void **)&iv)) {
497 size_t nsubelem = 8;
498 if (!options.dumpIdEntries) {
499 nsubelem -= 2;
500 }
501 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
502
503 RedisModule_ReplyWithSimpleString(ctx, "value");
504 RedisModule_ReplyWithStringBuffer(ctx, tag, len);
505
506 RedisModule_ReplyWithSimpleString(ctx, "num_entries");
507 RedisModule_ReplyWithLongLong(ctx, iv->numDocs);
508
509 RedisModule_ReplyWithSimpleString(ctx, "num_blocks");
510 RedisModule_ReplyWithLongLong(ctx, iv->size);
511
512 if (options.dumpIdEntries) {
513 RedisModule_ReplyWithSimpleString(ctx, "entries");
514 IndexReader *reader = NewTermIndexReader(iv, NULL, RS_FIELDMASK_ALL, NULL, 1);
515 ReplyReaderResults(reader, sctx->redisCtx);
516 }
517
518 RedisModule_ReplySetArrayLength(ctx, nsubelem);
519 }
520 TrieMapIterator_Free(iter);
521 RedisModule_ReplySetArrayLength(ctx, nvalues - 1);
522
523 reply_done:
524 RedisModule_ReplySetArrayLength(ctx, nelem);
525
526 end:
527 if (keyp) {
528 RedisModule_CloseKey(keyp);
529 }
530 SearchCtx_Free(sctx);
531 return REDISMODULE_OK;
532 }
533
replyDocFlags(const RSDocumentMetadata * dmd,RedisModuleCtx * ctx)534 static void replyDocFlags(const RSDocumentMetadata *dmd, RedisModuleCtx *ctx) {
535 char buf[1024] = {0};
536 sprintf(buf, "(0x%x):", dmd->flags);
537 if (dmd->flags & Document_Deleted) {
538 strcat(buf, "Deleted,");
539 }
540 if (dmd->flags & Document_HasPayload) {
541 strcat(buf, "HasPayload,");
542 }
543 if (dmd->flags & Document_HasSortVector) {
544 strcat(buf, "HasSortVector,");
545 }
546 if (dmd->flags & Document_HasOffsetVector) {
547 strcat(buf, "HasOffsetVector,");
548 }
549 RedisModule_ReplyWithSimpleString(ctx, buf);
550 }
551
replySortVector(const RSDocumentMetadata * dmd,RedisSearchCtx * sctx)552 static void replySortVector(const RSDocumentMetadata *dmd, RedisSearchCtx *sctx) {
553 RSSortingVector *sv = dmd->sortVector;
554 RedisModule_ReplyWithArray(sctx->redisCtx, REDISMODULE_POSTPONED_ARRAY_LEN);
555 size_t nelem = 0;
556 for (size_t ii = 0; ii < sv->len; ++ii) {
557 if (!sv->values[ii]) {
558 continue;
559 }
560 RedisModule_ReplyWithArray(sctx->redisCtx, 6);
561 RedisModule_ReplyWithSimpleString(sctx->redisCtx, "index");
562 RedisModule_ReplyWithLongLong(sctx->redisCtx, ii);
563 RedisModule_ReplyWithSimpleString(sctx->redisCtx, "field");
564 const FieldSpec *fs = IndexSpec_GetFieldBySortingIndex(sctx->spec, ii);
565 RedisModule_ReplyWithPrintf(sctx->redisCtx, "%s AS %s", fs ? fs->path : "!!!", fs ? fs->name : "???");
566 RedisModule_ReplyWithSimpleString(sctx->redisCtx, "value");
567 RSValue_SendReply(sctx->redisCtx, sv->values[ii], 0);
568 nelem++;
569 }
570 RedisModule_ReplySetArrayLength(sctx->redisCtx, nelem);
571 }
572
573 /**
574 * FT.DEBUG DOC_INFO <index> <doc>
575 */
DEBUG_COMMAND(DocInfo)576 DEBUG_COMMAND(DocInfo) {
577 if (argc < 2) {
578 return RedisModule_WrongArity(ctx);
579 }
580 GET_SEARCH_CTX(argv[0]);
581
582 const RSDocumentMetadata *dmd = DocTable_GetByKeyR(&sctx->spec->docs, argv[1]);
583 if (!dmd) {
584 SearchCtx_Free(sctx);
585 return RedisModule_ReplyWithError(ctx, "Document not found in index");
586 }
587
588 size_t nelem = 0;
589 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
590 RedisModule_ReplyWithSimpleString(ctx, "internal_id");
591 RedisModule_ReplyWithLongLong(ctx, dmd->id);
592 nelem += 2;
593 RedisModule_ReplyWithSimpleString(ctx, "flags");
594 replyDocFlags(dmd, ctx);
595 nelem += 2;
596 RedisModule_ReplyWithSimpleString(ctx, "score");
597 RedisModule_ReplyWithDouble(ctx, dmd->score);
598 nelem += 2;
599 RedisModule_ReplyWithSimpleString(ctx, "num_tokens");
600 RedisModule_ReplyWithLongLong(ctx, dmd->len);
601 nelem += 2;
602 RedisModule_ReplyWithSimpleString(ctx, "max_freq");
603 RedisModule_ReplyWithLongLong(ctx, dmd->maxFreq);
604 nelem += 2;
605 RedisModule_ReplyWithSimpleString(ctx, "refcount");
606 RedisModule_ReplyWithLongLong(ctx, dmd->ref_count);
607 nelem += 2;
608 if (dmd->sortVector) {
609 RedisModule_ReplyWithSimpleString(ctx, "sortables");
610 replySortVector(dmd, sctx);
611 nelem += 2;
612 }
613 RedisModule_ReplySetArrayLength(ctx, nelem);
614 SearchCtx_Free(sctx);
615 return REDISMODULE_OK;
616 }
617
618 typedef struct DebugCommandType {
619 char *name;
620 int (*callback)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
621 } DebugCommandType;
622
623 DebugCommandType commands[] = {{"DUMP_INVIDX", DumpInvertedIndex},
624 {"DUMP_NUMIDX", DumpNumericIndex},
625 {"DUMP_TAGIDX", DumpTagIndex},
626 {"INFO_TAGIDX", InfoTagIndex},
627 {"IDTODOCID", IdToDocId},
628 {"DOCIDTOID", DocIdToId},
629 {"DOCINFO", DocInfo},
630 {"DUMP_PHONETIC_HASH", DumpPhoneticHash},
631 {"DUMP_TERMS", DumpTerms},
632 {"INVIDX_SUMMARY", InvertedIndexSummary},
633 {"NUMIDX_SUMMARY", NumericIndexSummary},
634 {"GC_FORCEINVOKE", GCForceInvoke},
635 {"GC_FORCEBGINVOKE", GCForceBGInvoke},
636 {"GIT_SHA", GitSha},
637 {"TTL", ttl},
638 {NULL, NULL}};
639
DebugCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)640 int DebugCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
641
642 if (argc < 2) {
643 return RedisModule_WrongArity(ctx);
644 }
645
646 const char *subCommand = RedisModule_StringPtrLen(argv[1], NULL);
647
648 if (strcasecmp(subCommand, "help") == 0) {
649 size_t len = 0;
650 RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
651 for (DebugCommandType *c = &commands[0]; c->name != NULL; c++) {
652 RedisModule_ReplyWithStringBuffer(ctx, c->name, strlen(c->name));
653 ++len;
654 }
655 RedisModule_ReplySetArrayLength(ctx, len);
656 return REDISMODULE_OK;
657 }
658
659 for (DebugCommandType *c = &commands[0]; c->name != NULL; c++) {
660 if (strcasecmp(c->name, subCommand) == 0) {
661 return c->callback(ctx, argv + 2, argc - 2);
662 }
663 }
664
665 RedisModule_ReplyWithError(ctx, "subcommand was not found");
666
667 return REDISMODULE_OK;
668 }
669