Skip to content

Commit 3313f47

Browse files
ggivouglide
andauthored
Add Support for New BITOP Operations in Redis 8.2 (#4188) (#4190)
* Add Support for New BITOP Operations in Redis 8.2 (#4188) This PR adds support for the new BITOP operations introduced in Redis Community Edition 8.2: DIFF, DIFF1, ANDOR, and ONE. Changes - Added new enum values to BitOP.java for the new operations: DIFF, DIFF1, ANDOR, and ONE - Updated tests in BitCommandsTest.java and BitCommandsTestBase.java to verify the new operations New Operations - DIFF(X, Y1, Y2, ...): Members of X that are not members of any of Y1, Y2, ... Equivalent to: X ∧ ¬(Y1 ∨ Y2 ∨ ...) - DIFF1(X, Y1, Y2, ...): Members of one or more of Y1, Y2, ... that are not members of X Equivalent to: ¬X ∧ (Y1 ∨ Y2 ∨ ...) - ANDOR(X, Y1, Y2, ...): Members of X that are also members of one or more of Y1, Y2, ... Equivalent to: X ∧ (Y1 ∨ Y2 ∨ ...) - ONE(X1, X2, ...): Members of exactly one of X1, X2, ... For two bitmaps, this is equivalent to XOR(X1, X2) For more than two bitmaps, this represents elements that appear in exactly one input Closes #4188 * Apply suggestions from code review Co-authored-by: Igor Malinovskiy <[email protected]> * format new bitop tests * Bump of test infra to use Redis 8.2 will be handled with separate PR * format --------- Co-authored-by: Igor Malinovskiy <[email protected]>
1 parent ea61a48 commit 3313f47

File tree

5 files changed

+583
-2
lines changed

5 files changed

+583
-2
lines changed

src/main/java/redis/clients/jedis/args/BitOP.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
public enum BitOP implements Rawable {
99

10-
AND, OR, XOR, NOT;
10+
AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR, ONE;
1111

1212
private final byte[] raw;
1313

src/main/java/redis/clients/jedis/commands/BitBinaryCommands.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,13 @@ public interface BitBinaryCommands {
2626

2727
List<Long> bitfieldReadonly(byte[] key, byte[]... arguments);
2828

29+
/**
30+
* <b><a href="http://redis.io/commands/bitop">Bitop Command</a></b> Perform a bitwise operation
31+
* between multiple keys and store the result in the destKey.
32+
* @param op can be AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR and ONE
33+
* @param destKey
34+
* @param srcKeys
35+
* @return The size of the string stored in the destKey
36+
*/
2937
long bitop(BitOP op, byte[] destKey, byte[]... srcKeys);
3038
}

src/main/java/redis/clients/jedis/commands/BitCommands.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public interface BitCommands {
9999
/**
100100
* <b><a href="http://redis.io/commands/bitop">Bitop Command</a></b>
101101
* Perform a bitwise operation between multiple keys (containing string values) and store the result in the destKey.
102-
* @param op can be AND, OR, XOR or NOT
102+
* @param op can be AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR and ONE
103103
* @param destKey
104104
* @param srcKeys
105105
* @return The size of the string stored in the destKey

src/test/java/redis/clients/jedis/commands/jedis/BitCommandsTest.java

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
2121
import static org.junit.jupiter.api.Assertions.assertEquals;
2222
import static org.junit.jupiter.api.Assertions.assertFalse;
23+
import static org.junit.jupiter.api.Assertions.assertThrows;
2324
import static org.junit.jupiter.api.Assertions.assertTrue;
2425
import static org.junit.jupiter.api.Assertions.fail;
2526

@@ -308,4 +309,286 @@ public void testBinaryBitfieldReadonly() {
308309
assertEquals(1L, responses2.get(0).longValue());
309310
}
310311

312+
@Test
313+
@SinceRedisVersion("8.1.240")
314+
public void bitOpDiff() {
315+
// Use single-byte values for simplicity
316+
byte[] key1 = new byte[] { (byte) 0b00000111 }; // bits 0,1,2 set
317+
byte[] key2 = new byte[] { (byte) 0b00000010 }; // bit 1 set
318+
byte[] key3 = new byte[] { (byte) 0b00000100 }; // bit 2 set
319+
String destKey = "resultDiff";
320+
321+
// Set keys using byte arrays
322+
jedis.set("key1".getBytes(), key1);
323+
jedis.set("key2".getBytes(), key2);
324+
jedis.set("key3".getBytes(), key3);
325+
326+
// DIFF(key1, key2, key3) = key1 AND NOT(key2 OR key3)
327+
// key1 = 00000111 (bits 0,1,2 set)
328+
// key2 = 00000010 (bit 1 set)
329+
// key3 = 00000100 (bit 2 set)
330+
// key2 OR key3 = 00000110 (bits 1,2 set)
331+
// NOT(key2 OR key3) = 11111001 (all bits except 1,2 set)
332+
// key1 AND NOT(key2 OR key3) = 00000001 (only bit 0 set)
333+
jedis.bitop(BitOP.DIFF, destKey, "key1", "key2", "key3");
334+
335+
// Get result as bytes
336+
byte[] resultBytes = jedis.get(destKey).getBytes();
337+
338+
// Expected result: 00000001 (only bit 0 set)
339+
byte[] expectedBytes = new byte[] { (byte) 0b00000001 };
340+
341+
// Verify the result
342+
assertArrayEquals(expectedBytes, resultBytes);
343+
}
344+
345+
@Test
346+
@SinceRedisVersion("8.1.240")
347+
public void bitOpDiff1() {
348+
// Use single-byte values for simplicity
349+
byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set
350+
byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set
351+
byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set
352+
String destKey = "resultDiff1";
353+
354+
// Set keys using byte arrays
355+
jedis.set("key1".getBytes(), key1);
356+
jedis.set("key2".getBytes(), key2);
357+
jedis.set("key3".getBytes(), key3);
358+
359+
// DIFF1(key1, key2, key3) = NOT(key1) AND (key2 OR key3)
360+
// key1 = 00000111 (bits 0,1,2 set)
361+
// key2 = 00000010 (bit 1 set)
362+
// key3 = 00000100 (bit 2 set)
363+
// key2 OR key3 = 00000110 (bits 1,2 set)
364+
// NOT(key1) = 11111000 (all bits except 0,1,2 set)
365+
// NOT(key1) AND (key2 OR key3) = 00000000 (no bits set)
366+
jedis.bitop(BitOP.DIFF1, destKey, "key1", "key2", "key3");
367+
368+
// Get result as bytes
369+
byte[] resultBytes = jedis.get(destKey).getBytes();
370+
371+
// Expected result: 00000000 (no bits set)
372+
byte[] expectedBytes = new byte[] { (byte) 0x00 };
373+
374+
// Verify the result
375+
assertArrayEquals(expectedBytes, resultBytes);
376+
}
377+
378+
@Test
379+
@SinceRedisVersion("8.1.240")
380+
public void bitOpAndor() {
381+
// Use single-byte values for simplicity
382+
byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set
383+
byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set
384+
byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set
385+
String destKey = "resultAndor";
386+
387+
// Set keys using byte arrays
388+
jedis.set("key1".getBytes(), key1);
389+
jedis.set("key2".getBytes(), key2);
390+
jedis.set("key3".getBytes(), key3);
391+
392+
// ANDOR(key1, key2, key3) = key1 AND (key2 OR key3)
393+
// key1 = 00000111 (bits 0,1,2 set)
394+
// key2 = 00000010 (bit 1 set)
395+
// key3 = 00000100 (bit 2 set)
396+
// key2 OR key3 = 00000110 (bits 1,2 set)
397+
// key1 AND (key2 OR key3) = 00000110 (bits 1,2 set)
398+
jedis.bitop(BitOP.ANDOR, destKey, "key1", "key2", "key3");
399+
400+
// Get result as bytes
401+
byte[] resultBytes = jedis.get(destKey).getBytes();
402+
403+
// Expected result: 00000110 (bits 1,2 set)
404+
byte[] expectedBytes = new byte[] { (byte) 0x06 };
405+
406+
// Verify the result
407+
assertArrayEquals(expectedBytes, resultBytes);
408+
}
409+
410+
@Test
411+
@SinceRedisVersion("8.1.240")
412+
public void bitOpOne() {
413+
// Use single-byte values for simplicity
414+
byte[] key1 = new byte[] { (byte) 0x01 }; // 00000001 - bit 0 set
415+
byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set
416+
byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set
417+
String destKey = "resultOne";
418+
419+
// Set keys using byte arrays
420+
jedis.set("key1".getBytes(), key1);
421+
jedis.set("key2".getBytes(), key2);
422+
jedis.set("key3".getBytes(), key3);
423+
424+
// ONE(key1, key2, key3) = bits set in exactly one of the inputs
425+
// key1 = 00000001 (bit 0 set)
426+
// key2 = 00000010 (bit 1 set)
427+
// key3 = 00000100 (bit 2 set)
428+
// Result = 00000111 (bits 0,1,2 set - each in exactly one input)
429+
jedis.bitop(BitOP.ONE, destKey, "key1", "key2", "key3");
430+
431+
// Get result as bytes
432+
byte[] resultBytes = jedis.get(destKey).getBytes();
433+
434+
// Expected result: 00000111 (bits 0,1,2 set)
435+
byte[] expectedBytes = new byte[] { (byte) 0x07 };
436+
437+
// Verify the result
438+
assertArrayEquals(expectedBytes, resultBytes);
439+
}
440+
441+
@Test
442+
@SinceRedisVersion("8.1.240")
443+
public void bitOpDiffBinary() {
444+
byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set
445+
byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set
446+
byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set
447+
byte[] dest = "resultDiffBinary".getBytes();
448+
449+
jedis.set("key1".getBytes(), key1);
450+
jedis.set("key2".getBytes(), key2);
451+
jedis.set("key3".getBytes(), key3);
452+
453+
// DIFF(key1, key2, key3) = key1 AND NOT(key2 OR key3)
454+
// key1 = 00000111 (bits 0,1,2 set)
455+
// key2 = 00000010 (bit 1 set)
456+
// key3 = 00000100 (bit 2 set)
457+
// key2 OR key3 = 00000110 (bits 1,2 set)
458+
// NOT(key2 OR key3) = 11111001 (all bits except 1,2 set)
459+
// key1 AND NOT(key2 OR key3) = 00000001 (only bit 0 set)
460+
jedis.bitop(BitOP.DIFF, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes());
461+
462+
// Expected result: 00000001 (only bit 0 set)
463+
byte[] expectedBytes = new byte[] { (byte) 0x01 };
464+
465+
// Verify the result
466+
assertArrayEquals(expectedBytes, jedis.get(dest));
467+
}
468+
469+
@Test
470+
@SinceRedisVersion("8.1.240")
471+
public void bitOpDiff1Binary() {
472+
byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set
473+
byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set
474+
byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set
475+
byte[] dest = "resultDiff1Binary".getBytes();
476+
477+
jedis.set("key1".getBytes(), key1);
478+
jedis.set("key2".getBytes(), key2);
479+
jedis.set("key3".getBytes(), key3);
480+
481+
// DIFF1(key1, key2, key3) = NOT(key1) AND (key2 OR key3)
482+
// key1 = 00000111 (bits 0,1,2 set)
483+
// key2 = 00000010 (bit 1 set)
484+
// key3 = 00000100 (bit 2 set)
485+
// key2 OR key3 = 00000110 (bits 1,2 set)
486+
// NOT(key1) = 11111000 (all bits except 0,1,2 set)
487+
// NOT(key1) AND (key2 OR key3) = 00000000 (no bits set)
488+
jedis.bitop(BitOP.DIFF1, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes());
489+
490+
// Expected result: 00000000 (no bits set)
491+
byte[] expectedBytes = new byte[] { (byte) 0x00 };
492+
493+
// Verify the result
494+
assertArrayEquals(expectedBytes, jedis.get(dest));
495+
}
496+
497+
@Test
498+
@SinceRedisVersion("8.1.240")
499+
public void bitOpAndorBinary() {
500+
byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set
501+
byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set
502+
byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set
503+
byte[] dest = "resultAndorBinary".getBytes();
504+
505+
jedis.set("key1".getBytes(), key1);
506+
jedis.set("key2".getBytes(), key2);
507+
jedis.set("key3".getBytes(), key3);
508+
509+
// ANDOR(key1, key2, key3) = key1 AND (key2 OR key3)
510+
// key1 = 00000111 (bits 0,1,2 set)
511+
// key2 = 00000010 (bit 1 set)
512+
// key3 = 00000100 (bit 2 set)
513+
// key2 OR key3 = 00000110 (bits 1,2 set)
514+
// key1 AND (key2 OR key3) = 00000110 (bits 1,2 set)
515+
jedis.bitop(BitOP.ANDOR, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes());
516+
517+
// Expected result: 00000110 (bits 1,2 set)
518+
byte[] expectedBytes = new byte[] { (byte) 0x06 };
519+
520+
// Verify the result
521+
assertArrayEquals(expectedBytes, jedis.get(dest));
522+
}
523+
524+
@Test
525+
@SinceRedisVersion("8.1.240")
526+
public void bitOpOneBinary() {
527+
byte[] key1 = { (byte) 0x01 }; // 00000001 - bit 0 set
528+
byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set
529+
byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set
530+
byte[] dest = "resultOneBinary".getBytes();
531+
532+
jedis.set("key1".getBytes(), key1);
533+
jedis.set("key2".getBytes(), key2);
534+
jedis.set("key3".getBytes(), key3);
535+
536+
// ONE(key1, key2, key3) = bits set in exactly one of the inputs
537+
// key1 = 00000001 (bit 0 set)
538+
// key2 = 00000010 (bit 1 set)
539+
// key3 = 00000100 (bit 2 set)
540+
// Result = 00000111 (bits 0,1,2 set - each in exactly one input)
541+
jedis.bitop(BitOP.ONE, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes());
542+
543+
// Expected result: 00000111 (bits 0,1,2 set)
544+
byte[] expectedBytes = new byte[] { (byte) 0x07 };
545+
546+
// Verify the result
547+
assertArrayEquals(expectedBytes, jedis.get(dest));
548+
}
549+
550+
@Test
551+
@SinceRedisVersion("8.1.240")
552+
public void bitOpDiffSingleSourceShouldFail() {
553+
assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF, "dest", "src1"));
554+
}
555+
556+
@Test
557+
@SinceRedisVersion("8.1.240")
558+
public void bitOpDiff1SingleSourceShouldFail() {
559+
assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF1, "dest", "src1"));
560+
}
561+
562+
@Test
563+
@SinceRedisVersion("8.1.240")
564+
public void bitOpAndorSingleSourceShouldFail() {
565+
assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.ANDOR, "dest", "src1"));
566+
}
567+
568+
@Test
569+
@SinceRedisVersion("8.1.240")
570+
public void bitOpDiffBinarySingleSourceShouldFail() {
571+
byte[] dest = "dest".getBytes();
572+
byte[] src1 = "src1".getBytes();
573+
574+
assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF, dest, src1));
575+
}
576+
577+
@Test
578+
@SinceRedisVersion("8.1.240")
579+
public void bitOpDiff1BinarySingleSourceShouldFail() {
580+
byte[] dest = "dest".getBytes();
581+
byte[] src1 = "src1".getBytes();
582+
583+
assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF1, dest, src1));
584+
}
585+
586+
@Test
587+
@SinceRedisVersion("8.1.240")
588+
public void bitOpAndorBinarySingleSourceShouldFail() {
589+
byte[] dest = "dest".getBytes();
590+
byte[] src1 = "src1".getBytes();
591+
592+
assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.ANDOR, dest, src1));
593+
}
311594
}

0 commit comments

Comments
 (0)