Repository: redis/redis-py

Issue:
Missing "mexists" command for Cuckoo filter
It seems MEXISTS was not implemented for Cuckoo filter in the Python library:
https://redis.io/commands/cf.mexists/
I am using the latest versions of redis-py and redis.
Python 3.9

*for Bloom filter it was implemented,



Discussion:

Resolving patch:
diff --git a/redis/commands/bf/commands.py b/redis/commands/bf/commands.py
--- a/redis/commands/bf/commands.py
+++ b/redis/commands/bf/commands.py
@@ -18,6 +18,7 @@
 CF_INSERT = "CF.INSERT"
 CF_INSERTNX = "CF.INSERTNX"
 CF_EXISTS = "CF.EXISTS"
+CF_MEXISTS = "CF.MEXISTS"
 CF_DEL = "CF.DEL"
 CF_COUNT = "CF.COUNT"
 CF_SCANDUMP = "CF.SCANDUMP"
@@ -59,7 +60,7 @@ def create(self, key, errorRate, capacity, expansion=None, noScale=None):
         Create a new Bloom Filter `key` with desired probability of false positives
         `errorRate` expected entries to be inserted as `capacity`.
         Default expansion value is 2. By default, filter is auto-scaling.
-        For more information see `BF.RESERVE <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfreserve>`_.
+        For more information see `BF.RESERVE <https://redis.io/commands/bf.reserve>`_.
         """  # noqa
         params = [key, errorRate, capacity]
         self.append_expansion(params, expansion)
@@ -69,19 +70,16 @@ def create(self, key, errorRate, capacity, expansion=None, noScale=None):
     def add(self, key, item):
         """
         Add to a Bloom Filter `key` an `item`.
-        For more information see `BF.ADD <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfadd>`_.
+        For more information see `BF.ADD <https://redis.io/commands/bf.add>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(BF_ADD, *params)
+        return self.execute_command(BF_ADD, key, item)
 
     def madd(self, key, *items):
         """
         Add to a Bloom Filter `key` multiple `items`.
-        For more information see `BF.MADD <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfmadd>`_.
+        For more information see `BF.MADD <https://redis.io/commands/bf.madd>`_.
         """  # noqa
-        params = [key]
-        params += items
-        return self.execute_command(BF_MADD, *params)
+        return self.execute_command(BF_MADD, key, *items)
 
     def insert(
         self,
@@ -99,7 +97,7 @@ def insert(
         If `nocreate` remain `None` and `key` does not exist, a new Bloom Filter
         `key` will be created with desired probability of false positives `errorRate`
         and expected entries to be inserted as `size`.
-        For more information see `BF.INSERT <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfinsert>`_.
+        For more information see `BF.INSERT <https://redis.io/commands/bf.insert>`_.
         """  # noqa
         params = [key]
         self.append_capacity(params, capacity)
@@ -114,19 +112,16 @@ def insert(
     def exists(self, key, item):
         """
         Check whether an `item` exists in Bloom Filter `key`.
-        For more information see `BF.EXISTS <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfexists>`_.
+        For more information see `BF.EXISTS <https://redis.io/commands/bf.exists>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(BF_EXISTS, *params)
+        return self.execute_command(BF_EXISTS, key, item)
 
     def mexists(self, key, *items):
         """
         Check whether `items` exist in Bloom Filter `key`.
-        For more information see `BF.MEXISTS <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfmexists>`_.
+        For more information see `BF.MEXISTS <https://redis.io/commands/bf.mexists>`_.
         """  # noqa
-        params = [key]
-        params += items
-        return self.execute_command(BF_MEXISTS, *params)
+        return self.execute_command(BF_MEXISTS, key, *items)
 
     def scandump(self, key, iter):
         """
@@ -135,7 +130,7 @@ def scandump(self, key, iter):
         This is useful for large bloom filters which cannot fit into the normal SAVE and RESTORE model.
         The first time this command is called, the value of `iter` should be 0.
         This command will return successive (iter, data) pairs until (0, NULL) to indicate completion.
-        For more information see `BF.SCANDUMP <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfscandump>`_.
+        For more information see `BF.SCANDUMP <https://redis.io/commands/bf.scandump>`_.
         """  # noqa
         if HIREDIS_AVAILABLE:
             raise ModuleError("This command cannot be used when hiredis is available.")
@@ -152,15 +147,14 @@ def loadchunk(self, key, iter, data):
         See the SCANDUMP command for example usage.
         This command will overwrite any bloom filter stored under key.
         Ensure that the bloom filter will not be modified between invocations.
-        For more information see `BF.LOADCHUNK <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfloadchunk>`_.
+        For more information see `BF.LOADCHUNK <https://redis.io/commands/bf.loadchunk>`_.
         """  # noqa
-        params = [key, iter, data]
-        return self.execute_command(BF_LOADCHUNK, *params)
+        return self.execute_command(BF_LOADCHUNK, key, iter, data)
 
     def info(self, key):
         """
         Return capacity, size, number of filters, number of items inserted, and expansion rate.
-        For more information see `BF.INFO <https://oss.redis.com/redisbloom/master/Bloom_Commands/#bfinfo>`_.
+        For more information see `BF.INFO <https://redis.io/commands/bf.info>`_.
         """  # noqa
         return self.execute_command(BF_INFO, key)
 
@@ -187,8 +181,7 @@ def add(self, key, item):
         Add an `item` to a Cuckoo Filter `key`.
         For more information see `CF.ADD <https://redis.io/commands/cf.add>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(CF_ADD, *params)
+        return self.execute_command(CF_ADD, key, item)
 
     def addnx(self, key, item):
         """
@@ -196,8 +189,7 @@ def addnx(self, key, item):
         Command might be slower that `add`.
         For more information see `CF.ADDNX <https://redis.io/commands/cf.addnx>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(CF_ADDNX, *params)
+        return self.execute_command(CF_ADDNX, key, item)
 
     def insert(self, key, items, capacity=None, nocreate=None):
         """
@@ -230,24 +222,28 @@ def exists(self, key, item):
         Check whether an `item` exists in Cuckoo Filter `key`.
         For more information see `CF.EXISTS <https://redis.io/commands/cf.exists>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(CF_EXISTS, *params)
+        return self.execute_command(CF_EXISTS, key, item)
+
+    def mexists(self, key, *items):
+        """
+        Check whether an `items` exist in Cuckoo Filter `key`.
+        For more information see `CF.MEXISTS <https://redis.io/commands/cf.mexists>`_.
+        """  # noqa
+        return self.execute_command(CF_MEXISTS, key, *items)
 
     def delete(self, key, item):
         """
         Delete `item` from `key`.
         For more information see `CF.DEL <https://redis.io/commands/cf.del>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(CF_DEL, *params)
+        return self.execute_command(CF_DEL, key, item)
 
     def count(self, key, item):
         """
         Return the number of times an `item` may be in the `key`.
         For more information see `CF.COUNT <https://redis.io/commands/cf.count>`_.
         """  # noqa
-        params = [key, item]
-        return self.execute_command(CF_COUNT, *params)
+        return self.execute_command(CF_COUNT, key, item)
 
     def scandump(self, key, iter):
         """
@@ -260,8 +256,7 @@ def scandump(self, key, iter):
         (0, NULL) to indicate completion.
         For more information see `CF.SCANDUMP <https://redis.io/commands/cf.scandump>`_.
         """  # noqa
-        params = [key, iter]
-        return self.execute_command(CF_SCANDUMP, *params)
+        return self.execute_command(CF_SCANDUMP, key, iter)
 
     def loadchunk(self, key, iter, data):
         """
@@ -271,8 +266,7 @@ def loadchunk(self, key, iter, data):
         Ensure that the Cuckoo filter will not be modified between invocations.
         For more information see `CF.LOADCHUNK <https://redis.io/commands/cf.loadchunk>`_.
         """  # noqa
-        params = [key, iter, data]
-        return self.execute_command(CF_LOADCHUNK, *params)
+        return self.execute_command(CF_LOADCHUNK, key, iter, data)
 
     def info(self, key):
         """
@@ -292,17 +286,14 @@ def reserve(self, key, k, width, depth, decay):
         positives `errorRate` expected entries to be inserted as `size`.
         For more information see `TOPK.RESERVE <https://redis.io/commands/topk.reserve>`_.
         """  # noqa
-        params = [key, k, width, depth, decay]
-        return self.execute_command(TOPK_RESERVE, *params)
+        return self.execute_command(TOPK_RESERVE, key, k, width, depth, decay)
 
     def add(self, key, *items):
         """
         Add one `item` or more to a Top-K Filter `key`.
         For more information see `TOPK.ADD <https://redis.io/commands/topk.add>`_.
         """  # noqa
-        params = [key]
-        params += items
-        return self.execute_command(TOPK_ADD, *params)
+        return self.execute_command(TOPK_ADD, key, *items)
 
     def incrby(self, key, items, increments):
         """
@@ -323,18 +314,14 @@ def query(self, key, *items):
         Check whether one `item` or more is a Top-K item at `key`.
         For more information see `TOPK.QUERY <https://redis.io/commands/topk.query>`_.
         """  # noqa
-        params = [key]
-        params += items
-        return self.execute_command(TOPK_QUERY, *params)
+        return self.execute_command(TOPK_QUERY, key, *items)
 
     def count(self, key, *items):
         """
         Return count for one `item` or more from `key`.
         For more information see `TOPK.COUNT <https://redis.io/commands/topk.count>`_.
         """  # noqa
-        params = [key]
-        params += items
-        return self.execute_command(TOPK_COUNT, *params)
+        return self.execute_command(TOPK_COUNT, key, *items)
 
     def list(self, key, withcount=False):
         """
@@ -362,8 +349,7 @@ def create(self, key, compression):
         Allocate the memory and initialize the t-digest.
         For more information see `TDIGEST.CREATE <https://redis.io/commands/tdigest.create>`_.
         """  # noqa
-        params = [key, compression]
-        return self.execute_command(TDIGEST_CREATE, *params)
+        return self.execute_command(TDIGEST_CREATE, key, compression)
 
     def reset(self, key):
         """
@@ -391,8 +377,7 @@ def merge(self, toKey, fromKey):
         Merge all of the values from 'fromKey' to 'toKey' sketch.
         For more information see `TDIGEST.MERGE <https://redis.io/commands/tdigest.merge>`_.
         """  # noqa
-        params = [toKey, fromKey]
-        return self.execute_command(TDIGEST_MERGE, *params)
+        return self.execute_command(TDIGEST_MERGE, toKey, fromKey)
 
     def min(self, key):
         """
@@ -414,16 +399,14 @@ def quantile(self, key, quantile):
         added to this TDigest would be less than or equal to the cutoff.
         For more information see `TDIGEST.QUANTILE <https://redis.io/commands/tdigest.quantile>`_.
         """  # noqa
-        params = [key, quantile]
-        return self.execute_command(TDIGEST_QUANTILE, *params)
+        return self.execute_command(TDIGEST_QUANTILE, key, quantile)
 
     def cdf(self, key, value):
         """
         Return double fraction of all points added which are <= value.
         For more information see `TDIGEST.CDF <https://redis.io/commands/tdigest.cdf>`_.
         """  # noqa
-        params = [key, value]
-        return self.execute_command(TDIGEST_CDF, *params)
+        return self.execute_command(TDIGEST_CDF, key, value)
 
     def info(self, key):
         """
@@ -443,16 +426,14 @@ def initbydim(self, key, width, depth):
         Initialize a Count-Min Sketch `key` to dimensions (`width`, `depth`) specified by user.
         For more information see `CMS.INITBYDIM <https://redis.io/commands/cms.initbydim>`_.
         """  # noqa
-        params = [key, width, depth]
-        return self.execute_command(CMS_INITBYDIM, *params)
+        return self.execute_command(CMS_INITBYDIM, key, width, depth)
 
     def initbyprob(self, key, error, probability):
         """
         Initialize a Count-Min Sketch `key` to characteristics (`error`, `probability`) specified by user.
         For more information see `CMS.INITBYPROB <https://redis.io/commands/cms.initbyprob>`_.
         """  # noqa
-        params = [key, error, probability]
-        return self.execute_command(CMS_INITBYPROB, *params)
+        return self.execute_command(CMS_INITBYPROB, key, error, probability)
 
     def incrby(self, key, items, increments):
         """
@@ -473,9 +454,7 @@ def query(self, key, *items):
         Return count for an `item` from `key`. Multiple items can be queried with one call.
         For more information see `CMS.QUERY <https://redis.io/commands/cms.query>`_.
         """  # noqa
-        params = [key]
-        params += items
-        return self.execute_command(CMS_QUERY, *params)
+        return self.execute_command(CMS_QUERY, key, *items)
 
     def merge(self, destKey, numKeys, srcKeys, weights=[]):
         """


Test patch:
diff --git a/tests/test_bloom.py b/tests/test_bloom.py
--- a/tests/test_bloom.py
+++ b/tests/test_bloom.py
@@ -173,6 +173,7 @@ def test_cf_exists_and_del(client):
     assert client.cf().add("cuckoo", "filter")
     assert client.cf().exists("cuckoo", "filter")
     assert not client.cf().exists("cuckoo", "notexist")
+    assert [1, 0] == client.cf().mexists("cuckoo", "filter", "notexist")
     assert 1 == client.cf().count("cuckoo", "filter")
     assert 0 == client.cf().count("cuckoo", "notexist")
     assert client.cf().delete("cuckoo", "filter")