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

com.lambdaworks.redis.RedisCommandBuilder Maven / Gradle / Ivy

Go to download

Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.

The newest version!
/*
 * Copyright 2011-2016 the original author or authors.
 *
 * 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 com.lambdaworks.redis;

import static com.lambdaworks.redis.LettuceStrings.string;
import static com.lambdaworks.redis.protocol.CommandKeyword.*;
import static com.lambdaworks.redis.protocol.CommandType.*;

import java.util.*;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.lambdaworks.redis.codec.RedisCodec;
import com.lambdaworks.redis.internal.LettuceAssert;
import com.lambdaworks.redis.internal.LettuceLists;
import com.lambdaworks.redis.output.*;
import com.lambdaworks.redis.protocol.BaseRedisCommandBuilder;
import com.lambdaworks.redis.protocol.Command;
import com.lambdaworks.redis.protocol.CommandArgs;
import com.lambdaworks.redis.protocol.RedisCommand;

/**
 * @param 
 * @param 
 * @author Mark Paluch
 */
@SuppressWarnings({ "unchecked", "Convert2Diamond", "WeakerAccess", "varargs" })
class RedisCommandBuilder extends BaseRedisCommandBuilder {

    static final String MUST_NOT_CONTAIN_NULL_ELEMENTS = "must not contain null elements";
    static final String MUST_NOT_BE_EMPTY = "must not be empty";
    static final String MUST_NOT_BE_NULL = "must not be null";

    private static final byte[] MINUS_BYTES = { '-' };
    private static final byte[] PLUS_BYTES = { '+' };

    public RedisCommandBuilder(RedisCodec codec) {
        super(codec);
    }

    public Command append(K key, V value) {
        notNullKey(key);

        return createCommand(APPEND, new IntegerOutput(codec), key, value);
    }

    public Command auth(String password) {
        LettuceAssert.notNull(password, "Password " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(password, "Password " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(password);
        return createCommand(AUTH, new StatusOutput(codec), args);
    }

    public Command bgrewriteaof() {
        return createCommand(BGREWRITEAOF, new StatusOutput(codec));
    }

    public Command bgsave() {
        return createCommand(BGSAVE, new StatusOutput(codec));
    }

    public Command bitcount(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key);
        return createCommand(BITCOUNT, new IntegerOutput(codec), args);
    }

    public Command bitcount(K key, long start, long end) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(start).add(end);
        return createCommand(BITCOUNT, new IntegerOutput(codec), args);
    }

    public Command> bitfield(K key, BitFieldArgs bitFieldArgs) {
        notNullKey(key);
        LettuceAssert.notNull(bitFieldArgs, "BitFieldArgs must not be null");

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key);

        bitFieldArgs.build(args);

        return createCommand(BITFIELD, (CommandOutput) new ArrayOutput(codec), args);
    }

    public Command>> bitfieldValue(K key, BitFieldArgs bitFieldArgs) {
        notNullKey(key);
        LettuceAssert.notNull(bitFieldArgs, "BitFieldArgs must not be null");

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key);

        bitFieldArgs.build(args);

        return createCommand(BITFIELD, (CommandOutput) new ValueValueListOutput(codec), args);
    }

    public Command bitpos(K key, boolean state) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(state ? 1 : 0);
        return createCommand(BITPOS, new IntegerOutput(codec), args);
    }

    public Command bitpos(K key, boolean state, long start, long end) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(state ? 1 : 0).add(start).add(end);
        return createCommand(BITPOS, new IntegerOutput(codec), args);
    }

    public Command bitopAnd(K destination, K... keys) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec);
        args.add(AND).addKey(destination).addKeys(keys);
        return createCommand(BITOP, new IntegerOutput(codec), args);
    }

    public Command bitopNot(K destination, K source) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.add(NOT).addKey(destination).addKey(source);
        return createCommand(BITOP, new IntegerOutput(codec), args);
    }

    public Command bitopOr(K destination, K... keys) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec);
        args.add(OR).addKey(destination).addKeys(keys);
        return createCommand(BITOP, new IntegerOutput(codec), args);
    }

    public Command bitopXor(K destination, K... keys) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec);
        args.add(XOR).addKey(destination).addKeys(keys);
        return createCommand(BITOP, new IntegerOutput(codec), args);
    }

    public Command> blpop(long timeout, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys).add(timeout);
        return createCommand(BLPOP, new KeyValueOutput(codec), args);
    }

    public Command> brpop(long timeout, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys).add(timeout);
        return createCommand(BRPOP, new KeyValueOutput(codec), args);
    }

    public Command brpoplpush(long timeout, K source, K destination) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(source).addKey(destination).add(timeout);
        return createCommand(BRPOPLPUSH, new ValueOutput(codec), args);
    }

    public Command clientGetname() {
        CommandArgs args = new CommandArgs(codec).add(GETNAME);
        return createCommand(CLIENT, new KeyOutput(codec), args);
    }

    public Command clientSetname(K name) {
        LettuceAssert.notNull(name, "Name " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).add(SETNAME).addKey(name);
        return createCommand(CLIENT, new StatusOutput(codec), args);
    }

    public Command clientKill(String addr) {
        LettuceAssert.notNull(addr, "Addr " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(addr, "Addr " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(KILL).add(addr);
        return createCommand(CLIENT, new StatusOutput(codec), args);
    }

    public Command clientKill(KillArgs killArgs) {
        LettuceAssert.notNull(killArgs, "KillArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).add(KILL);
        killArgs.build(args);
        return createCommand(CLIENT, new IntegerOutput(codec), args);
    }

    public Command clientPause(long timeout) {
        CommandArgs args = new CommandArgs(codec).add(PAUSE).add(timeout);
        return createCommand(CLIENT, new StatusOutput(codec), args);
    }

    public Command clientList() {
        CommandArgs args = new CommandArgs(codec).add(LIST);
        return createCommand(CLIENT, new StatusOutput(codec), args);
    }

    public Command> command() {
        CommandArgs args = new CommandArgs(codec);
        return createCommand(COMMAND, new ArrayOutput(codec), args);
    }

    public Command> commandInfo(String... commands) {
        LettuceAssert.notNull(commands, "Commands " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(commands, "Commands " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(commands, "Commands " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec);
        args.add(INFO);

        for (String command : commands) {
            args.add(command);
        }

        return createCommand(COMMAND, new ArrayOutput(codec), args);
    }

    public Command commandCount() {
        CommandArgs args = new CommandArgs(codec).add(COUNT);
        return createCommand(COMMAND, new IntegerOutput(codec), args);
    }

    public Command configRewrite() {
        CommandArgs args = new CommandArgs(codec).add(REWRITE);
        return createCommand(CONFIG, new StatusOutput(codec), args);
    }

    public Command> configGet(String parameter) {
        LettuceAssert.notNull(parameter, "Parameter " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(parameter, "Parameter " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(GET).add(parameter);
        return createCommand(CONFIG, new StringListOutput(codec), args);
    }

    public Command configResetstat() {
        CommandArgs args = new CommandArgs(codec).add(RESETSTAT);
        return createCommand(CONFIG, new StatusOutput(codec), args);
    }

    public Command configSet(String parameter, String value) {
        LettuceAssert.notNull(parameter, "Parameter " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(parameter, "Parameter " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(value, "Value " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).add(SET).add(parameter).add(value);
        return createCommand(CONFIG, new StatusOutput(codec), args);
    }

    public Command dbsize() {
        return createCommand(DBSIZE, new IntegerOutput(codec));
    }

    public Command debugCrashAndRecover(Long delay) {
        CommandArgs args = new CommandArgs(codec).add("CRASH-AND-RECOVER");
        if (delay != null) {
            args.add(delay);
        }
        return createCommand(DEBUG, new StatusOutput(codec), args);
    }

    public Command debugObject(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).add(OBJECT).addKey(key);
        return createCommand(DEBUG, new StatusOutput(codec), args);
    }

    public Command debugOom() {
        return createCommand(DEBUG, null, new CommandArgs(codec).add("OOM"));
    }

    public Command debugHtstats(int db) {
        CommandArgs args = new CommandArgs(codec).add(HTSTATS).add(db);
        return createCommand(DEBUG, new StatusOutput(codec), args);
    }

    public Command debugReload() {
        return createCommand(DEBUG, new StatusOutput(codec), new CommandArgs(codec).add(RELOAD));
    }

    public Command debugRestart(Long delay) {
        CommandArgs args = new CommandArgs(codec).add(RESTART);
        if (delay != null) {
            args.add(delay);
        }
        return createCommand(DEBUG, new StatusOutput(codec), args);
    }

    public Command debugSdslen(K key) {
        notNullKey(key);

        return createCommand(DEBUG, new StatusOutput(codec), new CommandArgs(codec).add("SDSLEN").addKey(key));
    }

    public Command debugSegfault() {
        CommandArgs args = new CommandArgs(codec).add(SEGFAULT);
        return createCommand(DEBUG, null, args);
    }

    public Command decr(K key) {
        notNullKey(key);

        return createCommand(DECR, new IntegerOutput(codec), key);
    }

    public Command decrby(K key, long amount) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(amount);
        return createCommand(DECRBY, new IntegerOutput(codec), args);
    }

    public Command del(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(DEL, new IntegerOutput(codec), args);
    }

    public Command del(Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(DEL, new IntegerOutput(codec), args);
    }

    public Command unlink(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(UNLINK, new IntegerOutput(codec), args);
    }

    public Command unlink(Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(UNLINK, new IntegerOutput(codec), args);
    }

    public Command discard() {
        return createCommand(DISCARD, new StatusOutput(codec));
    }

    public Command dump(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key);
        return createCommand(DUMP, new ByteArrayOutput(codec), args);
    }

    public Command echo(V msg) {
        LettuceAssert.notNull(msg, "message " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addValue(msg);
        return createCommand(ECHO, new ValueOutput(codec), args);
    }

    public  Command eval(String script, ScriptOutputType type, K... keys) {
        LettuceAssert.notNull(script, "Script " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(script, "Script " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(type, "ScriptOutputType " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.add(script).add(keys.length).addKeys(keys);
        CommandOutput output = newScriptOutput(codec, type);
        return createCommand(EVAL, output, args);
    }

    public  Command eval(String script, ScriptOutputType type, K[] keys, V... values) {
        LettuceAssert.notNull(script, "Script " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(script, "Script " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(type, "ScriptOutputType " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(values, "Values " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.add(script).add(keys.length).addKeys(keys).addValues(values);
        CommandOutput output = newScriptOutput(codec, type);
        return createCommand(EVAL, output, args);
    }

    public  Command evalsha(String digest, ScriptOutputType type, K... keys) {
        LettuceAssert.notNull(digest, "Digest " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(digest, "Digest " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(type, "ScriptOutputType " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.add(digest).add(keys.length).addKeys(keys);
        CommandOutput output = newScriptOutput(codec, type);
        return createCommand(EVALSHA, output, args);
    }

    public  Command evalsha(String digest, ScriptOutputType type, K[] keys, V... values) {
        LettuceAssert.notNull(digest, "Digest " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(digest, "Digest " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(type, "ScriptOutputType " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(values, "Values " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.add(digest).add(keys.length).addKeys(keys).addValues(values);
        CommandOutput output = newScriptOutput(codec, type);
        return createCommand(EVALSHA, output, args);
    }

    public Command exists(K key) {
        notNullKey(key);

        return createCommand(EXISTS, new BooleanOutput(codec), key);
    }

    public Command exists(K... keys) {
        notEmpty(keys);

        return createCommand(EXISTS, new IntegerOutput(codec), new CommandArgs(codec).addKeys(keys));
    }

    public Command exists(Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        return createCommand(EXISTS, new IntegerOutput(codec), new CommandArgs(codec).addKeys(keys));
    }

    public Command expire(K key, long seconds) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(seconds);
        return createCommand(EXPIRE, new BooleanOutput(codec), args);
    }

    public Command expireat(K key, long timestamp) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(timestamp);
        return createCommand(EXPIREAT, new BooleanOutput(codec), args);
    }

    public Command flushall() {
        return createCommand(FLUSHALL, new StatusOutput(codec));
    }

    public Command flushallAsync() {
        return createCommand(FLUSHALL, new StatusOutput(codec), new CommandArgs(codec).add(ASYNC));
    }

    public Command flushdb() {
        return createCommand(FLUSHDB, new StatusOutput(codec));
    }

    public Command flushdbAsync() {
        return createCommand(FLUSHDB, new StatusOutput(codec), new CommandArgs(codec).add(ASYNC));
    }

    public Command get(K key) {
        notNullKey(key);

        return createCommand(GET, new ValueOutput(codec), key);
    }

    public Command getbit(K key, long offset) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(offset);
        return createCommand(GETBIT, new IntegerOutput(codec), args);
    }

    public Command getrange(K key, long start, long end) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(end);
        return createCommand(GETRANGE, new ValueOutput(codec), args);
    }

    public Command getset(K key, V value) {
        notNullKey(key);

        return createCommand(GETSET, new ValueOutput(codec), key, value);
    }

    public Command hdel(K key, K... fields) {
        notNullKey(key);
        LettuceAssert.notNull(fields, "Fields " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(fields, "Fields " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields);
        return createCommand(HDEL, new IntegerOutput(codec), args);
    }

    public Command hexists(K key, K field) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field);
        return createCommand(HEXISTS, new BooleanOutput(codec), args);
    }

    public Command hget(K key, K field) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field);
        return createCommand(HGET, new ValueOutput(codec), args);
    }

    public Command hincrby(K key, K field, long amount) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field).add(amount);
        return createCommand(HINCRBY, new IntegerOutput(codec), args);
    }

    public Command hincrbyfloat(K key, K field, double amount) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field).add(amount);
        return createCommand(HINCRBYFLOAT, new DoubleOutput(codec), args);
    }

    public Command> hgetall(K key) {
        notNullKey(key);

        return createCommand(HGETALL, new MapOutput(codec), key);
    }

    public Command hgetall(KeyValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return createCommand(HGETALL, new KeyValueStreamingOutput(codec, channel), key);
    }

    public Command> hkeys(K key) {
        notNullKey(key);

        return createCommand(HKEYS, new KeyListOutput(codec), key);
    }

    public Command hkeys(KeyStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return createCommand(HKEYS, new KeyStreamingOutput(codec, channel), key);
    }

    public Command hlen(K key) {
        notNullKey(key);

        return createCommand(HLEN, new IntegerOutput(codec), key);
    }

    public Command hstrlen(K key, K field) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field);
        return createCommand(HSTRLEN, new IntegerOutput(codec), args);
    }

    public Command> hmget(K key, K... fields) {
        notNullKey(key);
        LettuceAssert.notNull(fields, "Fields " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(fields, "Fields " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields);
        return createCommand(HMGET, new ValueListOutput(codec), args);
    }

    public Command>> hmgetKeyValue(K key, K... fields) {
        notNullKey(key);
        LettuceAssert.notNull(fields, "Fields " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(fields, "Fields " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields);
        return createCommand(HMGET, new KeyValueListOutput(codec, Arrays.asList(fields)), args);
    }

    public Command hmget(ValueStreamingChannel channel, K key, K... fields) {
        notNullKey(key);
        LettuceAssert.notNull(fields, "Fields " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(fields, "Fields " + MUST_NOT_BE_EMPTY);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields);
        return createCommand(HMGET, new ValueStreamingOutput(codec, channel), args);
    }

    public Command hmget(KeyValueStreamingChannel channel, K key, K... fields) {
        notNullKey(key);
        LettuceAssert.notNull(fields, "Fields " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(fields, "Fields " + MUST_NOT_BE_EMPTY);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields);
        return createCommand(HMGET, new KeyValueStreamingOutput(codec, channel, Arrays.asList(fields)), args);
    }

    public Command hmset(K key, Map map) {
        notNullKey(key);
        LettuceAssert.notNull(map, "Map " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!map.isEmpty(), "Map " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(map);
        return createCommand(HMSET, new StatusOutput(codec), args);
    }

    public Command hset(K key, K field, V value) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field).addValue(value);
        return createCommand(HSET, new BooleanOutput(codec), args);
    }

    public Command hsetnx(K key, K field, V value) {
        notNullKey(key);
        LettuceAssert.notNull(field, "Field " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(field).addValue(value);
        return createCommand(HSETNX, new BooleanOutput(codec), args);
    }

    public Command> hvals(K key) {
        notNullKey(key);

        return createCommand(HVALS, new ValueListOutput(codec), key);
    }

    public Command hvals(ValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return createCommand(HVALS, new ValueStreamingOutput(codec, channel), key);
    }

    public Command incr(K key) {
        notNullKey(key);

        return createCommand(INCR, new IntegerOutput(codec), key);
    }

    public Command incrby(K key, long amount) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(amount);
        return createCommand(INCRBY, new IntegerOutput(codec), args);
    }

    public Command incrbyfloat(K key, double amount) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(amount);
        return createCommand(INCRBYFLOAT, new DoubleOutput(codec), args);
    }

    public Command info() {
        return createCommand(INFO, new StatusOutput(codec));
    }

    public Command info(String section) {
        LettuceAssert.notNull(section, "Section " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).add(section);
        return createCommand(INFO, new StatusOutput(codec), args);
    }

    public Command> keys(K pattern) {
        LettuceAssert.notNull(pattern, "Pattern " + MUST_NOT_BE_NULL);

        return createCommand(KEYS, new KeyListOutput(codec), pattern);
    }

    public Command keys(KeyStreamingChannel channel, K pattern) {
        LettuceAssert.notNull(pattern, "Pattern " + MUST_NOT_BE_NULL);
        notNull(channel);

        return createCommand(KEYS, new KeyStreamingOutput(codec, channel), pattern);
    }

    public Command lastsave() {
        return createCommand(LASTSAVE, new DateOutput(codec));
    }

    public Command lindex(K key, long index) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(index);
        return createCommand(LINDEX, new ValueOutput(codec), args);
    }

    public Command linsert(K key, boolean before, V pivot, V value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(before ? BEFORE : AFTER).addValue(pivot).addValue(value);
        return createCommand(LINSERT, new IntegerOutput(codec), args);
    }

    public Command llen(K key) {
        notNullKey(key);

        return createCommand(LLEN, new IntegerOutput(codec), key);
    }

    public Command lpop(K key) {
        notNullKey(key);

        return createCommand(LPOP, new ValueOutput(codec), key);
    }

    public Command lpush(K key, V... values) {
        notNullKey(key);
        notEmptyValues(values);

        return createCommand(LPUSH, new IntegerOutput(codec), key, values);
    }

    public Command lpushx(K key, V... values) {
        notNullKey(key);
        notEmptyValues(values);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValues(values);

        return createCommand(LPUSHX, new IntegerOutput(codec), args);
    }

    public Command> lrange(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(LRANGE, new ValueListOutput(codec), args);
    }

    public Command lrange(ValueStreamingChannel channel, K key, long start, long stop) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(LRANGE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command lrem(K key, long count, V value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(count).addValue(value);
        return createCommand(LREM, new IntegerOutput(codec), args);
    }

    public Command lset(K key, long index, V value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(index).addValue(value);
        return createCommand(LSET, new StatusOutput(codec), args);
    }

    public Command ltrim(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(LTRIM, new StatusOutput(codec), args);
    }

    public Command migrate(String host, int port, K key, int db, long timeout) {
        LettuceAssert.notNull(host, "Host " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(host, "Host " + MUST_NOT_BE_EMPTY);
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.add(host).add(port).addKey(key).add(db).add(timeout);
        return createCommand(MIGRATE, new StatusOutput(codec), args);
    }

    public Command migrate(String host, int port, int db, long timeout, MigrateArgs migrateArgs) {
        LettuceAssert.notNull(host, "Host " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(host, "Host " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(migrateArgs, "migrateArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);

        args.add(host).add(port);

        if (migrateArgs.keys.size() == 1) {
            args.addKey(migrateArgs.keys.get(0));
        } else {
            args.add("");
        }

        args.add(db).add(timeout);
        migrateArgs.build(args);

        return createCommand(MIGRATE, new StatusOutput(codec), args);
    }

    public Command> mget(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new ValueListOutput(codec), args);
    }

    public Command>> mgetKeyValue(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new KeyValueListOutput(codec, Arrays.asList(keys)), args);
    }

    public Command> mget(Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new ValueListOutput(codec), args);
    }

    public Command>> mgetKeyValue(Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new KeyValueListOutput(codec, keys), args);
    }

    public Command mget(ValueStreamingChannel channel, K... keys) {
        notEmpty(keys);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new ValueStreamingOutput(codec, channel), args);
    }

    public Command mget(KeyValueStreamingChannel channel, K... keys) {
        notEmpty(keys);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new KeyValueStreamingOutput(codec, channel, Arrays.asList(keys)), args);
    }

    public Command mget(ValueStreamingChannel channel, Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new ValueStreamingOutput(codec, channel), args);
    }

    public Command mget(KeyValueStreamingChannel channel, Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(MGET, new KeyValueStreamingOutput(codec, channel, keys), args);
    }

    public Command move(K key, int db) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(db);
        return createCommand(MOVE, new BooleanOutput(codec), args);
    }

    public Command multi() {
        return createCommand(MULTI, new StatusOutput(codec));
    }

    public Command mset(Map map) {
        LettuceAssert.notNull(map, "Map " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!map.isEmpty(), "Map " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(map);
        return createCommand(MSET, new StatusOutput(codec), args);
    }

    public Command msetnx(Map map) {
        LettuceAssert.notNull(map, "Map " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!map.isEmpty(), "Map " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(map);
        return createCommand(MSETNX, new BooleanOutput(codec), args);
    }

    public Command objectEncoding(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).add(ENCODING).addKey(key);
        return createCommand(OBJECT, new StatusOutput(codec), args);
    }

    public Command objectIdletime(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).add(IDLETIME).addKey(key);
        return createCommand(OBJECT, new IntegerOutput(codec), args);
    }

    public Command objectRefcount(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).add(REFCOUNT).addKey(key);
        return createCommand(OBJECT, new IntegerOutput(codec), args);
    }

    public Command persist(K key) {
        notNullKey(key);

        return createCommand(PERSIST, new BooleanOutput(codec), key);
    }

    public Command pexpire(K key, long milliseconds) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(milliseconds);
        return createCommand(PEXPIRE, new BooleanOutput(codec), args);
    }

    public Command pexpireat(K key, long timestamp) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(timestamp);
        return createCommand(PEXPIREAT, new BooleanOutput(codec), args);
    }

    public Command ping() {
        return createCommand(PING, new StatusOutput(codec));
    }

    public Command readOnly() {
        return createCommand(READONLY, new StatusOutput(codec));
    }

    public Command readWrite() {
        return createCommand(READWRITE, new StatusOutput(codec));
    }

    public Command pttl(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key);
        return createCommand(PTTL, new IntegerOutput(codec), args);
    }

    public Command publish(K channel, V message) {
        LettuceAssert.notNull(channel, "Channel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(channel).addValue(message);
        return createCommand(PUBLISH, new IntegerOutput(codec), args);
    }

    public Command> pubsubChannels() {
        CommandArgs args = new CommandArgs(codec).add(CHANNELS);
        return createCommand(PUBSUB, new KeyListOutput(codec), args);
    }

    public Command> pubsubChannels(K pattern) {
        LettuceAssert.notNull(pattern, "Pattern " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).add(CHANNELS).addKey(pattern);
        return createCommand(PUBSUB, new KeyListOutput(codec), args);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Command> pubsubNumsub(K... pattern) {
        LettuceAssert.notNull(pattern, "Pattern " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(pattern, "Pattern " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(NUMSUB).addKeys(pattern);
        return createCommand(PUBSUB, (MapOutput) new MapOutput((RedisCodec) codec), args);
    }

    public Command pubsubNumpat() {
        CommandArgs args = new CommandArgs(codec).add(NUMPAT);
        return createCommand(PUBSUB, new IntegerOutput(codec), args);
    }

    public Command quit() {
        return createCommand(QUIT, new StatusOutput(codec));
    }

    public Command randomkey() {
        return createCommand(RANDOMKEY, new ValueOutput(codec));
    }

    public Command> role() {
        return createCommand(ROLE, new ArrayOutput(codec));
    }

    public Command rename(K key, K newKey) {
        notNullKey(key);
        LettuceAssert.notNull(newKey, "NewKey " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(newKey);
        return createCommand(RENAME, new StatusOutput(codec), args);
    }

    public Command renamenx(K key, K newKey) {
        notNullKey(key);
        LettuceAssert.notNull(newKey, "NewKey " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKey(newKey);
        return createCommand(RENAMENX, new BooleanOutput(codec), args);
    }

    public Command restore(K key, long ttl, byte[] value) {
        notNullKey(key);
        LettuceAssert.notNull(value, "Value " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(ttl).add(value);
        return createCommand(RESTORE, new StatusOutput(codec), args);
    }

    public Command rpop(K key) {
        notNullKey(key);

        return createCommand(RPOP, new ValueOutput(codec), key);
    }

    public Command rpoplpush(K source, K destination) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(source).addKey(destination);
        return createCommand(RPOPLPUSH, new ValueOutput(codec), args);
    }

    public Command rpush(K key, V... values) {
        notNullKey(key);
        notEmptyValues(values);

        return createCommand(RPUSH, new IntegerOutput(codec), key, values);
    }

    public Command rpushx(K key, V... values) {
        notNullKey(key);
        notEmptyValues(values);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValues(values);
        return createCommand(RPUSHX, new IntegerOutput(codec), args);
    }

    public Command sadd(K key, V... members) {
        notNullKey(key);
        LettuceAssert.notNull(members, "Members " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(members, "Members " + MUST_NOT_BE_EMPTY);

        return createCommand(SADD, new IntegerOutput(codec), key, members);
    }

    public Command save() {
        return createCommand(SAVE, new StatusOutput(codec));
    }

    public Command scard(K key) {
        notNullKey(key);

        return createCommand(SCARD, new IntegerOutput(codec), key);
    }

    public Command> scriptExists(String... digests) {
        LettuceAssert.notNull(digests, "Digests " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(digests, "Digests " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(digests, "Digests " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).add(EXISTS);
        for (String sha : digests) {
            args.add(sha);
        }
        return createCommand(SCRIPT, new BooleanListOutput(codec), args);
    }

    public Command scriptFlush() {
        CommandArgs args = new CommandArgs(codec).add(FLUSH);
        return createCommand(SCRIPT, new StatusOutput(codec), args);
    }

    public Command scriptKill() {
        CommandArgs args = new CommandArgs(codec).add(KILL);
        return createCommand(SCRIPT, new StatusOutput(codec), args);
    }

    public Command scriptLoad(V script) {
        LettuceAssert.notNull(script, "Script " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).add(LOAD).addValue(script);
        return createCommand(SCRIPT, new StatusOutput(codec), args);
    }

    public Command> sdiff(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(SDIFF, new ValueSetOutput(codec), args);
    }

    public Command sdiff(ValueStreamingChannel channel, K... keys) {
        notEmpty(keys);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(SDIFF, new ValueStreamingOutput(codec, channel), args);
    }

    public Command sdiffstore(K destination, K... keys) {
        notEmpty(keys);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(destination).addKeys(keys);
        return createCommand(SDIFFSTORE, new IntegerOutput(codec), args);
    }

    public Command select(int db) {
        CommandArgs args = new CommandArgs(codec).add(db);
        return createCommand(SELECT, new StatusOutput(codec), args);
    }

    public Command swapdb(int db1, int db2) {
        CommandArgs args = new CommandArgs(codec).add(db1).add(db2);
        return createCommand(SWAPDB, new StatusOutput(codec), args);
    }

    public Command set(K key, V value) {
        notNullKey(key);

        return createCommand(SET, new StatusOutput(codec), key, value);
    }

    public Command set(K key, V value, SetArgs setArgs) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValue(value);
        setArgs.build(args);
        return createCommand(SET, new StatusOutput(codec), args);
    }

    public Command setbit(K key, long offset, int value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(offset).add(value);
        return createCommand(SETBIT, new IntegerOutput(codec), args);
    }

    public Command setex(K key, long seconds, V value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(seconds).addValue(value);
        return createCommand(SETEX, new StatusOutput(codec), args);
    }

    public Command psetex(K key, long milliseconds, V value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(milliseconds).addValue(value);
        return createCommand(PSETEX, new StatusOutput(codec), args);
    }

    public Command setnx(K key, V value) {
        notNullKey(key);
        return createCommand(SETNX, new BooleanOutput(codec), key, value);
    }

    public Command setrange(K key, long offset, V value) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(offset).addValue(value);
        return createCommand(SETRANGE, new IntegerOutput(codec), args);
    }

    public Command shutdown(boolean save) {
        CommandArgs args = new CommandArgs(codec);
        return createCommand(SHUTDOWN, new StatusOutput(codec), save ? args.add(SAVE) : args.add(NOSAVE));
    }

    public Command> sinter(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(SINTER, new ValueSetOutput(codec), args);
    }

    public Command sinter(ValueStreamingChannel channel, K... keys) {
        notEmpty(keys);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(SINTER, new ValueStreamingOutput(codec, channel), args);
    }

    public Command sinterstore(K destination, K... keys) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKey(destination).addKeys(keys);
        return createCommand(SINTERSTORE, new IntegerOutput(codec), args);
    }

    public Command sismember(K key, V member) {
        notNullKey(key);
        return createCommand(SISMEMBER, new BooleanOutput(codec), key, member);
    }

    public Command smove(K source, K destination, V member) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(source).addKey(destination).addValue(member);
        return createCommand(SMOVE, new BooleanOutput(codec), args);
    }

    public Command slaveof(String host, int port) {
        LettuceAssert.notNull(host, "Host " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(host, "Host " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(host).add(port);
        return createCommand(SLAVEOF, new StatusOutput(codec), args);
    }

    public Command slaveofNoOne() {
        CommandArgs args = new CommandArgs(codec).add(NO).add(ONE);
        return createCommand(SLAVEOF, new StatusOutput(codec), args);
    }

    public Command> slowlogGet() {
        CommandArgs args = new CommandArgs(codec).add(GET);
        return createCommand(SLOWLOG, new NestedMultiOutput(codec), args);
    }

    public Command> slowlogGet(int count) {
        CommandArgs args = new CommandArgs(codec).add(GET).add(count);
        return createCommand(SLOWLOG, new NestedMultiOutput(codec), args);
    }

    public Command slowlogLen() {
        CommandArgs args = new CommandArgs(codec).add(LEN);
        return createCommand(SLOWLOG, new IntegerOutput(codec), args);
    }

    public Command slowlogReset() {
        CommandArgs args = new CommandArgs(codec).add(RESET);
        return createCommand(SLOWLOG, new StatusOutput(codec), args);
    }

    public Command> smembers(K key) {
        notNullKey(key);

        return createCommand(SMEMBERS, new ValueSetOutput(codec), key);
    }

    public Command smembers(ValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return createCommand(SMEMBERS, new ValueStreamingOutput(codec, channel), key);
    }

    public Command> sort(K key) {
        notNullKey(key);

        return createCommand(SORT, new ValueListOutput(codec), key);
    }

    public Command> sort(K key, SortArgs sortArgs) {
        notNullKey(key);
        LettuceAssert.notNull(sortArgs, "SortArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key);
        sortArgs.build(args, null);
        return createCommand(SORT, new ValueListOutput(codec), args);
    }

    public Command sort(ValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return createCommand(SORT, new ValueStreamingOutput(codec, channel), key);
    }

    public Command sort(ValueStreamingChannel channel, K key, SortArgs sortArgs) {
        notNullKey(key);
        notNull(channel);
        LettuceAssert.notNull(sortArgs, "SortArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key);
        sortArgs.build(args, null);
        return createCommand(SORT, new ValueStreamingOutput(codec, channel), args);
    }

    public Command sortStore(K key, SortArgs sortArgs, K destination) {
        notNullKey(key);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(sortArgs, "SortArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key);
        sortArgs.build(args, destination);
        return createCommand(SORT, new IntegerOutput(codec), args);
    }

    public Command spop(K key) {
        notNullKey(key);

        return createCommand(SPOP, new ValueOutput(codec), key);
    }

    public Command> spop(K key, long count) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(count);
        return createCommand(SPOP, new ValueSetOutput(codec), args);
    }

    public Command srandmember(K key) {
        notNullKey(key);

        return createCommand(SRANDMEMBER, new ValueOutput(codec), key);
    }

    public Command> srandmember(K key, long count) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(count);
        return createCommand(SRANDMEMBER, new ValueListOutput(codec), args);
    }

    public Command srandmember(ValueStreamingChannel channel, K key, long count) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(count);
        return createCommand(SRANDMEMBER, new ValueStreamingOutput(codec, channel), args);
    }

    public Command srem(K key, V... members) {
        notNullKey(key);
        LettuceAssert.notNull(members, "Members " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(members, "Members " + MUST_NOT_BE_EMPTY);

        return createCommand(SREM, new IntegerOutput(codec), key, members);
    }

    public Command> sunion(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(SUNION, new ValueSetOutput(codec), args);
    }

    public Command sunion(ValueStreamingChannel channel, K... keys) {
        notEmpty(keys);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(SUNION, new ValueStreamingOutput(codec, channel), args);
    }

    public Command sunionstore(K destination, K... keys) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKey(destination).addKeys(keys);
        return createCommand(SUNIONSTORE, new IntegerOutput(codec), args);
    }

    public Command sync() {
        return createCommand(SYNC, new StatusOutput(codec));
    }

    public Command strlen(K key) {
        notNullKey(key);

        return createCommand(STRLEN, new IntegerOutput(codec), key);
    }

    public Command touch(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(TOUCH, new IntegerOutput(codec), args);
    }

    public Command touch(Iterable keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(TOUCH, new IntegerOutput(codec), args);
    }

    public Command ttl(K key) {
        notNullKey(key);

        return createCommand(TTL, new IntegerOutput(codec), key);
    }

    public Command type(K key) {
        notNullKey(key);

        return createCommand(TYPE, new StatusOutput(codec), key);
    }

    public Command watch(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(WATCH, new StatusOutput(codec), args);
    }

    public Command wait(int replicas, long timeout) {
        CommandArgs args = new CommandArgs(codec).add(replicas).add(timeout);

        return createCommand(WAIT, new IntegerOutput(codec), args);
    }

    public Command unwatch() {
        return createCommand(UNWATCH, new StatusOutput(codec));
    }

    public Command zadd(K key, ZAddArgs zAddArgs, double score, V member) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key);

        if (zAddArgs != null) {
            zAddArgs.build(args);
        }
        args.add(score).addValue(member);

        return createCommand(ZADD, new IntegerOutput(codec), args);
    }

    public Command zaddincr(K key, ZAddArgs zAddArgs, double score, V member) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key);

        if (zAddArgs != null) {
            zAddArgs.build(args);
        }

        args.add(INCR);
        args.add(score).addValue(member);

        return createCommand(ZADD, new DoubleOutput(codec), args);
    }

    @SuppressWarnings("unchecked")
    public Command zadd(K key, ZAddArgs zAddArgs, Object... scoresAndValues) {
        notNullKey(key);
        LettuceAssert.notNull(scoresAndValues, "ScoresAndValues " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(scoresAndValues, "ScoresAndValues " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(scoresAndValues, "ScoresAndValues " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).addKey(key);

        if (zAddArgs != null) {
            zAddArgs.build(args);
        }

        if (allElementsInstanceOf(scoresAndValues, ScoredValue.class)) {

            for (Object o : scoresAndValues) {
                ScoredValue scoredValue = (ScoredValue) o;

                args.add(scoredValue.getScore());
                args.addValue(scoredValue.getValue());
            }

        } else {
            LettuceAssert.isTrue(scoresAndValues.length % 2 == 0,
                    "ScoresAndValues.length must be a multiple of 2 and contain a "
                            + "sequence of score1, value1, score2, value2, scoreN,valueN");

            for (int i = 0; i < scoresAndValues.length; i += 2) {
                args.add((Double) scoresAndValues[i]);
                args.addValue((V) scoresAndValues[i + 1]);
            }
        }

        return createCommand(ZADD, new IntegerOutput(codec), args);
    }

    private boolean allElementsInstanceOf(Object[] objects, Class expectedAssignableType) {

        for (Object object : objects) {
            if (!expectedAssignableType.isAssignableFrom(object.getClass())) {
                return false;
            }
        }

        return true;
    }

    public Command zcard(K key) {
        notNullKey(key);

        return createCommand(ZCARD, new IntegerOutput(codec), key);
    }

    public Command zcount(K key, double min, double max) {
        return zcount(key, string(min), string(max));
    }

    public Command zcount(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(min).add(max);
        return createCommand(ZCOUNT, new IntegerOutput(codec), args);
    }

    public Command zcount(K key, Range range) {
        notNullKey(key);
        notNullRange(range);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(min(range)).add(max(range));
        return createCommand(ZCOUNT, new IntegerOutput(codec), args);
    }

    public Command zincrby(K key, double amount, K member) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(amount).addKey(member);
        return createCommand(ZINCRBY, new DoubleOutput(codec), args);
    }

    public Command zinterstore(K destination, K... keys) {
        notEmpty(keys);

        return zinterstore(destination, new ZStoreArgs(), keys);
    }

    public Command zinterstore(K destination, ZStoreArgs storeArgs, K... keys) {
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(storeArgs, "ZStoreArgs " + MUST_NOT_BE_NULL);
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKey(destination).add(keys.length).addKeys(keys);
        storeArgs.build(args);
        return createCommand(ZINTERSTORE, new IntegerOutput(codec), args);
    }

    public Command> zrange(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(ZRANGE, new ValueListOutput(codec), args);
    }

    public Command>> zrangeWithScores(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(start).add(stop).add(WITHSCORES);
        return createCommand(ZRANGE, new ScoredValueListOutput(codec), args);
    }

    public Command> zrangebyscore(K key, double min, double max) {
        return zrangebyscore(key, string(min), string(max));
    }

    public Command> zrangebyscore(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(min).add(max);
        return createCommand(ZRANGEBYSCORE, new ValueListOutput(codec), args);
    }

    public Command> zrangebyscore(K key, double min, double max, long offset, long count) {
        return zrangebyscore(key, string(min), string(max), offset, count);
    }

    public Command> zrangebyscore(K key, String min, String max, long offset, long count) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min).add(max).add(LIMIT).add(offset).add(count);
        return createCommand(ZRANGEBYSCORE, new ValueListOutput(codec), args);
    }

    public Command> zrangebyscore(K key, Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min(range)).add(max(range));

        if (limit.isLimited()) {
            args.add(LIMIT).add(limit.getOffset()).add(limit.getCount());
        }
        return createCommand(ZRANGEBYSCORE, new ValueListOutput(codec), args);
    }

    public Command>> zrangebyscoreWithScores(K key, double min, double max) {
        return zrangebyscoreWithScores(key, string(min), string(max));
    }

    public Command>> zrangebyscoreWithScores(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min).add(max).add(WITHSCORES);
        return createCommand(ZRANGEBYSCORE, new ScoredValueListOutput(codec), args);
    }

    public Command>> zrangebyscoreWithScores(K key, double min, double max, long offset, long count) {
        return zrangebyscoreWithScores(key, string(min), string(max), offset, count);
    }

    public Command>> zrangebyscoreWithScores(K key, String min, String max, long offset, long count) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min).add(max).add(WITHSCORES), Limit.create(offset, count));
        return createCommand(ZRANGEBYSCORE, new ScoredValueListOutput(codec), args);
    }

    public Command>> zrangebyscoreWithScores(K key, Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min(range)).add(max(range)).add(WITHSCORES), limit);
        return createCommand(ZRANGEBYSCORE, new ScoredValueListOutput(codec), args);
    }

    public Command zrange(ValueStreamingChannel channel, K key, long start, long stop) {
        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(ZRANGE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrangeWithScores(ScoredValueStreamingChannel channel, K key, long start, long stop) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(start).add(stop).add(WITHSCORES);
        return createCommand(ZRANGE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrangebyscore(ValueStreamingChannel channel, K key, double min, double max) {
        return zrangebyscore(channel, key, string(min), string(max));
    }

    public Command zrangebyscore(ValueStreamingChannel channel, K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);
        LettuceAssert.notNull(channel, "ScoredValueStreamingChannel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(min).add(max);
        return createCommand(ZRANGEBYSCORE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrangebyscore(ValueStreamingChannel channel, K key, double min, double max, long offset,
            long count) {
        return zrangebyscore(channel, key, string(min), string(max), offset, count);
    }

    public Command zrangebyscore(ValueStreamingChannel channel, K key, String min, String max, long offset,
            long count) {
        notNullKey(key);
        notNullMinMax(min, max);
        LettuceAssert.notNull(channel, "ScoredValueStreamingChannel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min).add(max), Limit.create(offset, count));
        return createCommand(ZRANGEBYSCORE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrangebyscore(ValueStreamingChannel channel, K key, Range range,
            Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);
        LettuceAssert.notNull(channel, "ScoredValueStreamingChannel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min(range)).add(max(range)), limit);
        return createCommand(ZRANGEBYSCORE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, double min, double max) {
        return zrangebyscoreWithScores(channel, key, string(min), string(max));
    }

    public Command zrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min).add(max).add(WITHSCORES);
        return createCommand(ZRANGEBYSCORE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, double min, double max,
            long offset, long count) {
        return zrangebyscoreWithScores(channel, key, string(min), string(max), offset, count);
    }

    public Command zrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, String min, String max,
            long offset, long count) {
        notNullKey(key);
        notNullMinMax(min, max);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min).add(max).add(WITHSCORES), Limit.create(offset, count));
        return createCommand(ZRANGEBYSCORE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key,
            Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min(range)).add(max(range)).add(WITHSCORES), limit);
        return createCommand(ZRANGEBYSCORE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrank(K key, V member) {
        notNullKey(key);

        return createCommand(ZRANK, new IntegerOutput(codec), key, member);
    }

    public Command zrem(K key, V... members) {
        notNullKey(key);
        LettuceAssert.notNull(members, "Members " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(members, "Members " + MUST_NOT_BE_EMPTY);

        return createCommand(ZREM, new IntegerOutput(codec), key, members);
    }

    public Command zremrangebyrank(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(ZREMRANGEBYRANK, new IntegerOutput(codec), args);
    }

    public Command zremrangebyscore(K key, double min, double max) {
        return zremrangebyscore(key, string(min), string(max));
    }

    public Command zremrangebyscore(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(min).add(max);
        return createCommand(ZREMRANGEBYSCORE, new IntegerOutput(codec), args);
    }

    public Command zremrangebyscore(K key, Range range) {
        notNullKey(key);
        notNullRange(range);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(min(range)).add(max(range));
        return createCommand(ZREMRANGEBYSCORE, new IntegerOutput(codec), args);
    }

    public Command> zrevrange(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(ZREVRANGE, new ValueListOutput(codec), args);
    }

    public Command> zrevrangebylex(K key, Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(maxValue(range)).add(minValue(range)), limit);
        return createCommand(ZREVRANGEBYLEX, new ValueListOutput(codec), args);
    }

    public Command>> zrevrangeWithScores(K key, long start, long stop) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(start).add(stop).add(WITHSCORES);
        return createCommand(ZREVRANGE, new ScoredValueListOutput(codec), args);
    }

    public Command> zrevrangebyscore(K key, double max, double min) {
        return zrevrangebyscore(key, string(max), string(min));
    }

    public Command> zrevrangebyscore(K key, String max, String min) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(max).add(min);
        return createCommand(ZREVRANGEBYSCORE, new ValueListOutput(codec), args);
    }

    public Command> zrevrangebyscore(K key, double max, double min, long offset, long count) {
        return zrevrangebyscore(key, string(max), string(min), offset, count);
    }

    public Command> zrevrangebyscore(K key, String max, String min, long offset, long count) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max).add(min), Limit.create(offset, count));
        return createCommand(ZREVRANGEBYSCORE, new ValueListOutput(codec), args);
    }

    public Command> zrevrangebyscore(K key, Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max(range)).add(min(range)), limit);
        return createCommand(ZREVRANGEBYSCORE, new ValueListOutput(codec), args);
    }

    public Command>> zrevrangebyscoreWithScores(K key, double max, double min) {
        return zrevrangebyscoreWithScores(key, string(max), string(min));
    }

    public Command>> zrevrangebyscoreWithScores(K key, String max, String min) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(max).add(min).add(WITHSCORES);
        return createCommand(ZREVRANGEBYSCORE, new ScoredValueListOutput(codec), args);
    }

    public Command>> zrevrangebyscoreWithScores(K key, double max, double min, long offset,
            long count) {
        return zrevrangebyscoreWithScores(key, string(max), string(min), offset, count);
    }

    public Command>> zrevrangebyscoreWithScores(K key, String max, String min, long offset,
            long count) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max).add(min).add(WITHSCORES), Limit.create(offset, count));
        return createCommand(ZREVRANGEBYSCORE, new ScoredValueListOutput(codec), args);
    }

    public Command>> zrevrangebyscoreWithScores(K key, Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max(range)).add(min(range)).add(WITHSCORES), limit);
        return createCommand(ZREVRANGEBYSCORE, new ScoredValueListOutput(codec), args);
    }

    public Command zrevrange(ValueStreamingChannel channel, K key, long start, long stop) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(start).add(stop);
        return createCommand(ZREVRANGE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangeWithScores(ScoredValueStreamingChannel channel, K key, long start, long stop) {
        notNullKey(key);
        LettuceAssert.notNull(channel, "ValueStreamingChannel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(start).add(stop).add(WITHSCORES);
        return createCommand(ZREVRANGE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangebyscore(ValueStreamingChannel channel, K key, double max, double min) {
        return zrevrangebyscore(channel, key, string(max), string(min));
    }

    public Command zrevrangebyscore(ValueStreamingChannel channel, K key, String max, String min) {
        notNullKey(key);
        notNullMinMax(min, max);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(max).add(min);
        return createCommand(ZREVRANGEBYSCORE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangebyscore(ValueStreamingChannel channel, K key, double max, double min, long offset,
            long count) {
        return zrevrangebyscore(channel, key, string(max), string(min), offset, count);
    }

    public Command zrevrangebyscore(ValueStreamingChannel channel, K key, String max, String min, long offset,
            long count) {
        notNullKey(key);
        notNullMinMax(min, max);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max).add(min), Limit.create(offset, count));
        return createCommand(ZREVRANGEBYSCORE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangebyscore(ValueStreamingChannel channel, K key, Range range,
            Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max(range)).add(min(range)), limit);
        return createCommand(ZREVRANGEBYSCORE, new ValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, double max,
            double min) {
        return zrevrangebyscoreWithScores(channel, key, string(max), string(min));
    }

    public Command zrevrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, String max,
            String min) {
        notNullKey(key);
        notNullMinMax(min, max);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(max).add(min).add(WITHSCORES);
        return createCommand(ZREVRANGEBYSCORE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, double max, double min,
            long offset, long count) {
        notNullKey(key);
        LettuceAssert.notNull(min, "Min " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(max, "Max " + MUST_NOT_BE_NULL);
        notNull(channel);
        return zrevrangebyscoreWithScores(channel, key, string(max), string(min), offset, count);
    }

    public Command zrevrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, String max, String min,
            long offset, long count) {
        notNullKey(key);
        notNullMinMax(min, max);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max).add(min).add(WITHSCORES), Limit.create(offset, count));
        return createCommand(ZREVRANGEBYSCORE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key,
            Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(max(range)).add(min(range)).add(WITHSCORES), limit);
        return createCommand(ZREVRANGEBYSCORE, new ScoredValueStreamingOutput(codec, channel), args);
    }

    public Command zrevrank(K key, V member) {
        notNullKey(key);

        return createCommand(ZREVRANK, new IntegerOutput(codec), key, member);
    }

    public Command zscore(K key, V member) {
        notNullKey(key);

        return createCommand(ZSCORE, new DoubleOutput(codec), key, member);
    }

    public Command zunionstore(K destination, K... keys) {
        notEmpty(keys);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);

        return zunionstore(destination, new ZStoreArgs(), keys);
    }

    public Command zunionstore(K destination, ZStoreArgs storeArgs, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(destination).add(keys.length).addKeys(keys);
        storeArgs.build(args);
        return createCommand(ZUNIONSTORE, new IntegerOutput(codec), args);
    }

    public RedisCommand zlexcount(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min).add(max);
        return createCommand(ZLEXCOUNT, new IntegerOutput(codec), args);
    }

    public RedisCommand zlexcount(K key, Range range) {
        notNullKey(key);
        notNullRange(range);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(minValue(range)).add(maxValue(range));
        return createCommand(ZLEXCOUNT, new IntegerOutput(codec), args);
    }

    public RedisCommand zremrangebylex(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min).add(max);
        return createCommand(ZREMRANGEBYLEX, new IntegerOutput(codec), args);
    }

    public RedisCommand zremrangebylex(K key, Range range) {
        notNullKey(key);
        notNullRange(range);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(minValue(range)).add(maxValue(range));
        return createCommand(ZREMRANGEBYLEX, new IntegerOutput(codec), args);
    }

    public RedisCommand> zrangebylex(K key, String min, String max) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key).add(min).add(max);
        return createCommand(ZRANGEBYLEX, new ValueListOutput(codec), args);
    }

    public RedisCommand> zrangebylex(K key, String min, String max, long offset, long count) {
        notNullKey(key);
        notNullMinMax(min, max);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(min).add(max), Limit.create(offset, count));
        return createCommand(ZRANGEBYLEX, new ValueListOutput(codec), args);
    }

    public RedisCommand> zrangebylex(K key, Range range, Limit limit) {
        notNullKey(key);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs(codec);
        addLimit(args.addKey(key).add(minValue(range)).add(maxValue(range)), limit);
        return createCommand(ZRANGEBYLEX, new ValueListOutput(codec), args);
    }

    public Command> time() {
        CommandArgs args = new CommandArgs(codec);
        return createCommand(TIME, new ValueListOutput(codec), args);
    }

    public Command> scan() {
        return scan(ScanCursor.INITIAL, null);
    }

    public Command> scan(ScanCursor scanCursor) {
        return scan(scanCursor, null);
    }

    public Command> scan(ScanArgs scanArgs) {
        return scan(ScanCursor.INITIAL, scanArgs);
    }

    public Command> scan(ScanCursor scanCursor, ScanArgs scanArgs) {
        CommandArgs args = new CommandArgs(codec);

        scanArgs(scanCursor, scanArgs, args);

        KeyScanOutput output = new KeyScanOutput(codec);
        return createCommand(SCAN, output, args);
    }

    protected void scanArgs(ScanCursor scanCursor, ScanArgs scanArgs, CommandArgs args) {
        LettuceAssert.notNull(scanCursor, "ScanCursor " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!scanCursor.isFinished(), "ScanCursor must not be finished");

        args.add(scanCursor.getCursor());

        if (scanArgs != null) {
            scanArgs.build(args);
        }
    }

    public Command scanStreaming(KeyStreamingChannel channel) {
        notNull(channel);
        LettuceAssert.notNull(channel, "KeyStreamingChannel " + MUST_NOT_BE_NULL);

        return scanStreaming(channel, ScanCursor.INITIAL, null);
    }

    public Command scanStreaming(KeyStreamingChannel channel, ScanCursor scanCursor) {
        notNull(channel);
        LettuceAssert.notNull(channel, "KeyStreamingChannel " + MUST_NOT_BE_NULL);

        return scanStreaming(channel, scanCursor, null);
    }

    public Command scanStreaming(KeyStreamingChannel channel, ScanArgs scanArgs) {
        notNull(channel);
        LettuceAssert.notNull(channel, "KeyStreamingChannel " + MUST_NOT_BE_NULL);

        return scanStreaming(channel, ScanCursor.INITIAL, scanArgs);
    }

    public Command scanStreaming(KeyStreamingChannel channel, ScanCursor scanCursor,
            ScanArgs scanArgs) {
        notNull(channel);
        LettuceAssert.notNull(channel, "KeyStreamingChannel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec);
        scanArgs(scanCursor, scanArgs, args);

        KeyScanStreamingOutput output = new KeyScanStreamingOutput(codec, channel);
        return createCommand(SCAN, output, args);
    }

    public Command> sscan(K key) {
        notNullKey(key);

        return sscan(key, ScanCursor.INITIAL, null);
    }

    public Command> sscan(K key, ScanCursor scanCursor) {
        notNullKey(key);

        return sscan(key, scanCursor, null);
    }

    public Command> sscan(K key, ScanArgs scanArgs) {
        notNullKey(key);

        return sscan(key, ScanCursor.INITIAL, scanArgs);
    }

    public Command> sscan(K key, ScanCursor scanCursor, ScanArgs scanArgs) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key);

        scanArgs(scanCursor, scanArgs, args);

        ValueScanOutput output = new ValueScanOutput(codec);
        return createCommand(SSCAN, output, args);
    }

    public Command sscanStreaming(ValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return sscanStreaming(channel, key, ScanCursor.INITIAL, null);
    }

    public Command sscanStreaming(ValueStreamingChannel channel, K key, ScanCursor scanCursor) {
        notNullKey(key);
        notNull(channel);

        return sscanStreaming(channel, key, scanCursor, null);
    }

    public Command sscanStreaming(ValueStreamingChannel channel, K key, ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        return sscanStreaming(channel, key, ScanCursor.INITIAL, scanArgs);
    }

    public Command sscanStreaming(ValueStreamingChannel channel, K key, ScanCursor scanCursor,
            ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);

        args.addKey(key);
        scanArgs(scanCursor, scanArgs, args);

        ValueScanStreamingOutput output = new ValueScanStreamingOutput(codec, channel);
        return createCommand(SSCAN, output, args);
    }

    public Command> hscan(K key) {
        notNullKey(key);

        return hscan(key, ScanCursor.INITIAL, null);
    }

    public Command> hscan(K key, ScanCursor scanCursor) {
        notNullKey(key);

        return hscan(key, scanCursor, null);
    }

    public Command> hscan(K key, ScanArgs scanArgs) {
        notNullKey(key);

        return hscan(key, ScanCursor.INITIAL, scanArgs);
    }

    public Command> hscan(K key, ScanCursor scanCursor, ScanArgs scanArgs) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key);

        scanArgs(scanCursor, scanArgs, args);

        MapScanOutput output = new MapScanOutput(codec);
        return createCommand(HSCAN, output, args);
    }

    public Command hscanStreaming(KeyValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return hscanStreaming(channel, key, ScanCursor.INITIAL, null);
    }

    public Command hscanStreaming(KeyValueStreamingChannel channel, K key,
            ScanCursor scanCursor) {
        notNullKey(key);
        notNull(channel);

        return hscanStreaming(channel, key, scanCursor, null);
    }

    public Command hscanStreaming(KeyValueStreamingChannel channel, K key, ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        return hscanStreaming(channel, key, ScanCursor.INITIAL, scanArgs);
    }

    public Command hscanStreaming(KeyValueStreamingChannel channel, K key, ScanCursor scanCursor,
            ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);

        args.addKey(key);
        scanArgs(scanCursor, scanArgs, args);

        KeyValueScanStreamingOutput output = new KeyValueScanStreamingOutput(codec, channel);
        return createCommand(HSCAN, output, args);
    }

    public Command> zscan(K key) {
        notNullKey(key);

        return zscan(key, ScanCursor.INITIAL, null);
    }

    public Command> zscan(K key, ScanCursor scanCursor) {
        notNullKey(key);

        return zscan(key, scanCursor, null);
    }

    public Command> zscan(K key, ScanArgs scanArgs) {
        notNullKey(key);

        return zscan(key, ScanCursor.INITIAL, scanArgs);
    }

    public Command> zscan(K key, ScanCursor scanCursor, ScanArgs scanArgs) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec);
        args.addKey(key);

        scanArgs(scanCursor, scanArgs, args);

        ScoredValueScanOutput output = new ScoredValueScanOutput(codec);
        return createCommand(ZSCAN, output, args);
    }

    public Command zscanStreaming(ScoredValueStreamingChannel channel, K key) {
        notNullKey(key);
        notNull(channel);

        return zscanStreaming(channel, key, ScanCursor.INITIAL, null);
    }

    public Command zscanStreaming(ScoredValueStreamingChannel channel, K key,
            ScanCursor scanCursor) {
        notNullKey(key);
        notNull(channel);

        return zscanStreaming(channel, key, scanCursor, null);
    }

    public Command zscanStreaming(ScoredValueStreamingChannel channel, K key, ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        return zscanStreaming(channel, key, ScanCursor.INITIAL, scanArgs);
    }

    public Command zscanStreaming(ScoredValueStreamingChannel channel, K key, ScanCursor scanCursor,
            ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs(codec);

        args.addKey(key);
        scanArgs(scanCursor, scanArgs, args);

        ScoredValueScanStreamingOutput output = new ScoredValueScanStreamingOutput(codec, channel);
        return createCommand(ZSCAN, output, args);
    }

    public Command pfadd(K key, V value, V... moreValues) {
        notNullKey(key);
        LettuceAssert.notNull(value, "Value " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(moreValues, "MoreValues " + MUST_NOT_BE_NULL);
        LettuceAssert.noNullElements(moreValues, "MoreValues " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValue(value).addValues(moreValues);
        return createCommand(PFADD, new IntegerOutput(codec), args);
    }

    public Command pfadd(K key, V... values) {
        notNullKey(key);
        notEmptyValues(values);
        LettuceAssert.noNullElements(values, "Values " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValues(values);
        return createCommand(PFADD, new IntegerOutput(codec), args);
    }

    public Command pfcount(K key, K... moreKeys) {
        notNullKey(key);
        LettuceAssert.notNull(moreKeys, "MoreKeys " + MUST_NOT_BE_NULL);
        LettuceAssert.noNullElements(moreKeys, "MoreKeys " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(moreKeys);
        return createCommand(PFCOUNT, new IntegerOutput(codec), args);
    }

    public Command pfcount(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs(codec).addKeys(keys);
        return createCommand(PFCOUNT, new IntegerOutput(codec), args);
    }

    @SuppressWarnings("unchecked")
    public Command pfmerge(K destkey, K sourcekey, K... moreSourceKeys) {
        LettuceAssert.notNull(destkey, "Destkey " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(sourcekey, "Sourcekey " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(moreSourceKeys, "MoreSourceKeys " + MUST_NOT_BE_NULL);
        LettuceAssert.noNullElements(moreSourceKeys, "MoreSourceKeys " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).addKeys(destkey).addKey(sourcekey).addKeys(moreSourceKeys);
        return createCommand(PFMERGE, new StatusOutput(codec), args);
    }

    @SuppressWarnings("unchecked")
    public Command pfmerge(K destkey, K... sourcekeys) {
        LettuceAssert.notNull(destkey, "Destkey " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(sourcekeys, "Sourcekeys " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(sourcekeys, "Sourcekeys " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(sourcekeys, "Sourcekeys " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs(codec).addKeys(destkey).addKeys(sourcekeys);
        return createCommand(PFMERGE, new StatusOutput(codec), args);
    }

    public Command clusterBumpepoch() {
        CommandArgs args = new CommandArgs(codec).add(BUMPEPOCH);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterMeet(String ip, int port) {
        LettuceAssert.notNull(ip, "IP " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(ip, "IP " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).add(MEET).add(ip).add(port);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterForget(String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add(FORGET).add(nodeId);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterAddslots(int[] slots) {
        notEmptySlots(slots);

        CommandArgs args = new CommandArgs(codec).add(ADDSLOTS);

        for (int slot : slots) {
            args.add(slot);
        }
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterDelslots(int[] slots) {
        notEmptySlots(slots);

        CommandArgs args = new CommandArgs(codec).add(DELSLOTS);

        for (int slot : slots) {
            args.add(slot);
        }
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterInfo() {
        CommandArgs args = new CommandArgs(codec).add(INFO);

        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterMyId() {
        CommandArgs args = new CommandArgs(codec).add(MYID);

        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterNodes() {
        CommandArgs args = new CommandArgs(codec).add(NODES);

        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command> clusterGetKeysInSlot(int slot, int count) {
        CommandArgs args = new CommandArgs(codec).add(GETKEYSINSLOT).add(slot).add(count);
        return createCommand(CLUSTER, new KeyListOutput(codec), args);
    }

    public Command clusterCountKeysInSlot(int slot) {
        CommandArgs args = new CommandArgs(codec).add(COUNTKEYSINSLOT).add(slot);
        return createCommand(CLUSTER, new IntegerOutput(codec), args);
    }

    public Command clusterCountFailureReports(String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add("COUNT-FAILURE-REPORTS").add(nodeId);
        return createCommand(CLUSTER, new IntegerOutput(codec), args);
    }

    public Command clusterKeyslot(K key) {
        CommandArgs args = new CommandArgs(codec).add(KEYSLOT).addKey(key);
        return createCommand(CLUSTER, new IntegerOutput(codec), args);
    }

    public Command clusterSaveconfig() {
        CommandArgs args = new CommandArgs(codec).add(SAVECONFIG);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterSetConfigEpoch(long configEpoch) {
        CommandArgs args = new CommandArgs(codec).add("SET-CONFIG-EPOCH").add(configEpoch);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command> clusterSlots() {
        CommandArgs args = new CommandArgs(codec).add(SLOTS);
        return createCommand(CLUSTER, new ArrayOutput(codec), args);
    }

    public Command clusterSetSlotNode(int slot, String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add(SETSLOT).add(slot).add(NODE).add(nodeId);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterSetSlotStable(int slot) {

        CommandArgs args = new CommandArgs(codec).add(SETSLOT).add(slot).add(STABLE);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterSetSlotMigrating(int slot, String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add(SETSLOT).add(slot).add(MIGRATING).add(nodeId);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterSetSlotImporting(int slot, String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add(SETSLOT).add(slot).add(IMPORTING).add(nodeId);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterReplicate(String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add(REPLICATE).add(nodeId);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command asking() {

        CommandArgs args = new CommandArgs(codec);
        return createCommand(ASKING, new StatusOutput(codec), args);
    }

    public Command clusterFlushslots() {

        CommandArgs args = new CommandArgs(codec).add(FLUSHSLOTS);
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command> clusterSlaves(String nodeId) {
        assertNodeId(nodeId);

        CommandArgs args = new CommandArgs(codec).add(SLAVES).add(nodeId);
        return createCommand(CLUSTER, new StringListOutput(codec), args);
    }

    public Command clusterFailover(boolean force) {

        CommandArgs args = new CommandArgs(codec).add(FAILOVER);
        if (force) {
            args.add(FORCE);
        }
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command clusterReset(boolean hard) {

        CommandArgs args = new CommandArgs(codec).add(RESET);
        if (hard) {
            args.add(HARD);
        } else {
            args.add(SOFT);
        }
        return createCommand(CLUSTER, new StatusOutput(codec), args);
    }

    public Command geoadd(K key, double longitude, double latitude, V member) {
        notNullKey(key);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(longitude).add(latitude).addValue(member);
        return createCommand(GEOADD, new IntegerOutput(codec), args);
    }

    public Command geoadd(K key, Object[] lngLatMember) {

        notNullKey(key);
        LettuceAssert.notNull(lngLatMember, "LngLatMember " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(lngLatMember, "LngLatMember " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(lngLatMember, "LngLatMember " + MUST_NOT_CONTAIN_NULL_ELEMENTS);
        LettuceAssert.isTrue(lngLatMember.length % 3 == 0, "LngLatMember.length must be a multiple of 3 and contain a "
                + "sequence of longitude1, latitude1, member1, longitude2, latitude2, member2, ... longitudeN, latitudeN, memberN");

        CommandArgs args = new CommandArgs(codec).addKey(key);

        for (int i = 0; i < lngLatMember.length; i += 3) {
            args.add((Double) lngLatMember[i]);
            args.add((Double) lngLatMember[i + 1]);
            args.addValue((V) lngLatMember[i + 2]);
        }

        return createCommand(GEOADD, new IntegerOutput(codec), args);
    }

    public Command>> geohash(K key, V... members) {
        notNullKey(key);
        LettuceAssert.notNull(members, "Members " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(members, "Members " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValues(members);
        return createCommand(GEOHASH, new StringValueListOutput(codec), args);
    }

    public Command> georadius(K key, double longitude, double latitude, double distance, String unit) {
        notNullKey(key);
        LettuceAssert.notNull(unit, "Unit " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(unit, "Unit " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).add(longitude).add(latitude).add(distance).add(unit);
        return createCommand(GEORADIUS, new ValueSetOutput(codec), args);
    }

    public Command>> georadius(K key, double longitude, double latitude, double distance, String unit,
            GeoArgs geoArgs) {

        notNullKey(key);
        LettuceAssert.notNull(unit, "Unit " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(unit, "Unit " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(geoArgs, "GeoArgs " + MUST_NOT_BE_NULL);
        CommandArgs args = new CommandArgs(codec).addKey(key).add(longitude).add(latitude).add(distance).add(unit);
        geoArgs.build(args);

        return createCommand(GEORADIUS, new GeoWithinListOutput(codec, geoArgs.isWithDistance(), geoArgs.isWithHash(),
                geoArgs.isWithCoordinates()), args);
    }

    public Command georadius(K key, double longitude, double latitude, double distance, String unit,
            GeoRadiusStoreArgs geoRadiusStoreArgs) {

        notNullKey(key);
        LettuceAssert.notNull(unit, "Unit " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(unit, "Unit " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(geoRadiusStoreArgs, "GeoRadiusStoreArgs " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(geoRadiusStoreArgs.getStoreKey() != null || geoRadiusStoreArgs.getStoreDistKey() != null,
                "At least STORE key or STORDIST key is required");

        CommandArgs args = new CommandArgs(codec).addKey(key).add(longitude).add(latitude).add(distance).add(unit);
        geoRadiusStoreArgs.build(args);

        return createCommand(GEORADIUS, new IntegerOutput(codec), args);
    }

    public Command> georadiusbymember(K key, V member, double distance, String unit) {

        notNullKey(key);
        LettuceAssert.notNull(unit, "Unit " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(unit, "Unit " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValue(member).add(distance).add(unit);
        return createCommand(GEORADIUSBYMEMBER, new ValueSetOutput(codec), args);
    }

    public Command>> georadiusbymember(K key, V member, double distance, String unit, GeoArgs geoArgs) {

        notNullKey(key);
        LettuceAssert.notNull(geoArgs, "GeoArgs " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(unit, "Unit " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(unit, "Unit " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValue(member).add(distance).add(unit);
        geoArgs.build(args);

        return createCommand(GEORADIUSBYMEMBER, new GeoWithinListOutput(codec, geoArgs.isWithDistance(),
                geoArgs.isWithHash(), geoArgs.isWithCoordinates()), args);
    }

    public Command georadiusbymember(K key, V member, double distance, String unit,
            GeoRadiusStoreArgs geoRadiusStoreArgs) {

        notNullKey(key);
        LettuceAssert.notNull(geoRadiusStoreArgs, "GeoRadiusStoreArgs " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(unit, "Unit " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(unit, "Unit " + MUST_NOT_BE_EMPTY);
        LettuceAssert.isTrue(geoRadiusStoreArgs.getStoreKey() != null || geoRadiusStoreArgs.getStoreDistKey() != null,
                "At least STORE key or STORDIST key is required");

        CommandArgs args = new CommandArgs(codec).addKey(key).addValue(member).add(distance).add(unit);
        geoRadiusStoreArgs.build(args);

        return createCommand(GEORADIUSBYMEMBER, new IntegerOutput(codec), args);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Command> geopos(K key, V[] members) {
        notNullKey(key);
        LettuceAssert.notNull(members, "Members " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(members, "Members " + MUST_NOT_BE_EMPTY);
        CommandArgs args = new CommandArgs(codec).addKey(key).addValues(members);

        return (Command) createCommand(GEOPOS, new GeoCoordinatesListOutput(codec), args);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Command>> geoposValues(K key, V[] members) {
        notNullKey(key);
        LettuceAssert.notNull(members, "Members " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(members, "Members " + MUST_NOT_BE_EMPTY);
        CommandArgs args = new CommandArgs(codec).addKey(key).addValues(members);

        return (Command) createCommand(GEOPOS, new GeoCoordinatesValueListOutput(codec), args);
    }

    public Command geodist(K key, V from, V to, GeoArgs.Unit unit) {
        notNullKey(key);
        LettuceAssert.notNull(from, "From " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(from, "To " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs(codec).addKey(key).addValue(from).addValue(to);

        if (unit != null) {
            args.add(unit.name());
        }

        return createCommand(GEODIST, new DoubleOutput(codec), args);
    }

    public void notNull(ScoredValueStreamingChannel channel) {
        LettuceAssert.notNull(channel, "ScoredValueStreamingChannel " + MUST_NOT_BE_NULL);
    }

    public void notNull(KeyStreamingChannel channel) {
        LettuceAssert.notNull(channel, "KeyValueStreamingChannel " + MUST_NOT_BE_NULL);
    }

    public void notNull(ValueStreamingChannel channel) {
        LettuceAssert.notNull(channel, "ValueStreamingChannel " + MUST_NOT_BE_NULL);
    }

    public void notNull(KeyValueStreamingChannel channel) {
        LettuceAssert.notNull(channel, "KeyValueStreamingChannel " + MUST_NOT_BE_NULL);
    }

    private void notNullKey(K key) {
        LettuceAssert.notNull(key, "Key " + MUST_NOT_BE_NULL);
    }

    private void notNullRange(Range range) {
        LettuceAssert.notNull(range, "Range " + MUST_NOT_BE_NULL);
    }

    private void notNullLimit(Limit limit) {
        LettuceAssert.notNull(limit, "Limit " + MUST_NOT_BE_NULL);
    }

    public void notNullMinMax(String min, String max) {
        LettuceAssert.notNull(min, "Min " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(max, "Max " + MUST_NOT_BE_NULL);
    }

    private void notEmpty(K[] keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(keys, "Keys " + MUST_NOT_BE_EMPTY);
    }

    private void notEmptyValues(V[] values) {
        LettuceAssert.notNull(values, "Values " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(values, "Values " + MUST_NOT_BE_EMPTY);
    }

    private void assertNodeId(String nodeId) {
        LettuceAssert.notNull(nodeId, "NodeId " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(nodeId, "NodeId " + MUST_NOT_BE_EMPTY);
    }

    private void notEmptySlots(int[] slots) {
        LettuceAssert.notNull(slots, "Slots " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(slots, "Slots " + MUST_NOT_BE_EMPTY);
    }

    private void addLimit(CommandArgs args, Limit limit) {

        if (limit.isLimited()) {
            args.add(LIMIT).add(limit.getOffset()).add(limit.getCount());
        }
    }

    private String min(Range range) {

        Range.Boundary lower = range.getLower();

        if (lower.getValue() == null
                || lower.getValue() instanceof Double && lower.getValue().doubleValue() == Double.NEGATIVE_INFINITY) {
            return "-inf";
        }

        if (!lower.isIncluding()) {
            return "(" + lower.getValue();
        }

        return lower.getValue().toString();
    }

    private String max(Range range) {

        Range.Boundary upper = range.getUpper();

        if (upper.getValue() == null
                || upper.getValue() instanceof Double && upper.getValue().doubleValue() == Double.POSITIVE_INFINITY) {
            return "+inf";
        }

        if (!upper.isIncluding()) {
            return "(" + upper.getValue();
        }

        return upper.getValue().toString();
    }

    private byte[] minValue(Range range) {

        Range.Boundary lower = range.getLower();

        if (lower.getValue() == null) {
            return MINUS_BYTES;
        }

        ByteBuffer encoded = codec.encodeValue(lower.getValue());
        ByteBuffer allocated = ByteBuffer.allocate(encoded.remaining() + 1);
        allocated.put(lower.isIncluding() ? (byte) '[' : (byte) '(').put(encoded);

        return allocated.array();
    }

    private byte[] maxValue(Range range) {

        Range.Boundary upper = range.getUpper();

        if (upper.getValue() == null) {
            return PLUS_BYTES;
        }

        ByteBuffer encoded = codec.encodeValue(upper.getValue());
        ByteBuffer allocated = ByteBuffer.allocate(encoded.remaining() + 1);
        allocated.put(upper.isIncluding() ? (byte) '[' : (byte) '(').put(encoded);

        return allocated.array();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy