{
  "schema_version": 2,
  "id": "develop/ai/search-and-query/query/vector-search",
  "title": "Vector search",
  "url": "https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search/",
  "summary": "Query for data based on vector embeddings",
  "aliases": [
    "/develop/interact/search-and-query/query/vector-search"
  ],
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-11T15:33:04+01:00",
  "page_type": "content",
  "content_hash": "711daee047d46675ed8a1fd7f0da94ea163dd2cf166377993be21ec30ce0622a",
  "sections": [
    {
      "id": "k-neareast-neighbours-knn",
      "title": "K-neareast neighbours (KNN)",
      "role": "content",
      "text": "The Redis command [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) takes the index name, the query string, and additional query parameters as arguments. You need to pass the number of nearest neighbors, the vector field name, and the vector's binary representation in the following way:\n\n[code example]\n\nHere is a more detailed explanation of this query:\n\n1. **Pre-filter**: The first expression within the round brackets is a filter. It allows you to decide which vectors should be taken into account before the vector search is performed. The expression `(*)` means that all vectors are considered.\n2. **Next step**: The `=>` arrow indicates that the pre-filtering happens before the vector search.\n3. **KNN query**: The expression `[KNN num_neighbours @field $vector]` is a parameterized query expression. A parameter name is indicated by the `$` prefix within the query string.\n4. **Vector binary data**: You need to use the `PARAMS` argument to substitute `$vector` with the binary representation of the vector. The value `2` indicates that `PARAMS` is followed by two arguments, the parameter name `vector` and the parameter value.\n5. **Dialect**: The vector search feature has been available since version two of the query dialect.\n\nYou can read more about the `PARAMS` argument in the [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) command reference.\n\nThe following example shows you how to query for three bikes based on their description embeddings, and by using the field alias `vector`. The result is returned in ascending order based on the distance. You can see that the query only returns the fields `__vector_score` and `description`. The field `__vector_score` is present by default. Because you can have multiple vector fields in your schema, the vector score field name depends on the name of the vector field. If you change the field name `@vector` to `@foo`, the score field name changes to `__foo_score`.\n\nK-nearest neighbors: Find the k closest vectors to a query vector using KNN when you need to retrieve similar items based on embeddings\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n\n\n> [!NOTE]\n> The binary value of the query vector is significantly shortened in the CLI example above."
    },
    {
      "id": "radius",
      "title": "Radius",
      "role": "content",
      "text": "Instead of the number of nearest neighbors, you need to pass the radius along with the index name, the vector field name, and the vector's binary value:\n\n[code example]\n\nIf you want to sort by distance, then you must yield the distance via the range query parameter `$YIELD_DISTANCE_AS`:\n\n[code example]\n\nHere is a more detailed explanation of this query:\n\n1. **Range query**: the syntax of a radius query is very similar to the regular range query, except for the keyword `VECTOR_RANGE`. You can also combine a vector radius query with other queries in the same way as regular range queries.  See [combined queries article](https://redis.io/docs/latest/develop/ai/search-and-query/query/combined) for more details.\n2. **Additional step**: the `=>` arrow means that the range query is followed by evaluating additional parameters.\n3. **Range query parameters**: parameters such as `$YIELD_DISTANCE_AS` can be found in the [vectors reference documentation](https://redis.io/docs/latest/develop/ai/search-and-query/vectors).\n4. **Vector binary data**: you need to use `PARAMS` to pass the binary representation of the vector.\n5. **Dialect**: vector search has been available since version two of the query dialect.\n\n\n> [!NOTE]\n> By default, [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) returns only the first ten results. The [range query article](https://redis.io/docs/latest/develop/ai/search-and-query/query/range) explains to you how to scroll through the result set.\n\nThe example below shows a radius query that returns the description and the distance within a radius of `0.5`. The result is sorted by the distance.\n\nRadius search: Find all vectors within a specified distance threshold using VECTOR_RANGE when you need to retrieve similar items with a distance constraint\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"
    },
    {
      "id": "cluster-optimization",
      "title": "Cluster optimization",
      "role": "content",
      "text": "In Redis cluster environments, you can optimize vector search performance using the `$SHARD_K_RATIO` query attribute. This parameter controls how many results each shard retrieves relative to the requested `top_k`, creating a tunable trade-off between accuracy and performance."
    },
    {
      "id": "basic-cluster-optimization",
      "title": "Basic cluster optimization",
      "role": "content",
      "text": "Retrieve 100 nearest neighbors with each shard providing 60% of the requested results:\n\nCluster optimization: Optimize KNN queries in cluster environments using SHARD_K_RATIO to balance accuracy and performance when you need efficient distributed vector search\n\n**Difficulty:** Advanced\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"
    },
    {
      "id": "combined-with-filtering",
      "title": "Combined with filtering",
      "role": "content",
      "text": "You can combine `$SHARD_K_RATIO` with pre-filtering to optimize searches on specific subsets of data:\n\nFiltered vector search: Combine pre-filtering with KNN and cluster optimization to search within a subset of data when you need to find similar items matching specific criteria\n\n**Difficulty:** Advanced\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n> [!NOTE]\n> The `$SHARD_K_RATIO` parameter is only applicable in Redis cluster environments and has no effect in standalone Redis instances."
    }
  ],
  "examples": [
    {
      "id": "k-neareast-neighbours-knn-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH index \"(*)=>[KNN num_neighbours @field $vector]\" PARAMS 2 vector \"binary_data\" DIALECT 2",
      "section_id": "k-neareast-neighbours-knn"
    },
    {
      "id": "k-neareast-neighbours-knn-ex1",
      "language": "plaintext",
      "code": "FT.SEARCH idx:bikes_vss \"(*)=>[KNN 3 @vector $query_vector]\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" SORTBY \"__vector_score\" ASC RETURN 2 \"__vector_score\" \"description\" DIALECT 2",
      "section_id": "k-neareast-neighbours-knn"
    },
    {
      "id": "k-neareast-neighbours-knn-ex2",
      "language": "javascript",
      "code": "const res1 = await client.ft.search('idx:bicycle', \n  '*=>[KNN 3 @vector $query_vector AS score]', {\n    PARAMS: { query_vector: vector_query },\n    RETURN: ['description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res1.total); // >>> 3\nconsole.log(res1); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:0', value: [Object: null prototype] {} },\n//    { id: 'bicycle:2', value: [Object: null prototype] {} },\n//    { id: 'bicycle:9', value: [Object: null prototype] {} }\n//  ]\n//}",
      "section_id": "k-neareast-neighbours-knn"
    },
    {
      "id": "k-neareast-neighbours-knn-ex3",
      "language": "python",
      "code": "query = \"Bike for small kids\"\nquery_vector = embed_text(model, query)\nprint(query_vector[:10]) # >>> b'\\x02=c=\\x93\\x0e\\xe0=aC'\n\nvquery = VectorQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    num_results=3,\n    return_score=True,\n    return_fields=[\"description\"]\n)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6b702e8b...', 'vector_distance': '0.399111807346', 'description': 'Kids want...",
      "section_id": "k-neareast-neighbours-knn"
    },
    {
      "id": "radius-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH index \"@field:[VECTOR_RANGE radius $vector]\" PARAMS 2 vector \"binary_data\" DIALECT 2",
      "section_id": "radius"
    },
    {
      "id": "radius-ex1",
      "language": "plaintext",
      "code": "FT.SEARCH index \"@field:[VECTOR_RANGE radius $vector]=>{$YIELD_DISTANCE_AS: dist_field}\" PARAMS 2 vector \"binary_data\" SORTBY dist_field DIALECT 2",
      "section_id": "radius"
    },
    {
      "id": "radius-ex2",
      "language": "plaintext",
      "code": "FT.SEARCH idx:bikes_vss \"@vector:[VECTOR_RANGE 0.5 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" SORTBY vector_dist ASC RETURN 2 vector_dist description DIALECT 2",
      "section_id": "radius"
    },
    {
      "id": "radius-ex3",
      "language": "javascript",
      "code": "const res2 = await client.ft.search('idx:bicycle', \n  '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', {\n    PARAMS: { query_vector: vector_query },\n    SORTBY: 'vector_dist',\n    RETURN: ['vector_dist', 'description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ]\n//}",
      "section_id": "radius"
    },
    {
      "id": "radius-ex4",
      "language": "python",
      "code": "vquery = RangeQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    distance_threshold=0.5,\n    return_score=True\n).return_fields(\"description\").dialect(2)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6bcb1bb4...', 'vector_distance': '0.399111807346', 'description': 'Kids want...",
      "section_id": "radius"
    },
    {
      "id": "basic-cluster-optimization-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH idx:bikes_vss \"(*)=>[KNN 100 @vector $query_vector]=>{$SHARD_K_RATIO: 0.6; $YIELD_DISTANCE_AS: vector_distance}\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" SORTBY vector_distance ASC RETURN 2 \"vector_distance\" \"description\" DIALECT 2",
      "section_id": "basic-cluster-optimization"
    },
    {
      "id": "basic-cluster-optimization-ex1",
      "language": "javascript",
      "code": "import assert from 'node:assert';\nimport fs from 'node:fs';\nimport { createClient } from 'redis';\nimport { SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from '@redis/search';\nimport { pipeline } from '@xenova/transformers';\n\nfunction float32Buffer(arr) {\n  const floatArray = new Float32Array(arr);\n  const float32Buffer = Buffer.from(floatArray.buffer);\n  return float32Buffer;\n}\n\nasync function embedText(sentence) {\n  let modelName = 'Xenova/all-MiniLM-L6-v2';\n  let pipe = await pipeline('feature-extraction', modelName);\n\n  let vectorOutput = await pipe(sentence, {\n      pooling: 'mean',\n      normalize: true,\n  });\n\n  const embedding = Object.values(vectorOutput?.data);\n\n  return embedding;\n}\n\nconst vector_query = float32Buffer(await embedText('That is a very happy person'));\n\nconst client = createClient();\nawait client.connect().catch(console.error);\n\n// create index\nawait client.ft.create('idx:bicycle', {\n    '$.description': {\n      type: SCHEMA_FIELD_TYPE.TEXT,\n      AS: 'description'\n    },\n    '$.description_embeddings': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT,\n        DIM: 384,\n        DISTANCE_METRIC: 'COSINE',\n        AS: 'vector'\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'bicycle:'\n});\n\n// load data\nconst bicycles = JSON.parse(fs.readFileSync('data/query_vector.json', 'utf8'));\n\nawait Promise.all(\n  bicycles.map((bicycle, bid) => {\n      return client.json.set(`bicycle:${bid}`, '$', bicycle);\n  })\n);\n\nconst res1 = await client.ft.search('idx:bicycle', \n  '*=>[KNN 3 @vector $query_vector AS score]', {\n    PARAMS: { query_vector: vector_query },\n    RETURN: ['description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res1.total); // >>> 3\nconsole.log(res1); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:0', value: [Object: null prototype] {} },\n//    { id: 'bicycle:2', value: [Object: null prototype] {} },\n//    { id: 'bicycle:9', value: [Object: null prototype] {} }\n//  ]\n//}\n\nconst res2 = await client.ft.search('idx:bicycle', \n  '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', {\n    PARAMS: { query_vector: vector_query },\n    SORTBY: 'vector_dist',\n    RETURN: ['vector_dist', 'description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ]\n//}",
      "section_id": "basic-cluster-optimization"
    },
    {
      "id": "basic-cluster-optimization-ex2",
      "language": "python",
      "code": "import json\nimport warnings\nimport redis\nimport numpy as np\nfrom redisvl.index import SearchIndex\nfrom redisvl.query import RangeQuery, VectorQuery\nfrom redisvl.schema import IndexSchema\nfrom sentence_transformers import SentenceTransformer\n\n\ndef embed_text(model, text):\n    return np.array(model.encode(text)).astype(np.float32).tobytes()\n\nr = redis.Redis(decode_responses=True)\n\nwarnings.filterwarnings(\"ignore\", category=FutureWarning, message=r\".*clean_up_tokenization_spaces.*\")\nmodel = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')\n\n# create index\nschema = IndexSchema.from_yaml('data/query_vector_idx.yaml')\nindex = SearchIndex(schema, r)\nindex.create(overwrite=True, drop=True)\n\n# load data\nwith open(\"data/query_vector.json\") as f:\n    bicycles = json.load(f)\nindex.load(bicycles)\n\nquery = \"Bike for small kids\"\nquery_vector = embed_text(model, query)\nprint(query_vector[:10]) # >>> b'\\x02=c=\\x93\\x0e\\xe0=aC'\n\nvquery = VectorQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    num_results=3,\n    return_score=True,\n    return_fields=[\"description\"]\n)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6b702e8b...', 'vector_distance': '0.399111807346', 'description': 'Kids want...\n\nvquery = RangeQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    distance_threshold=0.5,\n    return_score=True\n).return_fields(\"description\").dialect(2)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6bcb1bb4...', 'vector_distance': '0.399111807346', 'description': 'Kids want...",
      "section_id": "basic-cluster-optimization"
    },
    {
      "id": "combined-with-filtering-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH idx:bikes_vss \"(@brand:trek)=>[KNN 50 @vector $query_vector]=>{$SHARD_K_RATIO: 0.4; $YIELD_DISTANCE_AS: similarity}\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" SORTBY similarity ASC RETURN 2 \"similarity\" \"description\" DIALECT 2",
      "section_id": "combined-with-filtering"
    },
    {
      "id": "combined-with-filtering-ex1",
      "language": "javascript",
      "code": "import assert from 'node:assert';\nimport fs from 'node:fs';\nimport { createClient } from 'redis';\nimport { SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from '@redis/search';\nimport { pipeline } from '@xenova/transformers';\n\nfunction float32Buffer(arr) {\n  const floatArray = new Float32Array(arr);\n  const float32Buffer = Buffer.from(floatArray.buffer);\n  return float32Buffer;\n}\n\nasync function embedText(sentence) {\n  let modelName = 'Xenova/all-MiniLM-L6-v2';\n  let pipe = await pipeline('feature-extraction', modelName);\n\n  let vectorOutput = await pipe(sentence, {\n      pooling: 'mean',\n      normalize: true,\n  });\n\n  const embedding = Object.values(vectorOutput?.data);\n\n  return embedding;\n}\n\nconst vector_query = float32Buffer(await embedText('That is a very happy person'));\n\nconst client = createClient();\nawait client.connect().catch(console.error);\n\n// create index\nawait client.ft.create('idx:bicycle', {\n    '$.description': {\n      type: SCHEMA_FIELD_TYPE.TEXT,\n      AS: 'description'\n    },\n    '$.description_embeddings': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT,\n        DIM: 384,\n        DISTANCE_METRIC: 'COSINE',\n        AS: 'vector'\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'bicycle:'\n});\n\n// load data\nconst bicycles = JSON.parse(fs.readFileSync('data/query_vector.json', 'utf8'));\n\nawait Promise.all(\n  bicycles.map((bicycle, bid) => {\n      return client.json.set(`bicycle:${bid}`, '$', bicycle);\n  })\n);\n\nconst res1 = await client.ft.search('idx:bicycle', \n  '*=>[KNN 3 @vector $query_vector AS score]', {\n    PARAMS: { query_vector: vector_query },\n    RETURN: ['description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res1.total); // >>> 3\nconsole.log(res1); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:0', value: [Object: null prototype] {} },\n//    { id: 'bicycle:2', value: [Object: null prototype] {} },\n//    { id: 'bicycle:9', value: [Object: null prototype] {} }\n//  ]\n//}\n\nconst res2 = await client.ft.search('idx:bicycle', \n  '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', {\n    PARAMS: { query_vector: vector_query },\n    SORTBY: 'vector_dist',\n    RETURN: ['vector_dist', 'description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ]\n//}",
      "section_id": "combined-with-filtering"
    },
    {
      "id": "combined-with-filtering-ex2",
      "language": "python",
      "code": "import json\nimport warnings\nimport redis\nimport numpy as np\nfrom redisvl.index import SearchIndex\nfrom redisvl.query import RangeQuery, VectorQuery\nfrom redisvl.schema import IndexSchema\nfrom sentence_transformers import SentenceTransformer\n\n\ndef embed_text(model, text):\n    return np.array(model.encode(text)).astype(np.float32).tobytes()\n\nr = redis.Redis(decode_responses=True)\n\nwarnings.filterwarnings(\"ignore\", category=FutureWarning, message=r\".*clean_up_tokenization_spaces.*\")\nmodel = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')\n\n# create index\nschema = IndexSchema.from_yaml('data/query_vector_idx.yaml')\nindex = SearchIndex(schema, r)\nindex.create(overwrite=True, drop=True)\n\n# load data\nwith open(\"data/query_vector.json\") as f:\n    bicycles = json.load(f)\nindex.load(bicycles)\n\nquery = \"Bike for small kids\"\nquery_vector = embed_text(model, query)\nprint(query_vector[:10]) # >>> b'\\x02=c=\\x93\\x0e\\xe0=aC'\n\nvquery = VectorQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    num_results=3,\n    return_score=True,\n    return_fields=[\"description\"]\n)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6b702e8b...', 'vector_distance': '0.399111807346', 'description': 'Kids want...\n\nvquery = RangeQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    distance_threshold=0.5,\n    return_score=True\n).return_fields(\"description\").dialect(2)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6bcb1bb4...', 'vector_distance': '0.399111807346', 'description': 'Kids want...",
      "section_id": "combined-with-filtering"
    }
  ]
}
