diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index d60087ee1b..a8bca0fb67 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -23,10 +23,13 @@ import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.async.*; import io.lettuce.core.array.*; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.core.cluster.PipelinedRedisFuture; import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands; import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser; @@ -116,7 +119,7 @@ public abstract class AbstractRedisAsyncCommands implements RedisAclAsyncC RedisHLLAsyncCommands, BaseRedisAsyncCommands, RedisTransactionalAsyncCommands, RedisGeoAsyncCommands, RedisClusterAsyncCommands, RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, RediSearchAsyncCommands, RedisArrayAsyncCommands, - RedisBloomFilterAsyncCommands { + RedisBloomFilterAsyncCommands, RedisCuckooFilterAsyncCommands { private final StatefulConnection connection; @@ -132,6 +135,8 @@ public abstract class AbstractRedisAsyncCommands implements RedisAclAsyncC private final RedisBloomFilterCommandBuilder bloomFilterCommandBuilder; + private final RedisCuckooFilterCommandBuilder cuckooFilterCommandBuilder; + private final Supplier parser; /** @@ -151,6 +156,7 @@ public AbstractRedisAsyncCommands(StatefulConnection connection, RedisCode this.searchCommandBuilder = new RediSearchCommandBuilder<>(codec); this.arrayCommandBuilder = new RedisArrayCommandBuilder<>(codec); this.bloomFilterCommandBuilder = new RedisBloomFilterCommandBuilder<>(codec); + this.cuckooFilterCommandBuilder = new RedisCuckooFilterCommandBuilder<>(codec); } /** @@ -4293,8 +4299,105 @@ public RedisFuture bfReserve(K key, double errorRate, long capacity, BfR } @Override - public RedisFuture bfScanDump(K key, long iterator) { + public RedisFuture bfScanDump(K key, long iterator) { return dispatch(bloomFilterCommandBuilder.bfScanDump(key, iterator)); } + // --- Redis Cuckoo Filter Commands --- + + @Override + public RedisFuture cfReserve(K key, long capacity) { + return dispatch(cuckooFilterCommandBuilder.cfReserve(key, capacity)); + } + + @Override + public RedisFuture cfReserve(K key, long capacity, CfReserveArgs args) { + return dispatch(cuckooFilterCommandBuilder.cfReserve(key, capacity, args)); + } + + @Override + public RedisFuture cfAdd(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfAdd(key, value)); + } + + @Override + public RedisFuture cfAddNx(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfAddNx(key, value)); + } + + @Override + public RedisFuture> cfInsert(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, value)); + } + + @Override + public RedisFuture> cfInsert(K key, CfInsertArgs args, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, args, value)); + } + + @Override + public RedisFuture> cfInsert(K key, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, values)); + } + + @Override + public RedisFuture> cfInsert(K key, CfInsertArgs args, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, args, values)); + } + + @Override + public RedisFuture> cfInsertNx(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, value)); + } + + @Override + public RedisFuture> cfInsertNx(K key, CfInsertArgs args, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, args, value)); + } + + @Override + public RedisFuture> cfInsertNx(K key, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, values)); + } + + @Override + public RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, args, values)); + } + + @Override + public RedisFuture cfExists(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfExists(key, value)); + } + + @Override + public RedisFuture> cfMExists(K key, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfMExists(key, values)); + } + + @Override + public RedisFuture cfDel(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfDel(key, value)); + } + + @Override + public RedisFuture cfCount(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfCount(key, value)); + } + + @Override + public RedisFuture cfScanDump(K key, long cursor) { + return dispatch(cuckooFilterCommandBuilder.cfScanDump(key, cursor)); + } + + @Override + public RedisFuture cfLoadChunk(K key, long cursor, byte[] data) { + return dispatch(cuckooFilterCommandBuilder.cfLoadChunk(key, cursor, data)); + } + + @Override + public RedisFuture cfInfo(K key) { + return dispatch(cuckooFilterCommandBuilder.cfInfo(key)); + } + } diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 70d58bedef..44d2c9f403 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -23,10 +23,13 @@ import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.reactive.*; import io.lettuce.core.array.*; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands; import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser; import io.lettuce.core.cluster.models.partitions.RedisClusterNode; @@ -115,13 +118,14 @@ * @author dae won * @since 4.0 */ -public abstract class AbstractRedisReactiveCommands implements RedisAclReactiveCommands, - RedisHashReactiveCommands, RedisKeyReactiveCommands, RedisStringReactiveCommands, - RedisListReactiveCommands, RedisSetReactiveCommands, RedisSortedSetReactiveCommands, - RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisHLLReactiveCommands, - BaseRedisReactiveCommands, RedisTransactionalReactiveCommands, RedisGeoReactiveCommands, - RedisClusterReactiveCommands, RedisJsonReactiveCommands, RedisVectorSetReactiveCommands, - RediSearchReactiveCommands, RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands { +public abstract class AbstractRedisReactiveCommands + implements RedisAclReactiveCommands, RedisHashReactiveCommands, RedisKeyReactiveCommands, + RedisStringReactiveCommands, RedisListReactiveCommands, RedisSetReactiveCommands, + RedisSortedSetReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands, + RedisHLLReactiveCommands, BaseRedisReactiveCommands, RedisTransactionalReactiveCommands, + RedisGeoReactiveCommands, RedisClusterReactiveCommands, RedisJsonReactiveCommands, + RedisVectorSetReactiveCommands, RediSearchReactiveCommands, RedisArrayReactiveCommands, + RedisBloomFilterReactiveCommands, RedisCuckooFilterReactiveCommands { private final StatefulConnection connection; @@ -137,6 +141,8 @@ public abstract class AbstractRedisReactiveCommands implements RedisAclRea private final RedisBloomFilterCommandBuilder bloomFilterCommandBuilder; + private final RedisCuckooFilterCommandBuilder cuckooFilterCommandBuilder; + private final Supplier parser; private final ClientResources clientResources; @@ -162,6 +168,7 @@ public AbstractRedisReactiveCommands(StatefulConnection connection, RedisC this.searchCommandBuilder = new RediSearchCommandBuilder<>(codec); this.arrayCommandBuilder = new RedisArrayCommandBuilder<>(codec); this.bloomFilterCommandBuilder = new RedisBloomFilterCommandBuilder<>(codec); + this.cuckooFilterCommandBuilder = new RedisCuckooFilterCommandBuilder<>(codec); this.clientResources = connection.getResources(); this.tracingEnabled = clientResources.tracing().isEnabled(); } @@ -4340,8 +4347,105 @@ public Mono bfReserve(K key, double errorRate, long capacity, BfReserveA } @Override - public Mono bfScanDump(K key, long iterator) { + public Mono bfScanDump(K key, long iterator) { return createMono(() -> bloomFilterCommandBuilder.bfScanDump(key, iterator)); } + // --- Redis Cuckoo Filter Commands --- + + @Override + public Mono cfReserve(K key, long capacity) { + return createMono(() -> cuckooFilterCommandBuilder.cfReserve(key, capacity)); + } + + @Override + public Mono cfReserve(K key, long capacity, CfReserveArgs args) { + return createMono(() -> cuckooFilterCommandBuilder.cfReserve(key, capacity, args)); + } + + @Override + public Mono cfAdd(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfAdd(key, value)); + } + + @Override + public Mono cfAddNx(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfAddNx(key, value)); + } + + @Override + public Flux cfInsert(K key, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, value)); + } + + @Override + public Flux cfInsert(K key, CfInsertArgs args, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, args, value)); + } + + @Override + public Flux cfInsert(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, values)); + } + + @Override + public Flux cfInsert(K key, CfInsertArgs args, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, args, values)); + } + + @Override + public Flux cfInsertNx(K key, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, value)); + } + + @Override + public Flux cfInsertNx(K key, CfInsertArgs args, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, args, value)); + } + + @Override + public Flux cfInsertNx(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, values)); + } + + @Override + public Flux cfInsertNx(K key, CfInsertArgs args, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, args, values)); + } + + @Override + public Mono cfExists(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfExists(key, value)); + } + + @Override + public Flux cfMExists(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfMExists(key, values)); + } + + @Override + public Mono cfDel(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfDel(key, value)); + } + + @Override + public Mono cfCount(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfCount(key, value)); + } + + @Override + public Mono cfScanDump(K key, long cursor) { + return createMono(() -> cuckooFilterCommandBuilder.cfScanDump(key, cursor)); + } + + @Override + public Mono cfLoadChunk(K key, long cursor, byte[] data) { + return createMono(() -> cuckooFilterCommandBuilder.cfLoadChunk(key, cursor, data)); + } + + @Override + public Mono cfInfo(K key) { + return createMono(() -> cuckooFilterCommandBuilder.cfInfo(key)); + } + } diff --git a/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java index e75b9b91c6..79e6d47240 100644 --- a/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java @@ -8,12 +8,12 @@ import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfInfoValueParser; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.BfScanDumpValueParser; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValueParser; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValueParser; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.output.*; import io.lettuce.core.protocol.BaseRedisCommandBuilder; @@ -183,12 +183,12 @@ Command bfReserve(K key, double errorRate, long capacity, BfReserv return createCommand(BF_RESERVE, new StatusOutput<>(codec), args); } - Command bfScanDump(K key, long iterator) { + Command bfScanDump(K key, long iterator) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator); - return createCommand(BF_SCANDUMP, new EncodedComplexOutput<>(codec, BfScanDumpValueParser.INSTANCE), args); + return createCommand(BF_SCANDUMP, new EncodedComplexOutput<>(codec, ScanDumpValueParser.INSTANCE), args); } } diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java new file mode 100644 index 0000000000..1d90ec80f9 --- /dev/null +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core; + +import java.util.List; + +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValueParser; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValueParser; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; +import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.output.*; +import io.lettuce.core.protocol.BaseRedisCommandBuilder; +import io.lettuce.core.protocol.Command; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +import static io.lettuce.core.protocol.CommandType.*; + +/** + * Implementation of the {@link BaseRedisCommandBuilder} handling Cuckoo Filter commands. + * + * @param Key type. + * @param Value type. + * @author Gyumin Hwang + * @since 7.7 + */ +class RedisCuckooFilterCommandBuilder extends BaseRedisCommandBuilder { + + RedisCuckooFilterCommandBuilder(RedisCodec codec) { + super(codec); + } + + Command cfReserve(K key, long capacity) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(capacity); + + return createCommand(CF_RESERVE, new StatusOutput<>(codec), args); + } + + Command cfReserve(K key, long capacity, CfReserveArgs reserveArgs) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(capacity); + reserveArgs.build(args); + + return createCommand(CF_RESERVE, new StatusOutput<>(codec), args); + } + + Command cfAdd(K key, V value) { + notNullKey(key); + + return createCommand(CF_ADD, new BooleanOutput<>(codec), key, value); + } + + Command cfAddNx(K key, V value) { + notNullKey(key); + + return createCommand(CF_ADDNX, new BooleanOutput<>(codec), key, value); + } + + Command> cfInsert(K key, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); + } + + Command> cfInsert(K key, CfInsertArgs insertArgs, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsert(K key, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsert(K key, CfInsertArgs insertArgs, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); + } + + Command> cfInsertNx(K key, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); + } + + Command> cfInsertNx(K key, CfInsertArgs insertArgs, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsertNx(K key, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsertNx(K key, CfInsertArgs insertArgs, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); + } + + Command cfExists(K key, V value) { + notNullKey(key); + + return createCommand(CF_EXISTS, new BooleanOutput<>(codec), key, value); + } + + @SafeVarargs + final Command> cfMExists(K key, V... values) { + notNullKey(key); + + return createCommand(CF_MEXISTS, new BooleanListOutput<>(codec), key, values); + } + + Command cfDel(K key, V value) { + notNullKey(key); + + return createCommand(CF_DEL, new BooleanOutput<>(codec), key, value); + } + + Command cfCount(K key, V value) { + notNullKey(key); + + return createCommand(CF_COUNT, new IntegerOutput<>(codec), key, value); + } + + Command cfScanDump(K key, long iterator) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator); + + return createCommand(CF_SCANDUMP, new EncodedComplexOutput<>(codec, ScanDumpValueParser.INSTANCE), args); + } + + Command cfLoadChunk(K key, long iterator, byte[] data) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator).add(data); + + return createCommand(CF_LOADCHUNK, new StatusOutput<>(codec), args); + } + + Command cfInfo(K key) { + notNullKey(key); + + return createCommand(CF_INFO, new EncodedComplexOutput<>(codec, CfInfoValueParser.INSTANCE), key); + } + +} diff --git a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java index 6c17d6e97d..88d2bfe6ca 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java @@ -40,7 +40,8 @@ public interface RedisAsyncCommands extends BaseRedisAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands, RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, RedisStringAsyncCommands, RedisTransactionalAsyncCommands, RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, - RediSearchAsyncCommands, RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands { + RediSearchAsyncCommands, RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands, + RedisCuckooFilterAsyncCommands { /** * Authenticate to the server. diff --git a/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java index adbdfbb8c5..7397aa113b 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java @@ -8,10 +8,10 @@ import java.util.List; import io.lettuce.core.RedisFuture; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Asynchronous executed commands for Bloom Filter. @@ -165,8 +165,8 @@ public interface RedisBloomFilterAsyncCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - RedisFuture bfScanDump(K key, long iterator); + RedisFuture bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java new file mode 100644 index 0000000000..9dd383888c --- /dev/null +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.async; + +import java.util.List; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; + +/** + * Asynchronous executed commands for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateAsyncApi + */ +public interface RedisCuckooFilterAsyncCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + RedisFuture cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + RedisFuture cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand (future fails with this + * exception). + */ + RedisFuture cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + RedisFuture cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). + */ + RedisFuture> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). + */ + RedisFuture> cfInsert(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). + */ + RedisFuture> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). + */ + RedisFuture> cfInsert(K key, CfInsertArgs args, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, CfInsertArgs args, V value); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + RedisFuture cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + RedisFuture> cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + RedisFuture cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + RedisFuture cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + RedisFuture cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + RedisFuture cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + RedisFuture cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java index dc640e95ba..01c5919583 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.api.reactive; import io.lettuce.core.Value; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -165,8 +165,8 @@ public interface RedisBloomFilterReactiveCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - Mono bfScanDump(K key, long iterator); + Mono bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java new file mode 100644 index 0000000000..40b4911dde --- /dev/null +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.reactive; + +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Reactive executed commands for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateReactiveApi + */ +public interface RedisCuckooFilterReactiveCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Mono cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Mono cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. The + * {@code Mono} signals an error with {@code io.lettuce.core.RedisCommandExecutionException} if the filter is full + * and cannot expand. + */ + Mono cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Mono cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. + */ + Flux cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. + */ + Flux cfInsert(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. + */ + Flux cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. + */ + Flux cfInsert(K key, CfInsertArgs args, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, CfInsertArgs args, V value); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Mono cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return Boolean of {@code true} or {@code false} where {@code true} means that, with high probability, the item was + * already added to the filter, and {@code false} means that the item was definitely not added to the filter. + */ + Flux cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Mono cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Mono cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + Mono cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + Mono cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + Mono cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java index e727a4f538..74ae30a69c 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java @@ -32,14 +32,14 @@ * @author Yordan Tsintsov * @since 5.0 */ -public interface RedisReactiveCommands - extends BaseRedisReactiveCommands, RedisAclReactiveCommands, RedisClusterReactiveCommands, - RedisFunctionReactiveCommands, RedisGeoReactiveCommands, RedisHashReactiveCommands, - RedisHLLReactiveCommands, RedisKeyReactiveCommands, RedisListReactiveCommands, - RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisSetReactiveCommands, - RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, RedisStringReactiveCommands, - RedisTransactionalReactiveCommands, RedisJsonReactiveCommands, RedisVectorSetReactiveCommands, - RediSearchReactiveCommands, RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands { +public interface RedisReactiveCommands extends BaseRedisReactiveCommands, RedisAclReactiveCommands, + RedisClusterReactiveCommands, RedisFunctionReactiveCommands, RedisGeoReactiveCommands, + RedisHashReactiveCommands, RedisHLLReactiveCommands, RedisKeyReactiveCommands, + RedisListReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands, + RedisSetReactiveCommands, RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, + RedisStringReactiveCommands, RedisTransactionalReactiveCommands, RedisJsonReactiveCommands, + RedisVectorSetReactiveCommands, RediSearchReactiveCommands, RedisArrayReactiveCommands, + RedisBloomFilterReactiveCommands, RedisCuckooFilterReactiveCommands { /** * Authenticate to the server. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java index b92493a572..0c23419b9b 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.api.sync; import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Synchronous executed commands for Bloom Filter. @@ -164,8 +164,8 @@ public interface RedisBloomFilterCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - BfScanDumpValue bfScanDump(K key, long iterator); + ScanDumpValue bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCommands.java index ca302b64aa..2c2488fe25 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCommands.java @@ -39,7 +39,7 @@ public interface RedisCommands extends BaseRedisCommands, RedisAclCo RedisKeyCommands, RedisListCommands, RedisScriptingCommands, RedisServerCommands, RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, RedisStringCommands, RedisTransactionalCommands, RedisJsonCommands, RedisVectorSetCommands, RediSearchCommands, - RedisArrayCommands, RedisBloomFilterCommands { + RedisArrayCommands, RedisBloomFilterCommands, RedisCuckooFilterCommands { /** * Authenticate to the server. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java new file mode 100644 index 0000000000..dbbf3a3ee5 --- /dev/null +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.sync; + +import java.util.List; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; + +/** + * Synchronous executed commands for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateSyncApi + */ +public interface RedisCuckooFilterCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. + */ + Boolean cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Boolean cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, CfInsertArgs args, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V value); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Boolean cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + List cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Boolean cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Long cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + ScanDumpValue cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + String cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + CfInfoValue cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java index e49a62f864..195c6e23f9 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java @@ -16,5 +16,5 @@ public interface NodeSelectionAsyncCommands extends BaseNodeSelectionAsync NodeSelectionScriptingAsyncCommands, NodeSelectionServerAsyncCommands, NodeSelectionSetAsyncCommands, NodeSelectionSortedSetAsyncCommands, NodeSelectionStreamCommands, NodeSelectionStringAsyncCommands, NodeSelectionJsonAsyncCommands, NodeSelectionVectorSetAsyncCommands, NodeSelectionSearchAsyncCommands, - NodeSelectionBloomFilterAsyncCommands { + NodeSelectionBloomFilterAsyncCommands, NodeSelectionCuckooFilterAsyncCommands { } diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java index 64380e9d5b..69e453fd9a 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.cluster.api.async; import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Asynchronous executed commands on a node selection for Bloom Filter. @@ -164,8 +164,8 @@ public interface NodeSelectionBloomFilterAsyncCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - AsyncExecutions bfScanDump(K key, long iterator); + AsyncExecutions bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java new file mode 100644 index 0000000000..7fde717895 --- /dev/null +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cluster.api.async; + +import java.util.List; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; + +/** + * Asynchronous executed commands on a node selection for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateAsyncNodeSelectionClusterApi + */ +public interface NodeSelectionCuckooFilterAsyncCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + AsyncExecutions cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + AsyncExecutions cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand (future fails with this + * exception). + */ + AsyncExecutions cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + AsyncExecutions cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + AsyncExecutions> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + AsyncExecutions> cfInsert(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + AsyncExecutions> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + AsyncExecutions> cfInsert(K key, CfInsertArgs args, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V value); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + AsyncExecutions cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + AsyncExecutions> cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + AsyncExecutions cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + AsyncExecutions cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + AsyncExecutions cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + AsyncExecutions cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + AsyncExecutions cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java index a6c05f8b18..2c1c396ec3 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java @@ -41,13 +41,13 @@ * @author Yordan Tsintsov * @since 4.0 */ -public interface RedisClusterAsyncCommands - extends BaseRedisAsyncCommands, RedisAclAsyncCommands, RedisFunctionAsyncCommands, - RedisGeoAsyncCommands, RedisHashAsyncCommands, RedisHLLAsyncCommands, RedisKeyAsyncCommands, - RedisListAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, - RedisSetAsyncCommands, RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, - RedisStringAsyncCommands, RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, - RediSearchAsyncCommands, RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands { +public interface RedisClusterAsyncCommands extends BaseRedisAsyncCommands, RedisAclAsyncCommands, + RedisFunctionAsyncCommands, RedisGeoAsyncCommands, RedisHashAsyncCommands, + RedisHLLAsyncCommands, RedisKeyAsyncCommands, RedisListAsyncCommands, + RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands, + RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, RedisStringAsyncCommands, + RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, RediSearchAsyncCommands, + RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands, RedisCuckooFilterAsyncCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java index b6756db506..0ec6330320 100644 --- a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java @@ -48,7 +48,7 @@ public interface RedisClusterReactiveCommands extends BaseRedisReactiveCom RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisSetReactiveCommands, RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, RedisStringReactiveCommands, RedisJsonReactiveCommands, RedisVectorSetReactiveCommands, RediSearchReactiveCommands, - RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands { + RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands, RedisCuckooFilterReactiveCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java index 9aa2101890..22d026db3f 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.cluster.api.sync; import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Synchronous executed commands on a node selection for Bloom Filter. @@ -164,8 +164,8 @@ public interface NodeSelectionBloomFilterCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - Executions bfScanDump(K key, long iterator); + Executions bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java index d18afd2360..52dbb6a94a 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java @@ -8,10 +8,11 @@ * @author Mark Paluch * @author Tihomir Mateev */ -public interface NodeSelectionCommands extends BaseNodeSelectionCommands, NodeSelectionFunctionCommands, - NodeSelectionGeoCommands, NodeSelectionHashCommands, NodeSelectionHLLCommands, - NodeSelectionKeyCommands, NodeSelectionListCommands, NodeSelectionScriptingCommands, - NodeSelectionServerCommands, NodeSelectionSetCommands, NodeSelectionSortedSetCommands, - NodeSelectionStreamCommands, NodeSelectionStringCommands, NodeSelectionJsonCommands, - NodeSelectionVectorSetCommands, NodeSelectionSearchCommands, NodeSelectionBloomFilterCommands { +public interface NodeSelectionCommands + extends BaseNodeSelectionCommands, NodeSelectionFunctionCommands, NodeSelectionGeoCommands, + NodeSelectionHashCommands, NodeSelectionHLLCommands, NodeSelectionKeyCommands, + NodeSelectionListCommands, NodeSelectionScriptingCommands, NodeSelectionServerCommands, + NodeSelectionSetCommands, NodeSelectionSortedSetCommands, NodeSelectionStreamCommands, + NodeSelectionStringCommands, NodeSelectionJsonCommands, NodeSelectionVectorSetCommands, + NodeSelectionSearchCommands, NodeSelectionBloomFilterCommands, NodeSelectionCuckooFilterCommands { } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java new file mode 100644 index 0000000000..7080f147aa --- /dev/null +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cluster.api.sync; + +import java.util.List; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; + +/** + * Synchronous executed commands on a node selection for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateSyncNodeSelectionClusterApi + */ +public interface NodeSelectionCuckooFilterCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Executions cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Executions cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. + */ + Executions cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Executions cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + Executions> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + Executions> cfInsert(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + Executions> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + Executions> cfInsert(K key, CfInsertArgs args, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, CfInsertArgs args, V value); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Executions cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + Executions> cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Executions cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Executions cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + Executions cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + Executions cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + Executions cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java index 15e1426b1c..63880b297e 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java @@ -46,7 +46,7 @@ public interface RedisClusterCommands RedisHashCommands, RedisHLLCommands, RedisKeyCommands, RedisListCommands, RedisScriptingCommands, RedisServerCommands, RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, RedisStringCommands, RedisJsonCommands, RedisVectorSetCommands, - RediSearchCommands, RedisArrayCommands, RedisBloomFilterCommands { + RediSearchCommands, RedisArrayCommands, RedisBloomFilterCommands, RedisCuckooFilterCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/bf/BfInfoValue.java b/src/main/java/io/lettuce/core/probabilistic/BfInfoValue.java similarity index 98% rename from src/main/java/io/lettuce/core/bf/BfInfoValue.java rename to src/main/java/io/lettuce/core/probabilistic/BfInfoValue.java index 8b121961fa..742ad9a095 100644 --- a/src/main/java/io/lettuce/core/bf/BfInfoValue.java +++ b/src/main/java/io/lettuce/core/probabilistic/BfInfoValue.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import java.util.Map; diff --git a/src/main/java/io/lettuce/core/bf/BfInfoValueParser.java b/src/main/java/io/lettuce/core/probabilistic/BfInfoValueParser.java similarity index 96% rename from src/main/java/io/lettuce/core/bf/BfInfoValueParser.java rename to src/main/java/io/lettuce/core/probabilistic/BfInfoValueParser.java index 57affe8027..e4fd2b5614 100644 --- a/src/main/java/io/lettuce/core/bf/BfInfoValueParser.java +++ b/src/main/java/io/lettuce/core/probabilistic/BfInfoValueParser.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import java.nio.ByteBuffer; import java.util.LinkedHashMap; diff --git a/src/main/java/io/lettuce/core/probabilistic/CfInfoValue.java b/src/main/java/io/lettuce/core/probabilistic/CfInfoValue.java new file mode 100644 index 0000000000..410217e698 --- /dev/null +++ b/src/main/java/io/lettuce/core/probabilistic/CfInfoValue.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic; + +import java.util.Map; + +/** + * Represents the result of the Redis CF.INFO command. + * + * @author HwangRock + * @since 7.7 + */ +public class CfInfoValue { + + private final Map rawInfo; + + private final Long size; + + private final Long numberOfBuckets; + + private final Long numberOfFilters; + + private final Long numberOfItemsInserted; + + private final Long numberOfItemsDeleted; + + private final Long bucketSize; + + private final Long expansionRate; + + private final Long maxIterations; + + public CfInfoValue(Map rawInfo) { + this.rawInfo = rawInfo; + this.size = (Long) rawInfo.get("Size"); + this.numberOfBuckets = (Long) rawInfo.get("Number of buckets"); + this.numberOfFilters = (Long) rawInfo.get("Number of filters"); + this.numberOfItemsInserted = (Long) rawInfo.get("Number of items inserted"); + this.numberOfItemsDeleted = (Long) rawInfo.get("Number of items deleted"); + this.bucketSize = (Long) rawInfo.get("Bucket size"); + this.expansionRate = (Long) rawInfo.get("Expansion rate"); + this.maxIterations = (Long) rawInfo.get("Max iterations"); + } + + /** + * Returns the raw info map returned by the Redis server. + * + * @return the raw info map returned by the Redis server + */ + public Map getRawInfo() { + return rawInfo; + } + + /** + * Returns the current size of the filter in bytes. + * + * @return the current size of the filter in bytes + */ + public Long getSize() { + return size; + } + + /** + * Returns the number of buckets in the filter. + * + * @return the number of buckets in the filter + */ + public Long getNumberOfBuckets() { + return numberOfBuckets; + } + + /** + * Returns the number of sub-filters in the filter. + * + * @return the number of sub-filters in the filter + */ + public Long getNumberOfFilters() { + return numberOfFilters; + } + + /** + * Returns the number of items inserted into the filter. + * + * @return the number of items inserted into the filter + */ + public Long getNumberOfItemsInserted() { + return numberOfItemsInserted; + } + + /** + * Returns the number of items deleted from the filter. + * + * @return the number of items deleted from the filter + */ + public Long getNumberOfItemsDeleted() { + return numberOfItemsDeleted; + } + + /** + * Returns the bucket size of the filter. + * + * @return the bucket size of the filter + */ + public Long getBucketSize() { + return bucketSize; + } + + /** + * Returns the expansion rate of the filter. + * + * @return the expansion rate of the filter + */ + public Long getExpansionRate() { + return expansionRate; + } + + /** + * Returns the maximum number of iterations when inserting items into the filter. + * + * @return the maximum number of iterations when inserting items into the filter + */ + public Long getMaxIterations() { + return maxIterations; + } + +} diff --git a/src/main/java/io/lettuce/core/probabilistic/CfInfoValueParser.java b/src/main/java/io/lettuce/core/probabilistic/CfInfoValueParser.java new file mode 100644 index 0000000000..7098a69daa --- /dev/null +++ b/src/main/java/io/lettuce/core/probabilistic/CfInfoValueParser.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic; + +import java.nio.ByteBuffer; +import java.util.LinkedHashMap; +import java.util.Map; + +import io.lettuce.core.codec.StringCodec; +import io.lettuce.core.output.ComplexData; +import io.lettuce.core.output.ComplexDataParser; + +/** + * Parser for Redis CF.INFO command output. + * + * @author HwangRock + * @since 7.7 + */ +public final class CfInfoValueParser implements ComplexDataParser { + + public static final CfInfoValueParser INSTANCE = new CfInfoValueParser(); + + private CfInfoValueParser() { + } + + @Override + public CfInfoValue parse(ComplexData data) { + if (data == null) { + throw new IllegalArgumentException("Failed parsing CF.INFO: data must not be null"); + } + Map raw = data.getDynamicMap(); + Map info = new LinkedHashMap<>(raw.size()); + for (Map.Entry e : raw.entrySet()) { + String k = StringCodec.UTF8.decodeKey((ByteBuffer) e.getKey()); + info.put(k, e.getValue()); + } + return new CfInfoValue(info); + } + +} diff --git a/src/main/java/io/lettuce/core/bf/BfScanDumpValue.java b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValue.java similarity index 61% rename from src/main/java/io/lettuce/core/bf/BfScanDumpValue.java rename to src/main/java/io/lettuce/core/probabilistic/ScanDumpValue.java index a7205b822e..65140ca094 100644 --- a/src/main/java/io/lettuce/core/bf/BfScanDumpValue.java +++ b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValue.java @@ -4,21 +4,23 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; /** - * Value object for the Redis BF.SCANDUMP command. + * Value object for the scan-dump commands of the RedisBloom module, such as + * BF.SCANDUMP and + * CF.SCANDUMP. * * @author Yordan Tsintsov * @since 7.7 */ -public class BfScanDumpValue { +public class ScanDumpValue { private final long iterator; private final byte[] data; - public BfScanDumpValue(long iterator, byte[] data) { + public ScanDumpValue(long iterator, byte[] data) { this.iterator = iterator; this.data = data; } diff --git a/src/main/java/io/lettuce/core/bf/BfScanDumpValueParser.java b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValueParser.java similarity index 51% rename from src/main/java/io/lettuce/core/bf/BfScanDumpValueParser.java rename to src/main/java/io/lettuce/core/probabilistic/ScanDumpValueParser.java index d03f46faf7..e5277876b3 100644 --- a/src/main/java/io/lettuce/core/bf/BfScanDumpValueParser.java +++ b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValueParser.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import java.nio.ByteBuffer; import java.util.List; @@ -13,26 +13,28 @@ import io.lettuce.core.output.ComplexDataParser; /** - * Parser for Redis BF.SCANDUMP command output. + * Parser for the scan-dump command output of the RedisBloom module, such as + * BF.SCANDUMP and + * CF.SCANDUMP. * * @author Yordan Tsintsov * @since 7.7 */ -public final class BfScanDumpValueParser implements ComplexDataParser { +public final class ScanDumpValueParser implements ComplexDataParser { - public static final BfScanDumpValueParser INSTANCE = new BfScanDumpValueParser(); + public static final ScanDumpValueParser INSTANCE = new ScanDumpValueParser(); - private BfScanDumpValueParser() { + private ScanDumpValueParser() { } @Override - public BfScanDumpValue parse(ComplexData data) { + public ScanDumpValue parse(ComplexData data) { if (data == null) { - throw new IllegalArgumentException("Failed parsing BF.SCANDUMP: data must not be null"); + throw new IllegalArgumentException("Failed parsing SCANDUMP: data must not be null"); } List raw = data.getDynamicList(); if (raw == null || raw.size() != 2) { - throw new IllegalArgumentException("Failed parsing BF.SCANDUMP: data must be a list of two elements"); + throw new IllegalArgumentException("Failed parsing SCANDUMP: data must be a list of two elements"); } long iterator = ((Number) raw.get(0)).longValue(); ByteBuffer dataByteBuffer = (ByteBuffer) raw.get(1); @@ -43,7 +45,7 @@ public BfScanDumpValue parse(ComplexData data) { dataBytes = new byte[dataByteBuffer.remaining()]; dataByteBuffer.get(dataBytes); } - return new BfScanDumpValue(iterator, dataBytes); + return new ScanDumpValue(iterator, dataBytes); } } diff --git a/src/main/java/io/lettuce/core/bf/arguments/BfInsertArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/BfInsertArgs.java similarity index 98% rename from src/main/java/io/lettuce/core/bf/arguments/BfInsertArgs.java rename to src/main/java/io/lettuce/core/probabilistic/arguments/BfInsertArgs.java index 527afab229..85fb1f6da5 100644 --- a/src/main/java/io/lettuce/core/bf/arguments/BfInsertArgs.java +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/BfInsertArgs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf.arguments; +package io.lettuce.core.probabilistic.arguments; import io.lettuce.core.CompositeArgument; import io.lettuce.core.protocol.CommandArgs; diff --git a/src/main/java/io/lettuce/core/bf/arguments/BfReserveArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/BfReserveArgs.java similarity index 97% rename from src/main/java/io/lettuce/core/bf/arguments/BfReserveArgs.java rename to src/main/java/io/lettuce/core/probabilistic/arguments/BfReserveArgs.java index df7e3105ea..b62a86997b 100644 --- a/src/main/java/io/lettuce/core/bf/arguments/BfReserveArgs.java +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/BfReserveArgs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf.arguments; +package io.lettuce.core.probabilistic.arguments; import io.lettuce.core.CompositeArgument; import io.lettuce.core.protocol.CommandArgs; diff --git a/src/main/java/io/lettuce/core/probabilistic/arguments/CfInsertArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/CfInsertArgs.java new file mode 100644 index 0000000000..5adaa1120f --- /dev/null +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/CfInsertArgs.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic.arguments; + +import io.lettuce.core.CompositeArgument; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +/** + * Argument list builder for the Redis CF.INSERT command. + *

+ * {@link CfInsertArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @author Gyumin Hwang + * @since 7.7 + */ +public class CfInsertArgs implements CompositeArgument { + + private Long capacity; + + private boolean noCreate; + + /** + * Builder entry points for {@link CfInsertArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates a new {@link CfInsertArgs} and sets the desired capacity of the filter. + * + * @return a new {@link CfInsertArgs} with capacity configured. + */ + public static CfInsertArgs capacity(long capacity) { + return new CfInsertArgs().capacity(capacity); + } + + /** + * Creates a new {@link CfInsertArgs} and sets the no create flag. + * + * @return a new {@link CfInsertArgs} with no create flag configured. + */ + public static CfInsertArgs noCreate() { + return new CfInsertArgs().noCreate(); + } + + /** + * Creates a new {@link CfInsertArgs} with default settings. + * + * @return a new {@link CfInsertArgs} with default settings. + */ + public static CfInsertArgs defaults() { + return new CfInsertArgs(); + } + + } + + /** + * Set the desired capacity of the filter. + * + * @return {@code this} {@link CfInsertArgs}. + */ + public CfInsertArgs capacity(long capacity) { + this.capacity = capacity; + return this; + } + + /** + * Set the no create flag. + * + * @return {@code this} {@link CfInsertArgs}. + */ + public CfInsertArgs noCreate() { + this.noCreate = true; + return this; + } + + /** + * Set the default settings. + * + * @return {@code this} {@link CfInsertArgs}. + */ + public CfInsertArgs defaults() { + this.capacity = null; + this.noCreate = false; + return this; + } + + @Override + public void build(CommandArgs args) { + + if (capacity != null) { + args.add(CommandKeyword.CAPACITY).add(capacity); + } + if (noCreate) { + args.add(CommandKeyword.NOCREATE); + } + } + +} diff --git a/src/main/java/io/lettuce/core/probabilistic/arguments/CfReserveArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/CfReserveArgs.java new file mode 100644 index 0000000000..556a2d72d3 --- /dev/null +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/CfReserveArgs.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic.arguments; + +import io.lettuce.core.CompositeArgument; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +/** + * Argument list builder for the Redis CF.RESERVE command. + *

+ * {@link CfReserveArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @author Gyumin Hwang + * @since 7.7 + */ +public class CfReserveArgs implements CompositeArgument { + + private Long bucketSize; + + private Long maxIterations; + + private Long expansion; + + /** + * Builder entry points for {@link CfReserveArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates a new {@link CfReserveArgs} and sets the bucket size. + * + * @return a new {@link CfReserveArgs} with bucket size configured. + */ + public static CfReserveArgs bucketSize(long bucketSize) { + return new CfReserveArgs().bucketSize(bucketSize); + } + + /** + * Creates a new {@link CfReserveArgs} and sets the max iterations. + * + * @return a new {@link CfReserveArgs} with max iterations configured. + */ + public static CfReserveArgs maxIterations(long maxIterations) { + return new CfReserveArgs().maxIterations(maxIterations); + } + + /** + * Creates a new {@link CfReserveArgs} and sets the expansion rate of the filter. + * + * @return a new {@link CfReserveArgs} with expansion rate configured. + */ + public static CfReserveArgs expansion(long expansion) { + return new CfReserveArgs().expansion(expansion); + } + + } + + /** + * Sets the bucket size. + * + * @return this + */ + public CfReserveArgs bucketSize(long bucketSize) { + this.bucketSize = bucketSize; + return this; + } + + /** + * Sets the max iterations. + * + * @return this + */ + public CfReserveArgs maxIterations(long maxIterations) { + this.maxIterations = maxIterations; + return this; + } + + /** + * Sets the expansion rate of the filter. + * + * @return this + */ + public CfReserveArgs expansion(long expansion) { + this.expansion = expansion; + return this; + } + + @Override + public void build(CommandArgs args) { + + if (bucketSize != null) { + args.add(CommandKeyword.BUCKETSIZE).add(bucketSize); + } + if (maxIterations != null) { + args.add(CommandKeyword.MAXITERATIONS).add(maxIterations); + } + if (expansion != null) { + args.add(CommandKeyword.EXPANSION).add(expansion); + } + } + +} diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index 7d9f604018..2465b77b80 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -71,7 +71,7 @@ CAS, EF, ELE, SETATTR, M, NOQUANT, BIN, Q8, FILTER, FILTER_EF( // INCREX keywords BYFLOAT, BYINT, ENX, LBOUND, SATURATE, UBOUND, - CAPACITY, SIZE, FILTERS, ITEMS, EXPANSION, NONSCALING, ERROR, NOCREATE; + CAPACITY, SIZE, FILTERS, ITEMS, EXPANSION, NONSCALING, ERROR, NOCREATE, BUCKETSIZE, MAXITERATIONS; public final byte[] bytes; diff --git a/src/main/java/io/lettuce/core/protocol/CommandType.java b/src/main/java/io/lettuce/core/protocol/CommandType.java index 56d0313c16..658f76b37d 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandType.java +++ b/src/main/java/io/lettuce/core/protocol/CommandType.java @@ -133,6 +133,11 @@ public enum CommandType implements ProtocolKeyword { BF_ADD("BF.ADD"), BF_CARD("BF.CARD"), BF_EXISTS("BF.EXISTS"), BF_INFO("BF.INFO"), BF_INSERT("BF.INSERT"), BF_LOADCHUNK( "BF.LOADCHUNK"), BF_MADD("BF.MADD"), BF_MEXISTS("BF.MEXISTS"), BF_RESERVE("BF.RESERVE"), BF_SCANDUMP("BF.SCANDUMP"), + // Cuckoo Filter + CF_ADD("CF.ADD"), CF_ADDNX("CF.ADDNX"), CF_COUNT("CF.COUNT"), CF_DEL("CF.DEL"), CF_EXISTS("CF.EXISTS"), CF_INFO( + "CF.INFO"), CF_INSERT("CF.INSERT"), CF_INSERTNX("CF.INSERTNX"), CF_LOADCHUNK( + "CF.LOADCHUNK"), CF_MEXISTS("CF.MEXISTS"), CF_RESERVE("CF.RESERVE"), CF_SCANDUMP("CF.SCANDUMP"), + // Others TIME, WAIT, diff --git a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java index 795689d4ce..23d486cc8d 100644 --- a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java +++ b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java @@ -84,7 +84,9 @@ enum CommandName { ZCARD, ZCOUNT, ZLEXCOUNT, ZRANGE, // ZRANDMEMBER, ZRANGEBYLEX, ZRANGEBYSCORE, ZRANK, ZREVRANGE, ZREVRANGEBYLEX, ZREVRANGEBYSCORE, ZREVRANK, ZSCAN, ZSCORE, // RediSearch (keyless read-only) - FT_AGGREGATE, FT_SEARCH, FT_HYBRID, FT_EXPLAIN, FT_SPELLCHECK, FT_TAGVALS, FT_SYNDUMP, FT_LIST, FT_DICTDUMP, BF_CARD, BF_EXISTS, BF_INFO, BF_MEXISTS, + FT_AGGREGATE, FT_SEARCH, FT_HYBRID, FT_EXPLAIN, FT_SPELLCHECK, FT_TAGVALS, FT_SYNDUMP, FT_LIST, FT_DICTDUMP, BF_CARD, BF_EXISTS, BF_INFO, BF_MEXISTS, BF_SCANDUMP, + // Cuckoo Filter read-only commands + CF_EXISTS, CF_MEXISTS, CF_COUNT, CF_INFO, CF_SCANDUMP, } /** diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt index 6616121639..3c68bbb5d1 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt @@ -8,10 +8,10 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import kotlinx.coroutines.flow.Flow -import io.lettuce.core.bf.BfInfoValue -import io.lettuce.core.bf.arguments.BfInsertArgs -import io.lettuce.core.bf.arguments.BfReserveArgs -import io.lettuce.core.bf.BfScanDumpValue +import io.lettuce.core.probabilistic.BfInfoValue +import io.lettuce.core.probabilistic.arguments.BfInsertArgs +import io.lettuce.core.probabilistic.arguments.BfReserveArgs +import io.lettuce.core.probabilistic.ScanDumpValue /** * Coroutine executed commands for Bloom Filter. @@ -166,9 +166,9 @@ interface RedisBloomFilterCoroutinesCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - suspend fun bfScanDump(key: K, iterator: Long): BfScanDumpValue? + suspend fun bfScanDump(key: K, iterator: Long): ScanDumpValue? } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt index 26de7a8fa4..35302b2d84 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt @@ -8,10 +8,10 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import io.lettuce.core.api.reactive.RedisBloomFilterReactiveCommands -import io.lettuce.core.bf.BfInfoValue -import io.lettuce.core.bf.BfScanDumpValue -import io.lettuce.core.bf.arguments.BfInsertArgs -import io.lettuce.core.bf.arguments.BfReserveArgs +import io.lettuce.core.probabilistic.BfInfoValue +import io.lettuce.core.probabilistic.ScanDumpValue +import io.lettuce.core.probabilistic.arguments.BfInsertArgs +import io.lettuce.core.probabilistic.arguments.BfReserveArgs import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.reactive.awaitFirstOrNull @@ -77,7 +77,7 @@ internal class RedisBloomFilterCoroutinesCommandsImpl( ): String? = ops.bfReserve(key, errorRate, capacity, reserveArgs).awaitFirstOrNull() - override suspend fun bfScanDump(key: K, iterator: Long): BfScanDumpValue? = + override suspend fun bfScanDump(key: K, iterator: Long): ScanDumpValue? = ops.bfScanDump(key, iterator).awaitFirstOrNull() } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt index 78146215d1..f9f0a10035 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt @@ -52,7 +52,8 @@ interface RedisCoroutinesCommands : RedisJsonCoroutinesCommands, RedisVectorSetCoroutinesCommands, RedisArrayCoroutinesCommands, - RedisBloomFilterCoroutinesCommands { + RedisBloomFilterCoroutinesCommands, + RedisCuckooFilterCoroutinesCommands { /** * Authenticate to the server. diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt index b29053c621..6b1b4e81ac 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt @@ -58,7 +58,8 @@ open class RedisCoroutinesCommandsImpl( RedisVectorSetCoroutinesCommands by RedisVectorSetCoroutinesCommandsImpl(ops), RediSearchCoroutinesCommands by RediSearchCoroutinesCommandsImpl(ops), RedisArrayCoroutinesCommands by RedisArrayCoroutinesCommandsImpl(ops), - RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops) { + RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops), + RedisCuckooFilterCoroutinesCommands by RedisCuckooFilterCoroutinesCommandsImpl(ops) { /** diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt new file mode 100644 index 0000000000..9d31145dc7 --- /dev/null +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.coroutines + +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import io.lettuce.core.probabilistic.CfInfoValue +import io.lettuce.core.probabilistic.ScanDumpValue +import io.lettuce.core.probabilistic.arguments.CfInsertArgs +import io.lettuce.core.probabilistic.arguments.CfReserveArgs + +/** + * Coroutine executed commands for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateKotlinCoroutinesApi + */ +@ExperimentalLettuceCoroutinesApi +interface RedisCuckooFilterCoroutinesCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply `OK` if `CF.RESERVE` was executed correctly. + */ + suspend fun cfReserve(key: K, capacity: Long): String? + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply `OK` if `CF.RESERVE` was executed correctly. + */ + suspend fun cfReserve(key: K, capacity: Long, args: CfReserveArgs): String? + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item was added, `false` if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. + */ + suspend fun cfAdd(key: K, value: V): Boolean? + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item was added, `false` if it was already in the filter. + */ + suspend fun cfAddNx(key: K, value: V): Boolean? + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, value: V): List + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, args: CfInsertArgs, value: V): List + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. + */ + suspend fun cfInsertNx(key: K, value: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. + */ + suspend fun cfInsertNx(key: K, args: CfInsertArgs, value: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. + */ + suspend fun cfInsertNx(key: K, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. + */ + suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item may exist in the filter, `false` if it definitely does not. + */ + suspend fun cfExists(key: K, value: V): Boolean? + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List of `true` or `false` where `true` means that, with high probability, the item was already added to + * the filter, and `false` means that the item was definitely not added to the filter. + */ + suspend fun cfMExists(key: K, vararg values: V): List + + /** + * Delete an item from the Cuckoo Filter. + * + * **Warning:** Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item was deleted, `false` if the item was not found. + */ + suspend fun cfDel(key: K, value: V): Boolean? + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + suspend fun cfCount(key: K, value: V): Long? + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + suspend fun cfScanDump(key: K, cursor: Long): ScanDumpValue? + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply `OK` if `CF.LOADCHUNK` was executed correctly. + */ + suspend fun cfLoadChunk(key: K, cursor: Long, data: ByteArray): String? + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + suspend fun cfInfo(key: K): CfInfoValue? + +} diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt new file mode 100644 index 0000000000..0832504c1a --- /dev/null +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.coroutines + +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import io.lettuce.core.api.reactive.RedisCuckooFilterReactiveCommands +import io.lettuce.core.probabilistic.CfInfoValue +import io.lettuce.core.probabilistic.ScanDumpValue +import io.lettuce.core.probabilistic.arguments.CfInsertArgs +import io.lettuce.core.probabilistic.arguments.CfReserveArgs +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.asFlow +import kotlinx.coroutines.reactive.awaitFirstOrNull + +/** + * Coroutine executed commands (based on reactive commands) for Cuckoo Filter commands. + * + * @param Key type. + * @param Value type. + * @author Gyumin Hwang + * @since 7.7 + */ +@ExperimentalLettuceCoroutinesApi +internal class RedisCuckooFilterCoroutinesCommandsImpl( + internal val ops: RedisCuckooFilterReactiveCommands +) : RedisCuckooFilterCoroutinesCommands { + + override suspend fun cfReserve(key: K, capacity: Long): String? = + ops.cfReserve(key, capacity).awaitFirstOrNull() + + override suspend fun cfReserve(key: K, capacity: Long, args: CfReserveArgs): String? = + ops.cfReserve(key, capacity, args).awaitFirstOrNull() + + override suspend fun cfAdd(key: K, value: V): Boolean? = + ops.cfAdd(key, value).awaitFirstOrNull() + + override suspend fun cfAddNx(key: K, value: V): Boolean? = + ops.cfAddNx(key, value).awaitFirstOrNull() + + override suspend fun cfInsert(key: K, value: V): List = + ops.cfInsert(key, value).asFlow().toList() + + override suspend fun cfInsert(key: K, args: CfInsertArgs, value: V): List = + ops.cfInsert(key, args, value).asFlow().toList() + + override suspend fun cfInsert(key: K, vararg values: V): List = + ops.cfInsert(key, *values).asFlow().toList() + + override suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List = + ops.cfInsert(key, args, *values).asFlow().toList() + + override suspend fun cfInsertNx(key: K, value: V): List = + ops.cfInsertNx(key, value).asFlow().toList() + + override suspend fun cfInsertNx(key: K, args: CfInsertArgs, value: V): List = + ops.cfInsertNx(key, args, value).asFlow().toList() + + override suspend fun cfInsertNx(key: K, vararg values: V): List = + ops.cfInsertNx(key, *values).asFlow().toList() + + override suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List = + ops.cfInsertNx(key, args, *values).asFlow().toList() + + override suspend fun cfExists(key: K, value: V): Boolean? = + ops.cfExists(key, value).awaitFirstOrNull() + + override suspend fun cfMExists(key: K, vararg values: V): List = + ops.cfMExists(key, *values).asFlow().toList() + + override suspend fun cfDel(key: K, value: V): Boolean? = + ops.cfDel(key, value).awaitFirstOrNull() + + override suspend fun cfCount(key: K, value: V): Long? = + ops.cfCount(key, value).awaitFirstOrNull() + + override suspend fun cfScanDump(key: K, cursor: Long): ScanDumpValue? = + ops.cfScanDump(key, cursor).awaitFirstOrNull() + + override suspend fun cfLoadChunk(key: K, cursor: Long, data: ByteArray): String? = + ops.cfLoadChunk(key, cursor, data).awaitFirstOrNull() + + override suspend fun cfInfo(key: K): CfInfoValue? = + ops.cfInfo(key).awaitFirstOrNull() + +} diff --git a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt index c6fc638a0f..39c1e35b4d 100644 --- a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt @@ -44,7 +44,9 @@ interface RedisClusterCoroutinesCommands : RedisSetCoroutinesCommands, RedisSortedSetCoroutinesCommands, RedisStreamCoroutinesCommands, - RedisStringCoroutinesCommands { + RedisStringCoroutinesCommands, + RedisBloomFilterCoroutinesCommands, + RedisCuckooFilterCoroutinesCommands { /** * Authenticate to the server. diff --git a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt index 45cb8e5b96..925be57333 100644 --- a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt @@ -53,7 +53,8 @@ internal class RedisClusterCoroutinesCommandsImpl( RedisSortedSetCoroutinesCommands by RedisSortedSetCoroutinesCommandsImpl(ops), RedisStreamCoroutinesCommands by RedisStreamCoroutinesCommandsImpl(ops), RedisStringCoroutinesCommands by RedisStringCoroutinesCommandsImpl(ops), - RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops) { + RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops), + RedisCuckooFilterCoroutinesCommands by RedisCuckooFilterCoroutinesCommandsImpl(ops) { /** * Authenticate to the server. diff --git a/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java index fc7a94b890..21c21f754f 100644 --- a/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java @@ -8,10 +8,10 @@ import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.ScanDumpValue; /** * ${intent} for Bloom Filter. @@ -164,8 +164,8 @@ public interface RedisBloomFilterCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - BfScanDumpValue bfScanDump(K key, long iterator); + ScanDumpValue bfScanDump(K key, long iterator); } diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java new file mode 100644 index 0000000000..fbeafb49b6 --- /dev/null +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api; + +import java.util.List; + +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; + +/** + * ${intent} for Cuckoo Filter. + * + * @author Gyumin Hwang + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + */ +public interface RedisCuckooFilterCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. + */ + Boolean cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Boolean cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, CfInsertArgs args, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V value); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Boolean cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + List cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Boolean cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Long cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return ScanDumpValue the scan dump value. + */ + ScanDumpValue cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + String cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + CfInfoValue cfInfo(K key); + +} diff --git a/src/test/java/io/lettuce/apigenerator/Constants.java b/src/test/java/io/lettuce/apigenerator/Constants.java index 5f00063b0d..32d7300cfe 100644 --- a/src/test/java/io/lettuce/apigenerator/Constants.java +++ b/src/test/java/io/lettuce/apigenerator/Constants.java @@ -32,7 +32,7 @@ class Constants { "RedisScriptingCommands", "RedisSentinelCommands", "RedisServerCommands", "RedisSetCommands", "RedisSortedSetCommands", "RedisStreamCommands", "RedisStringCommands", "RedisTransactionalCommands", "RedisJsonCommands", "RedisVectorSetCommands", "RediSearchCommands", "RedisArrayCommands", - "RedisBloomFilterCommands" }; + "RedisBloomFilterCommands", "RedisCuckooFilterCommands" }; public static final File TEMPLATES = new File("src/main/templates"); diff --git a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java index 21097102be..415ab58d9e 100644 --- a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java +++ b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java @@ -60,7 +60,7 @@ public class CreateReactiveApi { public static Set FORCE_FLUX_RESULT = LettuceSets.unmodifiableSet("eval", "evalsha", "evalReadOnly", "evalshaReadOnly", "fcall", "fcallReadOnly", "dispatch"); - public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield"); + public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield", "bfInsert", "bfMAdd"); private static final Map RESULT_SPEC; diff --git a/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java index 1ca580c29d..6c6791a272 100644 --- a/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java @@ -6,10 +6,10 @@ */ package io.lettuce.core; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.core.codec.StringCodec; import io.lettuce.core.protocol.Command; import io.netty.buffer.ByteBuf; @@ -179,7 +179,7 @@ void shouldCorrectlyConstructBfReserveCommandWithArgs() { @Test void shouldCorrectlyConstructBfScanDumpCommand() { - Command command = builder.bfScanDump(MY_KEY, 0); + Command command = builder.bfScanDump(MY_KEY, 0); ByteBuf buff = Unpooled.buffer(); command.encode(buff); diff --git a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java new file mode 100644 index 0000000000..56932a5b53 --- /dev/null +++ b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core; + +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; +import io.lettuce.core.codec.StringCodec; +import io.lettuce.core.protocol.Command; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static io.lettuce.TestTags.UNIT_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link RedisCuckooFilterCommandBuilder}. + * + * @author Gyumin Hwang + */ +@Tag(UNIT_TEST) +class RedisCuckooFilterCommandBuilderUnitTests { + + private static final String MY_KEY = "books:name"; + + private static final String MY_VALUE = "Dune"; + + private static final String MY_VALUE_2 = "Dune Messiah"; + + private final RedisCuckooFilterCommandBuilder builder = new RedisCuckooFilterCommandBuilder<>( + StringCodec.UTF8); + + @Test + void shouldCorrectlyConstructCfReserveCommand() { + Command command = builder.cfReserve(MY_KEY, 100); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$10\r\nCF.RESERVE\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$3\r\n100\r\n"); + } + + @Test + void shouldCorrectlyConstructCfReserveCommandWithArgs() { + CfReserveArgs reserveArgs = CfReserveArgs.Builder.bucketSize(4).maxIterations(10).expansion(2); + Command command = builder.cfReserve(MY_KEY, 100, reserveArgs); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo( + "*9\r\n" + "$10\r\nCF.RESERVE\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$3\r\n100\r\n" + "$10\r\nBUCKETSIZE\r\n" + + "$1\r\n4\r\n" + "$13\r\nMAXITERATIONS\r\n" + "$2\r\n10\r\n" + "$9\r\nEXPANSION\r\n" + "$1\r\n2\r\n"); + } + + @Test + void shouldCorrectlyConstructCfAddCommand() { + Command command = builder.cfAdd(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$6\r\nCF.ADD\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfAddNxCommand() { + Command command = builder.cfAddNx(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$8\r\nCF.ADDNX\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertSingleValueCommand() { + Command> command = builder.cfInsert(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommand() { + Command> command = builder.cfInsert(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommandWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100).noCreate(); + Command> command = builder.cfInsert(MY_KEY, insertArgs, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*7\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$8\r\nCAPACITY\r\n" + + "$3\r\n100\r\n" + "$8\r\nNOCREATE\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommandWithVarargs() { + Command> command = builder.cfInsert(MY_KEY, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*5\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommandVarargWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + Command> command = builder.cfInsert(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*7\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$8\r\nCAPACITY\r\n" + + "$3\r\n100\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxSingleValueCommand() { + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxCommand() { + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxCommandWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(200).noCreate(); + Command> command = builder.cfInsertNx(MY_KEY, insertArgs, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*7\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$8\r\nCAPACITY\r\n" + + "$3\r\n200\r\n" + "$8\r\nNOCREATE\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxCommandWithVarargs() { + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*5\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfExistsCommand() { + Command command = builder.cfExists(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$9\r\nCF.EXISTS\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfMExistsCommand() { + Command> command = builder.cfMExists(MY_KEY, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$10\r\nCF.MEXISTS\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfDelCommand() { + Command command = builder.cfDel(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$6\r\nCF.DEL\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfCountCommand() { + Command command = builder.cfCount(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$8\r\nCF.COUNT\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfScanDumpCommand() { + Command command = builder.cfScanDump(MY_KEY, 0); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$11\r\nCF.SCANDUMP\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$1\r\n0\r\n"); + } + + @Test + void shouldCorrectlyConstructCfLoadChunkCommand() { + byte[] valueBytes = MY_VALUE.getBytes(); + Command command = builder.cfLoadChunk(MY_KEY, 0, valueBytes); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$12\r\nCF.LOADCHUNK\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$1\r\n0\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInfoCommand() { + Command command = builder.cfInfo(MY_KEY); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*2\r\n" + "$7\r\nCF.INFO\r\n" + "$10\r\n" + MY_KEY + "\r\n"); + } + +} diff --git a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java index c08044ef7b..0c099147f5 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java @@ -20,7 +20,7 @@ class ClusterReadOnlyCommandsUnitTests { @Test void testCount() { - assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(117); + assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(123); } @Test diff --git a/src/test/java/io/lettuce/core/probabilistic/CfInfoValueParserUnitTests.java b/src/test/java/io/lettuce/core/probabilistic/CfInfoValueParserUnitTests.java new file mode 100644 index 0000000000..86b949254e --- /dev/null +++ b/src/test/java/io/lettuce/core/probabilistic/CfInfoValueParserUnitTests.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; + +import io.lettuce.core.output.ComplexData; + +/** + * Unit tests for {@link CfInfoValueParser}. + * + * @author HwangRock + * @since 7.7 + */ +class CfInfoValueParserUnitTests { + + private final CfInfoValueParser parser = CfInfoValueParser.INSTANCE; + + // --------------------------------------------------------------------------- + // Helper: build a ComplexData backed by a flat key-value list (RESP2 array) + // --------------------------------------------------------------------------- + private static ComplexData buildMapData(Object... pairs) { + // Use ArrayComplexData via package-accessible test stub instead. + // We need to produce a ComplexData whose getDynamicMap() returns the pairs. + // We'll use a simple anonymous subclass to avoid package-private visibility issues. + return new ComplexData() { + + @Override + public void storeObject(Object value) { + // not needed + } + + @Override + public java.util.Map getDynamicMap() { + java.util.LinkedHashMap map = new java.util.LinkedHashMap<>(); + for (int i = 0; i < pairs.length - 1; i += 2) { + map.put(pairs[i], pairs[i + 1]); + } + return map; + } + + }; + } + + private static ByteBuffer buf(String s) { + return ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)); + } + + // --------------------------------------------------------------------------- + // INSTANCE singleton + // --------------------------------------------------------------------------- + + @Test + void instanceIsSingleton() { + assertThat(CfInfoValueParser.INSTANCE).isSameAs(CfInfoValueParser.INSTANCE); + } + + // --------------------------------------------------------------------------- + // null guard + // --------------------------------------------------------------------------- + + @Test + void parseNullThrows() { + assertThatThrownBy(() -> parser.parse(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("CF.INFO"); + } + + // --------------------------------------------------------------------------- + // All 8 fields parsed correctly + // --------------------------------------------------------------------------- + + @Test + void parseAllEightFields() { + ComplexData data = buildMapData(buf("Size"), 100L, buf("Number of buckets"), 200L, buf("Number of filters"), 1L, + buf("Number of items inserted"), 50L, buf("Number of items deleted"), 5L, buf("Bucket size"), 2L, + buf("Expansion rate"), 1L, buf("Max iterations"), 20L); + + CfInfoValue value = parser.parse(data); + + assertThat(value.getSize()).isEqualTo(100L); + assertThat(value.getNumberOfBuckets()).isEqualTo(200L); + assertThat(value.getNumberOfFilters()).isEqualTo(1L); + assertThat(value.getNumberOfItemsInserted()).isEqualTo(50L); + assertThat(value.getNumberOfItemsDeleted()).isEqualTo(5L); + assertThat(value.getBucketSize()).isEqualTo(2L); + assertThat(value.getExpansionRate()).isEqualTo(1L); + assertThat(value.getMaxIterations()).isEqualTo(20L); + } + + // --------------------------------------------------------------------------- + // Plural field names — "Number of filters" and "Max iterations" + // --------------------------------------------------------------------------- + + @Test + void numberOfFiltersIsPlural() { + ComplexData data = buildMapData(buf("Number of filters"), 3L); + CfInfoValue value = parser.parse(data); + assertThat(value.getNumberOfFilters()).isEqualTo(3L); + } + + @Test + void maxIterationsIsPlural() { + ComplexData data = buildMapData(buf("Max iterations"), 15L); + CfInfoValue value = parser.parse(data); + assertThat(value.getMaxIterations()).isEqualTo(15L); + } + + // --------------------------------------------------------------------------- + // rawInfo preserved + // --------------------------------------------------------------------------- + + @Test + void rawInfoIsPreserved() { + ComplexData data = buildMapData(buf("Size"), 42L); + CfInfoValue value = parser.parse(data); + assertThat(value.getRawInfo()).containsKey("Size"); + assertThat(value.getRawInfo().get("Size")).isEqualTo(42L); + } + + // --------------------------------------------------------------------------- + // Missing fields yield null (not NPE) + // --------------------------------------------------------------------------- + + @Test + void missingFieldsYieldNull() { + ComplexData data = buildMapData(buf("Size"), 10L); + CfInfoValue value = parser.parse(data); + assertThat(value.getNumberOfBuckets()).isNull(); + assertThat(value.getNumberOfFilters()).isNull(); + assertThat(value.getNumberOfItemsInserted()).isNull(); + assertThat(value.getNumberOfItemsDeleted()).isNull(); + assertThat(value.getBucketSize()).isNull(); + assertThat(value.getExpansionRate()).isNull(); + assertThat(value.getMaxIterations()).isNull(); + } + + // --------------------------------------------------------------------------- + // Empty map — all fields null, no exception + // --------------------------------------------------------------------------- + + @Test + void emptyMapDoesNotThrow() { + ComplexData data = buildMapData(); + CfInfoValue value = parser.parse(data); + assertThat(value).isNotNull(); + assertThat(value.getSize()).isNull(); + assertThat(value.getRawInfo()).isEmpty(); + } + +} diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterClusterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterClusterIntegrationTests.java similarity index 95% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterClusterIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterClusterIntegrationTests.java index efc95a7e7a..c3ffd28d74 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterClusterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterClusterIntegrationTests.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterIntegrationTests.java similarity index 96% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterIntegrationTests.java index 2a3a662081..6868898245 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterIntegrationTests.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; import java.util.List; import io.lettuce.core.RedisCommandExecutionException; import io.lettuce.core.api.sync.RedisCommands; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.test.LettuceExtension; import io.lettuce.test.condition.EnabledOnCommand; import org.junit.jupiter.api.*; @@ -212,7 +212,7 @@ void bfScanDumpAndLoadChunk() { long iterator = 0; while (true) { - BfScanDumpValue chunkData = redis.bfScanDump("bloom-dump", iterator); + ScanDumpValue chunkData = redis.bfScanDump("bloom-dump", iterator); iterator = chunkData.getIterator(); if (iterator == 0L) { break; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterReactiveIntegrationTests.java similarity index 93% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterReactiveIntegrationTests.java index a8661f8595..67169faf49 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterReactiveIntegrationTests.java @@ -4,15 +4,15 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; import io.lettuce.core.Value; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.reactive.RedisReactiveCommands; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.test.ReactiveSyncInvocationHandler; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterResp2IntegrationTests.java similarity index 96% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterResp2IntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterResp2IntegrationTests.java index 6e140eefd2..c1522de45d 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterResp2IntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterResp2IntegrationTests.java @@ -4,7 +4,7 @@ * * Licensed under the MIT License. */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient; diff --git a/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterClusterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterClusterIntegrationTests.java new file mode 100644 index 0000000000..5c0078e9c8 --- /dev/null +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterClusterIntegrationTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic; + +import javax.inject.Inject; + +import org.junit.jupiter.api.Tag; + +import io.lettuce.core.cluster.ClusterTestUtil; +import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; + +import static io.lettuce.TestTags.INTEGRATION_TEST; + +/** + * Integration tests for Redis Cuckoo Filter commands using Redis Cluster. + * + * @author Gyumin Hwang + * @since 7.7 + */ +@Tag(INTEGRATION_TEST) +public class RedisCuckooFilterClusterIntegrationTests extends RedisCuckooFilterIntegrationTests { + + @Inject + RedisCuckooFilterClusterIntegrationTests(StatefulRedisClusterConnection connection) { + super(ClusterTestUtil.redisCommandsOverCluster(connection)); + } + +} diff --git a/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterIntegrationTests.java new file mode 100644 index 0000000000..30ba741957 --- /dev/null +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterIntegrationTests.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic; + +import javax.inject.Inject; +import java.util.List; + +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; +import io.lettuce.test.LettuceExtension; +import io.lettuce.test.condition.EnabledOnCommand; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; + +import static io.lettuce.TestTags.INTEGRATION_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link io.lettuce.core.api.sync.RedisCuckooFilterCommands}. + * + * @author Gyumin Hwang + */ +@Tag(INTEGRATION_TEST) +@ExtendWith(LettuceExtension.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@EnabledOnCommand("CF.ADD") +public class RedisCuckooFilterIntegrationTests { + + private static final String MY_KEY = "books:name"; + + private static final String MY_VALUE = "Dune"; + + private static final String MY_VALUE_2 = "Dune Messiah"; + + protected final RedisCommands redis; + + @Inject + protected RedisCuckooFilterIntegrationTests(RedisCommands redis) { + this.redis = redis; + } + + @BeforeEach + void prepare() { + redis.flushall(); + } + + @AfterAll + void teardown() { + redis.flushall(); + } + + @Test + void cfReserve() { + String result = redis.cfReserve(MY_KEY, 100); + + assertThat(result).isEqualTo("OK"); + } + + @Test + void cfReserveWithArgs() { + String result = redis.cfReserve(MY_KEY, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)); + + assertThat(result).isEqualTo("OK"); + } + + @Test + void cfAdd() { + Boolean result = redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(result).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + } + + @Test + void cfAddNx() { + assertThat(redis.cfAddNx(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfAddNx(MY_KEY, MY_VALUE)).isFalse(); + } + + @Test + void cfInsert() { + List result = redis.cfInsert(MY_KEY, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(true, true); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE_2)).isTrue(); + } + + @Test + void cfInsertWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + List result = redis.cfInsert(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(true, true); + } + + @Test + void cfInsertSingleValue() { + List result = redis.cfInsert(MY_KEY, MY_VALUE); + + assertThat(result).containsExactly(true); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + } + + @Test + void cfInsertNx() { + redis.cfAdd(MY_KEY, MY_VALUE); + + List result = redis.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(0L, 1L); + } + + @Test + void cfInsertNxWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + redis.cfAdd(MY_KEY, MY_VALUE); + + List result = redis.cfInsertNx(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(0L, 1L); + } + + @Test + void cfInsertNxSingleValue() { + List result = redis.cfInsertNx(MY_KEY, MY_VALUE); + + assertThat(result).containsExactly(1L); + } + + @Test + void cfExists() { + redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE_2)).isFalse(); + } + + @Test + void cfMExists() { + redis.cfAdd(MY_KEY, MY_VALUE); + + List result = redis.cfMExists(MY_KEY, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(true, false); + } + + @Test + void cfDel() { + redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(redis.cfDel(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isFalse(); + } + + @Test + void cfCount() { + redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(redis.cfCount(MY_KEY, MY_VALUE)).isEqualTo(1L); + assertThat(redis.cfCount(MY_KEY, MY_VALUE_2)).isEqualTo(0L); + } + + @Test + void cfInfo() { + redis.cfAdd(MY_KEY, MY_VALUE); + + CfInfoValue result = redis.cfInfo(MY_KEY); + + assertThat(result).isNotNull(); + assertThat(result.getNumberOfItemsInserted()).isEqualTo(1L); + assertThat(result.getRawInfo()).containsKey("Number of filters"); + assertThat(result.getRawInfo()).containsKey("Max iterations"); + } + + /** + * Verifies that CF.INSERTNX returns {@code 0L} ("already exists") which is distinct from {@code -1L} ("filter full"). + */ + @Test + void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { + String key = "cf:full:insertnx"; + redis.cfReserve(key, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)); + + redis.cfAdd(key, "known"); + + List existing = redis.cfInsertNx(key, "known"); + assertThat(existing).hasSize(1); + assertThat(existing.get(0)).isEqualTo(0L); + } + + /** + * Verifies that CF.INSERT returns {@code false} when the filter is full (server returns {@code -1}). + * + *

+ * BUCKETSIZE 1 EXPANSION 0: same value fills at most 2 slots, so inserting the same value 6 times causes filter-full + * conditions. RESP2 and RESP3 both produce {@code false} for filter-full via + * {@link io.lettuce.core.output.BooleanListOutput}. + */ + @Test + void cfInsertReturnsFalseWhenFilterIsFull() { + String key = "cf:full:insert"; + redis.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)); + + List result = redis.cfInsert(key, "W", "W", "W", "W", "W", "W"); + + assertThat(result).hasSize(6); + assertThat(result).startsWith(true, true); + for (int i = 2; i < result.size(); i++) { + assertThat(result.get(i)).as("result[%d] must be false (filter full)", i).isEqualTo(Boolean.FALSE); + } + } + + @Test + @Timeout(2) + void cfScanDumpAndLoadChunk() { + redis.cfAdd("cuckoo-dump", MY_VALUE); + + long cursor = 0; + while (true) { + ScanDumpValue chunkData = redis.cfScanDump("cuckoo-dump", cursor); + cursor = chunkData.getIterator(); + if (cursor == 0L) { + break; + } + assertThat(redis.cfLoadChunk("cuckoo-load", cursor, chunkData.getData())).isEqualTo("OK"); + } + + assertThat(redis.cfExists("cuckoo-load", MY_VALUE)).isTrue(); + } + +} diff --git a/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterReactiveIntegrationTests.java new file mode 100644 index 0000000000..a423d4347f --- /dev/null +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterReactiveIntegrationTests.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.probabilistic; + +import javax.inject.Inject; +import java.util.List; + +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; +import io.lettuce.test.ReactiveSyncInvocationHandler; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import reactor.test.StepVerifier; + +import static io.lettuce.TestTags.INTEGRATION_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Reactive integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} + * routing every call through the reactive API via {@link ReactiveSyncInvocationHandler}. + * + *

+ * Overrides that verify reactive-specific streaming behaviour (Flux) for cfInsert ({@code Flux}) and cfInsertNx + * ({@code Flux}). + * + * @author Gyumin Hwang + * @since 7.7 + */ +@Tag(INTEGRATION_TEST) +public class RedisCuckooFilterReactiveIntegrationTests extends RedisCuckooFilterIntegrationTests { + + private final RedisReactiveCommands reactive; + + @Inject + public RedisCuckooFilterReactiveIntegrationTests(StatefulRedisConnection connection) { + super(ReactiveSyncInvocationHandler.sync(connection)); + this.reactive = connection.reactive(); + } + + @Test + @Override + void cfInsert() { + StepVerifier.create(reactive.cfInsert("books:name", "Dune", "Dune Messiah")).expectNext(Boolean.TRUE) + .expectNext(Boolean.TRUE).verifyComplete(); + } + + @Test + @Override + void cfInsertWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + + StepVerifier.create(reactive.cfInsert("books:name", insertArgs, "Dune", "Dune Messiah")).expectNext(Boolean.TRUE) + .expectNext(Boolean.TRUE).verifyComplete(); + } + + @Test + @Override + void cfInsertSingleValue() { + StepVerifier.create(reactive.cfInsert("books:name", "Dune")).expectNext(Boolean.TRUE).verifyComplete(); + } + + @Test + @Override + void cfInsertNx() { + reactive.cfAdd("books:name", "Dune").block(); + + StepVerifier.create(reactive.cfInsertNx("books:name", "Dune", "Dune Messiah")).expectNext(0L).expectNext(1L) + .verifyComplete(); + } + + @Test + @Override + void cfInsertNxWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + reactive.cfAdd("books:name", "Dune").block(); + + StepVerifier.create(reactive.cfInsertNx("books:name", insertArgs, "Dune", "Dune Messiah")).expectNext(0L).expectNext(1L) + .verifyComplete(); + } + + @Test + @Override + void cfInsertNxSingleValue() { + StepVerifier.create(reactive.cfInsertNx("books:name", "Dune")).expectNext(1L).verifyComplete(); + } + + /** + * Reactive counterpart of {@link RedisCuckooFilterIntegrationTests#cfInsertNxDistinguishesAlreadyExistsFromFilterFull()}. + * + *

+ * Verifies that CF.INSERTNX emits {@code 0L} for "already exists", distinct from {@code -1L} for "filter full". + */ + @Test + @Override + void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { + String key = "cf:full:reactive:insertnx"; + reactive.cfReserve(key, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)).block(); + + reactive.cfAdd(key, "known").block(); + + List existing = reactive.cfInsertNx(key, "known").collectList().block(); + assertThat(existing).isNotNull().hasSize(1); + assertThat(existing.get(0)).isEqualTo(0L); + } + +} diff --git a/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterResp2IntegrationTests.java new file mode 100644 index 0000000000..dbaff1b58f --- /dev/null +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterResp2IntegrationTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2026, Redis Ltd. and Contributors + * All rights reserved. + * + * Licensed under the MIT License. + */ +package io.lettuce.core.probabilistic; + +import io.lettuce.core.ClientOptions; +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.protocol.ProtocolVersion; +import org.junit.jupiter.api.Tag; + +import javax.inject.Inject; + +import static io.lettuce.TestTags.INTEGRATION_TEST; + +/** + * RESP2 integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} + * using RESP2. + * + *

+ * RESP2 and RESP3 behave identically for CF.INSERT (filter-full = {@code false}) and CF.INSERTNX (filter-full = {@code -1L}), + * so no protocol-specific overrides are needed. The parent-class tests cover both protocols. + * + * @author Gyumin Hwang + * @since 7.7 + */ +@Tag(INTEGRATION_TEST) +public class RedisCuckooFilterResp2IntegrationTests extends RedisCuckooFilterIntegrationTests { + + @Inject + RedisCuckooFilterResp2IntegrationTests(RedisClient client) { + super(connectWithResp2(client)); + } + + private static RedisCommands connectWithResp2(RedisClient client) { + client.setOptions(ClientOptions.builder().protocolVersion(ProtocolVersion.RESP2).build()); + return client.connect().sync(); + } + +}