Repository: redis/redis

Problem statement:
[CHANGE] STRALGO LCS doesn't make sense
Given that `STRALGO LCS` can access keys or not, it makes no sense in terms of... well, everything, but we can focus the discussion on ACL if needed.

Suggestion: break compatibility and make it into `STRALGO LCSKEYS` and `STRALGO LCSSTRINGS`.

@redis/core-team your thoughts.


Hints:
I'm all for it. Hoping no one really uses this command anyway. 
I'd go even further and say that even though we now have sub commands that have the same capabilities of commands, maybe we can drop the STRALGO, and add the two and individual commands. 
Yes, let's drop STRALGO

LCSKEYS and LCSSTRINGS
Another alternative: STRALGO KEYS LCS and STRALGO STRINGS LCS would also
work, w/o removing the command itself.

On Sat, Nov 6, 2021, 15:44 guybe7 ***@***.***> wrote:

> Yes, let's drop STRALGO
>
> LCSKEYS and LCSSTRINGS
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/redis/redis/issues/9744#issuecomment-962454020>, or
> unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ABOHL7GCTCR4MXX37EKFHGDUKUWMZANCNFSM5HOGIOTQ>
> .
> Triage notifications on the go with GitHub Mobile for iOS
> <https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
> or Android
> <https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
>
>

Disclaimer

The information contained in this communication from the sender is confidential. It is intended solely for use by the recipient and others authorized to receive it. If you are not the recipient, you are hereby notified that any disclosure, copying, distribution or taking action in relation of the contents of this information is strictly prohibited and may be unlawful.

This email has been scanned for viruses and malware, and may have been automatically archived by Mimecast Ltd, an innovator in Software as a Service (SaaS) for business. Providing a safer and more useful place for your human generated data. Specializing in; Security, archiving and compliance. To find out more visit the Mimecast website.

I also agree, 7.0 is an opportunity to clean this up.
giving this some extra thought, LCSSTRINGS is not really Redis-y (can't think of any example of data manipulation that isn't executed on keys)

since we are already breaking BC, maybe just have
```
LCS key1 key2 [LEN] [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN]
```

or even more flexible
```
LCS numkeys key1 [key2 ...] [LEN] [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN]
```

I don't see value in making this change, it seems like change for the same of change.
LOL.
"change for the sake of change"? sounds like the exact same reason this command was introduced in the first place 8-)

p.s. just realized that considering #9733, COMMAND GETKEYS will also error on `stralgo lcs strings a b`.
so if we keep it, we need to add `no-mandatory-keys`.

the syntax of this command is all wrong IMHO:
* assume all (future) string algorithms will take similar arguments
* mix command take takes keys and one that doesn't in the same command.
* make it nearly impossible to expose the right key spec in COMMAND INFO (issues cluster clients)

considering that we estimate that no one really uses it, and the fact we're allowed to introduce breaking changes in a major release, i think we may wanna take this opportunity and do our first ever real breakage (apart from Gopher).

actually, i just realized what these two have in common.
they're both April fools day project.
> "change for the sake of change"? sounds like the exact same reason this command was introduced in the first place 8-)

Lol, so true.

I don't think the command really fits in with the rest of Redis, so I would vote we send it off the same way we did with Gopher if it's really such a problem.

Patch:
diff --git a/src/server.c b/src/server.c
index d587b76c574..a10fdd43cb5 100644
--- a/src/server.c
+++ b/src/server.c
@@ -523,20 +523,6 @@ struct redisCommand clientSubcommands[] = {
     {NULL},
 };
 
-struct redisCommand stralgoSubcommands[] = {
-    {"lcs",stralgoCommand,-5,
-     "read-only @string",
-      {{"read incomplete", /* We can't use "keyword" here because we may give false information. */
-        KSPEC_BS_UNKNOWN,{{0}},
-        KSPEC_FK_UNKNOWN,{{0}}}},
-     lcsGetKeys},
-
-    {"help",stralgoCommand,2,
-     "ok-loading ok-stale @string"},
-
-    {NULL},
-};
-
 struct redisCommand pubsubSubcommands[] = {
     {"channels",pubsubCommand,-2,
      "pub-sub ok-loading ok-stale"},
@@ -2038,9 +2024,12 @@ struct redisCommand redisCommandTable[] = {
      "sentinel",
      .subcommands=aclSubcommands},
 
-    {"stralgo",NULL,-2,
-     "",
-     .subcommands=stralgoSubcommands},
+    {"lcs",lcsCommand,-3,
+     "read-only @string",
+      {{"read",
+        KSPEC_BS_INDEX,.bs.index={1},
+        KSPEC_FK_RANGE,.fk.range={1,1,0}}},
+     lcsGetKeys},
 
     {"reset",resetCommand,1,
      "no-script ok-stale ok-loading fast @connection"},
diff --git a/src/server.h b/src/server.h
index 30aedcd84b6..da8c5b7c207 100644
--- a/src/server.h
+++ b/src/server.h
@@ -3014,7 +3014,7 @@ void xdelCommand(client *c);
 void xtrimCommand(client *c);
 void lolwutCommand(client *c);
 void aclCommand(client *c);
-void stralgoCommand(client *c);
+void lcsCommand(client *c);
 void resetCommand(client *c);
 void failoverCommand(client *c);
 
diff --git a/src/t_string.c b/src/t_string.c
index e1917958b96..d4334b1eb6a 100644
--- a/src/t_string.c
+++ b/src/t_string.c
@@ -726,40 +726,33 @@ void strlenCommand(client *c) {
     addReplyLongLong(c,stringObjectLen(o));
 }
 
-
-/* STRALGO -- Implement complex algorithms on strings.
- *
- * STRALGO <algorithm> ... arguments ... */
-void stralgoLCS(client *c);     /* This implements the LCS algorithm. */
-void stralgoCommand(client *c) {
-    char *subcmd = c->argv[1]->ptr;
-
-    if (c->argc == 2 && !strcasecmp(subcmd,"help")) {
-        const char *help[] = {
-"LCS",
-"    Run the longest common subsequence algorithm.",
-NULL
-        };
-        addReplyHelp(c, help);
-    } else if (!strcasecmp(subcmd,"lcs")) {
-        stralgoLCS(c);
-    } else {
-        addReplySubcommandSyntaxError(c);
-        return;
-    }
-}
-
-/* STRALGO <algo> [IDX] [LEN] [MINMATCHLEN <len>] [WITHMATCHLEN]
- *     STRINGS <string> <string> | KEYS <keya> <keyb>
- */
-void stralgoLCS(client *c) {
+/* LCS key1 key2 [LEN] [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN] */
+void lcsCommand(client *c) {
     uint32_t i, j;
     long long minmatchlen = 0;
     sds a = NULL, b = NULL;
     int getlen = 0, getidx = 0, withmatchlen = 0;
     robj *obja = NULL, *objb = NULL;
 
-    for (j = 2; j < (uint32_t)c->argc; j++) {
+    obja = lookupKeyRead(c->db,c->argv[1]);
+    objb = lookupKeyRead(c->db,c->argv[2]);
+    if ((obja && obja->type != OBJ_STRING) ||
+        (objb && objb->type != OBJ_STRING))
+    {
+        addReplyError(c,
+            "The specified keys must contain string values");
+        /* Don't cleanup the objects, we need to do that
+         * only after calling getDecodedObject(). */
+        obja = NULL;
+        objb = NULL;
+        goto cleanup;
+    }
+    obja = obja ? getDecodedObject(obja) : createStringObject("",0);
+    objb = objb ? getDecodedObject(objb) : createStringObject("",0);
+    a = obja->ptr;
+    b = objb->ptr;
+
+    for (j = 3; j < (uint32_t)c->argc; j++) {
         char *opt = c->argv[j]->ptr;
         int moreargs = (c->argc-1) - j;
 
@@ -774,37 +767,6 @@ void stralgoLCS(client *c) {
                 != C_OK) goto cleanup;
             if (minmatchlen < 0) minmatchlen = 0;
             j++;
-        } else if (!strcasecmp(opt,"STRINGS") && moreargs > 1) {
-            if (a != NULL) {
-                addReplyError(c,"Either use STRINGS or KEYS");
-                goto cleanup;
-            }
-            a = c->argv[j+1]->ptr;
-            b = c->argv[j+2]->ptr;
-            j += 2;
-        } else if (!strcasecmp(opt,"KEYS") && moreargs > 1) {
-            if (a != NULL) {
-                addReplyError(c,"Either use STRINGS or KEYS");
-                goto cleanup;
-            }
-            obja = lookupKeyRead(c->db,c->argv[j+1]);
-            objb = lookupKeyRead(c->db,c->argv[j+2]);
-            if ((obja && obja->type != OBJ_STRING) ||
-                (objb && objb->type != OBJ_STRING))
-            {
-                addReplyError(c,
-                    "The specified keys must contain string values");
-                /* Don't cleanup the objects, we need to do that
-                 * only after calling getDecodedObject(). */
-                obja = NULL;
-                objb = NULL;
-                goto cleanup;
-            }
-            obja = obja ? getDecodedObject(obja) : createStringObject("",0);
-            objb = objb ? getDecodedObject(objb) : createStringObject("",0);
-            a = obja->ptr;
-            b = objb->ptr;
-            j += 2;
         } else {
             addReplyErrorObject(c,shared.syntaxerr);
             goto cleanup;
@@ -812,14 +774,9 @@ void stralgoLCS(client *c) {
     }
 
     /* Complain if the user passed ambiguous parameters. */
-    if (a == NULL) {
-        addReplyError(c,"Please specify two strings: "
-                        "STRINGS or KEYS options are mandatory");
-        goto cleanup;
-    } else if (getlen && getidx) {
+    if (getlen && getidx) {
         addReplyError(c,
-            "If you want both the length and indexes, please "
-            "just use IDX.");
+            "If you want both the length and indexes, please just use IDX.");
         goto cleanup;
     }
 


Test patch:
diff --git a/tests/support/util.tcl b/tests/support/util.tcl
index 4b56436293c..3feb1e96123 100644
--- a/tests/support/util.tcl
+++ b/tests/support/util.tcl
@@ -631,7 +631,7 @@ proc errorrstat {cmd r} {
 proc generate_fuzzy_traffic_on_key {key duration} {
     # Commands per type, blocking commands removed
     # TODO: extract these from help.h or elsewhere, and improve to include other types
-    set string_commands {APPEND BITCOUNT BITFIELD BITOP BITPOS DECR DECRBY GET GETBIT GETRANGE GETSET INCR INCRBY INCRBYFLOAT MGET MSET MSETNX PSETEX SET SETBIT SETEX SETNX SETRANGE STRALGO STRLEN}
+    set string_commands {APPEND BITCOUNT BITFIELD BITOP BITPOS DECR DECRBY GET GETBIT GETRANGE GETSET INCR INCRBY INCRBYFLOAT MGET MSET MSETNX PSETEX SET SETBIT SETEX SETNX SETRANGE LCS STRLEN}
     set hash_commands {HDEL HEXISTS HGET HGETALL HINCRBY HINCRBYFLOAT HKEYS HLEN HMGET HMSET HSCAN HSET HSETNX HSTRLEN HVALS HRANDFIELD}
     set zset_commands {ZADD ZCARD ZCOUNT ZINCRBY ZINTERSTORE ZLEXCOUNT ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYLEX ZRANGEBYSCORE ZRANK ZREM ZREMRANGEBYLEX ZREMRANGEBYRANK ZREMRANGEBYSCORE ZREVRANGE ZREVRANGEBYLEX ZREVRANGEBYSCORE ZREVRANK ZSCAN ZSCORE ZUNIONSTORE ZRANDMEMBER}
     set list_commands {LINDEX LINSERT LLEN LPOP LPOS LPUSH LPUSHX LRANGE LREM LSET LTRIM RPOP RPOPLPUSH RPUSH RPUSHX}
diff --git a/tests/unit/type/string.tcl b/tests/unit/type/string.tcl
index 187035743ee..71b62b3372b 100644
--- a/tests/unit/type/string.tcl
+++ b/tests/unit/type/string.tcl
@@ -573,29 +573,27 @@ start_server {tags {"string"}} {
     set rna2 {ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}
     set rnalcs {ACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}
 
-    test {STRALGO LCS string output with STRINGS option} {
-        r STRALGO LCS STRINGS $rna1 $rna2
+    test {LCS basic} {
+        r set virus1{t} $rna1
+        r set virus2{t} $rna2
+        r LCS virus1{t} virus2{t}
     } $rnalcs
 
-    test {STRALGO LCS len} {
-        r STRALGO LCS LEN STRINGS $rna1 $rna2
-    } [string length $rnalcs]
-
-    test {LCS with KEYS option} {
+    test {LCS len} {
         r set virus1{t} $rna1
         r set virus2{t} $rna2
-        r STRALGO LCS KEYS virus1{t} virus2{t}
-    } $rnalcs
+        r LCS virus1{t} virus2{t} LEN
+    } [string length $rnalcs]
 
     test {LCS indexes} {
-        dict get [r STRALGO LCS IDX KEYS virus1{t} virus2{t}] matches
+        dict get [r LCS virus1{t} virus2{t} IDX] matches
     } {{{238 238} {239 239}} {{236 236} {238 238}} {{229 230} {236 237}} {{224 224} {235 235}} {{1 222} {13 234}}}
 
     test {LCS indexes with match len} {
-        dict get [r STRALGO LCS IDX KEYS virus1{t} virus2{t} WITHMATCHLEN] matches
+        dict get [r LCS virus1{t} virus2{t} IDX WITHMATCHLEN] matches
     } {{{238 238} {239 239} 1} {{236 236} {238 238} 1} {{229 230} {236 237} 2} {{224 224} {235 235} 1} {{1 222} {13 234} 222}}
 
     test {LCS indexes with match len and minimum match len} {
-        dict get [r STRALGO LCS IDX KEYS virus1{t} virus2{t} WITHMATCHLEN MINMATCHLEN 5] matches
+        dict get [r LCS virus1{t} virus2{t} IDX WITHMATCHLEN MINMATCHLEN 5] matches
     } {{{1 222} {13 234} 222}}
 }