All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redisson.RedissonListMultimap Maven / Gradle / Ivy

There is a newer version: 3.34.1
Show newest version
/**
 * Copyright 2018 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import org.redisson.api.RFuture;
import org.redisson.api.RList;
import org.redisson.api.RListMultimap;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.RedisStrictCommand;
import org.redisson.client.protocol.convertor.BooleanAmountReplayConvertor;
import org.redisson.command.CommandAsyncExecutor;

import io.netty.buffer.ByteBuf;

/**
 * @author Nikita Koksharov
 *
 * @param  key
 * @param  value
 */
public class RedissonListMultimap extends RedissonMultimap implements RListMultimap {

    private static final RedisStrictCommand LLEN_VALUE = new RedisStrictCommand("LLEN", new BooleanAmountReplayConvertor());

    public RedissonListMultimap(CommandAsyncExecutor connectionManager, String name) {
        super(connectionManager, name);
    }

    public RedissonListMultimap(Codec codec, CommandAsyncExecutor connectionManager, String name) {
        super(codec, connectionManager, name);
    }

    @Override
    public RFuture sizeAsync() {
        return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_INTEGER,
                "local keys = redis.call('hgetall', KEYS[1]); " +
                "local size = 0; " +
                "for i, v in ipairs(keys) do " +
                    "if i % 2 == 0 then " +
                        "local name = ARGV[1] .. v; " +
                        "size = size + redis.call('llen', name); " +
                    "end;" +
                "end; " +
                "return size; ",
                Arrays.asList(getName()), 
                prefix);
    }
    
    @Override
    public RFuture containsKeyAsync(Object key) {
        ByteBuf keyState = encodeMapKey(key);
        String keyHash = hashAndRelease(keyState);

        String setName = getValuesName(keyHash);
        return commandExecutor.readAsync(getName(), codec, LLEN_VALUE, setName);
    }

    @Override
    public RFuture containsValueAsync(Object value) {
        ByteBuf valueState = encodeMapValue(value);

        return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
                "local keys = redis.call('hgetall', KEYS[1]); " +
                "for i, v in ipairs(keys) do " +
                    "if i % 2 == 0 then " +
                        "local name = ARGV[2] .. v; " +

                        "local items = redis.call('lrange', name, 0, -1) " +
                        "for i=1,#items do " +
                            "if items[i] == ARGV[1] then " +
                                "return 1; " +
                            "end; " +
                        "end; " +
                    "end;" +
                "end; " +
                "return 0; ",
                Arrays.asList(getName()), 
                valueState, prefix);
    }

    @Override
    public boolean containsEntry(Object key, Object value) {
        return get(containsEntryAsync(key, value));
    }

    @Override
    public RFuture containsEntryAsync(Object key, Object value) {
        ByteBuf keyState = encodeMapKey(key);
        String keyHash = hashAndRelease(keyState);
        ByteBuf valueState = encodeMapValue(value);

        String setName = getValuesName(keyHash);

        return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
                "local items = redis.call('lrange', KEYS[1], 0, -1) " +
                "for i=1,#items do " +
                    "if items[i] == ARGV[1] then " +
                        "return 1; " +
                    "end; " +
                "end; " +
                "return 0; ",
                Collections.singletonList(setName), valueState);
    }

    @Override
    public boolean put(K key, V value) {
        return get(putAsync(key, value));
    }

    @Override
    public RFuture putAsync(K key, V value) {
        ByteBuf keyState = encodeMapKey(key);
        String keyHash = hash(keyState);
        ByteBuf valueState = encodeMapValue(value);

        String setName = getValuesName(keyHash);
        return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
                "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " +
                "redis.call('rpush', KEYS[2], ARGV[3]); " +
                "return 1; ",
            Arrays.asList(getName(), setName), keyState, keyHash, valueState);
    }

    @Override
    public RFuture removeAsync(Object key, Object value) {
        ByteBuf keyState = encodeMapKey(key);
        String keyHash = hash(keyState);
        ByteBuf valueState = encodeMapValue(value);

        String setName = getValuesName(keyHash);
        return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
                "local res = redis.call('lrem', KEYS[2], 1, ARGV[2]); "
              + "if res == 1 and redis.call('llen', KEYS[2]) == 0 then "
                  + "redis.call('hdel', KEYS[1], ARGV[1]); "
              + "end; "
              + "return res; ",
            Arrays.asList(getName(), setName), keyState, valueState);
    }

    @Override
    public RFuture putAllAsync(K key, Iterable values) {
        List params = new ArrayList();
        ByteBuf keyState = encodeMapKey(key);
        params.add(keyState);
        String keyHash = hash(keyState);
        params.add(keyHash);
        for (Object value : values) {
            ByteBuf valueState = encodeMapValue(value);
            params.add(valueState);
        }

        String setName = getValuesName(keyHash);
        return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_AMOUNT,
                "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " +
                "return redis.call('rpush', KEYS[2], unpack(ARGV, 3, #ARGV)); ",
            Arrays.asList(getName(), setName), params.toArray());
    }


    @Override
    public RList get(final K key) {
        final ByteBuf keyState = encodeMapKey(key);
        final String keyHash = hashAndRelease(keyState);
        final String setName = getValuesName(keyHash);

        return new RedissonList(codec, commandExecutor, setName, null) {
            
            @Override
            public RFuture addAsync(V value) {
                return RedissonListMultimap.this.putAsync(key, value);
            }
            
            @Override
            public RFuture addAllAsync(Collection c) {
                return RedissonListMultimap.this.putAllAsync(key, c);
            }
            
            @Override
            public RFuture removeAsync(Object value) {
                return RedissonListMultimap.this.removeAsync(key, value);
            }
            
            @Override
            public RFuture removeAllAsync(Collection c) {
                ByteBuf keyState = encodeMapKey(key);
                return commandExecutor.evalWriteAsync(RedissonListMultimap.this.getName(), codec, RedisCommands.EVAL_BOOLEAN,
                        "redis.call('hdel', KEYS[1], ARGV[1]); " +
                        "return redis.call('del', KEYS[2]) > 0; ",
                    Arrays.asList(RedissonListMultimap.this.getName(), setName), keyState);
            }
            
            @Override
            public RFuture deleteAsync() {
                ByteBuf keyState = encodeMapKey(key);
                return RedissonListMultimap.this.fastRemoveAsync(Arrays.asList(keyState), Arrays.asList(setName), RedisCommands.EVAL_BOOLEAN_AMOUNT);
            }
            
            @Override
            public RFuture clearExpireAsync() {
                throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");
            }
            
            @Override
            public RFuture expireAsync(long timeToLive, TimeUnit timeUnit) {
                throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");
            }
            
            @Override
            public RFuture expireAtAsync(long timestamp) {
                throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");
            }
            
            @Override
            public RFuture remainTimeToLiveAsync() {
                throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");
            }
            
            @Override
            public RFuture renameAsync(String newName) {
                throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");
            }
            
            @Override
            public RFuture renamenxAsync(String newName) {
                throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");
            }
            
        };
    }

    @Override
    public List getAll(K key) {
        return (List) get(getAllAsync(key));
    }

    @Override
    public RFuture> getAllAsync(K key) {
        ByteBuf keyState = encodeMapKey(key);
        String keyHash = hashAndRelease(keyState);
        String setName = getValuesName(keyHash);

        return commandExecutor.readAsync(getName(), codec, RedisCommands.LRANGE, setName, 0, -1);
    }

    @Override
    public List removeAll(Object key) {
        return (List) get(removeAllAsync(key));
    }

    @Override
    public RFuture> removeAllAsync(Object key) {
        ByteBuf keyState = encodeMapKey(key);
        String keyHash = hash(keyState);

        String setName = getValuesName(keyHash);
        return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LIST,
                "redis.call('hdel', KEYS[1], ARGV[1]); " +
                "local members = redis.call('lrange', KEYS[2], 0, -1); " +
                "redis.call('del', KEYS[2]); " +
                "return members; ",
            Arrays.asList(getName(), setName), keyState);
    }

    @Override
    public List replaceValues(K key, Iterable values) {
        return (List) get(replaceValuesAsync(key, values));
    }

    @Override
    public RFuture> replaceValuesAsync(K key, Iterable values) {
        List params = new ArrayList();
        ByteBuf keyState = encodeMapKey(key);
        params.add(keyState);
        String keyHash = hash(keyState);
        params.add(keyHash);
        for (Object value : values) {
            ByteBuf valueState = encodeMapValue(value);
            params.add(valueState);
        }

        String setName = getValuesName(keyHash);
        return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LIST,
                "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " +
                "local members = redis.call('lrange', KEYS[2], 0, -1); " +
                "redis.call('del', KEYS[2]); " +
                "redis.call('rpush', KEYS[2], unpack(ARGV, 3, #ARGV)); " +
                "return members; ",
            Arrays.asList(getName(), setName), params.toArray());
    }

    @Override
    Iterator valuesIterator() {
        return new RedissonListMultimapIterator(this, commandExecutor, codec) {
            @Override
            V getValue(V entry) {
                return (V) entry;
            }
        };
    }

    @Override
    RedissonMultiMapIterator> entryIterator() {
        return new RedissonListMultimapIterator>(this, commandExecutor, codec);
    }

}