Lettuce RedisCodec For Numbers
Asked Answered
U

3

8

Using Lettuce 5 as a Redis client for the first time, I'm finding it rather confusing to simply create a RedisCommands<String, Long> for getting/setting Redis values as a Long.

It's a little unclear to me how I can accomplish this. From what I gather, the simplest way is to use the RedisClient overloaded constructor which takes a RedisCodec and RedisURI, but it seems I also need to implement the codec decoding/encoding methods?

Since storing numbers is a fairly common use case with Redis, I find this approach rather bloated and I'm surprised there is no predefined codec for integer/long. Given this, I suspect there may be a simpler alternative that I have not come across. Is there an alternate approach?

Unplug answered 7/10, 2019 at 18:7 Comment(0)
I
2

I was experiencing a similar need and end up writing the following codec:

import io.lettuce.core.codec.RedisCodec;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;

public class StringLongRedisCodec implements RedisCodec<String, Long> {

    @Override
    public String decodeKey(final ByteBuffer bytes) {
        return StandardCharsets.US_ASCII.decode(bytes).toString();
    }

    @Override
    public Long decodeValue(final ByteBuffer bytes) {
        final CharBuffer charSequence = StandardCharsets.US_ASCII.decode(bytes);
        return Long.parseLong(charSequence, 0, charSequence.length(), 10);
    }

    @Override
    public ByteBuffer encodeKey(final String key) {
        return StandardCharsets.US_ASCII.encode(key);
    }

    @Override
    public ByteBuffer encodeValue(final Long value) {
        return ByteBuffer.wrap(Long.toString(value).getBytes());
    }

}
Incept answered 6/11, 2020 at 13:6 Comment(0)
A
0

This link might be helpful.

It mentions

Redis stores integers in their integer representation, so for string values that actually hold an integer, there is no overhead for storing the string representation of the integer.

So I think it would be safe to just save integers as strings.

Edit: Just Tried using strings, it works!

Aryanize answered 3/11, 2021 at 4:50 Comment(0)
C
0

Here is a more comprehensive RedisCodec for all native Number types:

import io.lettuce.core.codec.RedisCodec;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Objects;

class NumberRedisCodec implements RedisCodec<String, Number> {

    @Override
    public String decodeKey(ByteBuffer bytes) {
        return new String(bytes.array(), StandardCharsets.UTF_8);
    }

    @Override
    public Number decodeValue(ByteBuffer bytes) {
        NumberClassEnum enumValue = NumberClassEnum.forID(bytes.get(0));
        switch (enumValue) {
            case BYTE:
                return bytes.get(1);
            case SHORT:
                return bytes.getShort(1);
            case INT:
                return bytes.getInt(1);
            case LONG:
                return bytes.getLong(1);
            case FLOAT:
                return bytes.getFloat(1);
            case DOUBLE:
                return bytes.getDouble(1);
            default:
                throw new IllegalArgumentException("Number type not supported"); //should be unreachable
        }
    }

    @Override
    public ByteBuffer encodeKey(String key) {
        return ByteBuffer.wrap(key.getBytes(StandardCharsets.UTF_8));
    }

    @Override
    public ByteBuffer encodeValue(Number value) {
        NumberClassEnum enumValue = NumberClassEnum.forClass(value.getClass());
        if (Objects.isNull(enumValue)) {
            throw new IllegalArgumentException("Number type not supported");
        }
        ByteBuffer byteBuffer = ByteBuffer.allocate(enumValue.numBytes + 1);
        byteBuffer.put(0, enumValue.id);
        switch (enumValue) {
            case BYTE:
                byteBuffer.put(value.byteValue());
                break;
            case SHORT:
                byteBuffer.putShort(value.shortValue());
                break;
            case INT:
                byteBuffer.putInt(value.intValue());
                break;
            case LONG:
                byteBuffer.putLong(value.longValue());
                break;
            case FLOAT:
                byteBuffer.putFloat(value.floatValue());
                break;
            case DOUBLE:
                byteBuffer.putDouble(value.doubleValue());
                break;
            default:
                throw new IllegalArgumentException("Number type not supported"); //should be unreachable
        }
        return byteBuffer;
    }

    private enum NumberClassEnum {
        BYTE(Byte.class, 1, (byte) 1),
        SHORT(Short.class, 2, (byte) 2),
        INT(Integer.class, 4, (byte) 3),
        LONG(Long.class, 8, (byte) 4),
        FLOAT(Float.class, 4, (byte) 5),
        DOUBLE(Double.class, 8, (byte) 6);

        public final Class<? extends Number> numberClass;
        public final int numBytes;
        public final byte id;

        private static final HashMap<Byte, NumberClassEnum> enumIDMap;
        private static final HashMap<Class<? extends Number>, NumberClassEnum> enumClassMap;

        static {
            enumIDMap = new HashMap<>();
            enumIDMap.put(BYTE.id, BYTE);
            enumIDMap.put(SHORT.id, SHORT);
            enumIDMap.put(INT.id, INT);
            enumIDMap.put(LONG.id, LONG);
            enumIDMap.put(FLOAT.id, FLOAT);
            enumIDMap.put(DOUBLE.id, DOUBLE);

            enumClassMap = new HashMap<>();
            enumClassMap.put(BYTE.numberClass, BYTE);
            enumClassMap.put(SHORT.numberClass, SHORT);
            enumClassMap.put(INT.numberClass, INT);
            enumClassMap.put(LONG.numberClass, LONG);
            enumClassMap.put(FLOAT.numberClass, FLOAT);
            enumClassMap.put(DOUBLE.numberClass, DOUBLE);
        }

        public static NumberClassEnum forID(byte id) {
            return enumIDMap.get(id);
        }

        public static NumberClassEnum forClass(Class<? extends Number> clazz) {
            return enumClassMap.get(clazz);
        }

        NumberClassEnum(Class<? extends Number> numberClass, int numBytes, byte id) {
            this.numberClass = numberClass;
            this.numBytes = numBytes;
            this.id = id;
        }
    }
}
Coacher answered 3/11, 2021 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.