@@ -655,6 +655,7 @@ fn dbtype(parser: &mut ParsedCommand, db: &Database, dbindex: usize) -> Response
655655 Some ( Value :: Set ( _) ) => Response :: Data ( "set" . to_owned ( ) . into_bytes ( ) ) ,
656656 Some ( Value :: SortedSet ( _) ) => Response :: Data ( "zset" . to_owned ( ) . into_bytes ( ) ) ,
657657 Some ( Value :: Hash ( _) ) => Response :: Data ( "hash" . to_owned ( ) . into_bytes ( ) ) ,
658+ Some ( Value :: Bloom ( _) ) => Response :: Data ( "MBbloom--" . to_owned ( ) . into_bytes ( ) ) ,
658659 None => Response :: Data ( "none" . to_owned ( ) . into_bytes ( ) ) ,
659660 }
660661}
@@ -1018,6 +1019,100 @@ fn pfmerge(parser: &ParsedCommand, db: &mut Database, dbindex: usize) -> Respons
10181019 r
10191020}
10201021
1022+ fn bf_reserve ( parser : & mut ParsedCommand , db : & mut Database , dbindex : usize ) -> Response {
1023+ validate_arguments_exact ! ( parser, 4 ) ;
1024+ let key = try_validate ! ( parser. get_vec( 1 ) , "ERR invalid key" ) ;
1025+ let error_rate = try_validate ! ( parser. get_f64( 2 ) , "ERR invalid error rate" ) ;
1026+ let capacity = try_validate ! ( parser. get_i64( 3 ) , "ERR invalid capacity" ) ;
1027+
1028+ if capacity <= 0 {
1029+ return Response :: Error ( "ERR invalid bloom filter parameters" . to_owned ( ) ) ;
1030+ }
1031+
1032+ match db
1033+ . get_or_create ( dbindex, & key)
1034+ . bf_reserve ( error_rate, capacity as usize )
1035+ {
1036+ Ok ( ( ) ) => {
1037+ db. key_updated ( dbindex, & key) ;
1038+ Response :: Status ( "OK" . to_owned ( ) )
1039+ }
1040+ Err ( e) => Response :: Error ( e. to_string ( ) ) ,
1041+ }
1042+ }
1043+
1044+ fn bf_add ( parser : & mut ParsedCommand , db : & mut Database , dbindex : usize ) -> Response {
1045+ validate_arguments_exact ! ( parser, 3 ) ;
1046+ let key = try_validate ! ( parser. get_vec( 1 ) , "ERR invalid key" ) ;
1047+ let item = try_validate ! ( parser. get_slice( 2 ) , "ERR invalid item" ) . to_vec ( ) ;
1048+
1049+ match db. get_or_create ( dbindex, & key) . bf_add ( & item) {
1050+ Ok ( added) => {
1051+ db. key_updated ( dbindex, & key) ;
1052+ Response :: Integer ( if added { 1 } else { 0 } )
1053+ }
1054+ Err ( e) => Response :: Error ( e. to_string ( ) ) ,
1055+ }
1056+ }
1057+
1058+ fn bf_madd ( parser : & mut ParsedCommand , db : & mut Database , dbindex : usize ) -> Response {
1059+ validate_arguments_gte ! ( parser, 3 ) ;
1060+ let key = try_validate ! ( parser. get_vec( 1 ) , "ERR invalid key" ) ;
1061+ let mut items = Vec :: with_capacity ( parser. argv . len ( ) - 2 ) ;
1062+ for i in 2 ..parser. argv . len ( ) {
1063+ items. push ( try_validate ! ( parser. get_slice( i) , "ERR invalid item" ) . to_vec ( ) ) ;
1064+ }
1065+
1066+ let value = db. get_or_create ( dbindex, & key) ;
1067+ let mut result = Vec :: with_capacity ( items. len ( ) ) ;
1068+ for item in & items {
1069+ match value. bf_add ( item) {
1070+ Ok ( added) => result. push ( Response :: Integer ( if added { 1 } else { 0 } ) ) ,
1071+ Err ( e) => return Response :: Error ( e. to_string ( ) ) ,
1072+ }
1073+ }
1074+
1075+ db. key_updated ( dbindex, & key) ;
1076+ Response :: Array ( result)
1077+ }
1078+
1079+ fn bf_exists ( parser : & mut ParsedCommand , db : & Database , dbindex : usize ) -> Response {
1080+ validate_arguments_exact ! ( parser, 3 ) ;
1081+ let key = try_validate ! ( parser. get_vec( 1 ) , "ERR invalid key" ) ;
1082+ let item = try_validate ! ( parser. get_slice( 2 ) , "ERR invalid item" ) ;
1083+
1084+ match db. get ( dbindex, & key) {
1085+ Some ( value) => match value. bf_exists ( item) {
1086+ Ok ( exists) => Response :: Integer ( if exists { 1 } else { 0 } ) ,
1087+ Err ( e) => Response :: Error ( e. to_string ( ) ) ,
1088+ } ,
1089+ None => Response :: Integer ( 0 ) ,
1090+ }
1091+ }
1092+
1093+ fn bf_mexists ( parser : & mut ParsedCommand , db : & Database , dbindex : usize ) -> Response {
1094+ validate_arguments_gte ! ( parser, 3 ) ;
1095+ let key = try_validate ! ( parser. get_vec( 1 ) , "ERR invalid key" ) ;
1096+ let mut items = Vec :: with_capacity ( parser. argv . len ( ) - 2 ) ;
1097+ for i in 2 ..parser. argv . len ( ) {
1098+ items. push ( try_validate ! ( parser. get_slice( i) , "ERR invalid item" ) . to_vec ( ) ) ;
1099+ }
1100+
1101+ match db. get ( dbindex, & key) {
1102+ Some ( value) => {
1103+ let mut result = Vec :: with_capacity ( items. len ( ) ) ;
1104+ for item in & items {
1105+ match value. bf_exists ( item) {
1106+ Ok ( exists) => result. push ( Response :: Integer ( if exists { 1 } else { 0 } ) ) ,
1107+ Err ( e) => return Response :: Error ( e. to_string ( ) ) ,
1108+ }
1109+ }
1110+ Response :: Array ( result)
1111+ }
1112+ None => Response :: Array ( items. iter ( ) . map ( |_| Response :: Integer ( 0 ) ) . collect ( ) ) ,
1113+ }
1114+ }
1115+
10211116fn generic_push (
10221117 parser : & mut ParsedCommand ,
10231118 db : & mut Database ,
@@ -3081,6 +3176,7 @@ fn object(parser: &mut ParsedCommand, db: &mut Database, dbindex: usize) -> Resp
30813176 ValueHash :: ZipList ( _) => "ziplist" ,
30823177 ValueHash :: HashMap ( _) => "hashtable" ,
30833178 } ,
3179+ Value :: Bloom ( _) => "bloom" ,
30843180 Value :: Nil => return Response :: Nil ,
30853181 } ;
30863182 Response :: Data ( encoding. to_string ( ) . into_bytes ( ) )
@@ -4249,7 +4345,8 @@ fn command_cmd(parser: &mut ParsedCommand, _db: &Database) -> Response {
42494345 "ttl" , "pttl" , "persist" , "slaveof" , "role" , "config" , "subscribe" , "unsubscribe" ,
42504346 "psubscribe" , "punsubscribe" , "publish" , "pubsub" , "watch" , "unwatch" , "restore" ,
42514347 "dump" , "object" , "client" , "time" , "bitop" , "bitcount" , "bitpos" , "wait" , "command" ,
4252- "pfadd" , "pfcount" , "pfmerge" ,
4348+ "pfadd" , "pfcount" , "pfmerge" , "bf.reserve" , "bf.add" , "bf.madd" , "bf.exists" ,
4349+ "bf.mexists" ,
42534350 ] ;
42544351 #[ cfg( feature = "vector-search" ) ]
42554352 let mut commands = commands;
@@ -4718,6 +4815,11 @@ fn command_properties(command_name: &str) -> CommandProperties {
47184815 "pfadd" => ( -2 , wmf, 1 , 1 , 1 ) ,
47194816 "pfcount" => ( -2 , READONLY , 1 , -1 , 1 ) ,
47204817 "pfmerge" => ( -2 , wm, 1 , -1 , 1 ) ,
4818+ "bf.reserve" => ( 4 , wmf, 1 , 1 , 1 ) ,
4819+ "bf.add" => ( 3 , wmf, 1 , 1 , 1 ) ,
4820+ "bf.madd" => ( -3 , wmf, 1 , 1 , 1 ) ,
4821+ "bf.exists" => ( 3 , fr, 1 , 1 , 1 ) ,
4822+ "bf.mexists" => ( -3 , fr, 1 , 1 , 1 ) ,
47214823 "pfdebug" => ( -3 , WRITE , 0 , 0 , 0 ) ,
47224824 "latency" => ( -2 , ars | ls, 0 , 0 , 0 ) ,
47234825 // RediSearch (FT.*) commands (only when vector-search feature is enabled)
@@ -4771,17 +4873,17 @@ fn execute_command(
47714873 let raw_command = try_opt_validate ! ( parser. get_str( 0 ) , "Invalid command" ) ;
47724874 let lower_command = raw_command. to_ascii_lowercase ( ) ;
47734875
4774- // For FT.* commands, bypass mapped_command to ensure they're allowed
4775- // Check both lowercase and original case to handle any edge cases
4876+ // For module-style dotted commands, bypass mapped_command to ensure they're allowed.
47764877 let is_ft_command = lower_command. starts_with ( "ft." ) ;
4878+ let is_bf_command = lower_command. starts_with ( "bf." ) ;
47774879
47784880 // Debug logging
47794881 if raw_command. to_uppercase ( ) . starts_with ( "FT." ) || lower_command. starts_with ( "ft." ) {
47804882 eprintln ! ( "DEBUG: FT command detected: raw='{}', lower='{}', is_ft={}" , raw_command, lower_command, is_ft_command) ;
47814883 }
47824884
4783- let command_name = if is_ft_command {
4784- // FT.* commands - use directly, don't go through mapped_command
4885+ let command_name = if is_ft_command || is_bf_command {
4886+ // Module commands - use directly, don't go through mapped_command.
47854887 // Normalize to lowercase for consistency
47864888 & * lower_command
47874889 } else {
@@ -4906,6 +5008,11 @@ fn execute_command(
49065008 "pfadd" => pfadd ( parser, db, dbindex) ,
49075009 "pfcount" => pfcount ( parser, db, dbindex) ,
49085010 "pfmerge" => pfmerge ( parser, db, dbindex) ,
5011+ "bf.reserve" => bf_reserve ( parser, db, dbindex) ,
5012+ "bf.add" => bf_add ( parser, db, dbindex) ,
5013+ "bf.madd" => bf_madd ( parser, db, dbindex) ,
5014+ "bf.exists" => bf_exists ( parser, db, dbindex) ,
5015+ "bf.mexists" => bf_mexists ( parser, db, dbindex) ,
49095016 "pfselftest" => pfselftest ( parser, db, dbindex) ,
49105017 "pfdebug" => pfdebug ( parser, db, dbindex) ,
49115018 "exists" => exists ( parser, db, dbindex) ,
@@ -5805,6 +5912,133 @@ mod test_command {
58055912 ) ;
58065913 }
58075914
5915+ #[ test]
5916+ fn bf_reserve_add_exists_command ( ) {
5917+ let mut db = Database :: new ( Config :: new ( Logger :: new ( Level :: Warning ) ) ) ;
5918+ assert_eq ! (
5919+ command(
5920+ parser!( b"BF.RESERVE wallet_bloom 0.01 1000" ) ,
5921+ & mut db,
5922+ & mut Client :: mock( )
5923+ )
5924+ . unwrap( ) ,
5925+ Response :: Status ( "OK" . to_owned( ) )
5926+ ) ;
5927+ assert_eq ! (
5928+ command(
5929+ parser!( b"BF.EXISTS wallet_bloom 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6" ) ,
5930+ & mut db,
5931+ & mut Client :: mock( )
5932+ )
5933+ . unwrap( ) ,
5934+ Response :: Integer ( 0 )
5935+ ) ;
5936+ assert_eq ! (
5937+ command(
5938+ parser!( b"BF.ADD wallet_bloom 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6" ) ,
5939+ & mut db,
5940+ & mut Client :: mock( )
5941+ )
5942+ . unwrap( ) ,
5943+ Response :: Integer ( 1 )
5944+ ) ;
5945+ assert_eq ! (
5946+ command(
5947+ parser!( b"BF.ADD wallet_bloom 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6" ) ,
5948+ & mut db,
5949+ & mut Client :: mock( )
5950+ )
5951+ . unwrap( ) ,
5952+ Response :: Integer ( 0 )
5953+ ) ;
5954+ assert_eq ! (
5955+ command(
5956+ parser!( b"BF.EXISTS wallet_bloom 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6" ) ,
5957+ & mut db,
5958+ & mut Client :: mock( )
5959+ )
5960+ . unwrap( ) ,
5961+ Response :: Integer ( 1 )
5962+ ) ;
5963+ }
5964+
5965+ #[ test]
5966+ fn bf_madd_and_mexists_command ( ) {
5967+ let mut db = Database :: new ( Config :: new ( Logger :: new ( Level :: Warning ) ) ) ;
5968+ assert_eq ! (
5969+ command(
5970+ parser!( b"BF.MADD wallet_bloom evm_address tron_address evm_address" ) ,
5971+ & mut db,
5972+ & mut Client :: mock( )
5973+ )
5974+ . unwrap( ) ,
5975+ Response :: Array ( vec![
5976+ Response :: Integer ( 1 ) ,
5977+ Response :: Integer ( 1 ) ,
5978+ Response :: Integer ( 0 ) ,
5979+ ] )
5980+ ) ;
5981+ assert_eq ! (
5982+ command(
5983+ parser!( b"BF.MEXISTS wallet_bloom evm_address missing tron_address" ) ,
5984+ & mut db,
5985+ & mut Client :: mock( )
5986+ )
5987+ . unwrap( ) ,
5988+ Response :: Array ( vec![
5989+ Response :: Integer ( 1 ) ,
5990+ Response :: Integer ( 0 ) ,
5991+ Response :: Integer ( 1 ) ,
5992+ ] )
5993+ ) ;
5994+ }
5995+
5996+ #[ test]
5997+ fn bf_exists_missing_key_returns_zero ( ) {
5998+ let mut db = Database :: new ( Config :: new ( Logger :: new ( Level :: Warning ) ) ) ;
5999+ assert_eq ! (
6000+ command(
6001+ parser!( b"BF.EXISTS missing_bloom value" ) ,
6002+ & mut db,
6003+ & mut Client :: mock( )
6004+ )
6005+ . unwrap( ) ,
6006+ Response :: Integer ( 0 )
6007+ ) ;
6008+ assert_eq ! (
6009+ command(
6010+ parser!( b"BF.MEXISTS missing_bloom a b" ) ,
6011+ & mut db,
6012+ & mut Client :: mock( )
6013+ )
6014+ . unwrap( ) ,
6015+ Response :: Array ( vec![ Response :: Integer ( 0 ) , Response :: Integer ( 0 ) ] )
6016+ ) ;
6017+ }
6018+
6019+ #[ test]
6020+ fn bf_wrong_type_and_existing_key_errors ( ) {
6021+ let mut db = Database :: new ( Config :: new ( Logger :: new ( Level :: Warning ) ) ) ;
6022+ assert_eq ! (
6023+ command( parser!( b"SET key value" ) , & mut db, & mut Client :: mock( ) ) . unwrap( ) ,
6024+ Response :: Status ( "OK" . to_owned( ) )
6025+ ) ;
6026+ assert ! (
6027+ command( parser!( b"BF.ADD key value" ) , & mut db, & mut Client :: mock( ) )
6028+ . unwrap( )
6029+ . is_error( )
6030+ ) ;
6031+ assert ! (
6032+ command(
6033+ parser!( b"BF.RESERVE key 0.01 1000" ) ,
6034+ & mut db,
6035+ & mut Client :: mock( )
6036+ )
6037+ . unwrap( )
6038+ . is_error( )
6039+ ) ;
6040+ }
6041+
58086042 #[ test]
58096043 fn pfcount1_command ( ) {
58106044 let mut db = Database :: new ( Config :: new ( Logger :: new ( Level :: Warning ) ) ) ;
0 commit comments