/* This is an example of strings DMA access. Given a key containing a string * it toggles the case of each character from lower to upper case or the * other way around. * * No automatic memory management is used in this example (for the sake * of variety). * * HELLO.TOGGLE.CASE key */ int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); int keytype = RedisModule_KeyType(key); if (keytype != REDISMODULE_KEYTYPE_STRING && keytype != REDISMODULE_KEYTYPE_EMPTY) { RedisModule_CloseKey(key); return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } if (keytype == REDISMODULE_KEYTYPE_STRING) { size_t len, j; char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE); for (j = 0; j < len; j++) { if (isupper(s[j])) { s[j] = tolower(s[j]); } else { s[j] = toupper(s[j]); } } } RedisModule_CloseKey(key); RedisModule_ReplyWithSimpleString(ctx,"OK"); RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
// Create and populate index for specified property // (This function will create separate string and numeric indices if property has mixed types) void Index_Create(RedisModuleCtx *ctx, const char *graphName, AST_IndexNode *indexOp) { const char *label = indexOp->target.label; const char *prop_str = indexOp->target.property; LabelStore *store = LabelStore_Get(ctx, STORE_NODE, graphName, label); LabelStoreIterator it; LabelStore_Scan(store, &it); char *nodeId; uint16_t nodeIdLen; Node *node; EntityProperty *prop; RedisModuleKey *key = Index_LookupKey(ctx, graphName, label, prop_str); // Do nothing if this index already exists if (RedisModule_ModuleTypeGetType(key) == IndexRedisModuleType) { RedisModule_CloseKey(key); return; } Index *index = malloc(sizeof(Index)); RedisModule_ModuleTypeSetValue(key, IndexRedisModuleType, index); RedisModule_CloseKey(key); index->target.label = strdup(label); index->target.property = strdup(prop_str); index->string_sl = skiplistCreate(compareStrings, NULL, compareNodes, cloneKey, freeKey); index->numeric_sl = skiplistCreate(compareNumerics, NULL, compareNodes, cloneKey, freeKey); int prop_index = 0; while(LabelStoreIterator_Next(&it, &nodeId, &nodeIdLen, (void**)&node)) { // If the sought property is at a different offset than it occupied in the previous node, // then seek and update if (strcmp(prop_str, node->properties[prop_index].name)) { for (int i = 0; i < node->prop_count; i ++) { prop = node->properties + i; if (!strcmp(prop_str, prop->name)) { prop_index = i; break; } } } prop = node->properties + prop_index; // This value will be cloned within the skiplistInsert routine if necessary SIValue *key = &prop->value; if (key->type == T_STRING) { skiplistInsert(index->string_sl, key, node); } else if (key->type & SI_NUMERIC) { skiplistInsert(index->numeric_sl, key, node); } else { // This property was neither a string nor numeric value; raise a run-time error. assert(0); } } }
/** * Return the score for the given suggestion (number between 0 to 1). * In case the suggestion should not be added return -1. */ static double SpellCheck_GetScore(SpellCheckCtx *scCtx, char *suggestion, size_t len, t_fieldMask fieldMask) { RedisModuleKey *keyp = NULL; InvertedIndex *invidx = Redis_OpenInvertedIndexEx(scCtx->sctx, suggestion, len, 0, &keyp); double retVal = 0; if (!invidx) { // can not find inverted index key, score is 0. goto end; } IndexReader *reader = NewTermIndexReader(invidx, NULL, fieldMask, NULL, 1); IndexIterator *iter = NewReadIterator(reader); RSIndexResult *r; if (iter->Read(iter->ctx, &r) != INDEXREAD_EOF) { // we have at least one result, the suggestion is relevant. if (scCtx->fullScoreInfo) { retVal = invidx->numDocs; } else { retVal = invidx->numDocs; } } else { // fieldMask has filtered all docs, this suggestions should not be returned retVal = -1; } ReadIterator_Free(iter); end: if (keyp) { RedisModule_CloseKey(keyp); } return retVal; }
/* HELLO.LEXRANGE key min_lex max_lex min_age max_age * This command expects a sorted set stored at key in the following form: * - All the elements have score 0. * - Elements are pairs of "<name>:<age>", for example "Anna:52". * The command will return all the sorted set items that are lexicographically * between the specified range (using the same format as ZRANGEBYLEX) * and having an age between min_age and max_age. */ int HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 6) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) { return RedisModule_ReplyWithError(ctx,"invalid range"); } int arraylen = 0; RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); while(!RedisModule_ZsetRangeEndReached(key)) { double score; RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); RedisModule_ReplyWithString(ctx,ele); RedisModule_FreeString(ctx,ele); RedisModule_ZsetRangeNext(key); arraylen++; } RedisModule_ZsetRangeStop(key); RedisModule_ReplySetArrayLength(ctx,arraylen); RedisModule_CloseKey(key); return REDISMODULE_OK; }
Index* Index_Get(RedisModuleCtx *ctx, const char *graph, const char *label, const char *property) { RedisModuleKey *key = Index_LookupKey(ctx, graph, label, property); Index *idx = NULL; if (RedisModule_ModuleTypeGetType(key) == IndexRedisModuleType) { idx = RedisModule_ModuleTypeGetValue(key); } RedisModule_CloseKey(key); return idx; }
static bool SpellCheck_CheckDictExistence(SpellCheckCtx *scCtx, const char *dict) { #define BUFF_SIZE 1000 RedisModuleKey *k = NULL; Trie *t = SpellCheck_OpenDict(scCtx->sctx->redisCtx, dict, REDISMODULE_READ, &k); if (t == NULL) { char buff[BUFF_SIZE]; snprintf(buff, BUFF_SIZE, "Dict does not exist: %s", dict); RedisModule_ReplyWithError(scCtx->sctx->redisCtx, buff); return false; } RedisModule_CloseKey(k); return true; }
/* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API * where you can "open" keys, make low level operations, create new keys by * pushing elements into non-existing keys, and so forth. * * You'll find this command to be roughly as fast as the actual RPUSH * command. */ int HelloPushNative_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 3) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); RedisModule_ListPush(key,REDISMODULE_LIST_TAIL,argv[2]); size_t newlen = RedisModule_ValueLength(key); RedisModule_CloseKey(key); RedisModule_ReplyWithLongLong(ctx,newlen); return REDISMODULE_OK; }
/* HELLO.ZSUMRANGE key startscore endscore * Return the sum of all the scores elements between startscore and endscore. * * The computation is performed two times, one time from start to end and * another time backward. The two scores, returned as a two element array, * should match.*/ int HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { double score_start, score_end; if (argc != 4) return RedisModule_WrongArity(ctx); if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK || RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK) { return RedisModule_ReplyWithError(ctx,"ERR invalid range"); } RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } double scoresum_a = 0; double scoresum_b = 0; RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0); while(!RedisModule_ZsetRangeEndReached(key)) { double score; RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); RedisModule_FreeString(ctx,ele); scoresum_a += score; RedisModule_ZsetRangeNext(key); } RedisModule_ZsetRangeStop(key); RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0); while(!RedisModule_ZsetRangeEndReached(key)) { double score; RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); RedisModule_FreeString(ctx,ele); scoresum_b += score; RedisModule_ZsetRangePrev(key); } RedisModule_ZsetRangeStop(key); RedisModule_CloseKey(key); RedisModule_ReplyWithArray(ctx,2); RedisModule_ReplyWithDouble(ctx,scoresum_a); RedisModule_ReplyWithDouble(ctx,scoresum_b); return REDISMODULE_OK; }
/* HELLO.LIST.SPLICE srclist dstlist count * Moves 'count' elements from the tail of 'srclist' to the head of * 'dstlist'. If less than count elements are available, it moves as much * elements as possible. */ int HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 4) return RedisModule_WrongArity(ctx); RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2], REDISMODULE_READ|REDISMODULE_WRITE); /* Src and dst key must be empty or lists. */ if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST && RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) || (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST && RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY)) { RedisModule_CloseKey(srckey); RedisModule_CloseKey(dstkey); return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } long long count; if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) || (count < 0)) { RedisModule_CloseKey(srckey); RedisModule_CloseKey(dstkey); return RedisModule_ReplyWithError(ctx,"ERR invalid count"); } while(count-- > 0) { RedisModuleString *ele; ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL); if (ele == NULL) break; RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele); RedisModule_FreeString(ctx,ele); } size_t len = RedisModule_ValueLength(srckey); RedisModule_CloseKey(srckey); RedisModule_CloseKey(dstkey); RedisModule_ReplyWithLongLong(ctx,len); return REDISMODULE_OK; }
static bool SpellCheck_ReplyTermSuggestions(SpellCheckCtx *scCtx, char *term, size_t len, t_fieldMask fieldMask) { // searching the term on the term trie, if its there we just return false // because there is no need to return suggestions on it. if (SpellCheck_IsTermExistsInTrie(scCtx->sctx->spec->terms, term, len, NULL)) { if (scCtx->fullScoreInfo) { // if a full score info is requested we need to send information that // we found the term as is on the index RedisModule_ReplyWithArray(scCtx->sctx->redisCtx, 3); RedisModule_ReplyWithStringBuffer(scCtx->sctx->redisCtx, TERM, strlen(TERM)); RedisModule_ReplyWithStringBuffer(scCtx->sctx->redisCtx, term, len); RedisModule_ReplyWithStringBuffer(scCtx->sctx->redisCtx, FOUND_TERM_IN_INDEX, strlen(FOUND_TERM_IN_INDEX)); return true; } else { return false; } } // searching the term on the exclude list, if its there we just return false // because there is no need to return suggestions on it. for (int i = 0; i < array_len(scCtx->excludeDict); ++i) { RedisModuleKey *k = NULL; Trie *t = SpellCheck_OpenDict(scCtx->sctx->redisCtx, scCtx->excludeDict[i], REDISMODULE_READ, &k); if (t == NULL) { continue; } if (SpellCheck_IsTermExistsInTrie(t, term, len, NULL)) { RedisModule_CloseKey(k); return false; } RedisModule_CloseKey(k); } RS_Suggestions *s = RS_SuggestionsCreate(); SpellCheck_FindSuggestions(scCtx, scCtx->sctx->spec->terms, term, len, fieldMask, s, 1); // sorting results by score // searching the term on the include list for more suggestions. for (int i = 0; i < array_len(scCtx->includeDict); ++i) { RedisModuleKey *k = NULL; Trie *t = SpellCheck_OpenDict(scCtx->sctx->redisCtx, scCtx->includeDict[i], REDISMODULE_READ, &k); if (t == NULL) { continue; } SpellCheck_FindSuggestions(scCtx, t, term, len, fieldMask, s, 0); RedisModule_CloseKey(k); } SpellCheck_SendReplyOnTerm(scCtx->sctx->redisCtx, term, len, s, (!scCtx->fullScoreInfo) ? scCtx->sctx->spec->docs.size - 1 : 0); RS_SuggestionsFree(s); return true; }