title: EXISTS

summary: Determines whether one or more keys exist.

title: Examples

text:
#### Code Examples

Foundational: Check if one or more keys exist using EXISTS (returns count of existing keys, useful for conditional logic)

**Difficulty:** Beginner

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

Give these commands a try in the interactive console:

SET key1 "Hello"
EXISTS key1
EXISTS nosuchkey
SET key2 "World"
EXISTS key1 key2 nosuchkey

title: Return information

text:
**RESP2:**

[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that exist from those specified as arguments.

**RESP3:**

[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that exist from those specified as arguments.

code:
bool existsResult1 = db.StringSet("key1", "Hello");
        Console.WriteLine(existsResult1);  // >>> true

        bool existsResult2 = db.KeyExists("key1");
        Console.WriteLine(existsResult2);  // >>> true

        bool existsResult3 = db.KeyExists("nosuchkey");
        Console.WriteLine(existsResult3);  // >>> false

        bool existsResult4 = db.StringSet("key2", "World");
        Console.WriteLine(existsResult4);  // >>> true

        long existsResult5 = db.KeyExists(["key1", "key2", "nosuchkey"]);
        Console.WriteLine(existsResult5);  // >>> 2

code:
existsResult1, err := rdb.Set(ctx, "key1", "Hello", 0).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(existsResult1) // >>> OK

	existsResult2, err := rdb.Exists(ctx, "key1").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(existsResult2) // >>> 1

	existsResult3, err := rdb.Exists(ctx, "nosuchkey").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(existsResult3) // >>> 0

	existsResult4, err := rdb.Set(ctx, "key2", "World", 0).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(existsResult4) // >>> OK

	existsResult5, err := rdb.Exists(ctx, "key1", "key2", "nosuchkey").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(existsResult5) // >>> 2

code:
CompletableFuture<Void> existsExample = asyncCommands.set("key1", "Hello").thenCompose(res1 -> {
                System.out.println(res1); // >>> OK

                return asyncCommands.exists("key1");
            }).thenCompose(res2 -> {
                System.out.println(res2); // >>> 1

                return asyncCommands.exists("nosuchkey");
            }).thenCompose(res3 -> {
                System.out.println(res3); // >>> 0

                return asyncCommands.set("key2", "World");
            }).thenCompose(res4 -> {
                System.out.println(res4); // >>> OK

                return asyncCommands.exists("key1", "key2", "nosuchkey");
            }).thenAccept(res5 -> {
                System.out.println(res5); // >>> 2
            }).toCompletableFuture();

code:
Mono<Void> existsExample = reactiveCommands.set("key1", "Hello").doOnNext(res1 -> {
                System.out.println(res1); // >>> OK
            }).then(reactiveCommands.exists("key1")).doOnNext(res2 -> {
                System.out.println(res2); // >>> 1
            }).then(reactiveCommands.exists("nosuchkey")).doOnNext(res3 -> {
                System.out.println(res3); // >>> 0
            }).then(reactiveCommands.set("key2", "World")).doOnNext(res4 -> {
                System.out.println(res4); // >>> OK
            }).then(reactiveCommands.exists("key1", "key2", "nosuchkey")).doOnNext(res5 -> {
                System.out.println(res5); // >>> 2
            }).then();

code:
String existsResult1 = jedis.set("key1", "Hello");
        System.out.println(existsResult1); // >>> OK

        boolean existsResult2 = jedis.exists("key1");
        System.out.println(existsResult2); // >>> true

        boolean existsResult3 = jedis.exists("nosuchkey");
        System.out.println(existsResult3); // >>> false

        String existsResult4 = jedis.set("key2", "World");
        System.out.println(existsResult4); // >>> OK

        long existsResult5 = jedis.exists("key1", "key2", "nosuchkey");
        System.out.println(existsResult5); // >>> 2

code:
const existsRes1 = await client.set('key1', 'Hello');
console.log(existsRes1); // OK

const existsRes2 = await client.exists('key1');
console.log(existsRes2); // 1

const existsRes3 = await client.exists('nosuchkey');
console.log(existsRes3); // 0

const existsRes4 = await client.set('key2', 'World');
console.log(existsRes4); // OK

const existsRes5 = await client.exists(['key1', 'key2', 'nosuchkey']);
console.log(existsRes5); // 2

code:
$existsResult1 = $r->set('key1', 'Hello');
        echo $existsResult1 . PHP_EOL; // >>> OK

        $existsResult2 = $r->exists('key1');
        echo $existsResult2 . PHP_EOL; // >>> 1

        $existsResult3 = $r->exists('nosuchkey');
        echo $existsResult3 . PHP_EOL; // >>> 0

        $existsResult4 = $r->set('key2', 'World');
        echo $existsResult4 . PHP_EOL; // >>> OK

        $existsResult5 = $r->exists('key1', 'key2', 'nosuchkey');
        echo $existsResult5 . PHP_EOL; // >>> 2

code:
res = r.set("key1", "Hello")
print(res)
# >>> True

res = r.exists("key1")
print(res)
# >>> 1

res = r.exists("nosuchkey")
print(res)
# >>> 0

res = r.set("key2", "World")
print(res)
# >>> True

res = r.exists("key1", "key2", "nosuchkey")
print(res)
# >>> 2

code:
if let Ok(res) = r.set("key1", "Hello").await {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.exists("key1").await {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");    // >>> 1
            },
            Err(e) => {
                println!("Error checking key existence: {e}");
                return;
            }
        }

        match r.exists("nosuchkey").await {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");    // >>> 0
            },
            Err(e) => {
                println!("Error checking key existence: {e}");
                return;
            }
        }

        if let Ok(res) = r.set("key2", "World").await {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.exists(&["key1", "key2", "nosuchkey"]).await {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");    // >>> 2
            },
            Err(e) => {
                println!("Error checking key existence: {e}");
                return;
            }
        }

code:
if let Ok(res) = r.set("key1", "Hello") {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.exists("key1") {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");    // >>> 1
            },
            Err(e) => {
                println!("Error checking key existence: {e}");
                return;
            }
        }

        match r.exists("nosuchkey") {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");    // >>> 0
            },
            Err(e) => {
                println!("Error checking key existence: {e}");
                return;
            }
        }

        if let Ok(res) = r.set("key2", "World") {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.exists(&["key1", "key2", "nosuchkey"]) {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");    // >>> 2
            },
            Err(e) => {
                println!("Error checking key existence: {e}");
                return;
            }
        }


title: EXPIRE

summary: Sets the expiration time of a key in seconds.

page_type: content

title: Options

role: parameters

text:
The `EXPIRE` command supports a set of options:

* `NX` -- Set expiry only when the key has no expiry
* `XX` -- Set expiry only when the key has an existing expiry
* `GT` -- Set expiry only when the new expiry is greater than current one
* `LT` -- Set expiry only when the new expiry is less than current one

A non-volatile key is treated as an infinite TTL for the purpose of `GT` and `LT`.
The `GT`, `LT` and `NX` options are mutually exclusive.

title: Refreshing expires

role: content

text:
It is possible to call `EXPIRE` using as argument a key that already has an
existing expire set.
In this case the time to live of a key is _updated_ to the new value.
There are many useful applications for this, an example is documented in the
_Navigation session_ pattern section below.

title: Differences in Redis prior 2.1.3

text:
In Redis versions prior **2.1.3** altering a key with an expire set using a
command altering its value had the effect of removing the key entirely.
This semantics was needed because of limitations in the replication layer that
are now fixed.

`EXPIRE` would return 0 and not alter the timeout for a key with a timeout set.

title: Examples

role: example

text:
#### Code Examples

Foundational: Set key expiration time using EXPIRE (supports conditional options NX/XX/GT/LT, returns 1 if set or 0 if not)

**Difficulty:** Beginner

**Commands:** SET, EXPIRE, TTL

**Complexity:**
- SET: O(1)
- EXPIRE: O(1)
- TTL: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)

**C:**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

Give these commands a try in the interactive console:

SET mykey "Hello"
EXPIRE mykey 10
TTL mykey
SET mykey "Hello World"
TTL mykey
EXPIRE mykey 10 XX
TTL mykey
EXPIRE mykey 10 NX
TTL mykey

title: Pattern: Navigation session

text:
Imagine you have a web service and you are interested in the latest N pages
_recently_ visited by your users, such that each adjacent page view was not
performed more than 60 seconds after the previous.
Conceptually you may consider this set of page views as a _Navigation session_
of your user, that may contain interesting information about what kind of
products he or she is looking for currently, so that you can recommend related
products.

You can easily model this pattern in Redis using the following strategy: every
time the user does a page view you call the following commands:

[code example]

If the user will be idle more than 60 seconds, the key will be deleted and only
subsequent page views that have less than 60 seconds of difference will be
recorded.

This pattern is easily modified to use counters using [`INCR`]() instead of lists
using [`RPUSH`]().

title: Appendix: Redis expires

title: Keys with an expire

text:
Normally Redis keys are created without an associated time to live.
The key will simply live forever, unless it is removed by the user in an
explicit way, for instance using the [`DEL`]() command.

The `EXPIRE` family of commands is able to associate an expire to a given key,
at the cost of some additional memory used by the key.
When a key has an expire set, Redis will make sure to remove the key when the
specified amount of time elapsed.

The key time to live can be updated or entirely removed using the `EXPIRE` and
[`PERSIST`]() command (or other strictly related commands).

title: Expire accuracy

text:
In Redis 2.4 the expire might not be pin-point accurate, and it could be between
zero to one seconds out.

Since Redis 2.6 the expire error is from 0 to 1 milliseconds.

title: Expires and persistence

text:
Keys expiring information is stored as absolute Unix timestamps (in milliseconds
in case of Redis version 2.6 or greater).
This means that the time is flowing even when the Redis instance is not active.

For expires to work well, the computer time must be taken stable.
If you move an RDB file from two computers with a big desync in their clocks,
funny things may happen (like all the keys loaded to be expired at loading
time).

Even running instances will always check the computer clock, so for instance if
you set a key with a time to live of 1000 seconds, and then set your computer
time 2000 seconds in the future, the key will be expired immediately, instead of
lasting for 1000 seconds.

title: How Redis expires keys

text:
Redis keys are expired in two ways: a passive way and an active way.

A key is passively expired when a client tries to access it and the
key is timed out.

However, this is not enough as there are expired keys that will never be
accessed again.
These keys should be expired anyway, so periodically, Redis tests a few keys at
random amongst the set of keys with an expiration.
All the keys that are already expired are deleted from the keyspace.

title: How expires are handled in the replication link and AOF file

text:
In order to obtain a correct behavior without sacrificing consistency, when a
key expires, a [`DEL`]() operation is synthesized in both the AOF file and gains all
the attached replicas nodes.
This way the expiration process is centralized in the master instance, and there
is no chance of consistency errors.

However while the replicas connected to a master will not expire keys
independently (but will wait for the [`DEL`]() coming from the master), they'll
still take the full state of the expires existing in the dataset, so when a
replica is elected to master it will be able to expire the keys independently,
fully acting as a master.

title: Redis Search and expiration

text: Starting with Redis 8, Redis Search has enhanced behavior when handling expiring keys. For detailed information about how [`FT.SEARCH`]() and [`FT.AGGREGATE`]() commands interact with expiring keys, see [Key and field expiration behavior]().

title: Redis Software and Redis Cloud compatibility

text:
| Redis
Software | Redis
Cloud | Notes |
|:----------------------|:-----------------|:------|
| ✅ Standard
<nobr>✅ Active-Active</nobr> | ✅ Standard
<nobr>✅ Active-Active</nobr> |  |

title: Return information

role: returns

text:
**RESP2:**

One of the following:
* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set; for example, the key doesn't exist, or the operation was skipped because of the provided arguments.
* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.

**RESP3:**

One of the following:
* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set; for example, the key doesn't exist, or the operation was skipped because of the provided arguments.
* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.

code:
> SET mykey "Hello"
"OK"
> EXPIRE mykey 10
(integer) 1
> TTL mykey
(integer) 10
> SET mykey "Hello World"
"OK"
> TTL mykey
(integer) -1
> EXPIRE mykey 10 XX
(integer) 0
> TTL mykey
(integer) -1
> EXPIRE mykey 10 NX
(integer) 1
> TTL mykey
(integer) 10

section_id: examples

code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>

int main(int argc, char **argv) {
    redisContext *c = redisConnect("127.0.0.1", 6379);

    if (c == NULL || c->err) {
        if (c) {
            printf("Connection error: %s\n", c->errstr);
            redisFree(c);
        } else {
            printf("Connection error: can't allocate redis context\n");
        }
        return 1;
    }

    redisReply *reply;

    // Set up keys
    reply = redisCommand(c, "MSET %s %s %s %s %s %s",
        "firstname", "Jack", "lastname", "Stuntman", "age", "35");
    printf("MSET firstname Jack lastname Stuntman age 35: %s\n", reply->str);
    // >>> OK
    freeReplyObject(reply);

    // Keys matching *name*
    reply = redisCommand(c, "KEYS %s", "*name*");
    printf("KEYS *name*:\n");
    for (size_t i = 0; i < reply->elements; i++) {
        printf("  %s\n", reply->element[i]->str);
    }
    // >>> firstname
    // >>> lastname
    freeReplyObject(reply);

    // Keys matching a??
    reply = redisCommand(c, "KEYS %s", "a??");
    printf("KEYS a??:\n");
    for (size_t i = 0; i < reply->elements; i++) {
        printf("  %s\n", reply->element[i]->str);
    }
    // >>> age
    freeReplyObject(reply);

    // All keys
    reply = redisCommand(c, "KEYS %s", "*");
    printf("KEYS *:\n");
    for (size_t i = 0; i < reply->elements; i++) {
        printf("  %s\n", reply->element[i]->str);
    }
    // >>> age
    // >>> firstname
    // >>> lastname
    freeReplyObject(reply);

    redisFree(c);

    return 0;
}

code:
bool expireResult1 = db.StringSet("mykey", "Hello");
        Console.WriteLine(expireResult1);   // >>> true

        bool expireResult2 = db.KeyExpire("mykey", new TimeSpan(0, 0, 10));
        Console.WriteLine(expireResult2);   // >>> true

        TimeSpan expireResult3 = db.KeyTimeToLive("mykey") ?? TimeSpan.Zero;
        Console.WriteLine(Math.Round(expireResult3.TotalSeconds));   // >>> 10

        bool expireResult4 = db.StringSet("mykey", "Hello World");
        Console.WriteLine(expireResult4);   // >>> true

        TimeSpan expireResult5 = db.KeyTimeToLive("mykey") ?? TimeSpan.Zero;
        Console.WriteLine(Math.Round(expireResult5.TotalSeconds).ToString());   // >>> 0

        bool expireResult6 = db.KeyExpire("mykey", new TimeSpan(0, 0, 10), ExpireWhen.HasExpiry);
        Console.WriteLine(expireResult6);   // >>> false

        TimeSpan expireResult7 = db.KeyTimeToLive("mykey") ?? TimeSpan.Zero;
        Console.WriteLine(Math.Round(expireResult7.TotalSeconds));   // >>> 0

        bool expireResult8 = db.KeyExpire("mykey", new TimeSpan(0, 0, 10), ExpireWhen.HasNoExpiry);
        Console.WriteLine(expireResult8);   // >>> true

        TimeSpan expireResult9 = db.KeyTimeToLive("mykey") ?? TimeSpan.Zero;
        Console.WriteLine(Math.Round(expireResult9.TotalSeconds));   // >>> 10

code:
expireResult1, err := rdb.Set(ctx, "mykey", "Hello", 0).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult1) // >>> OK

	expireResult2, err := rdb.Expire(ctx, "mykey", 10*time.Second).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult2) // >>> true

	expireResult3, err := rdb.TTL(ctx, "mykey").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(math.Round(expireResult3.Seconds())) // >>> 10

	expireResult4, err := rdb.Set(ctx, "mykey", "Hello World", 0).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult4) // >>> OK

	expireResult5, err := rdb.TTL(ctx, "mykey").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult5) // >>> -1ns

	expireResult6, err := rdb.ExpireXX(ctx, "mykey", 10*time.Second).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult6) // >>> false

	expireResult7, err := rdb.TTL(ctx, "mykey").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult7) // >>> -1ns

	expireResult8, err := rdb.ExpireNX(ctx, "mykey", 10*time.Second).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(expireResult8) // >>> true

	expireResult9, err := rdb.TTL(ctx, "mykey").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(math.Round(expireResult9.Seconds())) // >>> 10

code:
package io.redis.examples.async;

import io.lettuce.core.*;

import io.lettuce.core.api.async.RedisAsyncCommands;

import io.lettuce.core.api.StatefulRedisConnection;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

public class CmdsGenericExample {

    public void run() {

            CompletableFuture<Void> existsExample = asyncCommands.set("key1", "Hello").thenCompose(res1 -> {
                System.out.println(res1); // >>> OK

                return asyncCommands.exists("key1");
            }).thenCompose(res2 -> {
                System.out.println(res2); // >>> 1

                return asyncCommands.exists("nosuchkey");
            }).thenCompose(res3 -> {
                System.out.println(res3); // >>> 0

                return asyncCommands.set("key2", "World");
            }).thenCompose(res4 -> {
                System.out.println(res4); // >>> OK

                return asyncCommands.exists("key1", "key2", "nosuchkey");
            }).thenAccept(res5 -> {
                System.out.println(res5); // >>> 2
            }).toCompletableFuture();
            existsExample.join();

            CompletableFuture<Void> keysExample = asyncCommands.mset(Map.of(
                    "firstname", "Jack",
                    "lastname", "Stuntman",
                    "age", "35"
            )).thenCompose(res1 -> {
                System.out.println(res1); // >>> OK

                return asyncCommands.keys("*name*");
            }).thenCompose(res2 -> {
                Collections.sort(res2);
                System.out.println(res2); // >>> [firstname, lastname]

                return asyncCommands.keys("a??");
            }).thenCompose(res3 -> {
                System.out.println(res3); // >>> [age]

                return asyncCommands.keys("*");
            }).thenAccept(res4 -> {
                Collections.sort(res4);
                System.out.println(res4); // >>> [age, firstname, lastname]
            }).toCompletableFuture();
            keysExample.join();
        } finally {
            redisClient.shutdown();
        }
    }
}

code:
package io.redis.examples.reactive;

import io.lettuce.core.*;
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import io.lettuce.core.api.StatefulRedisConnection;
import reactor.core.publisher.Mono;

import java.util.Collections;
import java.util.Map;

public class CmdsGenericExample {

    public void run() {
        RedisClient redisClient = RedisClient.create("redis://localhost:6379");

        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();

            Mono<Void> existsExample = reactiveCommands.set("key1", "Hello").doOnNext(res1 -> {
                System.out.println(res1); // >>> OK
            }).then(reactiveCommands.exists("key1")).doOnNext(res2 -> {
                System.out.println(res2); // >>> 1
            }).then(reactiveCommands.exists("nosuchkey")).doOnNext(res3 -> {
                System.out.println(res3); // >>> 0
            }).then(reactiveCommands.set("key2", "World")).doOnNext(res4 -> {
                System.out.println(res4); // >>> OK
            }).then(reactiveCommands.exists("key1", "key2", "nosuchkey")).doOnNext(res5 -> {
                System.out.println(res5); // >>> 2
            }).then();

            Mono.when(existsExample).block();

            Mono<Void> keysExample = reactiveCommands.mset(Map.of(
                    "firstname", "Jack",
                    "lastname", "Stuntman",
                    "age", "35"
            )).doOnNext(res1 -> {
                System.out.println(res1); // >>> OK
            }).then(reactiveCommands.keys("*name*").collectList()).doOnNext(res2 -> {
                Collections.sort(res2);
                System.out.println(res2); // >>> [firstname, lastname]
            }).then(reactiveCommands.keys("a??").collectList()).doOnNext(res3 -> {
                System.out.println(res3); // >>> [age]
            }).then(reactiveCommands.keys("*").collectList()).doOnNext(res4 -> {
                Collections.sort(res4);
                System.out.println(res4); // >>> [age, firstname, lastname]
            }).then();

            Mono.when(keysExample).block();

        } finally {
            redisClient.shutdown();
        }
    }

}

code:
String expireResult1 = jedis.set("mykey", "Hello");
        System.out.println(expireResult1);  // >>> OK

        long expireResult2 = jedis.expire("mykey", 10);
        System.out.println(expireResult2);  // >>> 1

        long expireResult3 = jedis.ttl("mykey");
        System.out.println(expireResult3);  // >>> 10

        String expireResult4 = jedis.set("mykey", "Hello World");
        System.out.println(expireResult4);  // >>> OK

        long expireResult5 = jedis.ttl("mykey");
        System.out.println(expireResult5);  // >>> -1

        long expireResult6 = jedis.expire("mykey", 10, ExpiryOption.XX);
        System.out.println(expireResult6);  // >>> 0

        long expireResult7 = jedis.ttl("mykey");
        System.out.println(expireResult7);  // >>> -1

        long expireResult8 = jedis.expire("mykey", 10, ExpiryOption.NX);
        System.out.println(expireResult8);  // >>> 1

        long expireResult9 = jedis.ttl("mykey");
        System.out.println(expireResult9);  // >>> 10

code:
const expireRes1 = await client.set('mykey', 'Hello');
console.log(expireRes1); // OK

const expireRes2 = await client.expire('mykey', 10);
console.log(expireRes2); // 1

const expireRes3 = await client.ttl('mykey');
console.log(expireRes3); // 10

const expireRes4 = await client.set('mykey', 'Hello World');
console.log(expireRes4); // OK

const expireRes5 = await client.ttl('mykey');
console.log(expireRes5); // -1

const expireRes6 = await client.expire('mykey', 10, "XX");
console.log(expireRes6); // 0

const expireRes7 = await client.ttl('mykey');
console.log(expireRes7); // -1

const expireRes8 = await client.expire('mykey', 10, "NX");
console.log(expireRes8); // 1

const expireRes9 = await client.ttl('mykey');
console.log(expireRes9); // 10

code:
import assert from 'node:assert';
import { Redis } from 'ioredis';

const redis = new Redis();

const keysRes1 = await redis.mset({ firstname: 'Jack', lastname: 'Stuntman', age: '35' });
console.log(keysRes1); // >>> OK

const keysRes2 = await redis.keys('*name*');
console.log(keysRes2.sort()); // >>> ['firstname', 'lastname']

const keysRes3 = await redis.keys('a??');
console.log(keysRes3); // >>> ['age']

const keysRes4 = await redis.keys('*');
console.log(keysRes4.sort()); // >>> ['age', 'firstname', 'lastname']

redis.disconnect();

code:
<?php
use PHPUnit\Framework\TestCase;
use Predis\Client as PredisClient;

class CmdsGenericTest
{
    public function testCmdsGeneric() {
        $r = new PredisClient([
            'scheme'   => 'tcp',
            'host'     => '127.0.0.1',
            'port'     => 6379,
            'password' => '',
            'database' => 0,
        ]);

        $existsResult1 = $r->set('key1', 'Hello');
        echo $existsResult1 . PHP_EOL; // >>> OK

        $existsResult2 = $r->exists('key1');
        echo $existsResult2 . PHP_EOL; // >>> 1

        $existsResult3 = $r->exists('nosuchkey');
        echo $existsResult3 . PHP_EOL; // >>> 0

        $existsResult4 = $r->set('key2', 'World');
        echo $existsResult4 . PHP_EOL; // >>> OK

        $existsResult5 = $r->exists('key1', 'key2', 'nosuchkey');
        echo $existsResult5 . PHP_EOL; // >>> 2

        $keysResult1 = $r->mset(['firstname' => 'Jack', 'lastname' => 'Stuntman', 'age' => '35']);
        echo $keysResult1 . PHP_EOL; // >>> OK

        $keysResult2 = $r->keys('*name*');
        sort($keysResult2);
        echo implode(', ', $keysResult2) . PHP_EOL; // >>> firstname, lastname

        $keysResult3 = $r->keys('a??');
        echo implode(', ', $keysResult3) . PHP_EOL; // >>> age

        $keysResult4 = $r->keys('*');
        sort($keysResult4);
        echo implode(', ', $keysResult4) . PHP_EOL; // >>> age, firstname, lastname

    }
}

code:
res = r.set("mykey", "Hello")
print(res)
# >>> True

res = r.expire("mykey", 10)
print(res)
# >>> True

res = r.ttl("mykey")
print(res)
# >>> 10

res = r.set("mykey", "Hello World")
print(res)
# >>> True

res = r.ttl("mykey")
print(res)
# >>> -1

res = r.expire("mykey", 10, xx=True)
print(res)
# >>> False

res = r.ttl("mykey")
print(res)
# >>> -1

res = r.expire("mykey", 10, nx=True)
print(res)
# >>> True

res = r.ttl("mykey")
print(res)
# >>> 10

code:
if let Ok(res) = r.set("mykey", "Hello").await {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.expire("mykey", 10).await {
            Ok(res) => {
                let res: bool = res;
                println!("{res}");    // >>> true
            },
            Err(e) => {
                println!("Error setting key expiration: {e}");
                return;
            }
        }

        match r.ttl("mykey").await {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> 10
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        if let Ok(res) = r.set("mykey", "Hello World").await {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.ttl("mykey").await {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> -1
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        // Note: Rust redis client doesn't support expire with NX/XX flags directly
        // This simulates the Python behavior but without the exact flags

        // Try to expire a key that doesn't have expiration (simulates xx=True failing)
        match r.ttl("mykey").await {
            Ok(res) => {
                let res: i64 = res;
                println!("false");    // >>> false (simulating expire xx=True failure)
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        match r.ttl("mykey").await {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> -1
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        // Now set expiration (simulates nx=True succeeding)
        match r.expire("mykey", 10).await {
            Ok(res) => {
                let res: bool = res;
                println!("{res}");    // >>> true
            },
            Err(e) => {
                println!("Error setting key expiration: {e}");
                return;
            }
        }

        match r.ttl("mykey").await {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> 10
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

code:
if let Ok(res) = r.set("mykey", "Hello") {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.expire("mykey", 10) {
            Ok(res) => {
                let res: bool = res;
                println!("{res}");    // >>> true
            },
            Err(e) => {
                println!("Error setting key expiration: {e}");
                return;
            }
        }

        match r.ttl("mykey") {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> 10
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        if let Ok(res) = r.set("mykey", "Hello World") {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.ttl("mykey") {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> -1
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        // Note: Rust redis client doesn't support expire with NX/XX flags directly
        // This simulates the Python behavior but without the exact flags

        // Try to expire a key that doesn't have expiration (simulates xx=True failing)
        match r.ttl("mykey") {
            Ok(res) => {
                let res: i64 = res;
                println!("false");    // >>> false (simulating expire xx=True failure)
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        match r.ttl("mykey") {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> -1
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

        // Now set expiration (simulates nx=True succeeding)
        match r.expire("mykey", 10) {
            Ok(res) => {
                let res: bool = res;
                println!("{res}");    // >>> true
            },
            Err(e) => {
                println!("Error setting key expiration: {e}");
                return;
            }
        }

        match r.ttl("mykey") {
            Ok(res) => {
                let res: i64 = res;
                println!("{res}");    // >>> 10
            },
            Err(e) => {
                println!("Error getting key TTL: {e}");
                return;
            }
        }

code:
MULTI
RPUSH pagewviews.user:<userid> http://.....
EXPIRE pagewviews.user:<userid> 60
EXEC

section_id: pattern-navigation-session


title: EXPIREAT

summary: Sets the expiration time of a key to a Unix timestamp.

title: Overview

role: overview

text:
`EXPIREAT` has the same effect and semantic as [`EXPIRE`](), but instead of
specifying the number of seconds representing the TTL (time to live), it takes
an absolute [Unix timestamp][hewowu] (seconds since January 1, 1970). A
timestamp in the past will delete the key immediately.

[hewowu]: http://en.wikipedia.org/wiki/Unix_time

Please for the specific semantics of the command refer to the documentation of
[`EXPIRE`]().

title: Background

text:
`EXPIREAT` was introduced in order to convert relative timeouts to absolute
timeouts for the AOF persistence mode.
Of course, it can be used directly to specify that a given key should expire at
a given time in the future.

title: Options

text:
The `EXPIREAT` command supports a set of options:

* `NX` -- Set expiry only when the key has no expiry
* `XX` -- Set expiry only when the key has an existing expiry
* `GT` -- Set expiry only when the new expiry is greater than current one
* `LT` -- Set expiry only when the new expiry is less than current one

A non-volatile key is treated as an infinite TTL for the purpose of `GT` and `LT`.
The `GT`, `LT` and `NX` options are mutually exclusive.

title: Examples

text:
SET mykey "Hello"
EXISTS mykey
EXPIREAT mykey 1293840000
EXISTS mykey

title: Return information


title: EXPIRETIME

summary: Returns the expiration time of a key as a Unix timestamp.

title: Overview

text:
Returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given key will expire.

See also the [`PEXPIRETIME`]() command which returns the same information with milliseconds resolution.

title: Examples

text:
SET mykey "Hello"
EXPIREAT mykey 33177117420
EXPIRETIME mykey

title: Return information

text:
**RESP2:**

One of the following:
* [Integer reply](../../develop/reference/protocol-spec#integers): the expiration Unix timestamp in seconds.
* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration time.
* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist.

**RESP3:**

One of the following:
* [Integer reply](../../develop/reference/protocol-spec#integers): the expiration Unix timestamp in seconds.
* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration time.
* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist.


title: FAILOVER

summary: Starts a coordinated failover from a server to one of its replicas.

title: Overview

text:
This command will start a coordinated failover between the currently-connected-to master and one of its replicas.
The failover is not synchronous, instead a background task will handle coordinating the failover.
It is designed to limit data loss and unavailability of the cluster during the failover.
This command is analogous to the [`CLUSTER FAILOVER`]() command for non-clustered Redis and is similar to the failover support provided by sentinel.

The specific details of the default failover flow are as follows:

1. The master will internally start a `CLIENT PAUSE WRITE`, which will pause incoming writes and prevent the accumulation of new data in the replication stream.
2. The master will monitor its replicas, waiting for a replica to indicate that it has fully consumed the replication stream. If the master has multiple replicas, it will only wait for the first replica to catch up.
3. The master will then demote itself to a replica. This is done to prevent any dual master scenarios. NOTE: The master will not discard its data, so it will be able to rollback if the replica rejects the failover request in the next step.
4. The previous master will send a special PSYNC request to the target replica, `PSYNC FAILOVER`, instructing the target replica to become a master.
5. Once the previous master receives acknowledgement the `PSYNC FAILOVER` was accepted it will unpause its clients. If the PSYNC request is rejected, the master will abort the failover and return to normal.

The field `master_failover_state` in `INFO replication` can be used to track the current state of the failover, which has the following values:

* `no-failover`: There is no ongoing coordinated failover.
* `waiting-for-sync`: The master is waiting for the replica to catch up to its replication offset.
* `failover-in-progress`: The master has demoted itself, and is attempting to hand off ownership to a target replica.

If the previous master had additional replicas attached to it, they will continue replicating from it as chained replicas. You will need to manually execute a [`REPLICAOF`]() on these replicas to start replicating directly from the new master.

title: Optional arguments

text:
The following optional arguments exist to modify the behavior of the failover flow:

* `TIMEOUT` *milliseconds* -- This option allows specifying a maximum time a master will wait in the `waiting-for-sync` state before aborting the failover attempt and rolling back.
This is intended to set an upper bound on the write outage the Redis cluster can experience.
Failovers typically happen in less than a second, but could take longer if there is a large amount of write traffic or the replica is already behind in consuming the replication stream.
If this value is not specified, the timeout can be considered to be "infinite".

* `TO` *HOST* *PORT* -- This option allows designating a specific replica, by its host and port, to failover to. The master will wait specifically for this replica to catch up to its replication offset, and then failover to it.

* `FORCE` -- If both the `TIMEOUT` and `TO` options are set, the force flag can also be used to designate that once the timeout has elapsed, the master should failover to the target replica instead of rolling back.
This can be used for a best-effort attempt at a failover without data loss, but limiting write outage.

NOTE: The master will always rollback if the `PSYNC FAILOVER` request is rejected by the target replica.

title: Failover abort

text:
The failover command is intended to be safe from data loss and corruption, but can encounter some scenarios it can not automatically remediate from and may get stuck.
For this purpose, the `FAILOVER ABORT` command exists, which will abort an ongoing failover and return the master to its normal state.
The command has no side effects if issued in the `waiting-for-sync` state but can introduce multi-master scenarios in the `failover-in-progress` state.
If a multi-master scenario is encountered, you will need to manually identify which master has the latest data and designate it as the master and have the other replicas.

NOTE: [`REPLICAOF`]() is disabled while a failover is in progress, this is to prevent unintended interactions with the failover that might cause data loss.

text:
| Redis
Software | Redis
Cloud | Notes |
|:----------------------|:-----------------|:------|
| ❌ Standard
<nobr>❌ Active-Active</nobr> | ❌ Standard
<nobr>❌ Active-Active</nobr> |  |

title: Return information

text:
**RESP2:**

[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was accepted and a coordinated failover is in progress. An error if the operation cannot be executed.

**RESP3:**

[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was accepted and a coordinated failover is in progress. An error if the operation cannot be executed.


title: FCALL

summary: Invokes a function.

title: Overview

text:
Invoke a function.

Functions are loaded to the server with the [`FUNCTION LOAD`]() command.
The first argument is the name of a loaded function.

The second argument is the number of input key name arguments, followed by all the keys accessed by the function.
In Lua, these names of input keys are available to the function as a table that is the callback's first argument.

**Important:**
To ensure the correct execution of functions, both in standalone and clustered deployments, all names of keys that a function accesses must be explicitly provided as input key arguments.
The function **should only** access keys whose names are given as input arguments.
Functions **should never** access keys with programmatically-generated names or based on the contents of data structures stored in the database.

Any additional input argument **should not** represent names of keys.
These are regular arguments and are passed in a Lua table as the callback's second argument.

For more information please refer to the [Redis Programmability]() and [Introduction to Redis Functions]() pages.

title: Examples

text:
The following example will create a library named `mylib` with a single function, `myfunc`, that returns the first argument it gets.

[code example]

title: Return information

text:
**RESP2:**

The return value depends on the function that was executed.

**RESP3:**

The return value depends on the function that was executed.

code:
redis> FUNCTION LOAD "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
"mylib"
redis> FCALL myfunc 0 hello
"hello"


title: FCALL_RO

summary: Invokes a read-only function.

title: Overview

text:
This is a read-only variant of the [`FCALL`]() command that cannot execute commands that modify data.

For more information about when to use this command vs [`FCALL`](), please refer to [Read-only scripts]().

For more information please refer to [Introduction to Redis Functions]().

text:
| Redis
Software | Redis
Cloud | Notes |
|:----------------------|:-----------------|:------|
| ✅ Standard
<nobr>✅ Active-Active</nobr> | ✅ Standard
<nobr>✅ Active-Active</nobr> |  |

title: Return information


title: FLUSHALL

summary: Removes all keys from all databases.

title: Notes

text:
* An asynchronous `FLUSHALL` command only deletes keys that were present at the time the command was invoked. Keys created during an asynchronous flush will be unaffected.
* This command does not delete functions.
* Other than emptying all databases (similar to `FLUSHDB`), this command clears the RDB persistence file, aborts any snapshot that is in progress, and, if the `save` config is enabled, saves an empty RDB file.

title: Behavior change history

text: *   `>= 6.2.0`: Default flush behavior now configurable by the **lazyfree-lazy-user-flush** configuration directive.

text:
| Redis
Software | Redis
Cloud | Notes |
|:----------------------|:-----------------|:------|
| ✅ Standard
<nobr>❌ Active-Active\*</nobr> | ✅ Standard
<nobr>❌ Active-Active</nobr> | \*Can use the [Active-Active flush API request](). |

title: Return information

text:
**RESP2:**

[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.

**RESP3:**

[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.


title: FLUSHDB

summary: Remove all keys from the current database.

title: Overview

text:
This command's behavior varies in clustered Redis environments. See the [multi-key operations]() page for more information.

Delete all the keys of the currently selected DB.
This command never fails.

By default, `FLUSHDB` will synchronously flush all keys from the database.
Starting with Redis 6.2, setting the **lazyfree-lazy-user-flush** configuration directive to "yes" changes the default flush mode to asynchronous.

It is possible to use one of the following modifiers to dictate the flushing mode explicitly:

* `ASYNC`: flushes the database asynchronously
* `SYNC`: flushes the database synchronously

title: Notes

text:
* An asynchronous `FLUSHDB` command only deletes keys that were present at the time the command was invoked. Keys created during an asynchronous flush will be unaffected.
* This command does not delete functions.
* When using Redis Cluster, this command is identical to `FLUSHALL` since a Redis Cluster supports only one database with an ID of zero.

title: Behavior change history

title: Return information


title: FT._LIST

summary: Returns a list of all existing indexes

title: Overview

text:
Returns a list of all existing indexes.

The prefix `_` in the command indicates, this is a temporary command.

In the future, a [`SCAN`]() type of command will be added, for use when a database
contains a large number of indices.

title: Examples

text: [code example]

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| ✅ Supported | ✅ Supported | ✅ Supported</nobr> |  |

title: Return information

text:
**RESP2:**

[Array]() of index names as [simple strings]().

**RESP3:**

[Set]() of index names as [simple strings]().

code:
FT._LIST
1) "idx"
2) "movies"
3) "imdb"


title: FT.AGGREGATE

title: Overview

text:
Run a search query on an index and perform aggregate transformations on the results.

This command will only return keys to which the user has read access.

[Examples](#examples)

title: Required arguments

text:
index

is index name against which the query is executed. You must first create the index using [`FT.CREATE`]().

query

is base filtering query that retrieves the documents. It follows the exact same syntax as the search query, including filters, unions, not, optional, and so on.

title: Optional arguments

text:
VERBATIM

if set, does not try to use stemming for query expansion but searches the query terms verbatim.

LOAD {nargs} {identifier} AS {property} …

loads document attributes from the source document.
 - `identifier` is either an attribute name for hashes and JSON or a JSON Path expression for JSON.
 - `property` is the optional name used in the result. If it is not provided, the `identifier` is used. This should be avoided.
 - If `*` is used as `nargs`, all attributes in a document are loaded.

Attributes needed for aggregations should be stored as `SORTABLE`, where they are available to the aggregation pipeline with very low latency. `LOAD` hurts the performance of aggregate queries considerably because every processed record needs to execute the equivalent of [`HMGET`]() against a Redis key, which when executed over millions of keys, amounts to high processing times.

GROUPBY {nargs} {property}

groups the results in the pipeline based on one or more properties. Each group should have at least one _reducer_, a function that handles the group entries,
  either counting them, or performing multiple aggregate operations (see below).

REDUCE {func} {nargs} {arg} … [AS {name}]

reduces the matching results in each group into a single record, using a reduction function. For example, `COUNT` counts the number of records in the group. The reducers can have their own property names using the `AS {name}` optional argument. If a name is not given, the resulting name will be the name of the reduce function and the group properties. For example, if a name is not given to `COUNT_DISTINCT` by property `@foo`, the resulting name will be `count_distinct(@foo)`.

See [Supported GROUPBY reducers]() for more details.

SORTBY {nargs} {property} {ASC|DESC} [MAX {num}]

sorts the pipeline up until the point of `SORTBY`, using a list of properties.

 - By default, sorting is ascending, but `ASC` or `DESC ` can be added for each property.
 - `nargs` is the number of sorting parameters, including `ASC` and `DESC`, for example, `SORTBY 4 @foo ASC @bar DESC`.
 - `MAX` is used to optimized sorting, by sorting only for the n-largest elements. Although it is not connected to `LIMIT`, you usually need just `SORTBY … MAX` for common queries.

Attributes needed for `SORTBY` should be stored as `SORTABLE` to be available with very low latency.

**Sorting Optimizations**: performance is optimized for sorting operations on `DIALECT 4` in different scenarios:
   - Skip Sorter - applied when there is no sort of any kind. The query can return after it reaches the `LIMIT` requested results.
   - Partial Range - applied when there is a `SORTBY` clause over a numeric field, with no filter or filter by the same numeric field, the query iterate on a range large enough to satisfy the `LIMIT` requested results.
   - Hybrid - applied when there is a `SORTBY` clause over a numeric field and another non-numeric filter. Some results will get filtered, and the initial range may not be large enough. The iterator is then rewinding with the following ranges, and an additional iteration takes place to collect the `LIMIT` requested results.
   - No optimization - If there is a sort by score or by non-numeric field, there is no other option but to retrieve all results and compare their values.

**Counts behavior**: optional `WITHCOUNT` argument returns accurate counts for the query results with sorting. This operation processes all results in order to get an accurate count, being less performant than the optimized option (default behavior on `DIALECT 4`)

You can also use `WITHOUTCOUNT` in place of `DIALECT 4` when used with either `FT.SEARCH` or `FT.AGGREGATE`.

APPLY {expr} AS {name}

applies a 1-to-1 transformation on one or more properties and either stores the result as a new property down the pipeline or replaces any property using this
  transformation.

`expr` is an expression that can be used to perform arithmetic operations on numeric properties, or functions that can be applied on properties depending on their types (see below), or any combination thereof. For example, `APPLY "sqrt(@foo)/log(@bar) + 5" AS baz` evaluates this expression dynamically for each record in the pipeline and store the result as a new property called `baz`, which can be referenced by further `APPLY`/`SORTBY`/`GROUPBY`/`REDUCE` operations down the
  pipeline.

See [APPLY expressions]() for details.

LIMIT {offset} {num}

limits the number of results to return just `num` results starting at index `offset` (zero-based). It is much more efficient to use `SORTBY … MAX` if you
  are interested in just limiting the output of a sort operation.
  If a key expires during the query, an attempt to `load` the key's value will return a null array.

However, limit can be used to limit results without sorting, or for paging the n-largest results as determined by `SORTBY MAX`. For example, getting results 50-100 of the top 100 results is most efficiently expressed as `SORTBY 1 @foo MAX 100 LIMIT 50 50`. Removing the `MAX` from `SORTBY` results in the pipeline sorting _all_ the records and then paging over results 50-100.

FILTER {expr}

filters the results using predicate expressions relating to values in each result.
  They are applied post query and relate to the current state of the pipeline.

WITHCURSOR {COUNT} {read_size} [MAXIDLE {idle_time}]

Scan part of the results with a quicker alternative than `LIMIT`.
See [Cursor API]() for more details.

TIMEOUT {milliseconds}

if set, overrides the timeout parameter of the module.

PARAMS {nargs} {name} {value}

defines one or more value parameters. Each parameter has a name and a value.

You can reference parameters in the `query` by a `$`, followed by the parameter name, for example, `$user`. Each such reference in the search query to a parameter name is substituted by the corresponding parameter value. For example, with parameter definition `PARAMS 4 lon 29.69465 lat 34.95126`, the expression `@loc:[$lon $lat 10 km]` is evaluated to `@loc:[29.69465 34.95126 10 km]`. You cannot reference parameters in the query string where concrete values are not allowed, such as in field names, for example, `@loc`. To use `PARAMS`, set `DIALECT` to `2` or greater than `2`.

SCORER {scorer}

uses a [built-in]() or a [user-provided]() scoring function.

ADDSCORES

The `ADDSCORES` option exposes the full-text score values to the aggregation pipeline.
You can use `@__score` in a pipeline as shown in the following example:

`FT.AGGREGATE idx 'hello' ADDSCORES SORTBY 2 @__score DESC`

DIALECT {dialect_version}

selects the dialect version under which to execute the query. If not specified, the query will execute under the default dialect version set during module initial loading or via [`FT.CONFIG SET`]() command.

title: Return multiple values

text:
See [Return multiple values]() in [`FT.SEARCH`]()
The `DIALECT` can be specified as a parameter in the FT.AGGREGATE command. If it is not specified, the `DEFAULT_DIALECT` is used, which can be set using [`FT.CONFIG SET`]() or by passing it as an argument to the `redisearch` module when it is loaded.
For example, with the following document and index:

[code example]
Notice the different replies, with and without `DIALECT 3`:

[code example]

title: Complexity

role: performance

text: Non-deterministic. Depends on the query and aggregations performed, but it is usually linear to the number of results returned.

title: Examples

text:
Sort page visits by day

Find visits to the page `about.html`, group them by the day of the visit, count the number of visits, and sort them by day.

FT.AGGREGATE idx "@url:\"about.html\""
    APPLY "day(@timestamp)" AS day
    GROUPBY 2 @day @country
      REDUCE count 0 AS num_visits
    SORTBY 1 @day

Find most books ever published

Find most books ever published in a single year.

FT.AGGREGATE books-idx *
    GROUPBY 1 @published_year
      REDUCE COUNT 0 AS num_published
    GROUPBY 0
      REDUCE MAX 1 @num_published AS max_books_published_per_year

Reduce all results

The last example used `GROUPBY 0`. Use `GROUPBY 0` to apply a `REDUCE` function over all results from the last step of an aggregation pipeline -- this works on both the  initial query and subsequent `GROUPBY` operations.

Search for libraries within 10 kilometers of the longitude -73.982254 and latitude 40.753181 then annotate them with the distance between their location and those coordinates.

 FT.AGGREGATE libraries-idx "@location:[-73.982254 40.753181 10 km]"
    LOAD 1 @location
    APPLY "geodistance(@location, -73.982254, 40.753181)"

Here, notice the required use of `LOAD` to pre-load the `@location` attribute because it is a GEO attribute.

Next, count GitHub events by user (actor), to produce the most active users.

127.0.0.1:6379> FT.AGGREGATE gh "*" GROUPBY 1 @actor REDUCE COUNT 0 AS num SORTBY 2 @num DESC MAX 10
 1) (integer) 284784
 2) 1) "actor"
    2) "lombiqbot"
    3) "num"
    4) "22197"
 3) 1) "actor"
    2) "codepipeline-test"
    3) "num"
    4) "17746"
 4) 1) "actor"
    2) "direwolf-github"
    3) "num"
    4) "10683"
 5) 1) "actor"
    2) "ogate"
    3) "num"
    4) "6449"
 6) 1) "actor"
    2) "openlocalizationtest"
    3) "num"
    4) "4759"
 7) 1) "actor"
    2) "digimatic"
    3) "num"
    4) "3809"
 8) 1) "actor"
    2) "gugod"
    3) "num"
    4) "3512"
 9) 1) "actor"
    2) "xdzou"
    3) "num"
    4) "3216"
[10](10)) 1) "actor"
    2) "opstest"
    3) "num"
    4) "2863"
11) 1) "actor"
    2) "jikker"
    3) "num"
    4) "2794"
(0.59s)

Use the case function for conditional logic

//Simple mapping
FT.AGGREGATE products "*"
APPLY case(@price > 100, "premium", "standard") AS category

//Nested conditions where an error should be returned
FT.AGGREGATE orders "*"
APPLY case(@status == "pending",
           case(@priority == "high", 1, 2),
           case(@status == "completed", 3, 4)) AS status_code

//Mapped approach
FT.AGGREGATE orders "*"
APPLY case(@status == "pending", 1, 0) AS is_pending
APPLY case(@is_pending == 1 && @priority == "high", 1,2) AS status_high
APPLY case(@is_pending == 0 && @priority == "high", 3,4) AS status_completed

title: Return information

text:
**RESP2:**

One of the following:
* [Array reply]() where each row is an array reply representing a single aggregate result. The [integer reply]() at position `1` does not represent a valid value.
* [Simple error reply]() in these cases: incorrect number of arguments, non-existent index, invalid query syntax.

**RESP3:**

One of the following:
* [Map]() with the following fields:
    - `attributes`: [Array]() of attribute names.
    - `format`: [Simple string]() - result format.
    - `results`: [Array]() of [maps]() containing aggregated data.
    - `total_results`: [Integer]() - total number of results.
    - `warning`: [Array]() of warning messages.
* [Simple error reply]() in these cases: incorrect number of arguments, non-existent index, invalid query syntax.

title: See also

role: related

text: [`FT.CONFIG SET`]() | [`FT.SEARCH`]()

title: Related topics

text:
- [Aggregations]()
- [Key and field expiration behavior]()
- [RediSearch]()

code:
127.0.0.1:6379> JSON.SET doc:1 $ '[{"arr": [1, 2, 3]}, {"val": "hello"}, {"val": "world"}]'
OK
127.0.0.1:6379> FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $..arr AS arr NUMERIC $..val AS val TEXT
OK

section_id: return-multiple-values


title: FT.ALIASADD

summary: Adds an alias to the index

title: Overview

text:
Add an alias to an index

[Examples](#examples)

title: Required arguments

text:
alias index

is alias to be added to an index.

Indexes can have more than one alias, but an alias cannot refer to another
alias.

FT.ALIASADD allows administrators to transparently redirect application queries to alternative indexes.

title: Examples

text:
Add an alias to an index

Add an alias to an index.

127.0.0.1:6379> FT.ALIASADD alias idx
OK

Attempting to add the same alias returns a message that the alias already exists.

127.0.0.1:6379> FT.ALIASADD alias idx
(error) Alias already exists

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: alias already exists, index does not exist.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: alias already exists, index does not exist.

title: See also

text: [`FT.ALIASDEL`]() | [`FT.ALIASUPDATE`]()


title: FT.ALIASDEL

summary: Deletes an alias from the index

title: Overview

text:
Remove an alias from an index

[Examples](#examples)

title: Required arguments

text:
alias

is index alias to be removed.

title: Examples

text:
Remove an alias from an index

Remove an alias from an index.

127.0.0.1:6379> FT.ALIASDEL alias
OK

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: alias does not exist.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: alias does not exist.

title: See also

text: [`FT.ALIASADD`]() | [`FT.ALIASUPDATE`]()


title: FT.ALIASUPDATE

summary: Adds or updates an alias to the index

title: Overview

text:
Add an alias to an index. If the alias is already associated with another
index, FT.ALIASUPDATE removes the alias association with the previous index.

[Examples](#examples)

title: Required arguments

text:
alias index

is alias to be added to an index.

title: Examples

text:
Update an index alias

Update the alias of an index.

127.0.0.1:6379> FT.ALIASUPDATE alias idx
OK

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| ✅ Supported | ✅ Supported | ✅ Supported</nobr> |  |

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: index does not exist.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: index does not exist.

title: See also

text: [`FT.ALIASADD`]() | [`FT.ALIASDEL`]()

title: Related topics

text: [RediSearch]()


title: FT.ALTER

summary: Adds a new field to the index

title: Overview

text:
Add a new attribute to the index. Adding an attribute to the index causes any future document updates to use the new attribute when indexing and reindexing existing documents.

[Examples](#examples)

title: Required arguments

text:
index

is index name to create.

SKIPINITIALSCAN

if set, does not scan and index.

SCHEMA ADD {attribute} {options} ...

after the SCHEMA keyword, declares which fields to add:

- `attribute` is attribute to add.
- `options` are attribute options. Refer to [`FT.CREATE`]() for more information.

<note>Note:

Depending on how the index was created, you may be limited by the number of additional text
attributes which can be added to an existing index. If the current index contains fewer than 32
text attributes, then `SCHEMA ADD` will only be able to add attributes up to 32 total attributes (meaning that the
index will only ever be able to contain 32 total text attributes). If you wish for the index to
contain more than 32 attributes, create it with the `MAXTEXTFIELDS` option.
</note>

title: Examples

text:
Alter an index

127.0.0.1:6379> FT.ALTER idx SCHEMA ADD id2 NUMERIC SORTABLE
OK

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: no such index, invalid schema syntax.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: no such index, invalid schema syntax.

title: See also

text: [`FT.CREATE`]()

title: Related topics

text: - [RediSearch]()


title: FT.CONFIG GET

summary: Retrieves runtime configuration options

title: Overview

text:
Retrieve configuration options

[Examples](#examples)

title: Required arguments

text:
option

is name of the configuration option, or '*' for all.

title: Examples

text:
Retrieve configuration options

127.0.0.1:6379> FT.CONFIG GET TIMEOUT
1) 1) TIMEOUT
   2) 42

127.0.0.1:6379> FT.CONFIG GET *
 1) 1) EXTLOAD
    2) (nil)
 2) 1) SAFEMODE
    2) true
 3) 1) CONCURRENT_WRITE_MODE
    2) false
 4) 1) NOGC
    2) false
 5) 1) MINPREFIX
    2) 2
 6) 1) FORKGC_SLEEP_BEFORE_EXIT
    2) 0
 7) 1) MAXDOCTABLESIZE
    2) 1000000
 8) 1) MAXSEARCHRESULTS
    2) 1000000
 9) 1) MAXAGGREGATERESULTS
    2) unlimited
10) 1) MAXEXPANSIONS
    2) 200
11) 1) MAXPREFIXEXPANSIONS
    2) 200
12) 1) TIMEOUT
    2) 42
13) 1) INDEX_THREADS
    2) 8
14) 1) SEARCH_THREADS
    2) 20
15) 1) FRISOINI
    2) (nil)
16) 1) ON_TIMEOUT
    2) return
17) 1) GCSCANSIZE
    2) 100
18) 1) MIN_PHONETIC_TERM_LEN
    2) 3
19) 1) GC_POLICY
    2) fork
20) 1) FORK_GC_RUN_INTERVAL
    2) 30
21) 1) FORK_GC_CLEAN_THRESHOLD
    2) 100
22) 1) FORK_GC_RETRY_INTERVAL
    2) 5
23) 1) FORK_GC_CLEAN_NUMERIC_EMPTY_NODES
    2) true
24) 1) _FORK_GC_CLEAN_NUMERIC_EMPTY_NODES
    2) true
25) 1) _MAX_RESULTS_TO_UNSORTED_MODE
    2) 1000
26) 1) UNION_ITERATOR_HEAP
    2) 20
27) 1) CURSOR_MAX_IDLE
    2) 300000
28) 1) NO_MEM_POOLS
    2) false
29) 1) PARTIAL_INDEXED_DOCS
    2) false
30) 1) UPGRADE_INDEX
    2) Upgrade config for upgrading
31) 1) _NUMERIC_COMPRESS
    2) false
32) 1) _FREE_RESOURCE_ON_THREAD
    2) true
33) 1) _PRINT_PROFILE_CLOCK
    2) true
34) 1) RAW_DOCID_ENCODING
    2) false
35) 1) _NUMERIC_RANGES_PARENTS
    2) 0
36) 1) MINSTEMLEN
    2) 4

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| <nobr>❌ Not supported | <nobr>⚠ Not supported1 | <nobr>❌ Not supported</nobr> |  |

1. [Contact support](https://redis.com/company/support/) to view the current configuration values or request configuration changes for Flexible or Annual Redis Cloud subscriptions.

title: Return information

text:
**RESP2:**

[Array]() of [arrays](), where each sub-array contains a configuration option name and its value.

**RESP3:**

[Map]() where keys are configuration option names and values are their corresponding values.

title: See also

text: [`FT.CONFIG SET`]() | [`FT.CONFIG HELP`]()

title: Related topics

text: [RediSearch]()


title: FT.CONFIG HELP

summary: Help description of runtime configuration options

title: Overview

text:
Describe configuration options

[Examples](#examples)

title: Required arguments

title: Examples

text:
Get configuration details

127.0.0.1:6379> FT.CONFIG HELP TIMEOUT
1) 1) TIMEOUT
   2) Description
   3) Query (search) timeout
   4) Value
   5) "42"

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| <nobr>❌ Not supported | <nobr>❌ Not supported | <nobr>❌ Not supported</nobr> |  |

title: Return information

text:
**RESP2:**

[Array]() of help information.

**RESP3:**

[Array]() of help information.

title: See also

text: [`FT.CONFIG SET`]() | [`FT.CONFIG GET`]()

title: Related topics

text: [RediSearch]()


title: FT.CONFIG SET

summary: Sets runtime configuration options

title: Overview

text:
Set the value of a RediSearch configuration parameter.

Values set using `FT.CONFIG SET` are not persisted after server restart.

RediSearch configuration parameters are detailed in [Configuration parameters]().

As detailed in the link above, not all RediSearch configuration parameters can be set at runtime.

[Examples](#examples)

title: Required arguments

text:
option

is name of the configuration option, or '*' for all.

value

is value of the configuration option.

title: Examples

text:
Set runtime configuration options

127.0.0.1:6379> FT.CONFIG SET TIMEOUT 42
OK

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| <nobr>⚠ Not supported1 | <nobr>⚠ Not supported2 | <nobr>❌ Not supported</nobr> |  |

1. Use [`rladmin`]() or the [REST API]() to change search and query configuration for Redis Software. See [search and query configuration compatibility with Redis Software]() for more information and examples.

2. [Contact support](https://redis.com/company/support/) to view the current configuration values or request configuration changes for Flexible or Annual Redis Cloud subscriptions.

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: invalid option, invalid value.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: invalid option, invalid value.

title: See also

text: [`FT.CONFIG GET`]() | [`FT.CONFIG HELP`]()

title: Related topics

text: [RediSearch]()


title: FT.CREATE

summary: Creates an index with the given spec

title: Description

text: Create an index with the given specification. For usage, see [Examples](#examples).

title: Required arguments

text:
index

is index name to create.
If such index already exists, returns an error reply `(error) Index already exists`.

SCHEMA {identifier} AS {attribute} {attribute type} {options...

after the SCHEMA keyword, declares which fields to index:

 - `{identifier}` for hashes, is a field name within the hash.
      For JSON, the identifier is a JSON Path expression.

 - `AS {attribute}` defines the attribute associated to the identifier. For example, you can use this feature to alias a complex JSONPath expression with more memorable (and easier to type) name.

 Field types are:

 - `TEXT` - Allows full-text search queries against the value in this attribute.

 - `TAG` - Allows exact-match queries, such as categories or primary keys, against the value in this attribute. For more information, see [Tag Fields]().

 - `NUMERIC` - Allows numeric range queries against the value in this attribute. See [query syntax docs]() for details on how to use numeric ranges.

 - `GEO` - Allows radius range queries against the value (point) in this attribute. The value of the attribute must be a string containing a longitude (first) and latitude separated by a comma.

 - `VECTOR` - Allows vector queries against the value in this attribute. This requires [query dialect 2]() or above (introduced in [RediSearch v2.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.4.3)). For more information, see [Vector Fields]().

 - `GEOSHAPE`- Allows polygon queries against the value in this attribute. The value of the attribute must follow a [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) list of 2D points representing the polygon edges `POLYGON((x1 y1, x2 y2, ...)` separated by a comma. A `GEOSHAPE` field type can be followed by one of the following coordinate systems:
   - `SPHERICAL` for Geographic longitude and latitude coordinates
   - `FLAT` for Cartesian X Y coordinates

    The default coordinate system is `SPHERICAL`.

    Currently `GEOSHAPE` doesn't support JSON multi-value and `SORTABLE` option.

 Field options are:

 - `SORTABLE` - `NUMERIC`, `TAG`, `TEXT`, or `GEO` attributes can have an optional **SORTABLE** argument. As the user [sorts the results by the value of this attribute](), the results are available with very low latency. Note that his adds memory overhead, so consider not declaring it on large text attributes. You can sort an attribute without the `SORTABLE` option, but the latency is not as good as with `SORTABLE`.

 - `UNF` - By default, for hashes (not with JSON) `SORTABLE` applies a normalization to the indexed value (characters set to lowercase, removal of diacritics). When using the unnormalized form (UNF), you can disable the normalization and keep the original form of the value. With JSON, `UNF` is implicit with `SORTABLE` (normalization is disabled).

 - `NOSTEM` - Text attributes can have the NOSTEM argument that disables stemming when indexing its values. This may be ideal for things like proper names.

 - `NOINDEX` - Attributes can have the `NOINDEX` option, which means they will not be indexed. This is useful in conjunction with `SORTABLE`, to create attributes whose update using PARTIAL will not cause full reindexing of the document. If an attribute has NOINDEX and doesn't have SORTABLE, it will just be ignored by the index.

 - `PHONETIC {matcher}` - Declaring a text attribute as `PHONETIC` will perform phonetic matching on it in searches by default. The obligatory {matcher} argument specifies the phonetic algorithm and language used. The following matchers are supported:

   - `dm:en` - Double metaphone for English
   - `dm:fr` - Double metaphone for French
   - `dm:pt` - Double metaphone for Portuguese
   - `dm:es` - Double metaphone for Spanish

   For more information, see [Phonetic Matching]().

  - `WEIGHT {weight}` for `TEXT` attributes, declares the importance of this attribute when calculating result accuracy. This is a multiplication factor, and defaults to 1 if not specified.

  - `SEPARATOR {sep}` for `TAG` attributes, indicates how the text contained in the attribute is to be split into individual tags. The default is `,`. The value must be a single character.

  - `CASESENSITIVE` for `TAG` attributes, keeps the original letter cases of the tags. If not specified, the characters are converted to lowercase.

  - `WITHSUFFIXTRIE` for `TEXT` and `TAG` attributes, keeps a suffix trie with all terms which match the suffix. It is used to optimize `contains` (*foo*) and `suffix` (*foo) queries. Otherwise, a brute-force search on the trie is performed. If suffix trie exists for some fields, these queries will be disabled for other fields.

  - `INDEXEMPTY` for `TEXT` and `TAG` attributes, introduced in v2.10, allows you to index and search for empty strings. By default, empty strings are not indexed.

  - `INDEXMISSING` for all field types, introduced in v2.10, allows you to search for missing values, that is, documents that do not contain a specific field. Note the difference between a field with an empty value and a document with a missing value. By default, missing values are not indexed.

title: Optional arguments

text:
ON {data_type}

currently supports HASH (default) and JSON. To index JSON, you must have the [RedisJSON]() module installed.

PREFIX {count} {prefix}

tells the index which keys it should index. You can add several prefixes to index. Because the argument is optional, the default is `*` (all keys).

FILTER {filter}

is a filter expression with the full RediSearch aggregation expression language. It is possible to use `@__key` to access the key that was just added/changed. A field can be used to set field name by passing `'FILTER @indexName=="myindexname"'`.

LANGUAGE {default_lang}

if set, indicates the default language for documents in the index. Default is English.

LANGUAGE_FIELD {lang_attribute}

is a document attribute set as the document language.

A stemmer is used for the supplied language during indexing. If an unsupported language is sent, the command returns an error. The supported
languages are `arabic`, `armenian`, `basque`, `catalan`, `danish`, `dutch`, `english`, `finnish`, `french`, `german`, `greek`, `hindi`,
`hungarian`, `indonesian`, `irish`, `italian`, `lithuanian`, `nepali`, `norwegian`, `portuguese`, `romanian`, `russian`, `serbian`,
`spanish`, `swedish`, `tamil`, `turkish`, `yiddish`, and `chinese`.

When adding Chinese language documents, set `LANGUAGE chinese` for the indexer to properly tokenize the terms. If you use the default language, then search terms are extracted based on punctuation characters and whitespace. The Chinese language tokenizer makes use of a segmentation algorithm (via [Friso](https://github.com/lionsoul2014/friso)), which segments text and checks it against a predefined dictionary. See [Stemming]() for more information.

SCORE {default_score}

is default score for documents in the index. Default score is 1.0.

SCORE_FIELD {score_attribute}

is document attribute that you use as the document rank based on the user ranking. Ranking must be between 0.0 and 1.0. If not set, the default score is 1.

PAYLOAD_FIELD {payload_attribute}

is document attribute that you use as a binary safe payload string to the document that can be evaluated at query time by a custom scoring function or retrieved to the client.

MAXTEXTFIELDS

forces RediSearch to encode indexes as if there were more than 32 text attributes, which allows you to add additional attributes (beyond 32) using [`FT.ALTER`](). For efficiency, RediSearch encodes indexes differently if they are created with less than 32 text attributes.

NOOFFSETS

does not store term offsets for documents. It saves memory, but does not allow exact searches or highlighting. It implies `NOHL`.

TEMPORARY {seconds}

creates a lightweight temporary index that expires after a specified period of inactivity, in seconds. The internal idle timer is reset whenever the index is searched or added to. Because such indexes are lightweight, you can create thousands of such indexes without negative performance implications and, therefore, you should consider using `SKIPINITIALSCAN` to avoid costly scanning.

When temporary indexes expire, they drop all the records associated with them.
[`FT.DROPINDEX`]() was introduced with a default of not deleting docs and a `DD` flag that enforced deletion.
However, for temporary indexes, documents are deleted along with the index.
Historically, RediSearch used an FT.ADD command, which made a connection between the document and the index. Then, FT.DROP, also a hystoric command, deleted documents by default.
In version 2.x, RediSearch indexes hashes and JSONs, and the dependency between the index and documents no longer exists.

NOHL

conserves storage space and memory by disabling highlighting support. If set, the corresponding byte offsets for term positions are not stored. `NOHL` is also implied by `NOOFFSETS`.

NOFIELDS

does not store attribute bits for each term. It saves memory, but it does not allow
  filtering by specific attributes.

NOFREQS

avoids saving the term frequencies in the index. It saves memory, but does not allow sorting based on the frequencies of a given term within the document.

STOPWORDS {count}

sets the index with a custom stopword list, to be ignored during indexing and search time. `{count}` is the number of stopwords, followed by a list of stopword arguments exactly the length of `{count}`.

If not set, FT.CREATE takes the default list of stopwords. If `{count}` is set to 0, the index does not have stopwords.

SKIPINITIALSCAN

if set, does not scan and index.

INDEXALL {ENABLE | DISABLE}

introduced in v8.0, maintains a compact index of existing document IDs to optimize wildcard queries, for example, `FT.SEARCH idx *`. When disabled, wildcard queries scan all IDs ever assigned—including deleted ones—which can hurt performance in write-heavy workloads. The default is DISABLE.

<note>Notes:

 - **Attribute number limits:** RediSearch supports up to 1024 attributes per schema, out of which at most 128 can be TEXT attributes. On 32 bit builds, at most 64 attributes can be TEXT attributes. The more attributes you have, the larger your index, as each additional 8 attributes require one extra byte per index record to encode. You can always use the `NOFIELDS` option and not encode attribute information into the index, for saving space, if you do not need filtering by text attributes. This will still allow filtering by numeric and geo attributes.
 - **Running in clustered databases:** When having several indices in a clustered database, you need to make sure the documents you want to index reside on the same shard as the index. You can achieve this by having your documents tagged by the index name.

   127.0.0.1:6379> HSET doc:1{idx} ...
   127.0.0.1:6379> FT.CREATE idx ... PREFIX 1 doc: ...

   When Running RediSearch in a clustered database, you can span the index across shards using [RSCoordinator](https://github.com/RedisLabsModules/RSCoordinator). In this case the above does not apply.

</note>

title: Examples

text:
Create an index

Create an index that stores the title, publication date, and categories of blog post hashes whose keys start with `blog:post:` (for example, `blog:post:1`).

127.0.0.1:6379> FT.CREATE idx ON HASH PREFIX 1 blog:post: SCHEMA title TEXT SORTABLE published_at NUMERIC SORTABLE category TAG SORTABLE
OK

Index the `sku` attribute from a hash as both a `TAG` and as `TEXT`:

127.0.0.1:6379> FT.CREATE idx ON HASH PREFIX 1 blog:post: SCHEMA sku AS sku_text TEXT sku AS sku_tag TAG SORTABLE

Index two different hashes, one containing author data and one containing books, in the same index:

127.0.0.1:6379> FT.CREATE author-books-idx ON HASH PREFIX 2 author:details: book:details: SCHEMA
author_id TAG SORTABLE author_ids TAG title TEXT name TEXT

In this example, keys for author data use the key pattern `author:details:<id>` while keys for book data use the pattern `book:details:<id>`.

Index authors whose names start with G.

127.0.0.1:6379> FT.CREATE g-authors-idx ON HASH PREFIX 1 author:details FILTER 'startswith(@name, "G")' SCHEMA name TEXT

Index only books that have a subtitle.

127.0.0.1:6379> FT.CREATE subtitled-books-idx ON HASH PREFIX 1 book:details FILTER '@subtitle != ""' SCHEMA title TEXT

Index books that have a "categories" attribute where each category is separated by a `;` character.

127.0.0.1:6379> FT.CREATE books-idx ON HASH PREFIX 1 book:details SCHEMA title TEXT categories TAG SEPARATOR ";"

Index a JSON document using a JSON Path expression

The following example uses data similar to the hash examples above but uses JSON instead.

127.0.0.1:6379> FT.CREATE idx ON JSON SCHEMA $.title AS title TEXT $.categories AS categories TAG

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| ✅ Supported | ✅ Supported | ✅ Supported</nobr> |  |

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: index already exists, invalid schema syntax.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: index already exists, invalid schema syntax.

title: See also

text: [`FT.ALTER`]() | [`FT.DROPINDEX`]()

title: Related topics

text:
- [RediSearch]()
- [RedisJSON]()
- [Friso](https://github.com/lionsoul2014/friso)
- [Stemming]()
- [Phonetic Matching]()
- [RSCoordinator](https://github.com/RedisLabsModules/RSCoordinator)


title: FT.CURSOR DEL

summary: Deletes a cursor

title: Overview

text:
Delete a cursor

[Examples](#examples)

title: Required arguments

text:
index

is index name.

cursor_id

is id of the cursor.

title: Examples

text:
Delete a cursor

redis> FT.CURSOR DEL idx 342459320
OK

Check that the cursor is deleted.

127.0.0.1:6379> FT.CURSOR DEL idx 342459320
(error) Cursor does not exist

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: cursor does not exist.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: cursor does not exist.

title: See also

text: [`FT.CURSOR READ`]()

title: Related topics

text: [RediSearch]()


title: FT.CURSOR READ

summary: Reads from a cursor

title: Overview

text:
Read next results from an existing cursor

[Examples](#examples)

See [Cursor API]() for more details.

title: Required arguments

text:
index

is index name.

cursor_id

is id of the cursor.

[COUNT read_size]

is number of results to read. This parameter overrides `COUNT` specified in [`FT.AGGREGATE`]().

title: Examples

text:
Read next results from a cursor

127.0.0.1:6379> FT.CURSOR READ idx 342459320 COUNT 50

title: Return information

text:
**RESP2:**

One of the following:
* [Array]() with search results and metadata.
* [Simple error reply]() in these cases: cursor not found.

**RESP3:**

One of the following:
* [Map]() with structured search results and metadata.
* [Simple error reply]() in these cases: cursor not found.

title: See also

text: [`FT.CURSOR DEL`]() | [`FT.AGGREGATE`]()

title: Related topics

text: [RediSearch]()


title: FT.DICTADD

summary: Adds terms to a dictionary

title: Overview

text:
Add terms to a dictionary

[Examples](#examples)

title: Required arguments

text:
dict

is dictionary name.

term

term to add to the dictionary.

title: Examples

text:
Add terms to a dictionary

127.0.0.1:6379> FT.DICTADD dict foo bar "hello world"
(integer) 3

title: Return information

text:
**RESP2:**

[Integer reply](): the number of new terms added to the dictionary.

**RESP3:**

[Integer reply](): the number of new terms added to the dictionary.

title: See also

text: [`FT.DICTDEL`]() | [`FT.DICTDUMP`]()

title: Related topics

text: [RediSearch]()


title: FT.DICTDEL

summary: Deletes terms from a dictionary

title: Overview

text:
Delete terms from a dictionary

[Examples](#examples)

title: Required arguments

text:
dict

is dictionary name.

term

term to delete from the dictionary.

title: Examples

text:
Delete terms from a dictionary

127.0.0.1:6379> FT.DICTDEL dict foo bar "hello world"
(integer) 3

title: Return information

text:
**RESP2:**

[Integer reply](): the number of terms deleted from the dictionary.

**RESP3:**

[Integer reply](): the number of terms deleted from the dictionary.

title: See also

text: [`FT.DICTADD`]() | [`FT.DICTDUMP`]()

title: Related topics

text: [RediSearch]()


title: FT.DICTDUMP

summary: Dumps all terms in the given dictionary

title: Overview

text:
Dump all terms in the given dictionary

[Examples](#examples)

title: Required argumemts

text:
dict

is dictionary name.

title: Examples

text:
Dump all terms in the dictionary

127.0.0.1:6379> FT.DICTDUMP dict
1) "foo"
2) "bar"
3) "hello world"

text:
| Redis
Software | Redis Cloud
Flexible & Annual | Redis Cloud
Free & Fixed | Notes |
|:----------------------|:-----------------|:-----------------|:------|
| ✅ Supported | ✅ Supported | ✅ Supported</nobr> |  |

title: Return information

text:
**RESP2:**

[Array]() of dictionary terms.

**RESP3:**

[Set]() of dictionary terms.

title: See also

text: [`FT.DICTADD`]() | [`FT.DICTDEL`]()

title: Related topics

text: [RediSearch]()


title: FT.DROPINDEX

summary: Deletes the index

title: Overview

text:
Delete an index

[Examples](#examples)

title: Required arguments

text:
index

is full-text index name. You must first create the index using [`FT.CREATE`]().

title: Optional arguments

text:
DD

drop index operation that, if set, deletes the actual document keys. `FT.DROPINDEX index DD` is an asynchronous operation.

By default, FT.DROPINDEX does not delete the documents associated with the index. Adding the `DD` option deletes the documents as well.
If an index creation is still running ([`FT.CREATE`]() is running asynchronously), only the document hashes that have already been indexed are deleted.
The document hashes left to be indexed remain in the database.
To check the completion of the indexing, use [`FT.INFO`]().

title: Examples

text:
Delete an index

127.0.0.1:6379> FT.DROPINDEX idx DD
OK

title: Return information

text:
**RESP2:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: no such index.

**RESP3:**

One of the following:
* [Simple string reply](): `OK` if executed correctly.
* [Simple error reply]() in these cases: no such index.

title: See also

text: [`FT.CREATE`]() | [`FT.INFO`]()

title: Related topics

text: [RediSearch]()


title: FT.EXPLAIN

summary: Returns the execution plan for a complex query

title: Overview

text:
Return the execution plan for a complex query

[Examples](#examples)

title: Required arguments

text:
index

is index name. You must first create the index using [`FT.CREATE`]().

query

is query string, as if sent to FT.SEARCH`.

title: Optional arguments

text:
DIALECT {dialect_version}

is dialect version under which to execute the query. If not specified, the query executes under the default dialect version set during module initial loading or via [`FT.CONFIG SET`]() command.

- In the returned response, a `+` on a term is an indication of stemming.
- Use `redis-cli --raw` to properly read line-breaks in the returned response.

title: Examples

text:
Return the execution plan for a complex query

$ redis-cli --raw

127.0.0.1:6379> FT.EXPLAIN rd "(foo bar)|(hello world) @date:[100 200]|@date:[500 +inf]"
INTERSECT {
  UNION {
    INTERSECT {
      foo
      bar
    }
    INTERSECT {
      hello
      world
    }
  }
  UNION {
    NUMERIC {100.000000 <= x <= 200.000000}
    NUMERIC {500.000000 <= x <= inf}
  }
}

title: Return information

text:
**RESP2:**

One of the following:
* [Bulk string]() containing the query execution plan.
* [Simple error reply]() in these cases: no such index, syntax error in query.

**RESP3:**

One of the following:
* [Bulk string]() containing the query execution plan.
* [Simple error reply]() in these cases: no such index, syntax error in query.

title: See also

text: [`FT.CREATE`]() | [`FT.SEARCH`]() | [`FT.CONFIG SET`]()

title: Related topics

text: [RediSearch]()


title: FT.EXPLAINCLI

title: Overview

text:
Return the execution plan for a complex query but formatted for easier reading without using `redis-cli --raw`

[Examples](#examples)

title: Required arguments

title: Optional arguments

text:
DIALECT {dialect_version}

is dialect version under which to execute the query. If not specified, the query executes under the default dialect version set during module initial loading or via [`FT.CONFIG SET`]() command.

In the returned response, a `+` on a term is an indication of stemming.

title: Examples

text:
Return the execution plan for a complex query

$ redis-cli

127.0.0.1:6379> FT.EXPLAINCLI rd "(foo bar)|(hello world) @date:[100 200]|@date:[500 +inf]"
 1) INTERSECT {
 2)   UNION {
 3)     INTERSECT {
 4)       UNION {
 5)         foo
 6)         +foo(expanded)
 7)       }
 8)       UNION {
 9)         bar
10)         +bar(expanded)
11)       }
12)     }
13)     INTERSECT {
14)       UNION {
15)         hello
16)         +hello(expanded)
17)       }
18)       UNION {
19)         world
20)         +world(expanded)
21)       }
22)     }
23)   }
24)   UNION {
25)     NUMERIC {100.000000 <= @date <= 200.000000}
26)     NUMERIC {500.000000 <= @date <= inf}
27)   }
28) }
29)

title: Return information

text:
**RESP2:**

One of the following:
* [Array]() of [bulk string replies]() containing the query execution plan in CLI format.
* [Simple error reply]() in these cases: no such index, syntax error in query.

**RESP3:**

One of the following:
* [Array]() of [bulk string replies]() containing the query execution plan in CLI format.
* [Simple error reply]() in these cases: no such index, syntax error in query.

title: See also

title: Related topics

text: [RediSearch]()