Vector set embeddings

Summary: Index and query embeddings with Redis vector sets

Initialize

Start by [installing]() `go-redis` if you haven't already done so. Note that you need `go-redis`
[v9.10.0](https://github.com/redis/go-redis/releases/tag/v9.10.0)
or later to use vector sets.

Also, install `hugot`:

[code example]

In a new Go file, add the required imports:

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

These include the `Hugot` library to generate an embedding from a section of text.
This example uses an instance of the `SentenceTransformer` model
[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)
for the embeddings. This model generates vectors with 384 dimensions, regardless
of the length of the input text, but note that the input is truncated to 256
tokens (see
[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)
at the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens
are related to the original text).

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

Create the data

The example data is contained in a map with some brief
descriptions of famous people:

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

Add the data to a vector set

The next step is to connect to Redis and add the data to a new vector set.

The code below iterates through the `peopleData` map and adds corresponding
elements to a vector set called `famousPeople`.

Use the
`RunPipeline()` method of `embeddingPipeline` to generate the
embedding as an array of `float32` values, then use a loop like the one
shown below to convert the `float32` array to a `float64` array.
You can then pass this array to the
[`VAdd()`]() command to set the embedding.

The call to `VAdd()` also adds the `born` and `died` values from the
original map as attribute data. You can access this during a query
or by using the [`VGetAttr()`]() method.

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

Query the vector set

You can now query the data in the set. The basic approach is to use the
`RunPipeline()` method to generate another embedding vector for the query text.
(This is the same method used to add the elements to the set.) Then, pass
the query vector to [`VSim()`]() to return elements
of the set, ranked in order of similarity to the query.

Start with a simple query for "actors":

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

This returns the following list of elements (formatted slightly for clarity):

[code example]

The first two people in the list are the two actors, as expected, but none of the
people from Linus Pauling onward was especially well-known for acting (and there certainly
isn't any information about that in the short description text).
As it stands, the search attempts to rank all the elements in the set, based
on the information contained in the embedding model.
You can use the `Count` parameter of `VSimWithArgs()` to limit the list of elements
to just the most relevant few items:

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

The reason for using text embeddings rather than simple text search
is that the embeddings represent semantic information. This allows a query
to find elements with a similar meaning even if the text is
different. For example, the word "entertainer" doesn't appear in any of the
descriptions but if you use it as a query, the actors and musicians are ranked
highest in the results list:

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

Similarly, if you use "science" as a query, you get the following results:

[code example]

The scientists are ranked highest but they are then followed by the
mathematicians. This seems reasonable given the connection between mathematics
and science.

You can also use
[filter expressions]()
with `VSimWithArgs()` to restrict the search further. For example,
repeat the "science" query, but this time limit the results to people
who died before the year 2000:

#### Code Examples

**Redis CLI:**

[code example]

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

**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]

**Python:**

[code example]

Note that the boolean filter expression is applied to items in the list
before the vector distance calculation is performed. Items that don't
pass the filter test are removed from the results completely, rather
than just reduced in rank. This can help to improve the performance of the
search because there is no need to calculate the vector distance for
elements that have already been filtered out of the search.

More information

See the [vector sets]()
docs for more information and code examples. See the
[Redis for AI]() section for more details
about text embeddings and other AI techniques you can use with Redis.

You may also be interested in
[vector search]().
This is a feature of
[Redis Search]()
that lets you retrieve
[JSON]() and
[hash]() documents based on
vector data stored in their fields.

// Suppress experimental API warnings for VectorSet
#pragma warning disable SER001

using StackExchange.Redis;

using Microsoft.ML;
using Microsoft.ML.Transforms.Text;

package main

import (
	"context"
	"fmt"
	"strings"

	"github.com/knights-analytics/hugot"
	"github.com/redis/go-redis/v9"
)

// Lettuce client and Redis Search classes.
import io.lettuce.core.*;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;

// Standard library classes for data manipulation and
// asynchronous programming.
import java.util.*;
import java.util.concurrent.CompletableFuture;

// DJL classes for model loading and inference.
import ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;
import ai.djl.inference.Predictor;
import ai.djl.repository.zoo.Criteria;
import ai.djl.training.util.ProgressBar;

// Lettuce client and Redis Search classes.
import io.lettuce.core.*;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.reactive.RedisReactiveCommands;

// Standard library classes for data manipulation and
// reactive programming.
import java.util.*;
import reactor.core.publisher.Mono;

// DJL classes for model loading and inference.
import ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;
import ai.djl.inference.Predictor;
import ai.djl.repository.zoo.Criteria;
import ai.djl.training.util.ProgressBar;

import redis.clients.jedis.RedisClient;
import redis.clients.jedis.params.VAddParams;
import redis.clients.jedis.params.VSimParams;

import java.util.*;

// DJL classes for model loading and inference.
import ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;
import ai.djl.inference.Predictor;
import ai.djl.repository.zoo.Criteria;
import ai.djl.training.util.ProgressBar;

import * as transformers from '@xenova/transformers';
import { createClient } from 'redis';

from sentence_transformers import SentenceTransformer

import redis
import numpy as np

static PredictionEngine<TextData, TransformedTextData> GetPredictionEngine()
{
    // Create a new ML context, for ML.NET operations. It can be used for
    // exception tracking and logging, as well as the source of randomness.
    var mlContext = new MLContext();

    // Create an empty list as the dataset
    var emptySamples = new List<TextData>();

    // Convert sample list to an empty IDataView.
    var emptyDataView = mlContext.Data.LoadFromEnumerable(emptySamples);

    // A pipeline for converting text into a 150-dimension embedding vector
    var textPipeline = mlContext.Transforms.Text.NormalizeText("Text")
        .Append(mlContext.Transforms.Text.TokenizeIntoWords("Tokens",
            "Text"))
        .Append(mlContext.Transforms.Text.ApplyWordEmbedding("Features",
            "Tokens", WordEmbeddingEstimator.PretrainedModelKind
            .SentimentSpecificWordEmbedding));

    // Fit to data.
    var textTransformer = textPipeline.Fit(emptyDataView);

    // Create the prediction engine to get the embedding vector from the input text/string.
    var predictionEngine = mlContext.Model.CreatePredictionEngine<TextData,
        TransformedTextData>(textTransformer);

    return predictionEngine;
}

// Create a new Hugot session
	session, err := hugot.NewGoSession()
	if err != nil {
		panic(err)
	}
	defer func() {
		err := session.Destroy()
		if err != nil {
			panic(err)
		}
	}()

	// Download the model.
	downloadOptions := hugot.NewDownloadOptions()
	downloadOptions.OnnxFilePath = "onnx/model.onnx"
	modelPath, err := hugot.DownloadModel(
		"sentence-transformers/all-MiniLM-L6-v2",
		"./models/",
		downloadOptions,
	)
	if err != nil {
		panic(err)
	}

	// Create feature extraction pipeline configuration
	config := hugot.FeatureExtractionConfig{
		ModelPath: modelPath,
		Name:      "embeddingPipeline",
	}

	// Create the feature extraction pipeline
	embeddingPipeline, err := hugot.NewPipeline(session, config)
	if err != nil {
		panic(err)
	}

Predictor<String, float[]> predictor = null;

        try {
            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)
                    .optModelUrls("djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2")
                    .optEngine("PyTorch").optTranslatorFactory(new TextEmbeddingTranslatorFactory())
                    .optProgress(new ProgressBar()).build();

            predictor = criteria.loadModel().newPredictor();
        } catch (Exception e) {
            // ...
        }

const pipe = await transformers.pipeline(
    'feature-extraction', 'Xenova/all-MiniLM-L6-v2'
);

const pipeOptions = {
    pooling: 'mean',
    normalize: true,
};

Dictionary<string, dynamic> peopleData = new Dictionary<string, dynamic>
{
    ["Marie Curie"] = new
    {
        born = 1867,
        died = 1934,
        description = @"
        Polish-French chemist and physicist. The only person ever to win
        two Nobel prizes for two different sciences.
        "
    },
    ["Linus Pauling"] = new
    {
        born = 1901,
        died = 1994,
        description = @"
        American chemist and peace activist. One of only two people to win two
        Nobel prizes in different fields (chemistry and peace).
        "
    },
    ["Freddie Mercury"] = new
    {
        born = 1946,
        died = 1991,
        description = @"
        British musician, best known as the lead singer of the rock band
        Queen.
        "
    },
    ["Marie Fredriksson"] = new
    {
        born = 1958,
        died = 2019,
        description = @"
        Swedish multi-instrumentalist, mainly known as the lead singer and
        keyboardist of the band Roxette.
        "
    },
    ["Paul Erdos"] = new
    {
        born = 1913,
        died = 1996,
        description = @"
        Hungarian mathematician, known for his eccentric personality almost
        as much as his contributions to many different fields of mathematics.
        "
    },
    ["Maryam Mirzakhani"] = new
    {
        born = 1977,
        died = 2017,
        description = @"
        Iranian mathematician. The first woman ever to win the Fields medal
        for her contributions to mathematics.
        "
    },
    ["Masako Natsume"] = new
    {
        born = 1957,
        died = 1985,
        description = @"
        Japanese actress. She was very famous in Japan but was primarily
        known elsewhere in the world for her portrayal of Tripitaka in the
        TV series Monkey.
        "
    },
    ["Chaim Topol"] = new
    {
        born = 1935,
        died = 2023,
        description = @"
        Israeli actor and singer, usually credited simply as 'Topol'. He was
        best known for his many appearances as Tevye in the musical Fiddler
        on the Roof.
        "
    }
};

type PersonData struct {
	Born        int
	Died        int
	Description string
}

var peopleData = map[string]PersonData{
	"Marie Curie": {
		Born: 1867, Died: 1934,
		Description: `Polish-French chemist and physicist. The only person ever to win
		two Nobel prizes for two different sciences.
		`,
	},
	"Linus Pauling": {
		Born: 1901, Died: 1994,
		Description: `American chemist and peace activist. One of only two people to win two
		Nobel prizes in different fields (chemistry and peace).
		`,
	},
	"Freddie Mercury": {
		Born: 1946, Died: 1991,
		Description: `British musician, best known as the lead singer of the rock band
		Queen.
		`,
	},
	"Marie Fredriksson": {
		Born: 1958, Died: 2019,
		Description: `Swedish multi-instrumentalist, mainly known as the lead singer and
		keyboardist of the band Roxette.
		`,
	},
	"Paul Erdos": {
		Born: 1913, Died: 1996,
		Description: `Hungarian mathematician, known for his eccentric personality almost
		as much as his contributions to many different fields of mathematics.
		`,
	},
	"Maryam Mirzakhani": {
		Born: 1977, Died: 2017,
		Description: `Iranian mathematician. The first woman ever to win the Fields medal
		for her contributions to mathematics.
		`,
	},
	"Masako Natsume": {
		Born: 1957, Died: 1985,
		Description: `Japanese actress. She was very famous in Japan but was primarily
		known elsewhere in the world for her portrayal of Tripitaka in the
		TV series Monkey.
		`,
	},
	"Chaim Topol": {
		Born: 1935, Died: 2023,
		Description: `Israeli actor and singer, usually credited simply as 'Topol'. He was
		best known for his many appearances as Tevye in the musical Fiddler
		on the Roof.
		`,
	},
}

final class Person {
            final String name;
            final int born;
            final int died;
            final String description;
            Person(String name, int born, int died, String description) {
                this.name = name; this.born = born; this.died = died; this.description = description;
            }
        }

        List<Person> people = Arrays.asList(
            new Person(
                "Marie Curie",
                1867, 1934,
                "Polish-French chemist and physicist. The only person ever to win" +
                " two Nobel prizes for two different sciences."
            ),
            new Person(
                "Linus Pauling",
                1901, 1994,
                "American chemist and peace activist. One of only two people to" +
                " win two Nobel prizes in different fields (chemistry and peace)."
            ),
            new Person(
                "Freddie Mercury",
                1946, 1991,
                "British musician, best known as the lead singer of the rock band Queen."
            ),
            new Person(
                "Marie Fredriksson",
                1958, 2019,
                "Swedish multi-instrumentalist, mainly known as the lead singer and" +
                " keyboardist of the band Roxette."
            ),
            new Person(
                "Paul Erdos",
                1913, 1996,
                "Hungarian mathematician, known for his eccentric personality almost" +
                " as much as his contributions to many different fields of mathematics."
            ),
            new Person(
                "Maryam Mirzakhani",
                1977, 2017,
                "Iranian mathematician. The first woman ever to win the Fields medal" +
                " for her contributions to mathematics."
            ),
            new Person(
                "Masako Natsume",
                1957, 1985,
                "Japanese actress. She was very famous in Japan but was primarily" +
                " known elsewhere in the world for her portrayal of Tripitaka in the" +
                " TV series Monkey."
            ),
            new Person(
                "Chaim Topol",
                1935, 2023,
                "Israeli actor and singer, usually credited simply as 'Topol'. He was" +
                " best known for his many appearances as Tevye in the musical Fiddler" +
                " on the Roof."
            )
        );

const peopleData = {
    "Marie Curie": {
        "born": 1867, "died": 1934,
        "description": `
        Polish-French chemist and physicist. The only person ever to win
        two Nobel prizes for two different sciences.
        `
    },
    "Linus Pauling": {
        "born": 1901, "died": 1994,
        "description": `
        American chemist and peace activist. One of only two people to win two
        Nobel prizes in different fields (chemistry and peace).
        `
    },
    "Freddie Mercury": {
        "born": 1946, "died": 1991,
        "description": `
        British musician, best known as the lead singer of the rock band
        Queen.
        `
    },
    "Marie Fredriksson": {
        "born": 1958, "died": 2019,
        "description": `
        Swedish multi-instrumentalist, mainly known as the lead singer and
        keyboardist of the band Roxette.
        `
    },
    "Paul Erdos": {
        "born": 1913, "died": 1996,
        "description": `
        Hungarian mathematician, known for his eccentric personality almost
        as much as his contributions to many different fields of mathematics.
        `
    },
    "Maryam Mirzakhani": {
        "born": 1977, "died": 2017,
        "description": `
        Iranian mathematician. The first woman ever to win the Fields medal
        for her contributions to mathematics.
        `
    },
    "Masako Natsume": {
        "born": 1957, "died": 1985,
        "description": `
        Japanese actress. She was very famous in Japan but was primarily
        known elsewhere in the world for her portrayal of Tripitaka in the
        TV series Monkey.
        `
    },
    "Chaim Topol": {
        "born": 1935, "died": 2023,
        "description": `
        Israeli actor and singer, usually credited simply as 'Topol'. He was
        best known for his many appearances as Tevye in the musical Fiddler
        on the Roof.
        `
    }
};

peopleData = {
    "Marie Curie": {
        "born": 1867, "died": 1934,
        "description": """
        Polish-French chemist and physicist. The only person ever to win
        two Nobel prizes for two different sciences.
        """
    },
    "Linus Pauling": {
        "born": 1901, "died": 1994,
        "description": """
        American chemist and peace activist. One of only two people to win two
        Nobel prizes in different fields (chemistry and peace).
        """
    },
    "Freddie Mercury": {
        "born": 1946, "died": 1991,
        "description": """
        British musician, best known as the lead singer of the rock band
        Queen.
        """
    },
    "Marie Fredriksson": {
        "born": 1958, "died": 2019,
        "description": """
        Swedish multi-instrumentalist, mainly known as the lead singer and
        keyboardist of the band Roxette.
        """
    },
    "Paul Erdos": {
        "born": 1913, "died": 1996,
        "description": """
        Hungarian mathematician, known for his eccentric personality almost
        as much as his contributions to many different fields of mathematics.
        """
    },
    "Maryam Mirzakhani": {
        "born": 1977, "died": 2017,
        "description": """
        Iranian mathematician. The first woman ever to win the Fields medal
        for her contributions to mathematics.
        """
    },
    "Masako Natsume": {
        "born": 1957, "died": 1985,
        "description": """
        Japanese actress. She was very famous in Japan but was primarily
        known elsewhere in the world for her portrayal of Tripitaka in the
        TV series Monkey.
        """
    },
    "Chaim Topol": {
        "born": 1935, "died": 2023,
        "description": """
        Israeli actor and singer, usually credited simply as 'Topol'. He was
        best known for his many appearances as Tevye in the musical Fiddler
        on the Roof.
        """
    }
}

ConnectionMultiplexer muxer = ConnectionMultiplexer.Connect("localhost:6379");
IDatabase db = muxer.GetDatabase();

PredictionEngine<TextData, TransformedTextData> model = GetPredictionEngine();

foreach (KeyValuePair<string, dynamic> person in peopleData)
{
    string name = person.Key;
    dynamic details = person.Value;
    float[] embedding = GetEmbedding(model, details.description);

    VectorSetAddRequest addRequest = VectorSetAddRequest.Member(name, embedding, null);
    db.VectorSetAdd("famousPeople", addRequest);

    // Set attributes separately
    string attributesJson = $"{{\"born\": {details.born}, \"died\": {details.died}}}";
    db.VectorSetSetAttributesJson("famousPeople", name, attributesJson);
}

for name, details := range peopleData {
		// Generate embeddings using Hugot
		result, err := embeddingPipeline.RunPipeline([]string{details.Description})
		if err != nil {
			panic(err)
		}

		// Convert embedding to float64 slice
		embFloat32 := result.Embeddings[0]
		embFloat64 := make([]float64, len(embFloat32))
		for i, v := range embFloat32 {
			embFloat64[i] = float64(v)
		}

		// Add vector to vector set
		_, err = rdb.VAdd(ctx, "famousPeople", name, &redis.VectorValues{Val: embFloat64}).Result()
		if err != nil {
			panic(err)
		}

		// Set attributes for the element
		_, err = rdb.VSetAttr(ctx, "famousPeople", name, map[string]interface{}{
			"born": details.Born,
			"died": details.Died,
		}).Result()
		if err != nil {
			panic(err)
		}
	}

RedisClient redisClient = RedisClient.create("redis://localhost:6379");

        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
            RedisAsyncCommands<String, String> asyncCommands = connection.async();

            CompletableFuture<?>[] vaddFutures = new CompletableFuture[people.size()];

            for (int i = 0; i < people.size(); i++) {
                Person person = people.get(i);
                Double[] embedding = null;

                try {
                    embedding = convertFloatsToDoubles(predictor.predict(person.description));
                } catch (Exception e) {
                    // ...
                }

                VAddArgs personArgs = VAddArgs.Builder.attributes(String.format("{\"born\": %d, \"died\": %d}", person.born, person.died));

                vaddFutures[i] = asyncCommands.vadd("famousPeople", person.name, personArgs, embedding)
                        .thenApply(result -> {
                            System.out.println(result); // >>> true
                            return result;
                        }).toCompletableFuture();
            }

            CompletableFuture.allOf(vaddFutures).join();

RedisClient redisClient = RedisClient.create("redis://localhost:6379");

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

            Mono<?>[] vaddFutures = new Mono[people.size()];

            for (int i = 0; i < people.size(); i++) {
                Person person = people.get(i);
                Double[] embedding = null;

                try {
                    embedding = convertFloatsToDoubles(predictor.predict(person.description));
                } catch (Exception e) {
                    // ...
                }

                VAddArgs personArgs = VAddArgs.Builder.attributes(String.format("{\"born\": %d, \"died\": %d}", person.born, person.died));

                vaddFutures[i] = reactiveCommands.vadd("famousPeople", person.name, personArgs, embedding)
                        .doOnNext(result -> {
                            System.out.println(result); // >>> true
                        });
            }

            Mono.when(vaddFutures).block();

RedisClient jedis = new RedisClient("redis://localhost:6379");

        for (Person person : people) {
            float[] embedding;
            try {
                embedding = predictor.predict(person.description);
            } catch (Exception e) {
                // This just allows the code to compile without errors.
                // In a real-world scenario, you would handle the exception properly.
                embedding = new float[384];
            }

            // Add element with attributes using VAddParams
            String attrs = String.format("{\"born\": %d, \"died\": %d}", person.born, person.died);
            boolean added = jedis.vadd("famousPeople", embedding, person.name, new VAddParams().setAttr(attrs));
            System.out.println(added); // >>> true
        }

const client = createClient({ url: 'redis://localhost:6379' });

client.on('error', err => console.log('Redis Client Error', err));
await client.connect();

for (const [name, details] of Object.entries(peopleData)) {
    const embedding = await pipe(details.description, pipeOptions);
    const embeddingArray = Array.from(embedding.data);

    await client.vAdd('famousPeople', embeddingArray, name);
    await client.vSetAttr('famousPeople', name, JSON.stringify({
        born: details.born,
        died: details.died
    }));
}

r = redis.Redis(decode_responses=True)

for name, details in peopleData.items():
    emb = model.encode(details["description"]).astype(np.float32).tobytes()

    r.vset().vadd(
        "famousPeople",
        emb,
        name,
        attributes={
            "born": details["born"],
            "died": details["died"]
        }
    )

string queryValue = "actors";

float[] queryEmbedding = GetEmbedding(model, queryValue);

VectorSetSimilaritySearchRequest basicQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);
using (Lease<VectorSetSimilaritySearchResult>? actorsResults = db.VectorSetSimilaritySearch("famousPeople", basicQuery))
{
    IEnumerable<string> resultIds = actorsResults!.Span.ToArray()
        .Select(r => (string?)r.Member)
        .Where(id => id != null)
        .Select(id => $"'{id!}'");
    Console.WriteLine($"'actors': [{string.Join(", ", resultIds)}]");
}

queryValue := "actors"

	queryResult, err := embeddingPipeline.RunPipeline([]string{queryValue})
	if err != nil {
		panic(err)
	}

	// Convert embedding to float64 slice
	queryFloat32 := queryResult.Embeddings[0]
	queryFloat64 := make([]float64, len(queryFloat32))
	for i, v := range queryFloat32 {
		queryFloat64[i] = float64(v)
	}

	actorsResults, err := rdb.VSim(ctx, "famousPeople", &redis.VectorValues{Val: queryFloat64}).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("'actors': %v\n", strings.Join(actorsResults, ", "))
	// >>> 'actors': Masako Natsume, Chaim Topol, Linus Pauling,
	// Marie Fredriksson, Maryam Mirzakhani, Marie Curie, Freddie Mercury,
	// Paul Erdos

Double[] actorsEmbedding = null;

            try {
                actorsEmbedding = convertFloatsToDoubles(predictor.predict("actors"));
            } catch (Exception e) {
                // ...
            }

            CompletableFuture<List<String>> basicQuery = asyncCommands.vsim("famousPeople", actorsEmbedding)
                    .thenApply(result -> {
                        System.out.println(result);
                        // >>> [Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson, Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos]
                        return result;
                    }).toCompletableFuture();

Double[] actorsEmbedding = null;

            try {
                actorsEmbedding = convertFloatsToDoubles(predictor.predict("actors"));
            } catch (Exception e) {
                // ...
            }

            Mono<java.util.List<String>> basicQuery = reactiveCommands.vsim("famousPeople", actorsEmbedding)
                    .collectList().doOnNext(result -> {
                        System.out.println(result);
                        // >>> [Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson, Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos]
                    });

float[] actorsEmbedding;
        try {
            actorsEmbedding = predictor.predict("actors");
        } catch (Exception e) {
            actorsEmbedding = new float[384];
        }

        List<String> basicResults = jedis.vsim("famousPeople", actorsEmbedding);
        System.out.println(basicResults);
        // >>> [Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson,
        // >>> Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos]

const queryValue = "actors";

const queryEmbedding = await pipe(queryValue, pipeOptions);
const queryArray = Array.from(queryEmbedding.data);

const actorsResults = await client.vSim('famousPeople', queryArray);

console.log(`'actors': ${JSON.stringify(actorsResults)}`);
// >>> 'actors': ["Masako Natsume","Chaim Topol","Linus Pauling",
// "Marie Fredriksson","Maryam Mirzakhani","Freddie Mercury",
// "Marie Curie","Paul Erdos"]

query_value = "actors"

actors_results = r.vset().vsim(
    "famousPeople",
    model.encode(query_value).astype(np.float32).tobytes(),
)

print(f"'actors': {actors_results}")

'actors': Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson,
    Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos

queryValue = "actors";

queryEmbedding = GetEmbedding(model, queryValue);

VectorSetSimilaritySearchRequest limitedQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);
limitedQuery.Count = 2;
using (Lease<VectorSetSimilaritySearchResult>? twoActorsResults = db.VectorSetSimilaritySearch("famousPeople", limitedQuery))
{
    IEnumerable<string> resultIds = twoActorsResults!.Span.ToArray()
        .Select(r => (string?)r.Member)
        .Where(id => id != null)
        .Select(id => $"'{id!}'");
    Console.WriteLine($"'actors (2)': [{string.Join(", ", resultIds)}]");
    // >>> 'actors (2)': ['Masako Natsume', 'Chaim Topol']
}

queryValue = "actors"

	queryResult, err = embeddingPipeline.RunPipeline([]string{queryValue})
	if err != nil {
		panic(err)
	}

	// Convert embedding to float64 slice
	queryFloat32 = queryResult.Embeddings[0]
	queryFloat64 = make([]float64, len(queryFloat32))
	for i, v := range queryFloat32 {
		queryFloat64[i] = float64(v)
	}

	twoActorsResults, err := rdb.VSimWithArgs(ctx, "famousPeople",
		&redis.VectorValues{Val: queryFloat64},
		&redis.VSimArgs{Count: 2}).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("'actors (2)': %v\n", strings.Join(twoActorsResults, ", "))
	// >>> 'actors (2)': Masako Natsume, Chaim Topol

VSimArgs limitArgs = VSimArgs.Builder.count(2L);

            CompletableFuture<List<String>> limitedQuery = asyncCommands.vsim("famousPeople", limitArgs, actorsEmbedding)
                    .thenApply(result -> {
                        System.out.println(result);
                        // >>> [Masako Natsume, Chaim Topol]
                        return result;
                    }).toCompletableFuture();

VSimArgs limitArgs = VSimArgs.Builder.count(2L);

            Mono<java.util.List<String>> limitedQuery = reactiveCommands.vsim("famousPeople", limitArgs, actorsEmbedding)
                    .collectList().doOnNext(result -> {
                        System.out.println(result);
                        // >>> [Masako Natsume, Chaim Topol]
                    });

VSimParams limitParams = new VSimParams().count(2);
        List<String> limitedResults = jedis.vsim("famousPeople", actorsEmbedding, limitParams);
        System.out.println(limitedResults);
        // >>> [Masako Natsume, Chaim Topol]

const queryValue2 = "actors";

const queryEmbedding2 = await pipe(queryValue2, pipeOptions);
const queryArray2 = Array.from(queryEmbedding2.data);

const twoActorsResults = await client.vSim('famousPeople', queryArray2, {
    COUNT: 2
});

console.log(`'actors (2)': ${JSON.stringify(twoActorsResults)}`);
// >>> 'actors (2)': ["Masako Natsume","Chaim Topol"]

query_value = "actors"

two_actors_results = r.vset().vsim(
    "famousPeople",
    model.encode(query_value).astype(np.float32).tobytes(),
    count=2
)

print(f"'actors (2)': {two_actors_results}")
# >>> 'actors (2)': ['Masako Natsume', 'Chaim Topol']

queryValue = "entertainer";

queryEmbedding = GetEmbedding(model, queryValue);

VectorSetSimilaritySearchRequest entertainerQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);
using (Lease<VectorSetSimilaritySearchResult>? entertainerResults = db.VectorSetSimilaritySearch("famousPeople", entertainerQuery))
{
    IEnumerable<string> resultIds = entertainerResults!.Span.ToArray()
        .Select(r => (string?)r.Member)
        .Where(id => id != null)
        .Select(id => $"'{id!}'");
    Console.WriteLine($"'entertainer': [{string.Join(", ", resultIds)}]");
    // >>> 'entertainer': ['Chaim Topol', 'Freddie Mercury',
    // 'Marie Fredriksson', 'Masako Natsume', 'Linus Pauling',
    // 'Paul Erdos', 'Maryam Mirzakhani', 'Marie Curie']
}

queryValue = "entertainer"

	queryResult, err = embeddingPipeline.RunPipeline([]string{queryValue})
	if err != nil {
		panic(err)
	}

	// Convert embedding to float64 slice
	queryFloat32 = queryResult.Embeddings[0]
	queryFloat64 = make([]float64, len(queryFloat32))
	for i, v := range queryFloat32 {
		queryFloat64[i] = float64(v)
	}

	entertainerResults, err := rdb.VSim(ctx, "famousPeople", &redis.VectorValues{Val: queryFloat64}).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("'entertainer': %v\n", strings.Join(entertainerResults, ", "))
	// >>> 'entertainer': Chaim Topol, Freddie Mercury, Marie Fredriksson,
	// Linus Pauling, Masako Natsume, Paul Erdos,
	// Maryam Mirzakhani, Marie Curie

Double[] entertainerEmbedding = null;

            try {
                entertainerEmbedding = convertFloatsToDoubles(predictor.predict("entertainers"));
            } catch (Exception e) {
                // ...
            }

            CompletableFuture<List<String>> entertainerQuery = asyncCommands.vsim("famousPeople", entertainerEmbedding)
                    .thenApply(result -> {
                        System.out.println(result);
                        // >>> [Freddie Mercury, Chaim Topol, Linus Pauling, Marie Fredriksson, Masako Natsume, Paul Erdos, Maryam Mirzakhani, Marie Curie]
                        return result;
                    }).toCompletableFuture();

Double[] entertainerEmbedding = null;

            try {
                entertainerEmbedding = convertFloatsToDoubles(predictor.predict("entertainers"));
            } catch (Exception e) {
                // ...
            }

            Mono<java.util.List<String>> entertainerQuery = reactiveCommands.vsim("famousPeople", entertainerEmbedding)
                    .collectList().doOnNext(result -> {
                        System.out.println(result);
                        // >>> [Freddie Mercury, Chaim Topol, Linus Pauling, Marie Fredriksson, Masako Natsume, Paul Erdos, Maryam Mirzakhani, Marie Curie]
                    });

float[] entertainerEmbedding;
        try {
            entertainerEmbedding = predictor.predict("entertainers");
        } catch (Exception e) {
            entertainerEmbedding = new float[384];
        }

        List<String> entertainerResults = jedis.vsim("famousPeople", entertainerEmbedding);
        System.out.println(entertainerResults);
        // >>> [Freddie Mercury, Chaim Topol, Linus Pauling, Marie Fredriksson,
        // >>> Masako Natsume, Paul Erdos, Maryam Mirzakhani, Marie Curie]

const queryValue3 = "entertainer";

const queryEmbedding3 = await pipe(queryValue3, pipeOptions);
const queryArray3 = Array.from(queryEmbedding3.data);

const entertainerResults = await client.vSim('famousPeople', queryArray3);

console.log(`'entertainer': ${JSON.stringify(entertainerResults)}`);
// >>> 'actors': ["Masako Natsume","Chaim Topol","Linus Pauling",
// "Marie Fredriksson","Maryam Mirzakhani","Freddie Mercury",
// "Marie Curie","Paul Erdos"]

query_value = "entertainer"

entertainer_results = r.vset().vsim(
    "famousPeople",
    model.encode(query_value).astype(np.float32).tobytes()
)

print(f"'entertainer': {entertainer_results}")
# >>> 'entertainer': ['Chaim Topol', 'Freddie Mercury',
# 'Marie Fredriksson', 'Masako Natsume', 'Linus Pauling',
# 'Paul Erdos', 'Maryam Mirzakhani', 'Marie Curie']

'science': Marie Curie, Linus Pauling, Maryam Mirzakhani, Paul Erdos,
Marie Fredriksson, Freddie Mercury, Masako Natsume, Chaim Topol

queryValue = "science";

queryEmbedding = GetEmbedding(model, queryValue);

VectorSetSimilaritySearchRequest filteredQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);
filteredQuery.FilterExpression = ".died < 2000";
using (Lease<VectorSetSimilaritySearchResult>? science2000Results = db.VectorSetSimilaritySearch("famousPeople", filteredQuery))
{
    IEnumerable<string> resultIds = science2000Results!.Span.ToArray()
        .Select(r => (string?)r.Member)
        .Where(id => id != null)
        .Select(id => $"'{id!}'");
    Console.WriteLine($"'science2000': [{string.Join(", ", resultIds)}]");
    // >>> 'science2000': ['Marie Curie', 'Linus Pauling',
    // 'Paul Erdos', 'Freddie Mercury', 'Masako Natsume']
}

queryValue = "science"

	queryResult, err = embeddingPipeline.RunPipeline([]string{queryValue})
	if err != nil {
		panic(err)
	}

	// Convert embedding to float64 slice
	queryFloat32 = queryResult.Embeddings[0]
	queryFloat64 = make([]float64, len(queryFloat32))
	for i, v := range queryFloat32 {
		queryFloat64[i] = float64(v)
	}

	science2000Results, err := rdb.VSimWithArgs(ctx, "famousPeople",
		&redis.VectorValues{Val: queryFloat64},
		&redis.VSimArgs{Filter: ".died < 2000"}).Result()
	if err != nil {
		panic(err)
	}

	fmt.Printf("'science2000': %v\n", strings.Join(science2000Results, ", "))
	// >>> 'science2000': Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury,
	// Masako Natsume

Double[] science2000Embedding = null;

            try {
                science2000Embedding = convertFloatsToDoubles(predictor.predict("science"));
            } catch (Exception e) {
                // ...
            }

            VSimArgs filterArgs = VSimArgs.Builder.filter(".died < 2000");

            CompletableFuture<List<String>> filteredQuery = asyncCommands.vsim("famousPeople", filterArgs, science2000Embedding)
                    .thenApply(result -> {
                        System.out.println(result);
                        // >>> [Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury, Masako Natsume]
                        return result;
                    }).toCompletableFuture();

Double[] science2000Embedding = null;

            try {
                science2000Embedding = convertFloatsToDoubles(predictor.predict("science"));
            } catch (Exception e) {
                // ...
            }

            VSimArgs filterArgs = VSimArgs.Builder.filter(".died < 2000");

            Mono<java.util.List<String>> filteredQuery = reactiveCommands.vsim("famousPeople", filterArgs, science2000Embedding)
                    .collectList().doOnNext(result -> {
                        System.out.println(result);
                        // >>> [Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury, Masako Natsume]
                    });

float[] science2000Embedding;
        try {
            science2000Embedding = predictor.predict("science");
        } catch (Exception e) {
            science2000Embedding = new float[384];
        }

        VSimParams filterParams = new VSimParams().filter(".died < 2000");
        List<String> filteredResults = jedis.vsim("famousPeople", science2000Embedding, filterParams);
        System.out.println(filteredResults);
        // >>> [Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury, Masako Natsume]

const queryValue5 = "science";

const queryEmbedding5 = await pipe(queryValue5, pipeOptions);
const queryArray5 = Array.from(queryEmbedding5.data);

const science2000Results = await client.vSim('famousPeople', queryArray5, {
    FILTER: '.died < 2000'
});

console.log(`'science2000': ${JSON.stringify(science2000Results)}`);
// >>> 'science2000': ["Linus Pauling","Marie Curie","Paul Erdos",
// "Masako Natsume","Freddie Mercury"]

query_value = "science"

science2000_results = r.vset().vsim(
    "famousPeople",
    model.encode(query_value).astype(np.float32).tobytes(),
    filter=".died < 2000"
)

print(f"'science2000': {science2000_results}")
# >>> 'science2000': ['Marie Curie', 'Linus Pauling',
# 'Paul Erdos', 'Freddie Mercury', 'Masako Natsume']


Connect

Summary: Connect to the server with `hiredis`.

Basic synchronous connection

The example below creates a simple synchronous connection to a local
Redis server and tests the connection, before closing it with
`redisFree()`. The `redisConnect()` function takes just a hostname
and port as its arguments, and returns a context object.

[code example]

Asynchronous connection

You can also connect to Redis using an asynchronous API.
The `redisAsyncConnect()` call that creates the context is
similar to the synchronous function `redisConnect()`, but it returns the
context object immediately before the connection is complete.
It lets you supply callbacks to respond when a connection is successful
or to handle any errors that may occur.

The following code creates an asynchronous connection and
sets the context callbacks. Note that you must also include the
`async.h` header to access the asynchronous API.

[code example]

The callback functions have a simple signature that receives
the context object and a status code. See
[Handling errors]()
for a list of the possible status codes.

[code example]

Use the `redisAsyncCommand()` function to issue Redis commands
with an asynchronous connection. This is similar to the equivalent
synchronous function `redisCommand()` but also lets you supply a callback
and a custom data pointer to process the response to the command. See
[Construct asynchronous commands]() for more
information.

Note that you should normally disconnect asynchronously from a
callback when you have finished using the connection.
Use `redisAsyncDisconnect()` to disconnect gracefully, letting
pending commands execute and activate their callbacks.
Use `redisAsyncFree()` to disconnect immediately. If you do this then
any pending callbacks from commands that have already executed will be
called with a `NULL` reply pointer.

#include <stdio.h>
#include <stdlib.h>

#include <hiredis/hiredis.h>
    .
    .
    .

// The `redisContext` type represents the connection
// to the Redis server. Here, we connect to the
// default host and port.
redisContext *c = redisConnect("127.0.0.1", 6379);

// Check if the context is null or if a specific
// error occurred.
if (c == NULL || c->err) {
    if (c != NULL) {
        printf("Error: %s\n", c->errstr);
        // handle error
    } else {
        printf("Can't allocate redis context\n");
    }

    exit(1);
}

// Set a string key.
redisReply *reply = redisCommand(c, "SET foo bar");
printf("Reply: %s\n", reply->str); // >>> Reply: OK
freeReplyObject(reply);

// Get the key we have just stored.
reply = redisCommand(c, "GET foo");
printf("Reply: %s\n", reply->str); // >>> Reply: bar
freeReplyObject(reply);

// Close the connection.
redisFree(c);

#include <stdio.h>
#include <stdlib.h>

#include <hiredis/hiredis.h>
#include <hiredis/async.h>
    .
    .
    .

redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);

if (c->err) {
    printf("Error: %s\n", c->errstr);
    return 1;
}

// Set callbacks to respond to successful or unsuccessful
// connection and disconnection.
redisAsyncSetConnectCallback(c, connectCallback);
redisAsyncSetDisconnectCallback(c, disconnectCallback);

char *key = "testkey";
char *value = "testvalue";

// Status reply is ignored.
redisAsyncCommand(c, NULL, NULL, "SET %s %s", key, value);

// Reply handled by `getCallback()` function.
redisAsyncCommand(c, getCallback, key, "GET %s", key);

void connectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Error: %s\n", c->errstr);
        return;
    }
    printf("Connected...\n");
}

void disconnectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Error: %s\n", c->errstr);
        return;
    }
    printf("Disconnected...\n");
}


Handle command replies

Summary: Handle command replies with `hiredis`.

Overview

The `redisCommand()` and `redisCommandArgv()` functions return
a pointer to a `redisReply` object when you issue a command (see
[Issue commands]()
for more information). This type supports all
reply formats defined in the
[RESP2 and RESP3]()
protocols, so its content varies greatly between calls.

A simple example is the status response returned by the [`SET`]()
command. The code below shows how to get this from the `redisReply`
object:

[code example]

A null reply indicates an error, so you should always check for this.
If an error does occur, then the `redisContext` object will have a
non-zero error number in its integer `err` field and a textual
description of the error in its `errstr` field.

For `SET`, a successful call will simply return an "OK" string that you
can access with the `reply->str` field. The code in the example prints
this to the console, but you should check for the specific value to ensure
the command executed correctly.

The `redisCommand()` call allocates memory for the reply, so you should
always free it using `freeReplyObject()` when you have finished using
the reply. If you want to reuse the reply variable then it is wise to
set it to `NULL` after you free it, so that you don't accidentally use
the stale pointer later.

Reply formats

The Redis
[`RESP`]()
protocols support several different reply formats for commands.

You can find the reply format for a command at the end of its
reference page in the RESP2/RESP3 Reply section (for example, the
[`INCRBY`]() page shows that the
command has an integer result). You can also determine the format
using the `type` field of the reply object. This contains a
different integer value for each type. The `hiredis.h` header file
defines constants for all of these integer values (for example `REDIS_REPLY_STRING`).

The `redisReply` struct has several fields to contain different
types of replies, with different fields being set depending on
the value of the `type` field. The table below shows the type
constants, the corresponding reply type, and the fields you can
use to access the reply value:

| Constant | Type | Relevant fields of `redisReply` | RESP protocol |
| :- | :- |:- | :- |
| `REDIS_REPLY_STATUS` | [Simple string]() | `reply->str`: the string value (`char*`)
 `reply->len`: the string length (`size_t`) | 2, 3 |
| `REDIS_REPLY_ERROR` | [Simple error]() | `reply->str`: the string value (`char*`)
 `reply->len`: the string length (`size_t`) | 2, 3 |
| `REDIS_REPLY_INTEGER` | [Integer]() | `reply->integer`: the integer value (`long long`)| 2, 3 |
| `REDIS_REPLY_NIL` | [Null]() | No data | 2, 3 |
| `REDIS_REPLY_STRING` | [Bulk string]() |`reply->str`: the string value (`char*`)
 `reply->len`: the string length (`size_t`) | 2, 3 |
| `REDIS_REPLY_ARRAY` | [Array]() | `reply->elements`: number of elements (`size_t`)
 `reply->element`: array elements (`redisReply`) | 2, 3 |
| `REDIS_REPLY_DOUBLE` | [Double]() | `reply->str`: double value as string (`char*`)
 `reply->len`: the string length (`size_t`) | 3 |
| `REDIS_REPLY_BOOL` | [Boolean]() | `reply->integer`: the boolean value, 0 or 1 (`long long`) | 3 |
| `REDIS_REPLY_MAP` | [Map]() | `reply->elements`: number of elements (`size_t`)
 `reply->element`: array elements (`redisReply`) | 3 |
| `REDIS_REPLY_SET` | [Set]() | `reply->elements`: number of elements (`size_t`)
 `reply->element`: array elements (`redisReply`) | 3 |
| `REDIS_REPLY_PUSH` | [Push]() | `reply->elements`: number of elements (`size_t`)
 `reply->element`: array elements (`redisReply`) | 3 |
| `REDIS_REPLY_BIGNUM` | [Big number]() | `reply->str`: number value as string (`char*`)
 `reply->len`: the string length (`size_t`) | 3 |
| `REDIS_REPLY_VERB` | [Verbatim string]() |`reply->str`: the string value (`char*`)
 `reply->len`: the string length (`size_t`)
 `reply->vtype`: content type (`char[3]`) | 3 |

Reply format processing examples

The sections below explain how to process specific reply types in
more detail.

Integers

The `REDIS_REPLY_INTEGER` and `REDIS_REPLY_BOOL` reply types both
contain values in `reply->integer`. However, `REDIS_REPLY_BOOL` is
rarely used. Even when the command essentially returns a boolean value,
the reply is usually reported as an integer.

[code example]

Strings

The `REDIS_REPLY_STATUS`, `REDIS_REPLY_ERROR`, `REDIS_REPLY_STRING`,
`REDIS_REPLY_DOUBLE`, `REDIS_REPLY_BIGNUM`, and `REDIS_REPLY_VERB`
are all returned as strings, with the main difference lying in how
you interpret them. For all these types, the string value is
returned in `reply->str` and the length of the string is in
`reply->len`. The example below shows some of the possibilities.

[code example]

Arrays and maps

Arrays (reply type `REDIS_REPLY_ARRAY`) and maps (reply type `REDIS_REPLY_MAP`)
are returned by commands that retrieve several values at the
same time. For both types, the number of elements in the reply is contained in
`reply->elements` and the pointer to the array itself is is `reply->element`.
Each item in the array is of type `redisReply`. The array elements
are typically simple types rather than arrays or maps.

The example below shows how to get the items from a
[list]():

[code example]

A map is essentially the same as an array but it has the extra
guarantee that the items will be listed in key-value pairs.
The example below shows how to get all the fields from a
[hash]() using
[`HGETALL`]():

[code example]

Handling errors

When a command executes successfully, the `err` field of the context
object will be set to zero. If a command fails, it will return either
`NULL` or `REDIS_ERR`, depending on which function and command you used. When
this happens, `context->err` will contain an error code

-   `REDIS_ERR_IO`: There was an I/O error while creating the connection,
    or while trying to write or read data. Whenever `context->err` contains
    `REDIS_ERR_IO`, you can use the features of the standard library file
    [`errno.h`](https://en.wikipedia.org/wiki/Errno.h) to find out more
    information about the error.
-   `REDIS_ERR_EOF`: The server closed the connection which resulted in an empty read.
-   `REDIS_ERR_PROTOCOL`: There was an error while parsing the
    [RESP protocol]().
-   `REDIS_ERR_OTHER`: Any other error. Currently, it is only used when the connection
    hostname can't be resolved.

The context object also has an `errstr` field that contains a descriptive error message.

redisReply *reply = redisCommand(c, "SET greeting Hello");

// Check and free the reply.
if (reply != NULL) {
    printf("Reply: %s\n", reply->str);
    freeReplyObject(reply);
    reply = NULL;
}

// Add some values to a set.
redisReply *reply = redisCommand(c, "SADD items bread milk peas");

if (reply->type == REDIS_REPLY_INTEGER) {
    // Report status.
    printf("Integer reply\n");
    printf("Number added: %lld\n", reply->integer);
    // >>> Number added: 3
}

freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "SISMEMBER items bread");

// This also gives an integer reply but you should interpret
// it as a boolean value.
if (reply->type == REDIS_REPLY_INTEGER) {
    // Respond to boolean integer value.
    printf("Integer reply\n");

    if (reply->integer == 0) {
        printf("Items set has no member 'bread'\n");
    } else {
        printf("'Bread' is a member of items set\n");
    }
    // >>> 'Bread' is a member of items set
}

freeReplyObject(reply);
reply = NULL;

// Set a numeric value in a string.
reply = redisCommand(c, "SET number 1.5");

// This gives a status reply.
if (reply->type == REDIS_REPLY_STATUS) {
    // Report status.
    printf("Status reply\n");
    printf("Reply: %s\n", reply->str); // >>> Reply: OK
}

freeReplyObject(reply);
reply = NULL;

// Attempt to interpret the key as a hash.
reply = redisCommand(c, "HGET number field1");

// This gives an error reply.
if (reply->type == REDIS_REPLY_ERROR) {
    // Report the error.
    printf("Error reply\n");
    printf("Reply: %s\n", reply->str);
    // >>> Reply: WRONGTYPE Operation against a key holding the wrong kind of value
}

freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "GET number");

// This gives a simple string reply.
if (reply->type == REDIS_REPLY_STRING) {
    // Display the string.
    printf("Simple string reply\n");
    printf("Reply: %s\n", reply->str); // >>> Reply: 1.5
}

freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "ZADD prices 1.75 bread 5.99 beer");

// This gives an integer reply.
if (reply->type == REDIS_REPLY_INTEGER) {
    // Display the integer.
    printf("Integer reply\n");
    printf("Number added: %lld\n", reply->integer);
    // >>> Number added: 2
}

freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "ZSCORE prices bread");

// This gives a string reply with RESP2 and a double reply
// with RESP3, but you handle it the same way in either case.
if (reply->type == REDIS_REPLY_STRING) {
    printf("String reply\n");

    char *endptr; // Not used.
    double price = strtod(reply->str, &endptr);
    double discounted = price * 0.75;
    printf("Discounted price: %.2f\n", discounted);
    // >>> Discounted price: 1.31
}

freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "RPUSH things thing0 thing1 thing2 thing3");

printf("Added %lld items\n", reply->integer);
// >>> Added 4 items
freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "LRANGE things 0 -1");

for (int i = 0; i < reply->elements; ++i) {
    if (reply->element[i]->type == REDIS_REPLY_STRING) {
        printf("List item %d: %s\n", i, reply->element[i]->str);
    }
}
// >>> List item 0: thing0
// >>> List item 1: thing1
// >>> List item 2: thing2
// >>> List item 3: thing3

const char *hashCommand[] = {
    "HSET", "details",
    "name", "Mr Benn",
    "address", "52 Festive Road",
    "hobbies", "Cosplay"
};

reply = redisCommandArgv(c, 8, hashCommand, NULL);

printf("Added %lld fields\n", reply->integer);
// >>> Added 3 fields

freeReplyObject(reply);
reply = NULL;

reply = redisCommand(c, "HGETALL details");

// This gives an array reply with RESP2 and a map reply with
// RESP3, but you handle it the same way in either case.
if (reply->type == REDIS_REPLY_ARRAY) {
    for (int i = 0; i < reply->elements; i += 2) {
        char *key = reply->element[i]->str;
        char *value = reply->element[i + 1]->str;
        printf("Key: %s, value: %s\n", key, value);
    }
    // >>> Key: name, value: Mr Benn
    // >>> Key: address, value: 52 Festive Road
    // >>> Key: hobbies, value: Cosplay
}


hiredis guide (C)

Summary: Connect your C application to a Redis database.

Summary: Connect to the server with `hiredis`.

Connect

Summary: Construct commands and send them to the Redis server.

Issue commands

Summary: Handle command replies with `hiredis`.

Handle command replies

Summary: Learn how to use Redis pipelines and transactions

Pipelines and transactions

Summary: Integrate hiredis with C++ and external frameworks.

Integration guides

Build and install

Clone or download the `hiredis` source from the [Github repository](https://github.com/redis/hiredis).
Then, in a terminal, go into the `hiredis` folder and run the `make` command to build
the dynamically-loaded library for `hiredis` (this has the name `libhiredis.dylib` on
MacOS and `libhiredis.so` on Linux). You can copy this library to your
project folder or run `sudo make install` to install it to `/usr/local/lib`.

Connect and test

The code in the example below connects to the server, stores and retrieves
a string key using [`SET`]() and
[`GET`](), and then finally closes the
connection. An explanation of the code follows the example.

#### Code Examples

**Redis CLI:**

[code example]

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

**C:**

[code example]

**C# (Asynchronous):**

[code example]

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java:**

[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]

**Ruby:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

For a real project, you would build your code with a makefile, but for
this simple test, you can just place it in a file called `main.c` and
build it with the following command. (If you didn't install `hiredis`
using `make install`, then you should also use the `-I` option to
specify the folder that contains the `hiredis` headers.)

[code example]

The default executable filename is `a.out`. If you run this file from
the terminal, you should see the following output:

[code example]

The code first uses `redisConnect()` to open the connection for
all subsequent commands to use. See
[Connect]() for
more information about connecting to Redis.

The `redisCommand()` function
issues commands to the server, each of which returns a
`redisReply` pointer. Here, the reply is a string, which you can
access using the `str` field of the reply. The `redisCommand()`
call allocates memory for the reply, so you should free this
with `freeReplyObject()` when you have finished using it.
See [Issue commands]()
and [Handle replies]()
for more information.

Finally, you should close the connection to Redis with a
call to `redisFree()`. This is not strictly necessary
for this short test program, but real-world code will typically
open and use many connections. You must free them after using them
to prevent errors.

More information

The [`hiredis`](https://github.com/redis/hiredis) Github repository contains
examples and details that may be useful if you are using `hiredis` to
implement a higher-level client for another programming language. There are
also examples showing how to use `hiredis` adapter headers to integrate with
various event handling frameworks.

See the other pages in this section for more information and examples.

#include <stdio.h>
#include <stdlib.h>

#include <hiredis/hiredis.h>

int main() {
    // The `redisContext` type represents the connection
    // to the Redis server. Here, we connect to the
    // default host and port.
    redisContext *c = redisConnect("127.0.0.1", 6379);

    // Check if the context is null or if a specific
    // error occurred.
    if (c == NULL || c->err) {
        if (c != NULL) {
            printf("Error: %s\n", c->errstr);
            // handle error
        } else {
            printf("Can't allocate redis context\n");
        }

        exit(1);
    }

    // Set a string key.
    redisReply *reply = redisCommand(c, "SET foo bar");
    printf("Reply: %s\n", reply->str); // >>> Reply: OK
    freeReplyObject(reply);

    // Get the key we have just stored.
    reply = redisCommand(c, "GET foo");
    printf("Reply: %s\n", reply->str); // >>> Reply: bar
    freeReplyObject(reply);

    // Close the connection.
    redisFree(c);
}

var muxer = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
        var db = muxer.GetDatabase();

var muxer = ConnectionMultiplexer.Connect("localhost:6379");
        var db = muxer.GetDatabase();

rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password
		DB:       0,  // use default DB
		Protocol: 2,
	})

	ctx := context.Background()

RedisURI uri = RedisURI.Builder
                .redis("localhost", 6379)
                .build();

        RedisClient client = RedisClient.create(uri);
        StatefulRedisConnection<String, String> connection = client.connect();
        RedisCommands<String, String> commands = connection.sync();

RedisClient jedis = new RedisClient("redis://localhost:6379");

import { createClient } from 'redis';

const client = createClient();

client.on('error', err => console.log('Redis Client Error', err));

await client.connect();

import { Redis } from 'ioredis';

const redis = new Redis();

<?php

require 'vendor/autoload.php';

use Predis\Client as PredisClient;

$r = new PredisClient([
                'scheme'   => 'tcp',
                'host'     => '127.0.0.1',
                'port'     => 6379,
                'password' => '',
                'database' => 0,
            ]);

import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => {
                match client.get_multiplexed_async_connection().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => {
                match client.get_connection() {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };


Integration guides

Summary: Use `hiredis` in conjunction with the Qt app framework.

Integrate hiredis with a Qt app

Summary: Use `hiredis` in conjunction with the `libevent` framework.

Integrate hiredis with a libevent app

Content

`hiredis` is compatible with C++ and the library source includes a set of
[adapters](https://github.com/redis/hiredis/tree/master/adapters)
to help you use it in conjunction with C and C++ libraries and frameworks.
The pages in this section explain how to integrate `hiredis` into
your app.


Integrate hiredis with a libevent app

Overview

The [`libevent`](https://libevent.org/) library provides an
implementation of an event loop that lets you call functions
asynchronously in response to events. This guide explains
how to use `hiredis` to connect to a Redis server from a
`libevent` app.

Install `libevent`

The [`libevent` home page](https://libevent.org/) has links to download
all versions of the library, but you should use the latest version
unless there is a specific version you need to target.

When you have downloaded `libevent`, follow the instructions in the
`README` file to compile and install the library.

Create a simple app

For a real project, you would build your code with a makefile, but for
this simple test, you can just place it in a file called `main.c` and
build it with the following command (assuming you used `make install` to
install the `libhiredis` and `libevent` libraries):

[code example]

See [Build and install]()
to learn how to build `hiredis`, if you have not already done so.

Now, add the following code in `main.c`. An explanation follows the
code example:

[code example]

The code calls
[`event_base_new()`](https://libevent.org/doc/event_8h.html#af34c025430d445427a2a5661082405c3)
to initialize the core
[`event_base`](https://libevent.org/doc/structevent__base.html)
object that manages the event loop. It then creates a standard
[asynchronous connection]()
to Redis and uses the `libevent` adapter function `redisLibeventAttach()` to
attach the connection to the event loop.

After setting the [connection callbacks](), the code issues two asynchronous
Redis commands (see
[Construct asynchronous commands]()
for more information).
The final step is to call
[`event_base_dispatch()`](https://libevent.org/doc/event_8h.html#a19d60cb72a1af398247f40e92cf07056)
to start the event loop. This will wait for the commands to be processed and
then exit when the Redis connection is closed in the `getCallback()` function.

Run the code

If you compile and run the code, you will see the following output,
showing that the callbacks executed correctly:

[code example]

You can use the
[`KEYS`]() command from
[`redis-cli`]() or
[Redis Insight]() to check
that the "testkey" string key was added to the Redis database.

cc main.c -L/usr/local/lib -lhiredis -levent

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <hiredis/hiredis.h>
#include <hiredis/async.h>
#include <hiredis/adapters/libevent.h>

// Callback for the `GET` command.
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
    redisReply *reply = r;
    char *key = privdata;

    if (reply == NULL) {
        if (c->errstr) {
            printf("errstr: %s\n", c->errstr);
        }
        return;
    }

    printf("Key: %s, value: %s\n", key, reply->str);

    /* Disconnect after receiving the reply to GET */
    redisAsyncDisconnect(c);
}

// Callback to respond to successful or unsuccessful connection.
void connectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Error: %s\n", c->errstr);
        return;
    }
    printf("Connected...\n");
}

// Callback to respond to intentional or unexpected disconnection.
void disconnectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Error: %s\n", c->errstr);
        return;
    }
    printf("Disconnected...\n");
}

int main (int argc, char **argv) {
#ifndef _WIN32
    signal(SIGPIPE, SIG_IGN);
#endif

    // Create the libevent `event_base` object to track all
    // events.
    struct event_base *base = event_base_new();

    redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);

    if (c->err) {
        printf("Error: %s\n", c->errstr);
        return 1;
    }

    // Use the Redis libevent adapter to attach the Redis connection
    // to the libevent main loop.
    redisLibeventAttach(c,base);

    redisAsyncSetConnectCallback(c, connectCallback);
    redisAsyncSetDisconnectCallback(c, disconnectCallback);

    char *key = "testkey";
    char *value = "testvalue";

    redisAsyncCommand(c, NULL, NULL, "SET %s %s", key, value);
    redisAsyncCommand(c, getCallback, key, "GET %s", key);

    // Run the event loop.
    event_base_dispatch(base);

    return 0;
}

Connected...
Key: testkey, value: testvalue
Disconnected...


Integrate hiredis with a Qt app

Overview

[Qt](https://www.qt.io/) is a popular cross-platform C++ framework that
you can use to build command line and GUI apps. This guide explains how
to use `hiredis` to connect to a Redis server from a Qt app.

Install Qt

You should first download and install the
[Qt development environment](https://www.qt.io/download-dev) for your
development platform, if you have not already done so. The example
below briefly explains how to use Qt Creator
to manage your project, but see the [Qt Creator](https://doc.qt.io/qtcreator/)
docs for an extensive set of examples and tutorials.

Create a simple app

We will use a simple console app to demonstrate how to connect
to Redis from Qt. Create the app project in Qt Creator using the
**File > New Project** command. The generated source code is a
single C++ file, called `main.cpp`, that uses a
[`QCoreApplication`](https://doc.qt.io/qt-6/qcoreapplication.html)
object to handle the main event loop. Although it will compile and run,
it doesn't do anything useful at this stage.

Add `hiredis` files

Build `hiredis` if you have not already done so (see
[Build and install]()
for more information).

You should also make the `libhiredis` library available to the project. For example,
if you have used the default option of [`cmake`](https://cmake.org/) as the project
build tool and you have installed the `.dylib` or `.so` file for `hiredis` in `/usr/local/lib`,
you should add the following lines to the `CMakeLists.txt` file:

[code example]

You should also modify the `target_link_libraries` directive to include
`hiredis`:

[code example]

Add code to access Redis

You can add a class using the **Add new** context menu
on the project folder in Qt Creator. The sections below give
examples of the code you should add to this class to
connect to Redis. The code is separated into header and
implementation files.

Header file

The header file for a class called `RedisExample` is shown below.
An explanation follows the code.

[code example]

[`QObject`](https://doc.qt.io/qt-6/qobject.html) is a key Qt class that
implements the [Object model](https://doc.qt.io/qt-6/object.html) for
communication between objects. When you create your class in Qt Creator,
you can specify that you want it to be a subclass of `QObject` (this will
add the appropriate header files and include the `Q_OBJECT` macro in the
class declaration).

The `QObject` communication model uses some instance methods as *signals*
to report events and others as *slots* to act as callbacks that process the
events (see [Signals and slots](https://doc.qt.io/qt-6/signalsandslots.html)
for an introduction). The Qt [meta-object compiler](https://doc.qt.io/qt-6/moc.html)
recognizes the non-standard C++ access specifiers `signals:` and `slots:`  in the
class declaration and adds extra code for them during compilation to enable
the communication mechanism.

In our class, there is a `run()` slot that will implement the code to access Redis.
The code eventually emits a `finished()` signal when it is complete to indicate that
the app should exit.

Our simple example code just sets and gets a Redis
[string]() key. The class contains
private attributes for the key and value (following the Qt `m_xxx` naming convention
for class members). These are set by the constructor along with a call to the
`QObject` constructor. The other attributes represent the connection context for
Redis (which should generally be
[asynchronous]()
for a Qt app) and an adapter object that `hiredis` uses to integrate with Qt.

Implementation file

The file that implements the methods declared in the header is shown
below. A full explanation follows the code.

[code example]

The code that accesses Redis is in the `run()` method (recall that this
implements a Qt slot that will be called in response to a signal). The
code connects to Redis and stores the connection context pointer in the
`m_ctx` attribute of the class instance. The call to `m_adapter.setContext()`
initializes the Qt support for the context. Note that we need an
asynchronous connection for Qt. See
[Asynchronous connection]()
for more information.

The code then issues two Redis commands to [`SET`]()
the string key and value that were supplied using the class's constructor. We are
not interested in the response returned by this command, but we are interested in the
response from the [`GET`]() command that follows it.
Because the commands are asynchronous, we need to set a callback to handle
the `GET` response when it arrives. In the `redisAsyncCommand()` call, we pass
a pointer to our `getCallback()` function and also pass a pointer to the
`RedisExample` instance. This is a custom data field that will simply
be passed on to the callback when it executes (see
[Construct asynchronous commands]()
for more information).

The code in the `getCallback()` function starts by casting the reply pointer
parameter to [`redisReply`]()
and the custom data pointer to `RedisExample`. Here, the example just prints
the reply string to the console, but you can process it in any way you like.
You can add methods to your class and call them within the callback using the
custom data pointer passed during the `redisAsyncCommand()` call. Here, we
simply use the pointer to call the `finish()` method.

The `finish()` method calls
`redisAsyncDisconnect()` to close the connection and then uses the
Qt signalling mechanism to emit the `finished()` signal. You may need to
process several commands with a particular connection context, but you should
close it from a callback when you have finished using it.

Main program

To access the `RedisExample` class, you should use code like the
following in the `main()` function defined in `main.cpp`:

[code example]

This creates the [`QCoreApplication`](https://doc.qt.io/qt-6/qcoreapplication.html)
instance that manages the main event loop for a console app. It
then creates the instance of `RedisExample` with the key ("url") and
value ("https://redis.io/") for our Redis string.

The two lines below set up the `QObject` communication mechanism
for the app. The call to
[`QTimer::singleShot()`](https://doc.qt.io/qt-6/qtimer.html#singleShot-2)
activates the `run()`
slot method on our `RedisExample` instance. The
[`QObject::connect()`](https://doc.qt.io/qt-6/qobject.html#connect-5)
call creates a communication link between the `finished()` signal of
out `RedisExample` instance and the `quit()` slot of our
`QCoreApplication` instance. This quits the application event loop and
exits the app when the `finished()` signal is emitted by the
`RedisExample` object. This happens when the `finish()` method is called
at the end of the `GET` command callback.

Run the code

When you have added the code, you can run it from the **Build** menu of
Qt Creator or from the toolbar at the left hand side of the window.
Assuming the connection to Redis succeeds, it will print the message
`Value: https://redis.io/` and quit. You can use the
[`KEYS`]() command from
[`redis-cli`]() or
[Redis Insight]() to check
that the "url" string key was added to the Redis database.

Key information

There are many ways you could use Redis with a Qt app, but our example
demonstrates some techniques that are broadly useful:

-   Use the `QObject` communication mechanism to simplify your code.
-   Use the `hiredis` asynchronous API. Add a `RedisQtAdapter` instance
    to your code and ensure you call its `setContext()` method to
    initialize it before issuing Redis commands.
-   Place all code and data you need to interact with Redis
    (including the connection context) in a single
    class or ensure it is available from a class via pointers and
    Qt signals. Pass a pointer to an instance of your class in the
    custom data parameter when you issue a Redis command with
    `redisAsyncCommand()` and use this to process the reply or
    issue more commands from the callback.

add_library(hiredis SHARED IMPORTED)
set_property(TARGET hiredis PROPERTY
             IMPORTED_LOCATION "/usr/local/lib/libhiredis.dylib")

// redisexample.h

#ifndef REDISEXAMPLE_H
#define REDISEXAMPLE_H

#include <QObject>

#include <hiredis/hiredis.h>
#include <hiredis/async.h>
#include <hiredis/adapters/qt.h>

class RedisExample : public QObject
{
    Q_OBJECT

public:
    // Constructor
    RedisExample(const char *keyForRedis, const char *valueForRedis, QObject *parent = 0)
        :QObject(parent), m_key(keyForRedis), m_value(valueForRedis) {}

public slots:
    // Slot method to hold the code that connects to Redis and issues
    // commands.
    void run();

signals:
    // Signal to indicate that our code has finished executing.
    void finished();

public:
    // Method to close the Redis connection and signal that we've
    // finished.
    void finish();

private:
    const char *m_key;          // Key for Redis string.
    const char *m_value;        // Value for Redis string.
    redisAsyncContext *m_ctx;   // Redis connection context.
    RedisQtAdapter m_adapter;   // Adapter to let `hiredis` work with Qt.
};

#endif // REDISEXAMPLE_H

// redisexample.cpp

#include <iostream>

#include "redisexample.h"

void RedisExample::finish() {
    // Disconnect gracefully.
    redisAsyncDisconnect(m_ctx);

    // Emit the `finished()` signal to indicate that the
    // execution is complete.
    emit finished();
}

// Callback used by our `GET` command in the `run()` method.
void getCallback(redisAsyncContext *, void * r, void * privdata) {

    // Cast data pointers to their appropriate types.
    redisReply *reply = static_cast<redisReply *>(r);
    RedisExample *ex = static_cast<RedisExample *>(privdata);

    if (reply == nullptr || ex == nullptr) {
        return;
    }

    std::cout << "Value: " << reply->str << std::endl;

    // Close the Redis connection and quit the app.
    ex->finish();
}

void RedisExample::run() {
    // Open the connection to Redis.
    m_ctx = redisAsyncConnect("localhost", 6379);

    if (m_ctx->err) {
        std::cout << "Error: " << m_ctx->errstr << std::endl;
        finish();
    }

    // Configure the connection to work with Qt.
    m_adapter.setContext(m_ctx);

    // Issue some simple commands. For the `GET` command, pass a
    // callback function and a pointer to this object instance
    // so that we can access the object's members from the callback.
    redisAsyncCommand(m_ctx, NULL, NULL, "SET %s %s", m_key, m_value);
    redisAsyncCommand(m_ctx, getCallback, this, "GET %s", m_key);
}

#include <QCoreApplication>
#include <QTimer>

#include "redisexample.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    // Instance of our object.
    RedisExample r("url", "https://redis.io/");

    // Call the `run()` slot on our `RedisExample` instance to
    // run our Redis commands.
    QTimer::singleShot(0, &r, SLOT(run()));

    // Set up a communication connection between our `finished()`
    // signal and the application's `quit()` slot.
    QObject::connect(&r, SIGNAL(finished()), &app, SLOT(quit()));

    // Start the app's main event loop.
    return app.exec();
}


Issue commands

Overview

Unlike the other [client libraries](),
`hiredis` doesn't provide an extensive API to construct the many different
Redis [commands](). However, it does provide a lightweight and
flexible API to help you construct commands and parse their replies from
your own code.

The sections below describe the available functions in
detail.

Construct synchronous commands

Use the `redisCommand()` function to send commands to the server:

[code example]

This function receives a `redisContext` pointer and a pointer
to a string containing the command (see
[Connect]()
to learn how to obtain the context pointer). The command text is the
same as the equivalent [`redis-cli`]()
command. For example, to issue the command:

[code example]

you would use the following command with an existing `redisContext* c`:

[code example]

See the [Command reference]() for examples
of CLI commands that you can use with `hiredis`. Most code examples
in other sections of the docs also have a CLI tab showing
command sequences that are equivalent to the code.

The command string is interpreted in a similar way to the format
string for `printf()`, so you can easily interpolate string values from
your code into the command with the `%s` format specifier:

[code example]

You may need to include binary data in the command (for example, to store
[vector embeddings]()
in fields of a [hash]()) object.
To do this, use the `%b` format specifier and pass a pointer to the
data buffer, followed by a `size_t` value indicating its length in bytes.
As the example below shows, you can freely mix `%s` and `%b` specifiers
in the same format string. Also, you can use the sequence `%%` to
denote a literal percent sign, but the other `printf()` specifiers,
such as `%d`, are not supported.

[code example]

The `redisCommand()` function has a variant called `redisCommandArgv()`:

[code example]

This doesn't take a format string but instead builds the command from an array
of strings passed in the `argv` parameter.

Use the `argc` value to
specify the length of this array and the `argvlen` array to specify
the lengths of each of the strings in the array. If you pass `NULL`
for `argvlen` then the function will attempt to use `strlen()` to
get the length of each string. However, this will not work if any of
the strings contains binary data, so you should pass `argvlen`
explicitly in this case. The example below shows how to use
`redisCommandArgv()` with a simple command:

[code example]

Construct asynchronous commands

Use the `redisAsyncCommand()` and `redisAsyncCommandArgv()`
functions to send commands to the server asynchronously:

[code example]

These work the same way as `redisCommand()` and `redisCommandArgv()`
(see [Construct synchronous commands](#construct-synchronous-commands)
above) but they have two extra parameters. The first is a pointer to
a optional callback function and the second is a pointer to your own
custom data, which will be passed to the callback when it
executes. Pass `NULL` for both of these pointers if you don't need
to use them.

The callback has the following signature:

[code example]

The first parameter is the asynchronous connection context and
the second is a pointer to the reply object. Use a cast to
`(redisReply *)` to access the reply in the usual way (see
[Handle command replies]()
for a full description of `redisReply`). The last parameter
is the custom data pointer that you supplied during the
`redisAsyncCommand()` call. This is passed to your function
without any modification.

The example below shows how you can use `redisAsyncCommand()` with
or without a reply callback:

[code example]

Note that you should normally disconnect asynchronously from a
callback when you have finished using the connection.
Use `redisAsyncDisconnect()` to disconnect gracefully, letting
pending commands execute and activate their callbacks.
Use `redisAsyncFree()` to disconnect immediately. If you do this then
any pending callbacks from commands that have already executed will be
called with a `NULL` reply pointer.

Command replies

The information in the `redisReply` object has several formats,
and the format for a particular reply depends on the command that generated it.
See
[Handle replies]()
to learn about the different reply formats and how to use them.

void *redisCommand(redisContext *c, const char *format, ...);

redisReply *reply = redisCommand(c, "SET foo bar");

char *myKeyNumber = "1";
char *myValue = "Hello";

// This issues the command 'SET key:1 Hello'.
redisReply *reply = redisCommand(c, "SET key:%s %s", myKeyNumber, myValue);

char *entryNumber = "1";
char *embedding = "<binary data>";
char *url = "https://redis.io/";
size_t embLength = 13;

redisReply *reply = redisCommand(c,
    "HSET entry:%s embedding %b  url %s",
    entryNumber,
    embedding, embLength,
    url
);

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

const char *argv[3] = { "SET", "greeting", "hello"};
int argc = 3;
const size_t argvlen[] = {3, 8, 5};

redisReply *reply = redisCommandArgv(c, argc, argv, argvlen);

#include <hiredis/async.h>
    .
    .
    .
int redisAsyncCommand(
  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
  const char *format, ...);
int redisAsyncCommandArgv(
  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
  int argc, const char **argv, const size_t *argvlen);

void(redisAsyncContext *c, void *reply, void *privdata);

// The callback expects the key for the data in the `privdata`
// custom data parameter.
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
    redisReply *reply = r;
    char *key = privdata;

    if (reply == NULL) {
        if (c->errstr) {
            printf("errstr: %s\n", c->errstr);
        }
        return;
    }

    printf("Key: %s, value: %s\n", key, reply->str);

    /* Disconnect after receiving the reply to GET */
    redisAsyncDisconnect(c);
}
    .
    .
    .

// Key and string value to pass to `SET`.
char *key = "testkey";
char *value = "testvalue";

// We aren't interested in the simple status reply for
// `SET`, so use NULL for the callback and custom data
// pointers.
redisAsyncCommand(c, NULL, NULL, "SET %s %s", key, value);

// The reply from `GET` is essential, so set a callback
// to retrieve it. Also, pass the key to the callback
// as the custom data.
redisAsyncCommand(c, getCallback, key, "GET %s", key);


Pipelines and transactions

Overview

Redis lets you send a sequence of commands to the server together in a batch.
There are two types of batch that you can use:

-   **Pipelines** avoid network and processing overhead by sending several commands
    to the server together in a single communication. The server then sends back
    a single communication with all the responses. See the
    [Pipelining]() page for more
    information.
-   **Transactions** guarantee that all the included commands will execute
    to completion without being interrupted by commands from other clients.
    See the [Transactions]()
    page for more information.

Execute a pipeline

There is no command to explicitly start a pipeline with `hiredis`,
but if you issue a command with the `redisAppendCommand()` function,
it will be added to an output buffer without being sent
immediately to the server.

There is also an input buffer that receives replies from
commands. If you call `redisGetReply()` when the input buffer is empty,
it will first send any commands that are queued in the output buffer and
then wait for replies to arrive in the input buffer. It will then return
the first reply only.

If you then make subsequent `redisGetReply()` calls, they will
find the input buffer is not empty, but still has replies
queued from previous commands. In this case, `redisGetReply()`
will just remove and return replies from the input buffer
until it is empty again.

The example below shows how to use `redisAppendCommand()`
and `redisGetReply()` together:

[code example]

`redisAppendCommand()` has the same call signature as `redisCommand()` except that
it doesn't return a `redisReply`. There is also a `redisAppendCommandArgv()`
function that is analogous to `redisCommandArgv()` (see
[Issue commands]()
for more information).

`redisGetReply()` receives the usual
context pointer and a pointer to a `redisReply` pointer (which you
must cast to `void**`). After `redisGetReply()` returns,
the reply pointer will point to the `redisReply` object returned by
the queued command (see
[Handle command replies]()
for more information).

Call `redisGetReply()` once for each command that you added to the pipeline.
You should check for errors after each call and free each reply object
when you have finished processing it, as in the example above.

Transactions

`hiredis` doesn't provide any special API to handle transactions, but
you can implement them yourself using the [`MULTI`](),
[`EXEC`](), and [`WATCH`]()
commands as you would from [`redis-cli`]().
See [Transactions]()
for more information.

redisAppendCommand(c, "SET fruit:0 Apple");
redisAppendCommand(c, "SET fruit:1 Banana");
redisAppendCommand(c, "SET fruit:2 Cherry");

redisAppendCommand(c, "GET fruit:0");
redisAppendCommand(c, "GET fruit:1");
redisAppendCommand(c, "GET fruit:2");

redisReply *reply;

// Iterate once for each of the six commands in the
// pipeline.
for (int i = 0; i < 6; ++i) {
    redisGetReply(c, (void**) &reply);

    // If an error occurs, the context object will
    // contain an error code and/or an error string.
    if (reply->type == REDIS_REPLY_ERROR) {
        printf("Error: %s", c->errstr);
    } else {
        printf("%s\n", reply->str);
    }

    freeReplyObject(reply);
}
// >>> OK
// >>> OK
// >>> OK
// >>> Apple
// >>> Banana
// >>> Cherry