{
  "schema_version": 2,
  "id": "develop/clients/redis-py/vecsearch",
  "title": "Index and query vectors",
  "url": "https://redis.io/docs/latest/develop/clients/redis-py/vecsearch/",
  "summary": "Learn how to index and query vector embeddings with Redis",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-10-16T16:44:30+01:00",
  "page_type": "content",
  "content_hash": "eddce63e49a6fc4618cd43b4011826b90ce6e79fff60d5559c179f6aa631775c",
  "sections": [
    {
      "id": "initialize",
      "title": "Initialize",
      "role": "content",
      "text": "Install [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) if you\nhave not already done so. Also, install `sentence-transformers` with the\nfollowing command:\n\n[code example]\n\nIn a new Python source file, start by importing the required classes:\n\nFoundational: Import required libraries for vector embeddings, Redis operations, and search functionality\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe first of these imports is the\n`SentenceTransformer` class, which generates an embedding from a section of text.\nHere, we create an instance of `SentenceTransformer` that uses the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel for the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nFoundational: Initialize a SentenceTransformer model to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"
    },
    {
      "id": "create-the-index",
      "title": "Create the index",
      "role": "content",
      "text": "Connect to Redis and delete any index previously created with the\nname `vector_idx`. (The `dropindex()` call throws an exception if\nthe index doesn't already exist, which is why you need the\n`try: except:` block.)\n\nFoundational: Connect to a Redis server and clean up existing vector indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nNext, create the index.\nThe schema in the example below specifies hash objects for storage and includes\nthree fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nFoundational: Create a vector search index for hash documents with HNSW algorithm and L2 distance metric\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"
    },
    {
      "id": "add-data",
      "title": "Add data",
      "role": "content",
      "text": "You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`hset()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nUse the `model.encode()` method of `SentenceTransformer`\nas shown below to create the embedding that represents the `content` field.\nThe `astype()` option that follows the `model.encode()` call specifies that\nwe want a vector of `float32` values. The `tobytes()` option encodes the\nvector components together as a single binary string.\nUse the binary string representation when you are indexing hashes\nor running a query (but use a list of `float` for\n[JSON documents](#differences-with-json-documents)).\n\nFoundational: Store hash documents with vector embeddings generated from text content\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"
    },
    {
      "id": "bulk-import-with-himport",
      "title": "Bulk import with HIMPORT",
      "role": "content",
      "text": "From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`himport_prepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`himport_set()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/redis-py/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\n`himport_set()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `redis-py` v8.1.0 or later, and `redis-py` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."
    },
    {
      "id": "run-a-query",
      "title": "Run a query",
      "role": "content",
      "text": "After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the similarity between the query vector and each\nembedding vector in the index as it runs the query. It then ranks the\nresults in order of this numeric similarity value.\n\nThe code below creates the query embedding using `model.encode()`, as with\nthe indexing, and passes it as a parameter when the query executes\n(see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\n\nVector similarity search: Find semantically similar documents by comparing query embeddings with indexed vectors using L2 distance\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe code is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because `sentence-transformers` must download the\n`all-MiniLM-L6-v2` model data before it can\ngenerate the embeddings). When you run the code, it outputs the following result\nobject (slightly formatted here for clarity):\n\n[code example]\n\nThe exact distances may differ slightly on your system.\n\nNote that the results are ordered according to the value of the `vector_distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs you would expect, the result for `doc:0` with the content text *\"That is a very happy person\"*\nis the result that is most similar in meaning to the query text\n*\"That is a happy person\"*."
    },
    {
      "id": "differences-with-json-documents",
      "title": "Differences with JSON documents",
      "role": "content",
      "text": "Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modelling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths (using the `as_name` keyword argument) to avoid typing it in full for\nevery query. Also, you must specify `IndexType.JSON` when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\nFoundational: Create a vector search index for JSON documents with JSON paths and field aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nUse [`json().set()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`hset()`](https://redis.io/docs/latest/commands/hset). The dictionaries\nthat specify the fields have the same structure as the ones used for `hset()`\nbut `json().set()` receives them in a positional argument instead of\nthe `mapping` keyword argument.\n\nAn important difference with JSON indexing is that the vectors are\nspecified using lists instead of binary strings. Generate the list\nusing the `tolist()` method instead of `tobytes()` as you would with a\nhash.\n\nFoundational: Store JSON documents with vector embeddings as lists (different from hash binary format)\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (using the `tobytes()` method), even though the data for\nthe `embedding` field of the JSON was specified as a list.\n\nVector similarity search: Query JSON documents using vector embeddings with field aliases for simplified syntax\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"
    },
    {
      "id": "learn-more",
      "title": "Learn more",
      "role": "related",
      "text": "See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."
    }
  ],
  "examples": [
    {
      "id": "initialize-ex0",
      "language": "bash",
      "code": "pip install sentence-transformers",
      "section_id": "initialize"
    },
    {
      "id": "initialize-ex1",
      "language": "python",
      "code": "from sentence_transformers import SentenceTransformer\nfrom redis.commands.search.query import Query\nfrom redis.commands.search.field import TextField, TagField, VectorField\nfrom redis.commands.search.index_definition import IndexDefinition, IndexType\nfrom redis.commands.json.path import Path\n\nimport numpy as np\nimport redis",
      "section_id": "initialize"
    },
    {
      "id": "initialize-ex2",
      "language": "python",
      "code": "model = SentenceTransformer(\"sentence-transformers/all-MiniLM-L6-v2\")",
      "section_id": "initialize"
    },
    {
      "id": "create-the-index-ex0",
      "language": "python",
      "code": "r = redis.Redis(decode_responses=True)\n\ntry:\n    r.ft(\"vector_idx\").dropindex(True)\nexcept redis.exceptions.ResponseError:\n    pass",
      "section_id": "create-the-index"
    },
    {
      "id": "create-the-index-ex1",
      "language": "python",
      "code": "schema = (\n    TextField(\"content\"),\n    TagField(\"genre\"),\n    VectorField(\"embedding\", \"HNSW\", {\n        \"TYPE\": \"FLOAT32\",\n        \"DIM\": 384,\n        \"DISTANCE_METRIC\":\"L2\"\n    })\n)\n\nr.ft(\"vector_idx\").create_index(\n    schema,\n    definition=IndexDefinition(\n        prefix=[\"doc:\"], index_type=IndexType.HASH\n    )\n)",
      "section_id": "create-the-index"
    },
    {
      "id": "add-data-ex0",
      "language": "python",
      "code": "content = \"That is a very happy person\"\n\nr.hset(\"doc:0\", mapping={\n    \"content\": content,\n    \"genre\": \"persons\",\n    \"embedding\": model.encode(content).astype(np.float32).tobytes(),\n})\n\ncontent = \"That is a happy dog\"\n\nr.hset(\"doc:1\", mapping={\n    \"content\": content,\n    \"genre\": \"pets\",\n    \"embedding\": model.encode(content).astype(np.float32).tobytes(),\n})\n\ncontent = \"Today is a sunny day\"\n\nr.hset(\"doc:2\", mapping={\n    \"content\": content,\n    \"genre\": \"weather\",\n    \"embedding\": model.encode(content).astype(np.float32).tobytes(),\n})",
      "section_id": "add-data"
    },
    {
      "id": "bulk-import-with-himport-ex0",
      "language": "python",
      "code": "r.himport_prepare(\"doc\", [\"content\", \"genre\", \"embedding\"])\n\ndocs = [\n    (\"doc:0\", \"That is a very happy person\", \"persons\"),\n    (\"doc:1\", \"That is a happy dog\", \"pets\"),\n    (\"doc:2\", \"Today is a sunny day\", \"weather\"),\n]\n\nwith r.pipeline(transaction=False) as pipe:\n    for key, content, genre in docs:\n        pipe.himport_set(key, \"doc\", [\n            content,\n            genre,\n            model.encode(content).astype(np.float32).tobytes(),\n        ])\n    pipe.execute()",
      "section_id": "bulk-import-with-himport"
    },
    {
      "id": "run-a-query-ex0",
      "language": "python",
      "code": "q = Query(\n    \"*=>[KNN 3 @embedding $vec AS vector_distance]\"\n).return_field(\"vector_distance\").return_field(\"content\").dialect(2)\n\nquery_text = \"That is a happy person\"\n\nres = r.ft(\"vector_idx\").search(\n    q, query_params={\n        \"vec\": model.encode(query_text).astype(np.float32).tobytes()\n    }\n)\n\nprint(res)",
      "section_id": "run-a-query"
    },
    {
      "id": "run-a-query-ex1",
      "language": "plaintext",
      "code": "Result{\n    3 total,\n    docs: [\n        Document {\n            'id': 'doc:0',\n            'payload': None,\n            'vector_distance': '0.114169985056',\n            'content': 'That is a very happy person'\n        },\n        Document {\n            'id': 'doc:1',\n            'payload': None,\n            'vector_distance': '0.610845386982',\n            'content': 'That is a happy dog'\n        },\n        Document {\n            'id': 'doc:2',\n            'payload': None,\n            'vector_distance': '1.48624813557',\n            'content': 'Today is a sunny day'\n        }\n    ]\n}",
      "section_id": "run-a-query"
    },
    {
      "id": "differences-with-json-documents-ex0",
      "language": "python",
      "code": "schema = (\n    TextField(\"$.content\", as_name=\"content\"),\n    TagField(\"$.genre\", as_name=\"genre\"),\n    VectorField(\n        \"$.embedding\", \"HNSW\", {\n            \"TYPE\": \"FLOAT32\",\n            \"DIM\": 384,\n            \"DISTANCE_METRIC\": \"L2\"\n        },\n        as_name=\"embedding\"\n    )\n)\n\nr.ft(\"vector_json_idx\").create_index(\n    schema,\n    definition=IndexDefinition(\n        prefix=[\"jdoc:\"], index_type=IndexType.JSON\n    )\n)",
      "section_id": "differences-with-json-documents"
    },
    {
      "id": "differences-with-json-documents-ex1",
      "language": "python",
      "code": "content = \"That is a very happy person\"\n\nr.json().set(\"jdoc:0\", Path.root_path(), {\n    \"content\": content,\n    \"genre\": \"persons\",\n    \"embedding\": model.encode(content).astype(np.float32).tolist(),\n})\n\ncontent = \"That is a happy dog\"\n\nr.json().set(\"jdoc:1\", Path.root_path(), {\n    \"content\": content,\n    \"genre\": \"pets\",\n    \"embedding\": model.encode(content).astype(np.float32).tolist(),\n})\n\ncontent = \"Today is a sunny day\"\n\nr.json().set(\"jdoc:2\", Path.root_path(), {\n    \"content\": content,\n    \"genre\": \"weather\",\n    \"embedding\": model.encode(content).astype(np.float32).tolist(),\n})",
      "section_id": "differences-with-json-documents"
    },
    {
      "id": "differences-with-json-documents-ex2",
      "language": "python",
      "code": "q = Query(\n    \"*=>[KNN 3 @embedding $vec AS vector_distance]\"\n).return_field(\"vector_distance\").return_field(\"content\").dialect(2)\n\nquery_text = \"That is a happy person\"\n\nres = r.ft(\"vector_json_idx\").search(\n    q, query_params={\n        \"vec\": model.encode(query_text).astype(np.float32).tobytes()\n    }\n)\n\nprint(repr(res))",
      "section_id": "differences-with-json-documents"
    },
    {
      "id": "differences-with-json-documents-ex3",
      "language": "plaintext",
      "code": "Result{\n    3 total,\n    docs: [\n        Document {\n            'id': 'jdoc:0',\n            'payload': None,\n            'vector_distance': '0.114169985056',\n            'content': 'That is a very happy person'\n        },\n            .\n            .\n            .",
      "section_id": "differences-with-json-documents"
    }
  ]
}
