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

io.lettuce.core.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-Present, Redis Ltd. and Contributors
 * All rights reserved.
 *
 * Licensed under the MIT License.
 *
 * This file contains contributions from third-party contributors
 * 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
 *
 *      https://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 io.lettuce.core;

import io.lettuce.core.Range.Boundary;
import io.lettuce.core.XReadArgs.StreamOffset;
import io.lettuce.core.codec.RedisCodec;
import io.lettuce.core.codec.StringCodec;
import io.lettuce.core.internal.LettuceAssert;
import io.lettuce.core.models.stream.ClaimedMessages;
import io.lettuce.core.models.stream.PendingMessage;
import io.lettuce.core.models.stream.PendingMessages;
import io.lettuce.core.output.*;
import io.lettuce.core.protocol.BaseRedisCommandBuilder;
import io.lettuce.core.protocol.Command;
import io.lettuce.core.protocol.CommandArgs;
import io.lettuce.core.protocol.CommandKeyword;
import io.lettuce.core.protocol.CommandType;
import io.lettuce.core.protocol.RedisCommand;

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

import static io.lettuce.core.internal.LettuceStrings.string;
import static io.lettuce.core.protocol.CommandKeyword.*;
import static io.lettuce.core.protocol.CommandType.*;
import static io.lettuce.core.protocol.CommandType.COPY;
import static io.lettuce.core.protocol.CommandType.SAVE;

/**
 * @param 
 * @param 
 * @author Mark Paluch
 * @author Zhang Jessey
 * @author Tugdual Grall
 * @author dengliming
 * @author Mikhael Sokolov
 * @author Tihomir Mateev
 * @author Ali Takavci
 * @author Seonghwan Lee
 */
@SuppressWarnings({ "unchecked", "varargs" })
class RedisCommandBuilder extends BaseRedisCommandBuilder {

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

    Command> aclCat() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(CAT);
        return createCommand(ACL, new EnumSetOutput<>(codec, AclCategory.class, String::toUpperCase, it -> null), args);
    }

    Command> aclCat(AclCategory category) {
        LettuceAssert.notNull(category, "Category " + MUST_NOT_BE_NULL);
        CommandArgs args = new CommandArgs<>(codec);
        args.add(CAT).add(category.name().toLowerCase());
        return createCommand(ACL, new EnumSetOutput<>(codec, CommandType.class, String::toUpperCase, it -> null), args);
    }

    Command aclDeluser(String... usernames) {
        notEmpty(usernames);
        CommandArgs args = new CommandArgs<>(codec);
        args.add(DELUSER);
        for (String username : usernames) {
            args.add(username);
        }
        return createCommand(ACL, new IntegerOutput<>(codec), args);
    }

    Command aclDryRun(String username, String command, String... commandArgs) {
        LettuceAssert.notNull(username, "username " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(command, "command " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(DRYRUN).add(username).add(command);

        for (String commandArg : commandArgs) {
            args.add(commandArg);
        }
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command aclDryRun(String username, RedisCommand command) {
        LettuceAssert.notNull(username, "username " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(command, "command " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(DRYRUN).add(username).add(command.getType()).addAll(command.getArgs());

        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command aclGenpass() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(GENPASS);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command aclGenpass(int bits) {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(GENPASS).add(bits);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command> aclGetuser(String username) {
        LettuceAssert.notNull(username, "Username " + MUST_NOT_BE_NULL);
        CommandArgs args = new CommandArgs<>(codec);
        args.add(GETUSER).add(username);
        return createCommand(ACL, new NestedMultiOutput<>(codec), args);
    }

    Command> aclList() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(LIST);
        return createCommand(ACL, new StringListOutput<>(codec), args);
    }

    Command aclLoad() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(LOAD);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command>> aclLog() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(LOG);
        return new Command(ACL, new ListOfGenericMapsOutput<>(StringCodec.ASCII), args);
    }

    Command>> aclLog(int count) {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(LOG).add(count);
        return new Command(ACL, new ListOfGenericMapsOutput<>(StringCodec.ASCII), args);
    }

    Command aclLogReset() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(LOG).add(RESET);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command aclSave() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(CommandKeyword.SAVE);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command aclSetuser(String username, AclSetuserArgs setuserArgs) {
        notNullKey(username);
        CommandArgs args = new CommandArgs<>(codec);
        args.add(SETUSER).add(username);
        setuserArgs.build(args);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

    Command> aclUsers() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(USERS);
        return createCommand(ACL, new StringListOutput<>(codec), args);
    }

    Command aclWhoami() {
        CommandArgs args = new CommandArgs<>(codec);
        args.add(WHOAMI);
        return createCommand(ACL, new StatusOutput<>(codec), args);
    }

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

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

    Command asking() {

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

    Command auth(CharSequence password) {
        LettuceAssert.notNull(password, "Password " + MUST_NOT_BE_NULL);

        char[] chars = new char[password.length()];
        for (int i = 0; i < password.length(); i++) {
            chars[i] = password.charAt(i);
        }
        return auth(chars);
    }

    Command auth(char[] password) {
        LettuceAssert.notNull(password, "Password " + MUST_NOT_BE_NULL);

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

    Command auth(String username, CharSequence password) {
        LettuceAssert.notNull(username, "Username " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!username.isEmpty(), "Username " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(password, "Password " + MUST_NOT_BE_NULL);

        char[] chars = new char[password.length()];
        for (int i = 0; i < password.length(); i++) {
            chars[i] = password.charAt(i);
        }
        return auth(username, chars);
    }

    Command auth(String username, char[] password) {
        LettuceAssert.notNull(username, "Username " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!username.isEmpty(), "Username " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(password, "Password " + MUST_NOT_BE_NULL);

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

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

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

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

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

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

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

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

    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);
    }

    Command blmove(K source, K destination, LMoveArgs lMoveArgs, long timeout) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(lMoveArgs, "LMoveArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKey(source).addKey(destination);
        lMoveArgs.build(args);
        args.add(timeout);
        return createCommand(BLMOVE, new ValueOutput<>(codec), args);
    }

    Command blmove(K source, K destination, LMoveArgs lMoveArgs, double timeout) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(lMoveArgs, "LMoveArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKey(source).addKey(destination);
        lMoveArgs.build(args);
        args.add(timeout);
        return createCommand(BLMOVE, new ValueOutput<>(codec), args);
    }

    Command>> blmpop(long timeout, LMPopArgs lmPopArgs, K... keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(lmPopArgs, "LMPopArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(timeout).add(keys.length).addKeys(keys);
        lmPopArgs.build(args);

        return createCommand(BLMPOP, new KeyValueValueListOutput<>(codec), args);
    }

    Command>> blmpop(double timeout, LMPopArgs lmPopArgs, K... keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(lmPopArgs, "LMPopArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(timeout).add(keys.length).addKeys(keys);
        lmPopArgs.build(args);

        return createCommand(BLMPOP, new KeyValueValueListOutput<>(codec), args);
    }

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

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

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

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

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

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

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

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

    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);
    }

    Command brpoplpush(double 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);
    }

    Command clientCaching(boolean enabled) {
        CommandArgs args = new CommandArgs<>(codec).add(CACHING).add(enabled ? YES : NO);
        return createCommand(CLIENT, new StatusOutput<>(codec), args);
    }

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

    Command clientGetredir() {
        CommandArgs args = new CommandArgs<>(codec).add(GETREDIR);
        return createCommand(CLIENT, new IntegerOutput<>(codec), args);
    }

    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);
    }

    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);
    }

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

    Command clientList(ClientListArgs clientListArgs) {
        LettuceAssert.notNull(clientListArgs, "ClientListArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(LIST);
        clientListArgs.build(args);
        return createCommand(CLIENT, new StatusOutput<>(codec), args);
    }

    Command clientInfo() {
        CommandArgs args = new CommandArgs<>(codec).add(CommandKeyword.INFO);
        return createCommand(CLIENT, new StatusOutput<>(codec), args);
    }

    Command clientNoEvict(boolean on) {
        CommandArgs args = new CommandArgs<>(codec).add("NO-EVICT").add(on ? ON : OFF);
        return createCommand(CLIENT, new StatusOutput<>(codec), args);
    }

    Command clientId() {
        CommandArgs args = new CommandArgs<>(codec).add(ID);
        return createCommand(CLIENT, new IntegerOutput<>(codec), args);
    }

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

    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);
    }

    Command clientSetinfo(String key, String value) {
        CommandArgs args = new CommandArgs<>(codec).add(SETINFO).add(key).add(value);
        return createCommand(CLIENT, new StatusOutput<>(codec), args);
    }

    Command clientTracking(TrackingArgs trackingArgs) {
        LettuceAssert.notNull(trackingArgs, "TrackingArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(TRACKING);
        trackingArgs.build(args);
        return createCommand(CLIENT, new StatusOutput<>(codec), args);
    }

    Command clientTrackinginfo() {
        CommandArgs args = new CommandArgs<>(codec).add(TRACKINGINFO);

        return new Command<>(CLIENT, new ComplexOutput<>(codec, TrackingInfoParser.INSTANCE), args);
    }

    Command clientUnblock(long id, UnblockType type) {
        LettuceAssert.notNull(type, "UnblockType " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(UNBLOCK).add(id).add(type);
        return createCommand(CLIENT, new IntegerOutput<>(codec), args);
    }

    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);
    }

    Command clusterAddSlotsRange(Range... ranges) {
        notEmptyRanges(ranges);

        CommandArgs args = new CommandArgs<>(codec).add(ADDSLOTSRANGE);

        for (Range range : ranges) {
            args.add(range.getLower().getValue());
            args.add(range.getUpper().getValue());
        }
        return createCommand(CLUSTER, new StatusOutput<>(codec), args);
    }

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

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

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

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

    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);
    }

    Command clusterDelSlotsRange(Range... ranges) {
        notEmptyRanges(ranges);

        CommandArgs args = new CommandArgs<>(codec).add(DELSLOTSRANGE);

        for (Range range : ranges) {
            args.add(range.getLower().getValue());
            args.add(range.getUpper().getValue());
        }
        return createCommand(CLUSTER, new StatusOutput<>(codec), args);
    }

    Command clusterFailover(boolean force) {
        return clusterFailover(force, false);
    }

    Command clusterFailover(boolean force, boolean takeOver) {

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

    Command clusterFlushslots() {

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

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

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

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

    Command clusterInfo() {
        CommandArgs args = new CommandArgs<>(codec).add(CommandType.INFO);

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

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

    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);
    }

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

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

    Command clusterMyShardId() {
        CommandArgs args = new CommandArgs<>(codec).add(MYSHARDID);

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

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

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

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

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

    Command> clusterReplicas(String nodeId) {
        assertNodeId(nodeId);

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

    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);
    }

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

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

    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);
    }

    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);
    }

    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);
    }

    Command clusterSetSlotStable(int slot) {

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

    Command> clusterShards() {

        CommandArgs args = new CommandArgs<>(codec).add(SHARDS);
        return createCommand(CLUSTER, new ArrayOutput<>(codec), args);
    }

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

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

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

    Command> command() {
        CommandArgs args = new CommandArgs<>(StringCodec.UTF8);
        return Command.class.cast(new Command(COMMAND, new ArrayOutput<>(StringCodec.UTF8), args));
    }

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

    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<>(StringCodec.UTF8);
        args.add(CommandKeyword.INFO);

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

        return Command.class.cast(new Command<>(COMMAND, new ArrayOutput<>(StringCodec.UTF8), args));
    }

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

        CommandArgs args = new CommandArgs<>(StringCodec.UTF8).add(GET).add(parameter);
        return Command.class.cast(new Command<>(CONFIG, new MapOutput<>(StringCodec.UTF8), args));
    }

    Command> configGet(String... parameters) {
        LettuceAssert.notNull(parameters, "Parameters " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(parameters, "Parameters " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs<>(StringCodec.UTF8).add(GET);
        for (String parameter : parameters) {
            args.add(parameter);
        }
        return Command.class.cast(new Command<>(CONFIG, new MapOutput<>(StringCodec.UTF8), args));
    }

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

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

    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);
    }

    Command configSet(Map configValues) {
        LettuceAssert.notNull(configValues, "ConfigValues " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(!configValues.isEmpty(), "ConfigValues " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs<>(codec).add(SET);
        configValues.forEach((parameter, value) -> {
            args.add(parameter);
            args.add(value);
        });
        return createCommand(CONFIG, new StatusOutput<>(codec), args);
    }

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

    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);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    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);
    }

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

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

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

    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);
    }

     Command eval(byte[] script, ScriptOutputType type, K[] keys, V... values) {
        return eval(script, type, false, keys, values);
    }

     Command eval(byte[] script, ScriptOutputType type, boolean readonly, K[] keys, V... values) {
        LettuceAssert.notNull(script, "Script " + MUST_NOT_BE_NULL);
        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(readonly ? EVAL_RO : EVAL, output, args);
    }

     Command evalsha(String digest, ScriptOutputType type, K[] keys, V... values) {
        return evalsha(digest, type, false, keys, values);
    }

     Command evalsha(String digest, ScriptOutputType type, boolean readonly, 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(readonly ? EVALSHA_RO : EVALSHA, output, args);
    }

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

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

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

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

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

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

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(seconds);

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

        return createCommand(EXPIRE, new BooleanOutput<>(codec), args);
    }

    Command> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(seconds);

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

        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HEXPIRE, new IntegerListOutput<>(codec), args);
    }

    Command> hexpireat(K key, long seconds, ExpireArgs expireArgs, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(seconds);

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

        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HEXPIREAT, new IntegerListOutput<>(codec), args);
    }

    Command> httl(K key, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HTTL, new IntegerListOutput<>(codec), args);
    }

    Command> hpexpire(K key, long milliseconds, ExpireArgs expireArgs, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(milliseconds);

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

        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HPEXPIRE, new IntegerListOutput<>(codec), args);
    }

    Command> hpexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(timestamp);

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

        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HPEXPIREAT, new IntegerListOutput<>(codec), args);
    }

    Command> hpexpiretime(K key, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HPEXPIRETIME, new IntegerListOutput<>(codec), args);
    }

    Command> hpttl(K key, K... fields) {
        keyAndFieldsProvided(key, fields);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HPTTL, new IntegerListOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(timestamp);

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

        return createCommand(EXPIREAT, new BooleanOutput<>(codec), args);
    }

    Command expiretime(K key) {
        notNullKey(key);

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

    Command> hexpiretime(K key, K... fields) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HEXPIRETIME, new IntegerListOutput<>(codec), args);
    }

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

    Command flushall(FlushMode flushMode) {
        LettuceAssert.notNull(flushMode, "FlushMode " + MUST_NOT_BE_NULL);

        return createCommand(FLUSHALL, new StatusOutput<>(codec), new CommandArgs<>(codec).add(flushMode));
    }

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

    Command flushdb(FlushMode flushMode) {
        LettuceAssert.notNull(flushMode, "FlushMode " + MUST_NOT_BE_NULL);

        return createCommand(FLUSHDB, new StatusOutput<>(codec), new CommandArgs<>(codec).add(flushMode));
    }

     Command fcall(String function, ScriptOutputType type, boolean readonly, K[] keys, V... values) {
        LettuceAssert.notEmpty(function, "Function " + 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(function).add(keys.length).addKeys(keys).addValues(values);
        CommandOutput output = newScriptOutput(codec, type);

        return createCommand(readonly ? FCALL_RO : FCALL, output, args);
    }

    Command functionLoad(byte[] functionCode, boolean replace) {
        LettuceAssert.notNull(functionCode, "Function code " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(LOAD);
        if (replace) {
            args.add(REPLACE);
        }
        args.add(functionCode);

        return createCommand(FUNCTION, new StatusOutput<>(codec), args);
    }

    Command functionDump() {
        return createCommand(FUNCTION, new ByteArrayOutput<>(codec), new CommandArgs<>(codec).add(DUMP));
    }

    Command functionRestore(byte dump[], FunctionRestoreMode mode) {

        LettuceAssert.notNull(dump, "Function dump " + MUST_NOT_BE_NULL);
        CommandArgs args = new CommandArgs<>(codec).add(RESTORE).add(dump);

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

        return createCommand(FUNCTION, new StatusOutput<>(codec), args);
    }

    Command functionFlush(FlushMode mode) {

        CommandArgs args = new CommandArgs<>(codec).add(FLUSH);

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

        return createCommand(FUNCTION, new StatusOutput<>(codec), args);
    }

    Command functionKill() {
        return createCommand(FUNCTION, new StatusOutput<>(codec), new CommandArgs<>(codec).add(KILL));
    }

    Command>> functionList(String pattern) {

        CommandArgs args = new CommandArgs<>(codec).add(LIST);

        if (pattern != null) {
            args.add("LIBRARYNAME").add(pattern);
        }

        return createCommand(FUNCTION, (CommandOutput) new ObjectOutput<>(StringCodec.UTF8), args);
    }

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

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

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

        args.add(longitude).add(latitude).addValue(member);
        return createCommand(GEOADD, new IntegerOutput<>(codec), args);
    }

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

        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);

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

        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);
    }

    Command geoadd(K key, GeoValue[] values, GeoAddArgs geoArgs) {

        notNullKey(key);
        LettuceAssert.notNull(values, "Values " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(values, "Values " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(values, "Values " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

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

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

        for (GeoValue value : values) {
            args.add(value.getCoordinates().getX().doubleValue());
            args.add(value.getCoordinates().getY().doubleValue());
            args.addValue(value.getValue());
        }

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

    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);
    }

    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);
    }

    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 createCommand(GEOPOS, new GeoCoordinatesListOutput<>(codec), args);
    }

    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 createCommand(GEOPOS, new GeoCoordinatesValueListOutput<>(codec), args);
    }

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

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

    Command>> georadius(CommandType commandType, 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(commandType,
                new GeoWithinListOutput<>(codec, geoArgs.isWithDistance(), geoArgs.isWithHash(), geoArgs.isWithCoordinates()),
                args);
    }

    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 STOREDIST 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);
    }

    Command> georadiusbymember(CommandType commandType, 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(commandType, new ValueSetOutput<>(codec), args);
    }

    Command>> georadiusbymember(CommandType commandType, 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(commandType,
                new GeoWithinListOutput<>(codec, geoArgs.isWithDistance(), geoArgs.isWithHash(), geoArgs.isWithCoordinates()),
                args);
    }

    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 STOREDIST 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);
    }

    Command> geosearch(K key, GeoSearch.GeoRef reference, GeoSearch.GeoPredicate predicate) {
        notNullKey(key);
        LettuceAssert.notNull(reference, "GeoRef " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(predicate, "GeoPredicate " + MUST_NOT_BE_NULL);

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

        reference.build(args);
        predicate.build(args);

        return createCommand(GEOSEARCH, new ValueSetOutput<>(codec), args);
    }

    Command>> geosearch(K key, GeoSearch.GeoRef reference, GeoSearch.GeoPredicate predicate,
            GeoArgs geoArgs) {
        notNullKey(key);
        LettuceAssert.notNull(reference, "GeoRef " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(predicate, "GeoPredicate " + MUST_NOT_BE_NULL);

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

        reference.build(args);
        predicate.build(args);
        geoArgs.build(args);

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

    Command geosearchstore(K destination, K key, GeoSearch.GeoRef reference, GeoSearch.GeoPredicate predicate,
            GeoArgs geoArgs, boolean storeDist) {
        notNullKey(key);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(key, "Key " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(reference, "GeoRef " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(predicate, "GeoPredicate " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(destination).addKey(key);

        reference.build(args);
        predicate.build(args);
        geoArgs.build(args);

        if (storeDist) {
            args.add("STOREDIST");
        }

        return createCommand(GEOSEARCHSTORE, new IntegerOutput<>(codec), args);
    }

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

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

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

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

    Command getdel(K key) {
        notNullKey(key);

        return createCommand(GETDEL, new ValueOutput<>(codec), key);
    }

    Command getex(K key, GetExArgs getExArgs) {
        notNullKey(key);
        LettuceAssert.notNull(getExArgs, "GetExArgs " + MUST_NOT_BE_NULL);

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

        getExArgs.build(args);

        return createCommand(GETEX, new ValueOutput<>(codec), args);
    }

    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);
    }

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

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

    Command hdel(K key, K... fields) {
        keyAndFieldsProvided(key, fields);

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

    Command> hello(int protocolVersion, String user, char[] password, String name) {

        CommandArgs args = new CommandArgs<>(StringCodec.ASCII).add(protocolVersion);

        if (user != null && password != null) {
            args.add(AUTH).add(user).add(password);
        }

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

        return new Command<>(HELLO, new GenericMapOutput<>(StringCodec.ASCII), args);
    }

    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);
    }

    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);
    }

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

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

    Command>> hgetallKeyValue(K key) {
        notNullKey(key);

        return createCommand(HGETALL, new KeyValueListOutput<>(codec), key);
    }

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

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

    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);
    }

    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);
    }

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

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

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

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

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

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

    Command> hmget(K key, K... fields) {
        keyAndFieldsProvided(key, fields);

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

    Command hmget(ValueStreamingChannel channel, K key, K... fields) {
        keyAndFieldsProvided(key, fields);

        notNull(channel);

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

    Command hmget(KeyValueStreamingChannel channel, K key, K... fields) {
        keyAndFieldsProvided(key, fields);

        notNull(channel);

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

    Command>> hmgetKeyValue(K key, K... fields) {
        keyAndFieldsProvided(key, fields);

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

    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);
    }

    Command hrandfield(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        return createCommand(HRANDFIELD, new KeyOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(count);
        return createCommand(HRANDFIELD, new KeyListOutput<>(codec), args);
    }

    Command> hrandfieldWithvalues(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(1).add(WITHVALUES);
        return createCommand(HRANDFIELD, new KeyValueOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(count).add(WITHVALUES);
        return createCommand(HRANDFIELD, new KeyValueListOutput<>(codec), args);
    }

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

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

    Command> hscanNovalues(K key) {
        notNullKey(key);

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

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

        return hscan(key, scanCursor, null);
    }

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

        return hscanNovalues(key, scanCursor, null);
    }

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

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

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

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

    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);
    }

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

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

        scanArgs(scanCursor, scanArgs, args);

        args.add(NOVALUES);

        KeyScanOutput output = new KeyScanOutput<>(codec);
        return createCommand(HSCAN, output, args);
    }

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

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

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

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

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

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

    Command hscanNoValuesStreaming(KeyStreamingChannel channel, K key, ScanCursor scanCursor) {
        notNullKey(key);
        notNull(channel);

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

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

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

    Command hscanNoValuesStreaming(KeyStreamingChannel channel, K key, ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

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

    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);
    }

    Command hscanNoValuesStreaming(KeyStreamingChannel channel, K key, ScanCursor scanCursor,
            ScanArgs scanArgs) {
        notNullKey(key);
        notNull(channel);

        CommandArgs args = new CommandArgs<>(codec);

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

        args.add(NOVALUES);

        KeyScanStreamingOutput output = new KeyScanStreamingOutput<>(codec, channel);
        return createCommand(HSCAN, output, args);
    }

    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);
    }

    Command hset(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(HSET, new IntegerOutput<>(codec), args);
    }

    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);
    }

    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);
    }

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

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

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

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

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

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

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

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

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

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

    Command info() {
        return createCommand(CommandType.INFO, new StatusOutput<>(codec));
    }

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

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

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

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

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

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

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

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

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

    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);
    }

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

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

    Command lmove(K source, K destination, LMoveArgs lMoveArgs) {
        LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(lMoveArgs, "LMoveArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKey(source).addKey(destination);
        lMoveArgs.build(args);
        return createCommand(LMOVE, new ValueOutput<>(codec), args);
    }

    Command>> lmpop(LMPopArgs lmPopArgs, K... keys) {
        LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(lmPopArgs, "LMPopArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys);
        lmPopArgs.build(args);

        return createCommand(LMPOP, new KeyValueValueListOutput<>(codec), args);
    }

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

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

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

        return createCommand(LPOP, new ValueListOutput<>(codec), new CommandArgs<>(codec).addKey(key).add(count));
    }

    Command lpos(K key, V value, LPosArgs lposArgs) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addValue(value);

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

        return createCommand(LPOS, new IntegerOutput<>(codec), args);
    }

    Command> lpos(K key, V value, long count, LPosArgs lposArgs) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addValue(value).add(COUNT).add(count);

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

        return createCommand(LPOS, new IntegerListOutput<>(codec), args);
    }

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

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

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    Command memoryUsage(K key) {
        return createCommand(MEMORY, new IntegerOutput<>(codec), new CommandArgs<>(codec).add(USAGE).addKey(key));
    }

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

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

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

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

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

    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);
    }

    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);
    }

    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);
    }

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

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

    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);
    }

    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);
    }

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

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

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

    Command objectFreq(K key) {
        notNullKey(key);

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

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

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

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

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

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

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

    Command> hpersist(K key, K... fields) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        args.add(FIELDS).add(fields.length).addKeys(fields);

        return createCommand(HPERSIST, new IntegerListOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(milliseconds);

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

        return createCommand(PEXPIRE, new BooleanOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(timestamp);

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

        return createCommand(PEXPIREAT, new BooleanOutput<>(codec), args);
    }

    Command pexpiretime(K key) {
        notNullKey(key);

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

    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);
    }

    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);
    }

    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);
    }

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

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

    @SuppressWarnings("unchecked")
    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")
    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);
    }

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

    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);
    }

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

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

    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);
    }

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

    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);
    }

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

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

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

    Command> pubsubShardChannels() {
        CommandArgs args = new CommandArgs<>(codec).add(SHARDCHANNELS);
        return createCommand(PUBSUB, new KeyListOutput<>(codec), args);
    }

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

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

    Command> pubsubShardNumsub(K... shardChannels) {
        LettuceAssert.notNull(shardChannels, "ShardChannels " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(shardChannels, "ShardChannels " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs<>(codec).add(SHARDNUMSUB).addKeys(shardChannels);
        return createCommand(PUBSUB, (MapOutput) new MapOutput((RedisCodec) codec), args);
    }

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

    Command randomkey() {
        return createCommand(RANDOMKEY, new KeyOutput<>(codec));
    }

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

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

    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);
    }

    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);
    }

    Command replicaof(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(REPLICAOF, new StatusOutput<>(codec), args);
    }

    Command replicaofNoOne() {
        CommandArgs args = new CommandArgs<>(codec).add(NO).add(ONE);
        return createCommand(REPLICAOF, new StatusOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(restoreArgs.ttl).add(value);

        restoreArgs.build(args);

        return createCommand(RESTORE, new StatusOutput<>(codec), args);
    }

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

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

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

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

        return createCommand(RPOP, new ValueListOutput<>(codec), new CommandArgs<>(codec).addKey(key).add(count));
    }

    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);
    }

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

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

    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);
    }

    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);
    }

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

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

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

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

    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);
        }
    }

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

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

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

        return scanStreaming(channel, scanCursor, null);
    }

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

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

    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);
    }

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

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

    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);
    }

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

    Command scriptFlush(FlushMode flushMode) {
        LettuceAssert.notNull(flushMode, "FlushMode " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(FLUSH).add(flushMode.name());
        return createCommand(SCRIPT, new StatusOutput<>(codec), args);
    }

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

    Command scriptLoad(byte[] script) {
        LettuceAssert.notNull(script, "Script " + MUST_NOT_BE_NULL);

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

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

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

    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);
    }

    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);
    }

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

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

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

    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);
    }

    Command setGet(K key, V value) {
        return setGet(key, value, new SetArgs());
    }

    Command setGet(K key, V value, SetArgs setArgs) {
        notNullKey(key);
        LettuceAssert.notNull(setArgs, "SetArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addValue(value);
        setArgs.build(args);
        args.add(GET);

        return createCommand(SET, new ValueOutput<>(codec), args);
    }

    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);
    }

    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);
    }

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

    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);
    }

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

    Command shutdown(ShutdownArgs shutdownArgs) {
        LettuceAssert.notNull(shutdownArgs, "shutdownArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        shutdownArgs.build(args);
        return createCommand(SHUTDOWN, new StatusOutput<>(codec), args);
    }

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

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

    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);
    }

    Command sintercard(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys);
        return createCommand(SINTERCARD, new IntegerOutput<>(codec), args);
    }

    Command sintercard(long limit, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys).add(LIMIT).add(limit);
        return createCommand(SINTERCARD, new IntegerOutput<>(codec), args);
    }

    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);
    }

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

    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);
    }

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

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

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

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

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

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

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

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

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

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

        return createCommand(SMISMEMBER, new BooleanListOutput<>(codec), key, members);
    }

    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);
    }

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

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

    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);
    }

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

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

    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);
    }

    Command> sortReadOnly(K key) {
        notNullKey(key);

        return createCommand(SORT_RO, new ValueListOutput<>(codec), key);
    }

    Command> sortReadOnly(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_RO, new ValueListOutput<>(codec), args);
    }

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

        return createCommand(SORT_RO, new ValueStreamingOutput<>(codec, channel), key);
    }

    Command sortReadOnly(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_RO, new ValueStreamingOutput<>(codec, channel), args);
    }

    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);
    }

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

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

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

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

    Command spublish(K shardChannel, V message) {
        LettuceAssert.notNull(shardChannel, "ShardChannel " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(shardChannel).addValue(message);
        return createCommand(SPUBLISH, new IntegerOutput<>(codec), args);
    }

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

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

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

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

    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);
    }

    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);
    }

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

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

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

        return sscan(key, scanCursor, null);
    }

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

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

    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);
    }

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

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

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

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

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

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

    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);
    }

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

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

    Command stralgoLcs(StrAlgoArgs strAlgoArgs) {
        LettuceAssert.notNull(strAlgoArgs, "StrAlgoArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        strAlgoArgs.build(args);
        return createCommand(STRALGO, new StringMatchResultOutput<>(codec), args);
    }

    Command lcs(LcsArgs lcsArgs) {
        LettuceAssert.notNull(lcsArgs, "lcsArgs" + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);
        lcsArgs.build(args);
        return createCommand(LCS, new StringMatchResultOutput<>(codec), args);
    }

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

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

    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);
    }

    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);
    }

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

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

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

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

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

    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);
    }

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

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

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

        return createCommand(CommandType.TYPE, new StatusOutput<>(codec), key);
    }

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

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

    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);
    }

    Command copy(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(COPY, new BooleanOutput<>(codec), args);
    }

    Command copy(K source, K destination, CopyArgs copyArgs) {
        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);
        copyArgs.build(args);
        return createCommand(COPY, new BooleanOutput<>(codec), args);
    }

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

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

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

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

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

    public Command xack(K key, K group, String[] messageIds) {
        notNullKey(key);
        LettuceAssert.notNull(group, "Group " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(messageIds, "MessageIds " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(messageIds, "MessageIds " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addKey(group);

        for (String messageId : messageIds) {
            args.add(messageId);
        }

        return createCommand(XACK, new IntegerOutput<>(codec), args);
    }

    public Command> xautoclaim(K key, XAutoClaimArgs xAutoClaimArgs) {
        notNullKey(key);
        LettuceAssert.notNull(xAutoClaimArgs, "XAutoClaimArgs " + MUST_NOT_BE_NULL);
        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        xAutoClaimArgs.build(args);
        return createCommand(XAUTOCLAIM, new ClaimedMessagesOutput<>(codec, key, xAutoClaimArgs.isJustid()), args);
    }

    public Command>> xclaim(K key, Consumer consumer, XClaimArgs xClaimArgs,
            String[] messageIds) {

        notNullKey(key);
        LettuceAssert.notNull(consumer, "Consumer " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(messageIds, "MessageIds " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(messageIds, "MessageIds " + MUST_NOT_CONTAIN_NULL_ELEMENTS);
        LettuceAssert.notNull(xClaimArgs, "XClaimArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addKey(consumer.group).addKey(consumer.name)
                .add(xClaimArgs.minIdleTime);

        for (String messageId : messageIds) {
            args.add(messageId);
        }

        xClaimArgs.build(args);

        return createCommand(XCLAIM, new StreamMessageListOutput<>(codec, key), args);
    }

    public Command xadd(K key, XAddArgs xAddArgs, Map map) {
        notNullKey(key);
        LettuceAssert.notNull(map, "Message body " + MUST_NOT_BE_NULL);

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

        if (xAddArgs != null) {
            xAddArgs.build(args);
        } else {
            args.add("*");
        }

        args.add(map);

        return createCommand(XADD, new StatusOutput<>(codec), args);
    }

    public Command xadd(K key, XAddArgs xAddArgs, Object[] body) {
        notNullKey(key);
        LettuceAssert.notNull(body, "Message body " + MUST_NOT_BE_NULL);
        LettuceAssert.notEmpty(body, "Message body " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(body, "Message body " + MUST_NOT_CONTAIN_NULL_ELEMENTS);
        LettuceAssert.isTrue(body.length % 2 == 0, "Message body.length must be a multiple of 2 and contain a "
                + "sequence of field1, value1, field2, value2, fieldN, valueN");

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

        if (xAddArgs != null) {
            xAddArgs.build(args);
        } else {
            args.add("*");
        }

        for (int i = 0; i < body.length; i += 2) {
            args.addKey((K) body[i]);
            args.addValue((V) body[i + 1]);
        }

        return createCommand(XADD, new StatusOutput<>(codec), args);
    }

    public Command xdel(K key, String[] messageIds) {
        notNullKey(key);
        LettuceAssert.notEmpty(messageIds, "MessageIds " + MUST_NOT_BE_EMPTY);
        LettuceAssert.noNullElements(messageIds, "MessageIds " + MUST_NOT_CONTAIN_NULL_ELEMENTS);

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

        for (String messageId : messageIds) {
            args.add(messageId);
        }

        return createCommand(XDEL, new IntegerOutput<>(codec), args);
    }

    public Command xgroupCreate(StreamOffset offset, K group, XGroupCreateArgs commandArgs) {
        LettuceAssert.notNull(offset, "StreamOffset " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(group, "Group " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add(CREATE).addKey(offset.getName()).addKey(group)
                .add(offset.getOffset());

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

        return createCommand(XGROUP, new StatusOutput<>(codec), args);
    }

    public Command xgroupCreateconsumer(K key, Consumer consumer) {
        notNullKey(key);
        LettuceAssert.notNull(consumer, "Consumer " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add("CREATECONSUMER").addKey(key).addKey(consumer.getGroup())
                .addKey(consumer.getName());

        return createCommand(XGROUP, new BooleanOutput<>(codec), args);
    }

    public Command xgroupDelconsumer(K key, Consumer consumer) {
        notNullKey(key);
        LettuceAssert.notNull(consumer, "Consumer " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add("DELCONSUMER").addKey(key).addKey(consumer.getGroup())
                .addKey(consumer.getName());

        return createCommand(XGROUP, new IntegerOutput<>(codec), args);
    }

    public Command xgroupDestroy(K key, K group) {
        notNullKey(key);
        LettuceAssert.notNull(group, "Group " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add("DESTROY").addKey(key).addKey(group);

        return createCommand(XGROUP, new BooleanOutput<>(codec), args);
    }

    public Command xgroupSetid(StreamOffset offset, K group) {
        LettuceAssert.notNull(offset, "StreamOffset " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(group, "Group " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).add("SETID").addKey(offset.getName()).addKey(group)
                .add(offset.getOffset());

        return createCommand(XGROUP, new StatusOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).add(STREAM).addKey(key);

        return createCommand(XINFO, new ArrayOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).add(GROUPS).addKey(key);

        return createCommand(XINFO, new ArrayOutput<>(codec), args);
    }

    public Command> xinfoConsumers(K key, K group) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).add(CONSUMERS).addKey(key).addKey(group);

        return createCommand(XINFO, new ArrayOutput<>(codec), args);
    }

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

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

        return createCommand(XLEN, new IntegerOutput<>(codec), args);
    }

    public Command xpending(K key, K group) {
        notNullKey(key);
        LettuceAssert.notNull(group, "Group " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addKey(group);

        return createCommand(XPENDING, new PendingMessagesOutput<>(codec), args);
    }

    public Command> xpending(K key, K group, Range range, Limit limit) {
        notNullKey(key);
        LettuceAssert.notNull(group, "Group " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(range, "Range " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(limit, "Limit " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addKey(group);

        args.add(getLowerValue(range)).add(getUpperValue(range));
        args.add(limit.isLimited() ? limit.getCount() : Long.MAX_VALUE);

        return createCommand(XPENDING, new PendingMessageListOutput<>(codec), args);
    }

    public Command> xpending(K key, Consumer consumer, Range range, Limit limit) {
        notNullKey(key);
        LettuceAssert.notNull(consumer, "Consumer " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(range, "Range " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(limit, "Limit " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addKey(consumer.group);

        args.add(getLowerValue(range)).add(getUpperValue(range));

        args.add(limit.isLimited() ? limit.getCount() : Long.MAX_VALUE);
        args.addKey(consumer.name);

        return createCommand(XPENDING, new PendingMessageListOutput<>(codec), args);
    }

    public Command> xpending(K key, XPendingArgs commandArgs) {
        notNullKey(key);
        LettuceAssert.notNull(commandArgs, "XPendingArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        commandArgs.build(args);
        return createCommand(XPENDING, new PendingMessageListOutput<>(codec), args);
    }

    public Command>> xrange(K key, Range range, Limit limit) {
        notNullKey(key);
        LettuceAssert.notNull(range, "Range " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(limit, "Limit " + MUST_NOT_BE_NULL);

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

        args.add(getLowerValue(range)).add(getUpperValue(range));

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

        return createCommand(XRANGE, new StreamMessageListOutput<>(codec, key), args);
    }

    public Command>> xrevrange(K key, Range range, Limit limit) {
        notNullKey(key);
        LettuceAssert.notNull(range, "Range " + MUST_NOT_BE_NULL);
        LettuceAssert.notNull(limit, "Limit " + MUST_NOT_BE_NULL);

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

        args.add(getUpperValue(range)).add(getLowerValue(range));

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

        return createCommand(XREVRANGE, new StreamMessageListOutput<>(codec, key), args);
    }

    public Command xtrim(K key, boolean approximateTrimming, long count) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).add(MAXLEN);

        if (approximateTrimming) {
            args.add("~");
        }

        args.add(count);

        return createCommand(XTRIM, new IntegerOutput<>(codec), args);
    }

    public Command xtrim(K key, XTrimArgs xTrimArgs) {
        notNullKey(key);
        LettuceAssert.notNull(xTrimArgs, "XTrimArgs " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec).addKey(key);
        xTrimArgs.build(args);
        return createCommand(XTRIM, new IntegerOutput<>(codec), args);
    }

    private static String getLowerValue(Range range) {

        Boundary boundary = range.getLower();

        return boundary.equals(Boundary.unbounded()) ? "-" : getRange(boundary);
    }

    private static String getUpperValue(Range range) {

        Boundary boundary = range.getUpper();

        return boundary.equals(Boundary.unbounded()) ? "+" : getRange(boundary);
    }

    private static String getRange(Boundary boundary) {
        return !boundary.isIncluding() ? "(" + boundary.getValue() : boundary.getValue();
    }

    public Command>> xread(XReadArgs xReadArgs, StreamOffset[] streams) {
        LettuceAssert.notNull(streams, "Streams " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(streams.length > 0, "Streams " + MUST_NOT_BE_EMPTY);

        CommandArgs args = new CommandArgs<>(codec);

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

        args.add("STREAMS");

        for (StreamOffset stream : streams) {
            args.addKey(stream.name);
        }

        for (StreamOffset stream : streams) {
            args.add(stream.offset);
        }

        return createCommand(XREAD, new StreamReadOutput<>(codec), args);
    }

    public Command>> xreadgroup(Consumer consumer, XReadArgs xReadArgs,
            StreamOffset... streams) {
        LettuceAssert.notNull(streams, "Streams " + MUST_NOT_BE_NULL);
        LettuceAssert.isTrue(streams.length > 0, "Streams " + MUST_NOT_BE_EMPTY);
        LettuceAssert.notNull(consumer, "Consumer " + MUST_NOT_BE_NULL);

        CommandArgs args = new CommandArgs<>(codec);

        args.add("GROUP").add(encode(consumer.group)).add(encode(consumer.name));

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

        args.add("STREAMS");

        for (StreamOffset stream : streams) {
            args.addKey(stream.name);
        }

        for (XReadArgs.StreamOffset stream : streams) {
            args.add(stream.offset);
        }

        return createCommand(XREADGROUP, new StreamReadOutput<>(codec), args);
    }

    private byte[] encode(K k) {

        ByteBuffer byteBuffer = codec.encodeKey(k);

        byte[] result = new byte[byteBuffer.remaining()];
        byteBuffer.get(result);

        return result;
    }

    Command>> bzmpop(long timeout, ZPopArgs popArgs, K[] keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(timeout).add(keys.length).addKeys(keys);

        popArgs.build(args);

        return createCommand(BZMPOP, new KeyValueOfScoredValueOutput<>(codec, keys[0]), args);
    }

    Command>> bzmpop(double timeout, ZPopArgs popArgs, K[] keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(timeout).add(keys.length).addKeys(keys);

        popArgs.build(args);

        return createCommand(BZMPOP, new KeyValueOfScoredValueOutput<>(codec, keys[0]), args);
    }

    Command>>> bzmpop(long timeout, long count, ZPopArgs popArgs, K[] keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(timeout).add(keys.length).addKeys(keys);

        popArgs.build(args);
        args.add(COUNT).add(count);

        return createCommand(BZMPOP, new KeyValueListScoredValueOutput<>(codec, keys[0]), args);
    }

    Command>>> bzmpop(double timeout, long count, ZPopArgs popArgs, K[] keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(timeout).add(keys.length).addKeys(keys);

        popArgs.build(args);
        args.add(COUNT).add(count);

        return createCommand(BZMPOP, new KeyValueListScoredValueOutput<>(codec, keys[0]), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKeys(keys).add(timeout);

        return createCommand(BZPOPMIN, new KeyValueScoredValueOutput<>(codec), args);
    }

    Command>> bzpopmin(double timeout, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).addKeys(keys).add(timeout);

        return createCommand(BZPOPMIN, new KeyValueScoredValueOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKeys(keys).add(timeout);

        return createCommand(BZPOPMAX, new KeyValueScoredValueOutput<>(codec), args);
    }

    Command>> bzpopmax(double timeout, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).addKeys(keys).add(timeout);

        return createCommand(BZPOPMAX, new KeyValueScoredValueOutput<>(codec), args);
    }

    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);
    }

    @SuppressWarnings("unchecked")
    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);
    }

    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);
    }

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

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

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

    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);
    }

    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);
    }

    Command> zdiff(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(keys.length).addKeys(keys);
        return createCommand(ZDIFF, new ValueListOutput<>(codec), args);
    }

    Command zdiffstore(K destKey, K... srcKeys) {
        notNullKey(destKey);
        notEmpty(srcKeys);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKey(destKey).add(srcKeys.length).addKeys(srcKeys);
        return createCommand(ZDIFFSTORE, new IntegerOutput<>(codec), args);
    }

    Command>> zdiffWithScores(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(keys.length).addKeys(keys).add(WITHSCORES);
        return createCommand(ZDIFF, new ScoredValueListOutput<>(codec), args);
    }

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

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

    Command> zinter(K... keys) {
        notEmpty(keys);

        return zinter(new ZAggregateArgs(), keys);
    }

    Command> zinter(ZAggregateArgs aggregateArgs, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(keys.length).addKeys(keys);
        aggregateArgs.build(args);
        return createCommand(ZINTER, new ValueListOutput<>(codec), args);
    }

    Command zintercard(K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys);
        return createCommand(ZINTERCARD, new IntegerOutput<>(codec), args);
    }

    Command zintercard(long limit, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys).add(LIMIT).add(limit);
        return createCommand(ZINTERCARD, new IntegerOutput<>(codec), args);
    }

    Command>> zinterWithScores(K... keys) {
        notEmpty(keys);

        return zinterWithScores(new ZAggregateArgs(), keys);
    }

    Command>> zinterWithScores(ZAggregateArgs aggregateArgs, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(keys.length).addKeys(keys).add(WITHSCORES);
        aggregateArgs.build(args);
        return createCommand(ZINTER, new ScoredValueListOutput<>(codec), args);
    }

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

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

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

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

    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);
    }

    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);
    }

    Command> zmscore(K key, V... members) {
        notNullKey(key);
        notEmpty(members);

        return createCommand(ZMSCORE, new DoubleListOutput<>(codec), key, members);
    }

    Command>> zmpop(ZPopArgs popArgs, K[] keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys);

        popArgs.build(args);

        return createCommand(ZMPOP, new KeyValueOfScoredValueOutput<>(codec, keys[0]), args);
    }

    Command>>> zmpop(long count, ZPopArgs popArgs, K[] keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec).add(keys.length).addKeys(keys);

        popArgs.build(args);
        args.add(COUNT).add(count);

        return createCommand(ZMPOP, new KeyValueListScoredValueOutput<>(codec, keys[0]), args);
    }

    Command> zpopmin(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKeys(key);

        return createCommand(ZPOPMIN, new ScoredValueOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKeys(key).add(count);

        return createCommand(ZPOPMIN, new ScoredValueListOutput<>(codec), args);
    }

    Command> zpopmax(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKeys(key);

        return createCommand(ZPOPMAX, new ScoredValueOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKeys(key).add(count);

        return createCommand(ZPOPMAX, new ScoredValueListOutput<>(codec), args);
    }

    Command zrandmember(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKeys(key);

        return createCommand(ZRANDMEMBER, new ValueOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKeys(key).add(count);

        return createCommand(ZRANDMEMBER, new ValueListOutput<>(codec), args);
    }

    Command> zrandmemberWithScores(K key) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKeys(key).add(1).add(WITHSCORES);

        return createCommand(ZRANDMEMBER, new ScoredValueOutput<>(codec), args);
    }

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

        CommandArgs args = new CommandArgs<>(codec).addKeys(key).add(count).add(WITHSCORES);

        return createCommand(ZRANDMEMBER, new ScoredValueListOutput<>(codec), args);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

    Command zrangestore(K dstKey, K srcKey, Range range, boolean rev) {
        notNullKey(srcKey);
        notNullKey(dstKey);
        notNullRange(range);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKeys(dstKey, srcKey);

        args.add(min(range)).add(max(range));

        if (rev) {
            args.add(REV);
        }
        return createCommand(ZRANGESTORE, new IntegerOutput<>(codec), args);
    }

    Command zrangestorebylex(K dstKey, K srcKey, Range range, Limit limit, boolean rev) {
        notNullKey(srcKey);
        notNullKey(dstKey);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKeys(dstKey, srcKey);

        if (rev) {
            args.add(maxValue(range)).add(minValue(range));
        } else {
            args.add(minValue(range)).add(maxValue(range));
        }

        args.add(BYLEX);

        if (rev) {
            args.add(REV);
        }
        addLimit(args, limit);
        return createCommand(ZRANGESTORE, new IntegerOutput<>(codec), args);
    }

    Command zrangestorebyscore(K dstKey, K srcKey, Range range, Limit limit, boolean rev) {
        notNullKey(srcKey);
        notNullKey(dstKey);
        notNullRange(range);
        notNullLimit(limit);

        CommandArgs args = new CommandArgs<>(codec);
        args.addKeys(dstKey, srcKey);

        if (rev) {
            args.add(max(range)).add(min(range));
        } else {
            args.add(min(range)).add(max(range));
        }

        args.add(BYSCORE);
        if (rev) {
            args.add(REV);
        }
        addLimit(args, limit);
        return createCommand(ZRANGESTORE, new IntegerOutput<>(codec), args);
    }

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

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

    Command> zrankWithScore(K key, V member) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addValue(member).add(WITHSCORE);
        return createCommand(ZRANK, (ScoredValueOutput) new ScoredValueOutput<>(LongCodec.INSTANCE), args);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

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

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

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

    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);
    }

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

    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);
    }

    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);
    }

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

    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);
    }

    Command zrevrangebyscoreWithScores(ScoredValueStreamingChannel channel, K key, double max, double min,
            long offset, long count) {
        notNullKey(key);
        notNull(channel);
        return zrevrangebyscoreWithScores(channel, key, string(max), string(min), offset, count);
    }

    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);
    }

    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);
    }

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

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

    Command> zrevrankWithScore(K key, V member) {
        notNullKey(key);

        CommandArgs args = new CommandArgs<>(codec).addKey(key).addValue(member).add(WITHSCORE);
        return createCommand(ZREVRANK, (ScoredValueOutput) new ScoredValueOutput<>(LongCodec.INSTANCE), args);
    }

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

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

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

        return zscan(key, scanCursor, null);
    }

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

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

    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);
    }

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

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

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

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

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

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

    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);
    }

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

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

    Command> zunion(K... keys) {
        notEmpty(keys);

        return zunion(new ZAggregateArgs(), keys);
    }

    Command> zunion(ZAggregateArgs aggregateArgs, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(keys.length).addKeys(keys);
        aggregateArgs.build(args);
        return createCommand(ZUNION, new ValueListOutput<>(codec), args);
    }

    Command>> zunionWithScores(K... keys) {
        notEmpty(keys);

        return zunionWithScores(new ZAggregateArgs(), keys);
    }

    Command>> zunionWithScores(ZAggregateArgs aggregateArgs, K... keys) {
        notEmpty(keys);

        CommandArgs args = new CommandArgs<>(codec);
        args.add(keys.length).addKeys(keys).add(WITHSCORES);
        aggregateArgs.build(args);
        return createCommand(ZUNION, new ScoredValueListOutput<>(codec), args);
    }

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

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

    Command zunionstore(K destination, ZAggregateArgs aggregateArgs, K... keys) {
        notEmpty(keys);

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

    Command>> clusterLinks() {
        CommandArgs args = new CommandArgs<>(codec).add(LINKS);
        return createCommand(CLUSTER, (CommandOutput) new ObjectOutput<>(StringCodec.UTF8), args);
    }

    enum LongCodec implements RedisCodec {

        INSTANCE;

        @Override
        public Long decodeKey(ByteBuffer bytes) {

            String s = StringCodec.ASCII.decodeKey(bytes);

            return s == null ? null : Long.valueOf(s);
        }

        @Override
        public Long decodeValue(ByteBuffer bytes) {
            return decodeKey(bytes);
        }

        @Override
        public ByteBuffer encodeKey(Long key) {
            return StringCodec.ASCII.encodeKey(key == null ? null : key.toString());
        }

        @Override
        public ByteBuffer encodeValue(Long value) {
            return encodeKey(value);
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy