Skip to content

Commit

Permalink
Add NOCREATE option for replace-only mode (#658)
Browse files Browse the repository at this point in the history
  • Loading branch information
mnunberg committed May 14, 2019
1 parent 4c01490 commit 6763d6f
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 13 deletions.
6 changes: 5 additions & 1 deletion docs/Commands.md
Expand Up @@ -121,7 +121,7 @@ OK or an error
```
FT.ADD {index} {docId} {score}
[NOSAVE]
[REPLACE [PARTIAL]]
[REPLACE [PARTIAL] [NOCREATE]]
[LANGUAGE {language}]
[PAYLOAD {payload}]
[IF {condition}]
Expand Down Expand Up @@ -162,6 +162,10 @@ FT.ADD idx doc1 1.0 FIELDS title hello world
document. Also, if only non-indexable fields, score or payload are set - we do not do a full
re-indexing of the document, and this will be a lot faster.

- **NOCREATE** (only applicable with REPLACE): If set, the document is only updated
and reindexed if it already exists. If the document does not exist, an error
will be returned.

- **FIELDS**: Following the FIELDS specifier, we are looking for pairs of `{field} {value}` to be
indexed. Each field will be scored based on the index spec given in `FT.CREATE`.
Passing fields that are not in the index spec will make them be stored as part of the document,
Expand Down
5 changes: 4 additions & 1 deletion src/document.h
Expand Up @@ -146,6 +146,7 @@ void Document_Free(Document *doc);
#define DOCUMENT_ADD_PARTIAL 0x02
#define DOCUMENT_ADD_NOSAVE 0x04
#define DOCUMENT_ADD_CURTHREAD 0x08 // Perform operation in main thread
#define DOCUMENT_ADD_NOCREATE 0x10 // Don't create document if not exist (replace ONLY)

struct ForwardIndex;
struct FieldIndexerData;
Expand Down Expand Up @@ -288,10 +289,12 @@ int Document_EvalExpression(RedisSearchCtx *sctx, RedisModuleString *key, const
int Redis_LoadDocumentEx(RedisSearchCtx *ctx, RedisModuleString *key, const char **fields,
size_t nfields, Document *doc, RedisModuleKey **keyp);

// Don't create document if it does not exist. Replace only
#define REDIS_SAVEDOC_NOCREATE 0x01
/**
* Save a document in the index. Used for returning contents in search results.
*/
int Redis_SaveDocument(RedisSearchCtx *ctx, Document *doc, QueryError *status);
int Redis_SaveDocument(RedisSearchCtx *ctx, Document *doc, int options, QueryError *status);

/* Serialzie the document's fields to a redis client */
int Document_ReplyFields(RedisModuleCtx *ctx, Document *doc);
Expand Down
19 changes: 10 additions & 9 deletions src/document_add.c
Expand Up @@ -57,9 +57,10 @@ static int parseDocumentOptions(AddDocumentOptions *opts, ArgsCursor *ac, QueryE
opts->numFieldElems = 0;
opts->options = 0;

ACArgSpec argList[] = {{.name = "NOSAVE", .type = AC_ARGTYPE_BOOLFLAG, .target = &nosave},
{.name = "REPLACE", .type = AC_ARGTYPE_BOOLFLAG, .target = &replace},
{.name = "PARTIAL", .type = AC_ARGTYPE_BOOLFLAG, .target = &partial},
ACArgSpec argList[] = {{AC_MKBITFLAG("NOSAVE", &opts->options, DOCUMENT_ADD_NOSAVE)},
{AC_MKBITFLAG("REPLACE", &opts->options, DOCUMENT_ADD_REPLACE)},
{AC_MKBITFLAG("PARTIAL", &opts->options, DOCUMENT_ADD_PARTIAL)},
{AC_MKBITFLAG("NOCREATE", &opts->options, DOCUMENT_ADD_NOCREATE)},
{.name = "PAYLOAD", .type = AC_ARGTYPE_RSTRING, .target = &opts->payload},
{.name = "LANGUAGE", .type = AC_ARGTYPE_STRING, .target = &opts->language},
{.name = "IF", .type = AC_ARGTYPE_STRING, .target = &opts->evalExpr},
Expand Down Expand Up @@ -159,8 +160,12 @@ int RS_AddDocument(RedisSearchCtx *sctx, RedisModuleString *name, const AddDocum

Document_PrepareForAdd(&doc, name, opts->score, opts, sctx->redisCtx);
if (!(opts->options & DOCUMENT_ADD_NOSAVE)) {
int saveopts = 0;
if (opts->options & DOCUMENT_ADD_NOCREATE) {
saveopts |= REDIS_SAVEDOC_NOCREATE;
}
RedisSearchCtx sctx_s = SEARCH_CTX_STATIC(sctx->redisCtx, sp);
if (Redis_SaveDocument(&sctx_s, &doc, status) != REDISMODULE_OK) {
if (Redis_SaveDocument(&sctx_s, &doc, saveopts, status) != REDISMODULE_OK) {
Document_FreeDetached(&doc, ctx);
goto error;
}
Expand Down Expand Up @@ -226,11 +231,7 @@ static int doAddDocument(RedisModuleCtx *ctx, RedisModuleString **argv, int argc
}

if (QueryError_HasError(&status)) {
if (status.code == QUERY_EDOCNOTADDED) {
RedisModule_ReplyWithSimpleString(ctx, "NOADD");
} else {
RedisModule_ReplyWithError(ctx, QueryError_GetError(&status));
}
RedisModule_ReplyWithError(ctx, QueryError_GetError(&status));
goto cleanup;
}

Expand Down
8 changes: 6 additions & 2 deletions src/document_basic.c
Expand Up @@ -91,8 +91,7 @@ void Document_FreeDetached(Document *doc, RedisModuleCtx *anyCtx) {
Document_Free(doc);
}

int Redis_SaveDocument(RedisSearchCtx *ctx, Document *doc, QueryError *status) {

int Redis_SaveDocument(RedisSearchCtx *ctx, Document *doc, int options, QueryError *status) {
RedisModuleKey *k =
RedisModule_OpenKey(ctx->redisCtx, doc->docKey, REDISMODULE_WRITE | REDISMODULE_READ);
if (k == NULL || (RedisModule_KeyType(k) != REDISMODULE_KEYTYPE_EMPTY &&
Expand All @@ -103,6 +102,11 @@ int Redis_SaveDocument(RedisSearchCtx *ctx, Document *doc, QueryError *status) {
}
return REDISMODULE_ERR;
}
if ((options & REDIS_SAVEDOC_NOCREATE) && RedisModule_KeyType(k) == REDISMODULE_KEYTYPE_EMPTY) {
RedisModule_CloseKey(k);
QueryError_SetError(status, QUERY_ENODOC, "Document does not exist");
return REDISMODULE_ERR;
}

for (int i = 0; i < doc->numFields; i++) {
RedisModule_HashSet(k, REDISMODULE_HASH_CFIELDS, doc->fields[i].name, doc->fields[i].text,
Expand Down
7 changes: 7 additions & 0 deletions src/pytest/test.py
Expand Up @@ -2002,6 +2002,13 @@ def testAlias(env):
env.expect('ft.alter', 'myIndex', 'alias', 'add', 'alias3').raiseError()
env.expect('ft.alter', 'myIndex', 'alias', 'del', 'alias2').raiseError()

def testNoCreate(env):
env.cmd('ft.create', 'idx', 'schema', 'f1', 'text')
env.expect('ft.add', 'idx', 'doc1', 1, 'nocreate', 'fields', 'f1', 'hello').raiseError()
env.expect('ft.add', 'idx', 'doc1', 1, 'replace', 'nocreate', 'fields', 'f1', 'hello').raiseError()
env.expect('ft.add', 'idx', 'doc1', 1, 'replace', 'fields', 'f1', 'hello').notRaiseError()
env.expect('ft.add', 'idx', 'doc1', 1, 'replace', 'nocreate', 'fields', 'f1', 'world').notRaiseError()

# Standalone functionality
def testIssue484(env):
# Issue with split
Expand Down

0 comments on commit 6763d6f

Please sign in to comment.