1 #include "spec.h"
2 #include "field_spec.h"
3 #include "document.h"
4 #include "rmutil/rm_assert.h"
5 #include "util/dict.h"
6 #include "query_node.h"
7 #include "search_options.h"
8 #include "query_internal.h"
9 #include "numeric_filter.h"
10 #include "query.h"
11 #include "indexer.h"
12 #include "extension.h"
13 #include "ext/default.h"
14 #include <float.h>
15 #include "rwlock.h"
16 #include "fork_gc.h"
17 #include "module.h"
18
RediSearch_GetCApiVersion()19 int RediSearch_GetCApiVersion() {
20 return REDISEARCH_CAPI_VERSION;
21 }
22
RediSearch_CreateIndex(const char * name,const RSIndexOptions * options)23 IndexSpec* RediSearch_CreateIndex(const char* name, const RSIndexOptions* options) {
24 RSIndexOptions opts_s = {.gcPolicy = GC_POLICY_FORK, .stopwordsLen = -1};
25 if (!options) {
26 options = &opts_s;
27 }
28 IndexSpec* spec = NewIndexSpec(name);
29 IndexSpec_MakeKeyless(spec);
30 spec->flags |= Index_Temporary; // temporary is so that we will not use threads!!
31 if (!spec->indexer) {
32 spec->indexer = NewIndexer(spec);
33 }
34
35 if (options->score || options->lang) {
36 spec->rule = rm_calloc(1, sizeof *spec->rule);
37 spec->rule->score_default = options->score ? options->score : DEFAULT_SCORE;
38 spec->rule->lang_default = options->lang ? options->lang : DEFAULT_LANGUAGE;
39 }
40
41 spec->getValue = options->gvcb;
42 spec->getValueCtx = options->gvcbData;
43 if (options->flags & RSIDXOPT_DOCTBLSIZE_UNLIMITED) {
44 spec->docs.maxSize = DOCID_MAX;
45 }
46 if (options->gcPolicy != GC_POLICY_NONE) {
47 IndexSpec_StartGCFromSpec(spec, GC_DEFAULT_HZ, options->gcPolicy);
48 }
49 if (options->stopwordsLen != -1) {
50 // replace default list which is a global so no need to free anything.
51 spec->stopwords = NewStopWordListCStr((const char **)options->stopwords,
52 options->stopwordsLen);
53 }
54 return spec;
55 }
56
RediSearch_DropIndex(IndexSpec * sp)57 void RediSearch_DropIndex(IndexSpec* sp) {
58 RWLOCK_ACQUIRE_WRITE();
59 IndexSpec_FreeInternals(sp);
60 RWLOCK_RELEASE();
61 }
62
RediSearch_IndexGetStopwords(IndexSpec * sp,size_t * size)63 char **RediSearch_IndexGetStopwords(IndexSpec* sp, size_t *size) {
64 return GetStopWordsList(sp->stopwords, size);
65 }
66
RediSearch_IndexGetScore(IndexSpec * sp)67 double RediSearch_IndexGetScore(IndexSpec* sp) {
68 if (sp->rule) {
69 return sp->rule->score_default;
70 }
71 return DEFAULT_SCORE;
72 }
73
RediSearch_IndexGetLanguage(IndexSpec * sp)74 const char *RediSearch_IndexGetLanguage(IndexSpec* sp) {
75 if (sp->rule) {
76 return RSLanguage_ToString(sp->rule->lang_default);
77 }
78 return RSLanguage_ToString(DEFAULT_LANGUAGE);
79 }
80
RediSearch_CreateField(IndexSpec * sp,const char * name,unsigned types,unsigned options)81 RSFieldID RediSearch_CreateField(IndexSpec* sp, const char* name, unsigned types,
82 unsigned options) {
83 RS_LOG_ASSERT(types, "types should not be RSFLDTYPE_DEFAULT");
84 RWLOCK_ACQUIRE_WRITE();
85
86 FieldSpec* fs = IndexSpec_CreateField(sp, name);
87 int numTypes = 0;
88
89 if (types & RSFLDTYPE_FULLTEXT) {
90 numTypes++;
91 int txtId = IndexSpec_CreateTextId(sp);
92 if (txtId < 0) {
93 RWLOCK_RELEASE();
94 return RSFIELD_INVALID;
95 }
96 fs->ftId = txtId;
97 FieldSpec_Initialize(fs, INDEXFLD_T_FULLTEXT);
98 }
99
100 if (types & RSFLDTYPE_NUMERIC) {
101 numTypes++;
102 FieldSpec_Initialize(fs, INDEXFLD_T_NUMERIC);
103 }
104 if (types & RSFLDTYPE_GEO) {
105 FieldSpec_Initialize(fs, INDEXFLD_T_GEO);
106 numTypes++;
107 }
108 if (types & RSFLDTYPE_TAG) {
109 FieldSpec_Initialize(fs, INDEXFLD_T_TAG);
110 numTypes++;
111 }
112
113 if (numTypes > 1) {
114 fs->options |= FieldSpec_Dynamic;
115 }
116
117 if (options & RSFLDOPT_NOINDEX) {
118 fs->options |= FieldSpec_NotIndexable;
119 }
120 if (options & RSFLDOPT_SORTABLE) {
121 fs->options |= FieldSpec_Sortable;
122 fs->sortIdx = RSSortingTable_Add(&sp->sortables, fs->name, fieldTypeToValueType(fs->types));
123 }
124 if (options & RSFLDOPT_TXTNOSTEM) {
125 fs->options |= FieldSpec_NoStemming;
126 }
127 if (options & RSFLDOPT_TXTPHONETIC) {
128 fs->options |= FieldSpec_Phonetics;
129 sp->flags |= Index_HasPhonetic;
130 }
131
132 RWLOCK_RELEASE();
133 return fs->index;
134 }
135
RediSearch_TextFieldSetWeight(IndexSpec * sp,RSFieldID id,double w)136 void RediSearch_TextFieldSetWeight(IndexSpec* sp, RSFieldID id, double w) {
137 FieldSpec* fs = sp->fields + id;
138 RS_LOG_ASSERT(FIELD_IS(fs, INDEXFLD_T_FULLTEXT), "types should be INDEXFLD_T_FULLTEXT");
139 fs->ftWeight = w;
140 }
141
RediSearch_TagFieldSetSeparator(IndexSpec * sp,RSFieldID id,char sep)142 void RediSearch_TagFieldSetSeparator(IndexSpec* sp, RSFieldID id, char sep) {
143 FieldSpec* fs = sp->fields + id;
144 RS_LOG_ASSERT(FIELD_IS(fs, INDEXFLD_T_TAG), "types should be INDEXFLD_T_TAG");
145 fs->tagSep = sep;
146 }
147
RediSearch_TagFieldSetCaseSensitive(IndexSpec * sp,RSFieldID id,int enable)148 void RediSearch_TagFieldSetCaseSensitive(IndexSpec* sp, RSFieldID id, int enable) {
149 FieldSpec* fs = sp->fields + id;
150 RS_LOG_ASSERT(FIELD_IS(fs, INDEXFLD_T_TAG), "types should be INDEXFLD_T_TAG");
151 if (enable) {
152 fs->tagFlags |= TagField_CaseSensitive;
153 } else {
154 fs->tagFlags &= ~TagField_CaseSensitive;
155 }
156 }
157
RediSearch_CreateDocument(const void * docKey,size_t len,double score,const char * lang)158 RSDoc* RediSearch_CreateDocument(const void* docKey, size_t len, double score, const char* lang) {
159 RedisModuleString* docKeyStr = RedisModule_CreateString(NULL, docKey, len);
160 RSLanguage language = lang ? RSLanguage_Find(lang) : DEFAULT_LANGUAGE;
161 Document* ret = rm_calloc(1, sizeof(*ret));
162 Document_Init(ret, docKeyStr, score, language);
163 Document_MakeStringsOwner(ret);
164 RedisModule_FreeString(RSDummyContext, docKeyStr);
165 return ret;
166 }
167
RediSearch_CreateDocument2(const void * docKey,size_t len,IndexSpec * sp,double score,const char * lang)168 RSDoc* RediSearch_CreateDocument2(const void* docKey, size_t len, IndexSpec* sp,
169 double score, const char* lang) {
170 RedisModuleString* docKeyStr = RedisModule_CreateString(NULL, docKey, len);
171
172 RSLanguage language = lang ? RSLanguage_Find(lang) :
173 (sp && sp->rule) ? sp->rule->lang_default : DEFAULT_LANGUAGE;
174 double docScore = !isnan(score) ? score :
175 (sp && sp->rule) ? sp->rule->score_default : DEFAULT_SCORE;
176
177 Document* ret = rm_calloc(1, sizeof(*ret));
178 Document_Init(ret, docKeyStr, docScore, language);
179 Document_MakeStringsOwner(ret);
180 RedisModule_FreeString(RSDummyContext, docKeyStr);
181 return ret;
182 }
183
RediSearch_FreeDocument(RSDoc * doc)184 void RediSearch_FreeDocument(RSDoc* doc) {
185 Document_Free(doc);
186 rm_free(doc);
187 }
188
RediSearch_DeleteDocument(IndexSpec * sp,const void * docKey,size_t len)189 int RediSearch_DeleteDocument(IndexSpec* sp, const void* docKey, size_t len) {
190 RWLOCK_ACQUIRE_WRITE();
191 int rc = REDISMODULE_OK;
192 t_docId id = DocTable_GetId(&sp->docs, docKey, len);
193 if (id == 0) {
194 rc = REDISMODULE_ERR;
195 } else {
196 if (DocTable_Delete(&sp->docs, docKey, len)) {
197 // Delete returns true/false, not RM_{OK,ERR}
198 sp->stats.numDocuments--;
199 if (sp->gc) {
200 GCContext_OnDelete(sp->gc);
201 }
202 } else {
203 rc = REDISMODULE_ERR;
204 }
205 }
206
207 RWLOCK_RELEASE();
208 return rc;
209 }
210
RediSearch_DocumentAddField(Document * d,const char * fieldName,RedisModuleString * value,unsigned as)211 void RediSearch_DocumentAddField(Document* d, const char* fieldName, RedisModuleString* value,
212 unsigned as) {
213 Document_AddField(d, fieldName, value, as);
214 }
215
RediSearch_DocumentAddFieldString(Document * d,const char * fieldname,const char * s,size_t n,unsigned as)216 void RediSearch_DocumentAddFieldString(Document* d, const char* fieldname, const char* s, size_t n,
217 unsigned as) {
218 Document_AddFieldC(d, fieldname, s, n, as);
219 }
220
RediSearch_DocumentAddFieldNumber(Document * d,const char * fieldname,double n,unsigned as)221 void RediSearch_DocumentAddFieldNumber(Document* d, const char* fieldname, double n, unsigned as) {
222 char buf[512];
223 size_t len = sprintf(buf, "%lf", n);
224 Document_AddFieldC(d, fieldname, buf, len, as);
225 }
226
RediSearch_DocumentAddFieldGeo(Document * d,const char * fieldname,double lat,double lon,unsigned as)227 int RediSearch_DocumentAddFieldGeo(Document* d, const char* fieldname,
228 double lat, double lon, unsigned as) {
229 if (lat > GEO_LAT_MAX || lat < GEO_LAT_MIN || lon > GEO_LONG_MAX || lon < GEO_LONG_MIN) {
230 // out of range
231 return REDISMODULE_ERR;
232 }
233 // The format for a geospacial point is "lon,lat"
234 char buf[24];
235 size_t len = sprintf(buf, "%.6lf,%.6lf", lon, lat);
236 Document_AddFieldC(d, fieldname, buf, len, as);
237 return REDISMODULE_OK;
238 }
239
240 typedef struct {
241 char** s;
242 int hasErr;
243 } RSError;
244
RediSearch_AddDocDone(RSAddDocumentCtx * aCtx,RedisModuleCtx * ctx,void * err)245 void RediSearch_AddDocDone(RSAddDocumentCtx* aCtx, RedisModuleCtx* ctx, void* err) {
246 RSError* ourErr = err;
247 if (QueryError_HasError(&aCtx->status)) {
248 if (ourErr->s) {
249 *ourErr->s = rm_strdup(QueryError_GetError(&aCtx->status));
250 }
251 ourErr->hasErr = aCtx->status.code;
252 }
253 }
254
RediSearch_IndexAddDocument(IndexSpec * sp,Document * d,int options,char ** errs)255 int RediSearch_IndexAddDocument(IndexSpec* sp, Document* d, int options, char** errs) {
256 RWLOCK_ACQUIRE_WRITE();
257
258 RSError err = {.s = errs};
259 QueryError status = {0};
260 RSAddDocumentCtx* aCtx = NewAddDocumentCtx(sp, d, &status);
261 if (aCtx == NULL) {
262 if (status.detail) {
263 QueryError_ClearError(&status);
264 }
265 RWLOCK_RELEASE();
266 return REDISMODULE_ERR;
267 }
268 aCtx->donecb = RediSearch_AddDocDone;
269 aCtx->donecbData = &err;
270 RedisSearchCtx sctx = {.redisCtx = NULL, .spec = sp};
271 int exists = !!DocTable_GetIdR(&sp->docs, d->docKey);
272 if (exists) {
273 if (options & REDISEARCH_ADD_REPLACE) {
274 options |= DOCUMENT_ADD_REPLACE;
275 } else {
276 if (errs) {
277 *errs = rm_strdup("Document already exists");
278 }
279 AddDocumentCtx_Free(aCtx);
280 RWLOCK_RELEASE();
281 return REDISMODULE_ERR;
282 }
283 }
284
285 options |= DOCUMENT_ADD_NOSAVE;
286 aCtx->stateFlags |= ACTX_F_NOBLOCK;
287 AddDocumentCtx_Submit(aCtx, &sctx, options);
288 rm_free(d);
289
290 RWLOCK_RELEASE();
291 return err.hasErr ? REDISMODULE_ERR : REDISMODULE_OK;
292 }
293
RediSearch_CreateTokenNode(IndexSpec * sp,const char * fieldName,const char * token)294 QueryNode* RediSearch_CreateTokenNode(IndexSpec* sp, const char* fieldName, const char* token) {
295 if (StopWordList_Contains(sp->stopwords, token, strlen(token))) {
296 return NULL;
297 }
298 QueryNode* ret = NewQueryNode(QN_TOKEN);
299
300 ret->tn = (QueryTokenNode){
301 .str = (char*)rm_strdup(token), .len = strlen(token), .expanded = 0, .flags = 0};
302 if (fieldName) {
303 ret->opts.fieldMask = IndexSpec_GetFieldBit(sp, fieldName, strlen(fieldName));
304 }
305 return ret;
306 }
307
RediSearch_CreateNumericNode(IndexSpec * sp,const char * field,double max,double min,int includeMax,int includeMin)308 QueryNode* RediSearch_CreateNumericNode(IndexSpec* sp, const char* field, double max, double min,
309 int includeMax, int includeMin) {
310 QueryNode* ret = NewQueryNode(QN_NUMERIC);
311 ret->nn.nf = NewNumericFilter(min, max, includeMin, includeMax);
312 ret->nn.nf->fieldName = rm_strdup(field);
313 ret->opts.fieldMask = IndexSpec_GetFieldBit(sp, field, strlen(field));
314 return ret;
315 }
316
RediSearch_CreateGeoNode(IndexSpec * sp,const char * field,double lat,double lon,double radius,RSGeoDistance unitType)317 QueryNode* RediSearch_CreateGeoNode(IndexSpec* sp, const char* field, double lat, double lon,
318 double radius, RSGeoDistance unitType) {
319 QueryNode* ret = NewQueryNode(QN_GEO);
320 ret->opts.fieldMask = IndexSpec_GetFieldBit(sp, field, strlen(field));
321
322 GeoFilter *flt = rm_malloc(sizeof(*flt));
323 flt->lat = lat;
324 flt->lon = lon;
325 flt->radius = radius;
326 flt->numericFilters = NULL;
327 flt->property = rm_strdup(field);
328 flt->unitType = (GeoDistance)unitType;
329
330 ret->gn.gf = flt;
331
332 return ret;
333 }
334
RediSearch_CreatePrefixNode(IndexSpec * sp,const char * fieldName,const char * s)335 QueryNode* RediSearch_CreatePrefixNode(IndexSpec* sp, const char* fieldName, const char* s) {
336 QueryNode* ret = NewQueryNode(QN_PREFX);
337 ret->pfx =
338 (QueryPrefixNode){.str = (char*)rm_strdup(s), .len = strlen(s), .expanded = 0, .flags = 0};
339 if (fieldName) {
340 ret->opts.fieldMask = IndexSpec_GetFieldBit(sp, fieldName, strlen(fieldName));
341 }
342 return ret;
343 }
344
RediSearch_CreateLexRangeNode(IndexSpec * sp,const char * fieldName,const char * begin,const char * end,int includeBegin,int includeEnd)345 QueryNode* RediSearch_CreateLexRangeNode(IndexSpec* sp, const char* fieldName, const char* begin,
346 const char* end, int includeBegin, int includeEnd) {
347 QueryNode* ret = NewQueryNode(QN_LEXRANGE);
348 if (begin) {
349 ret->lxrng.begin = begin ? rm_strdup(begin) : NULL;
350 ret->lxrng.includeBegin = includeBegin;
351 }
352 if (end) {
353 ret->lxrng.end = end ? rm_strdup(end) : NULL;
354 ret->lxrng.includeEnd = includeEnd;
355 }
356 if (fieldName) {
357 ret->opts.fieldMask = IndexSpec_GetFieldBit(sp, fieldName, strlen(fieldName));
358 }
359 return ret;
360 }
361
RediSearch_CreateTagNode(IndexSpec * sp,const char * field)362 QueryNode* RediSearch_CreateTagNode(IndexSpec* sp, const char* field) {
363 QueryNode* ret = NewQueryNode(QN_TAG);
364 ret->tag.fieldName = rm_strdup(field);
365 ret->tag.len = strlen(field);
366 ret->opts.fieldMask = IndexSpec_GetFieldBit(sp, field, strlen(field));
367 return ret;
368 }
369
RediSearch_CreateIntersectNode(IndexSpec * sp,int exact)370 QueryNode* RediSearch_CreateIntersectNode(IndexSpec* sp, int exact) {
371 QueryNode* ret = NewQueryNode(QN_PHRASE);
372 ret->pn.exact = exact;
373 return ret;
374 }
375
RediSearch_CreateUnionNode(IndexSpec * sp)376 QueryNode* RediSearch_CreateUnionNode(IndexSpec* sp) {
377 return NewQueryNode(QN_UNION);
378 }
379
RediSearch_CreateEmptyNode(IndexSpec * sp)380 QueryNode* RediSearch_CreateEmptyNode(IndexSpec* sp) {
381 return NewQueryNode(QN_NULL);
382 }
383
RediSearch_CreateNotNode(IndexSpec * sp)384 QueryNode* RediSearch_CreateNotNode(IndexSpec* sp) {
385 return NewQueryNode(QN_NOT);
386 }
387
RediSearch_QueryNodeGetFieldMask(QueryNode * qn)388 int RediSearch_QueryNodeGetFieldMask(QueryNode* qn) {
389 return qn->opts.fieldMask;
390 }
391
RediSearch_QueryNodeAddChild(QueryNode * parent,QueryNode * child)392 void RediSearch_QueryNodeAddChild(QueryNode* parent, QueryNode* child) {
393 QueryNode_AddChild(parent, child);
394 }
395
RediSearch_QueryNodeClearChildren(QueryNode * qn)396 void RediSearch_QueryNodeClearChildren(QueryNode* qn) {
397 QueryNode_ClearChildren(qn, 1);
398 }
399
RediSearch_QueryNodeGetChild(const QueryNode * qn,size_t ix)400 QueryNode* RediSearch_QueryNodeGetChild(const QueryNode* qn, size_t ix) {
401 return QueryNode_GetChild(qn, ix);
402 }
403
RediSearch_QueryNodeNumChildren(const QueryNode * qn)404 size_t RediSearch_QueryNodeNumChildren(const QueryNode* qn) {
405 return QueryNode_NumChildren(qn);
406 }
407
408 typedef struct RS_ApiIter {
409 IndexIterator* internal;
410 RSIndexResult* res;
411 const RSDocumentMetadata* lastmd;
412 ScoringFunctionArgs scargs;
413 RSScoringFunction scorer;
414 RSFreeFunction scorerFree;
415 double minscore; // Used for scoring
416 QueryAST qast; // Used for string queries..
417 } RS_ApiIter;
418
419 #define QUERY_INPUT_STRING 1
420 #define QUERY_INPUT_NODE 2
421
422 typedef struct {
423 int qtype;
424 union {
425 struct {
426 const char* qs;
427 size_t n;
428 } s;
429 QueryNode* qn;
430 } u;
431 } QueryInput;
432
handleIterCommon(IndexSpec * sp,QueryInput * input,char ** error)433 static RS_ApiIter* handleIterCommon(IndexSpec* sp, QueryInput* input, char** error) {
434 // here we only take the read lock and we will free it when the iterator will be freed
435 RWLOCK_ACQUIRE_READ();
436
437 RedisSearchCtx sctx = SEARCH_CTX_STATIC(NULL, sp);
438 RSSearchOptions options = {0};
439 QueryError status = {0};
440 RSSearchOptions_Init(&options);
441 RS_ApiIter* it = rm_calloc(1, sizeof(*it));
442
443 if (input->qtype == QUERY_INPUT_STRING) {
444 if (QAST_Parse(&it->qast, &sctx, &options, input->u.s.qs, input->u.s.n, &status) !=
445 REDISMODULE_OK) {
446 goto end;
447 }
448 } else {
449 it->qast.root = input->u.qn;
450 }
451
452 if (QAST_Expand(&it->qast, NULL, &options, &sctx, &status) != REDISMODULE_OK) {
453 goto end;
454 }
455
456 it->internal = QAST_Iterate(&it->qast, &options, &sctx, NULL);
457 if (!it->internal) {
458 goto end;
459 }
460
461 IndexSpec_GetStats(sp, &it->scargs.indexStats);
462 ExtScoringFunctionCtx* scoreCtx = Extensions_GetScoringFunction(&it->scargs, DEFAULT_SCORER_NAME);
463 RS_LOG_ASSERT(scoreCtx, "GetScoringFunction failed");
464 it->scorer = scoreCtx->sf;
465 it->scorerFree = scoreCtx->ff;
466 it->minscore = DBL_MAX;
467
468 // dummy statement for goto
469 ;
470 end:
471
472 if (QueryError_HasError(&status) || it->internal == NULL) {
473 if (it) {
474 RediSearch_ResultsIteratorFree(it);
475 it = NULL;
476 }
477 if (error) {
478 *error = rm_strdup(QueryError_GetError(&status));
479 }
480 }
481 QueryError_ClearError(&status);
482 return it;
483 }
484
RediSearch_DocumentExists(IndexSpec * sp,const void * docKey,size_t len)485 int RediSearch_DocumentExists(IndexSpec* sp, const void* docKey, size_t len) {
486 return DocTable_GetId(&sp->docs, docKey, len) != 0;
487 }
488
RediSearch_IterateQuery(IndexSpec * sp,const char * s,size_t n,char ** error)489 RS_ApiIter* RediSearch_IterateQuery(IndexSpec* sp, const char* s, size_t n, char** error) {
490 QueryInput input = {.qtype = QUERY_INPUT_STRING, .u = {.s = {.qs = s, .n = n}}};
491 return handleIterCommon(sp, &input, error);
492 }
493
RediSearch_GetResultsIterator(QueryNode * qn,IndexSpec * sp)494 RS_ApiIter* RediSearch_GetResultsIterator(QueryNode* qn, IndexSpec* sp) {
495 QueryInput input = {.qtype = QUERY_INPUT_NODE, .u = {.qn = qn}};
496 return handleIterCommon(sp, &input, NULL);
497 }
498
RediSearch_QueryNodeFree(QueryNode * qn)499 void RediSearch_QueryNodeFree(QueryNode* qn) {
500 QueryNode_Free(qn);
501 }
502
RediSearch_QueryNodeType(QueryNode * qn)503 int RediSearch_QueryNodeType(QueryNode* qn) {
504 return qn->type;
505 }
506
RediSearch_ResultsIteratorNext(RS_ApiIter * iter,IndexSpec * sp,size_t * len)507 const void* RediSearch_ResultsIteratorNext(RS_ApiIter* iter, IndexSpec* sp, size_t* len) {
508 while (iter->internal->Read(iter->internal->ctx, &iter->res) != INDEXREAD_EOF) {
509 const RSDocumentMetadata* md = DocTable_Get(&sp->docs, iter->res->docId);
510 if (md == NULL || ((md)->flags & Document_Deleted)) {
511 continue;
512 }
513 iter->lastmd = md;
514 if (len) {
515 *len = sdslen(md->keyPtr);
516 }
517 return md->keyPtr;
518 }
519 return NULL;
520 }
521
RediSearch_ResultsIteratorGetScore(const RS_ApiIter * it)522 double RediSearch_ResultsIteratorGetScore(const RS_ApiIter* it) {
523 return it->scorer(&it->scargs, it->res, it->lastmd, 0);
524 }
525
RediSearch_ResultsIteratorFree(RS_ApiIter * iter)526 void RediSearch_ResultsIteratorFree(RS_ApiIter* iter) {
527 if (iter->internal) {
528 iter->internal->Free(iter->internal);
529 } else {
530 printf("Not freeing internal iterator. internal iterator is null\n");
531 }
532 if (iter->scorerFree) {
533 iter->scorerFree(iter->scargs.extdata);
534 }
535 QAST_Destroy(&iter->qast);
536 rm_free(iter);
537
538 RWLOCK_RELEASE();
539 }
540
RediSearch_ResultsIteratorReset(RS_ApiIter * iter)541 void RediSearch_ResultsIteratorReset(RS_ApiIter* iter) {
542 iter->internal->Rewind(iter->internal->ctx);
543 }
544
RediSearch_CreateIndexOptions()545 RSIndexOptions* RediSearch_CreateIndexOptions() {
546 RSIndexOptions* ret = rm_calloc(1, sizeof(RSIndexOptions));
547 ret->gcPolicy = GC_POLICY_NONE;
548 ret->stopwordsLen = -1;
549 return ret;
550 }
551
RediSearch_FreeIndexOptions(RSIndexOptions * options)552 void RediSearch_FreeIndexOptions(RSIndexOptions* options) {
553 if (options->stopwordsLen > 0) {
554 for (int i = 0; i < options->stopwordsLen; i++) {
555 rm_free(options->stopwords[i]);
556 }
557 rm_free(options->stopwords);
558 }
559 rm_free(options);
560 }
561
RediSearch_IndexOptionsSetGetValueCallback(RSIndexOptions * options,RSGetValueCallback cb,void * ctx)562 void RediSearch_IndexOptionsSetGetValueCallback(RSIndexOptions* options, RSGetValueCallback cb,
563 void* ctx) {
564 options->gvcb = cb;
565 options->gvcbData = ctx;
566 }
567
RediSearch_IndexOptionsSetStopwords(RSIndexOptions * opts,const char ** stopwords,int stopwordsLen)568 void RediSearch_IndexOptionsSetStopwords(RSIndexOptions* opts, const char **stopwords, int stopwordsLen) {
569 if (opts->stopwordsLen > 0) {
570 for (int i = 0; i < opts->stopwordsLen; i++) {
571 rm_free(opts->stopwords[i]);
572 }
573 rm_free(opts->stopwords);
574 }
575
576 opts->stopwords = NULL;
577
578 if (stopwordsLen > 0) {
579 opts->stopwords = rm_malloc(sizeof(*opts->stopwords) * stopwordsLen);
580 for (int i = 0; i < stopwordsLen; i++) {
581 opts->stopwords[i] = rm_strdup(stopwords[i]);
582 }
583 }
584 opts->stopwordsLen = stopwordsLen;
585 }
586
RediSearch_IndexOptionsSetFlags(RSIndexOptions * options,uint32_t flags)587 void RediSearch_IndexOptionsSetFlags(RSIndexOptions* options, uint32_t flags) {
588 options->flags = flags;
589 }
590
RediSearch_IndexOptionsSetGCPolicy(RSIndexOptions * options,int policy)591 void RediSearch_IndexOptionsSetGCPolicy(RSIndexOptions* options, int policy) {
592 options->gcPolicy = policy;
593 }
594
595 #define REGISTER_API(name) \
596 if (RedisModule_ExportSharedAPI(ctx, "RediSearch_" #name, RediSearch_##name) != \
597 REDISMODULE_OK) { \
598 RedisModule_Log(ctx, "warning", "could not register RediSearch_" #name "\r\n"); \
599 return REDISMODULE_ERR; \
600 }
601
RediSearch_ExportCapi(RedisModuleCtx * ctx)602 int RediSearch_ExportCapi(RedisModuleCtx* ctx) {
603 if (RedisModule_ExportSharedAPI == NULL) {
604 RedisModule_Log(ctx, "warning", "Upgrade redis-server to use Redis Search's C API");
605 return REDISMODULE_ERR;
606 }
607 RS_XAPIFUNC(REGISTER_API)
608 return REDISMODULE_OK;
609 }
610
RediSearch_SetCriteriaTesterThreshold(size_t num)611 void RediSearch_SetCriteriaTesterThreshold(size_t num) {
612 if (num == 0) {
613 RSGlobalConfig.maxResultsToUnsortedMode = DEFAULT_MAX_RESULTS_TO_UNSORTED_MODE;
614 } else {
615 RSGlobalConfig.maxResultsToUnsortedMode = num;
616 }
617 }
618
RediSearch_StopwordsList_Contains(RSIndex * idx,const char * term,size_t len)619 int RediSearch_StopwordsList_Contains(RSIndex* idx, const char *term, size_t len) {
620 return StopWordList_Contains(idx->stopwords, term, len);
621 }
622