{
  "schema_version": 2,
  "id": "develop/clients/nodejs/vecsearch",
  "title": "Index and query vectors",
  "url": "https://redis.io/docs/latest/develop/clients/nodejs/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": "87b2fd0a51a15fb67eeea5d297f019e22fa0c2cb4bdf9c987ae6c77367e9db9f",
  "sections": [
    {
      "id": "initialize",
      "title": "Initialize",
      "role": "content",
      "text": "Install the required dependencies:\n\n1. Install [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) if you haven't already.\n2. Install `@xenova/transformers`:\n\n[code example]\n\nIn your JavaScript source file, import the required classes:\n\nFoundational: Import Redis client and transformer modules for vector embedding operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe `@xenova/transformers` module handles embedding models. This example uses the\n[`all-distilroberta-v1`](https://huggingface.co/sentence-transformers/all-distilroberta-v1)\nmodel, which:\n- Generates 768-dimensional vectors\n- Truncates input to 128 tokens\n- Uses word piece tokenization (see [Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\n  at the [Hugging Face](https://huggingface.co/) docs for details)\n\nThe `pipe` function generates embeddings. The `pipeOptions` object specifies how to generate sentence embeddings from token embeddings (see the\n[`all-distilroberta-v1`](https://huggingface.co/sentence-transformers/all-distilroberta-v1)\ndocumentation for details):\n\nFoundational: Configure a transformer pipeline to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "create-the-index",
      "title": "Create the index",
      "role": "content",
      "text": "First, connect to Redis and remove any existing index named `vector_idx`:\n\nFoundational: Connect to Redis and clean up existing vector indexes\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nNext, create the index with the following schema:\n-   `content`: Text field for the content to index\n-   `genre`: [Tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\n    field representing the text's genre\n-   `embedding`: [Vector](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n    field with:\n    -   [HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\n        indexing\n    -   [L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\n        distance metric\n    -   Float32 values\n    -   768 dimensions (matching the embedding model)\n\nFoundational: Create a vector search index with HNSW algorithm and L2 distance metric for semantic search\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "add-data",
      "title": "Add data",
      "role": "content",
      "text": "Add data objects to the index using `hSet()`. The index automatically processes objects with the `doc:` prefix.\n\nFor each document:\n1. Generate an embedding using the `pipe()` function and `pipeOptions`\n2. Convert the embedding to a binary string using `Buffer.from()`\n3. Store the document with `hSet()`\n\nUse `Promise.all()` to batch the commands and reduce network round trips:\n\nPractical pattern: Generate embeddings and store documents with vector fields using pipelined hSet() commands\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\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[`hImportPrepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`hImportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use `Promise.all()` to send\nall the imports as a [pipeline](https://redis.io/docs/latest/develop/clients/nodejs/transpipe),\nin one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using HIMPORT PREPARE and pipelined HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\n`hImportSet()` 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 `node-redis` v6.2.0 or later, and `node-redis` 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": "To query the index:\n1. Generate an embedding for your query text\n2. Pass the embedding as a parameter to the search\n3. Redis calculates vector distances and ranks results\n\nThe query returns an array of document objects. Each object contains:\n- `id`: The document's key\n- `value`: An object with fields specified in the `RETURN` option\n\nVector query: Execute a vector similarity search by generating a query embedding and finding nearest neighbors\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe first run may take longer as it downloads the model data. The output shows results ordered by score (vector distance), with lower scores indicating greater similarity:\n\n[code example]\n\nThe exact distances may differ slightly on your system."
    },
    {
      "id": "differences-with-json-documents",
      "title": "Differences with JSON documents",
      "role": "content",
      "text": "JSON documents support richer data modeling with nested fields. Key differences from hash documents:\n\n1. Use paths in the schema to identify fields\n2. Declare aliases for paths using the `AS` option\n3. Set `ON` to `JSON` when creating the index\n4. Use arrays instead of binary strings for vectors\n5. Use `json.set()` instead of `hSet()`\n\nCreate the index with path aliases:\n\nFoundational: Create a vector search index for JSON documents with path aliases and nested field support\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nAdd data using `json.set()`. Convert the `Float32Array` to a standard JavaScript array using the spread operator:\n\nPractical pattern: Store JSON documents with vector embeddings converted to arrays for nested field support\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nQuery JSON documents using the same syntax, but note that the vector parameter must still be a binary string:\n\nVector query: Execute vector similarity search on JSON documents with the same query syntax as hash documents\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe results are identical to the hash document query, except for the `jdoc:` prefix:\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 indexing options, distance metrics, and query format."
    }
  ],
  "examples": [
    {
      "id": "initialize-ex0",
      "language": "bash",
      "code": "npm install @xenova/transformers",
      "section_id": "initialize"
    },
    {
      "id": "initialize-ex1",
      "language": "javascript",
      "code": "import * as transformers from '@xenova/transformers';\nimport {\n    SCHEMA_VECTOR_FIELD_ALGORITHM,\n    createClient,\n    SCHEMA_FIELD_TYPE,\n} from 'redis';",
      "section_id": "initialize"
    },
    {
      "id": "initialize-ex2",
      "language": "javascript",
      "code": "let pipe = await transformers.pipeline(\n    'feature-extraction', 'Xenova/all-distilroberta-v1'\n);\n\nconst pipeOptions = {\n    pooling: 'mean',\n    normalize: true,\n};",
      "section_id": "initialize"
    },
    {
      "id": "create-the-index-ex0",
      "language": "javascript",
      "code": "const client = createClient({url: 'redis://localhost:6379'});\nawait client.connect();\n\ntry { \n    await client.ft.dropIndex('vector_idx'); \n} catch (e) {\n    // Index doesn't exist, which is fine\n}",
      "section_id": "create-the-index"
    },
    {
      "id": "create-the-index-ex1",
      "language": "javascript",
      "code": "await client.ft.create('vector_idx', {\n    'content': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n    },\n    'genre': {\n        type: SCHEMA_FIELD_TYPE.TAG,\n    },\n    'embedding': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW,\n        DISTANCE_METRIC: 'L2',\n        DIM: 768,\n    }\n}, {\n    ON: 'HASH',\n    PREFIX: 'doc:'\n});",
      "section_id": "create-the-index"
    },
    {
      "id": "add-data-ex0",
      "language": "javascript",
      "code": "const sentence1 = 'That is a very happy person';\nconst doc1 = {\n    'content': sentence1, \n    'genre': 'persons', \n    'embedding': Buffer.from(\n        (await pipe(sentence1, pipeOptions)).data.buffer\n    ),\n};\n\nconst sentence2 = 'That is a happy dog';\nconst doc2 = {\n    'content': sentence2, \n    'genre': 'pets', \n    'embedding': Buffer.from(\n        (await pipe(sentence2, pipeOptions)).data.buffer\n    )\n};\n\nconst sentence3 = 'Today is a sunny day';\nconst doc3 = {\n    'content': sentence3, \n    'genre': 'weather', \n    'embedding': Buffer.from(\n        (await pipe(sentence3, pipeOptions)).data.buffer\n    )\n};\n\nawait Promise.all([\n    client.hSet('doc:1', doc1),\n    client.hSet('doc:2', doc2),\n    client.hSet('doc:3', doc3)\n]);",
      "section_id": "add-data"
    },
    {
      "id": "bulk-import-with-himport-ex0",
      "language": "javascript",
      "code": "await client.hImportPrepare('doc', ['content', 'genre', 'embedding']);\n\nawait Promise.all(\n    [doc1, doc2, doc3].map((doc, i) => client.hImportSet(\n        `doc:${i + 1}`, 'doc', [doc.content, doc.genre, doc.embedding]\n    ))\n);",
      "section_id": "bulk-import-with-himport"
    },
    {
      "id": "run-a-query-ex0",
      "language": "javascript",
      "code": "const similar = await client.ft.search(\n    'vector_idx',\n    '*=>[KNN 3 @embedding $B AS score]',\n    {\n        'PARAMS': {\n            B: Buffer.from(\n                (await pipe('That is a happy person', pipeOptions)).data.buffer\n            ),\n        },\n        'RETURN': ['score', 'content'],\n        'DIALECT': '2'\n    },\n);\n\nfor (const doc of similar.documents) {\n    console.log(`${doc.id}: '${doc.value.content}', Score: ${doc.value.score}`);\n}",
      "section_id": "run-a-query"
    },
    {
      "id": "run-a-query-ex1",
      "language": "plaintext",
      "code": "doc:1: 'That is a very happy person', Score: 0.127055495977\ndoc:2: 'That is a happy dog', Score: 0.836842417717\ndoc:3: 'Today is a sunny day', Score: 1.50889515877",
      "section_id": "run-a-query"
    },
    {
      "id": "differences-with-json-documents-ex0",
      "language": "javascript",
      "code": "await client.ft.create('vector_json_idx', {\n    '$.content': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n        AS: 'content',\n    },\n    '$.genre': {\n        type: SCHEMA_FIELD_TYPE.TAG,\n        AS: 'genre',\n    },\n    '$.embedding': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW,\n        DISTANCE_METRIC: 'L2',\n        DIM: 768,\n        AS: 'embedding',\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'jdoc:'\n});",
      "section_id": "differences-with-json-documents"
    },
    {
      "id": "differences-with-json-documents-ex1",
      "language": "javascript",
      "code": "const jSentence1 = 'That is a very happy person';\nconst jdoc1 = {\n    'content': jSentence1,\n    'genre': 'persons',\n    'embedding': [...(await pipe(jSentence1, pipeOptions)).data],\n};\n\nconst jSentence2 = 'That is a happy dog';\nconst jdoc2 = {\n    'content': jSentence2,\n    'genre': 'pets',\n    'embedding': [...(await pipe(jSentence2, pipeOptions)).data],\n};\n\nconst jSentence3 = 'Today is a sunny day';\nconst jdoc3 = {\n    'content': jSentence3,\n    'genre': 'weather',\n    'embedding': [...(await pipe(jSentence3, pipeOptions)).data],\n};\n\nawait Promise.all([\n    client.json.set('jdoc:1', '$', jdoc1),\n    client.json.set('jdoc:2', '$', jdoc2),\n    client.json.set('jdoc:3', '$', jdoc3)\n]);",
      "section_id": "differences-with-json-documents"
    },
    {
      "id": "differences-with-json-documents-ex2",
      "language": "javascript",
      "code": "const jsons = await client.ft.search(\n    'vector_json_idx',\n    '*=>[KNN 3 @embedding $B AS score]',\n    {\n        \"PARAMS\": {\n            B: Buffer.from(\n                (await pipe('That is a happy person', pipeOptions)).data.buffer\n            ),\n        },\n        'RETURN': ['score', 'content'],\n        'DIALECT': '2'\n    },\n);",
      "section_id": "differences-with-json-documents"
    },
    {
      "id": "differences-with-json-documents-ex3",
      "language": "plaintext",
      "code": "jdoc:1: 'That is a very happy person', Score: 0.127055495977\njdoc:2: 'That is a happy dog', Score: 0.836842417717\njdoc:3: 'Today is a sunny day', Score: 1.50889515877",
      "section_id": "differences-with-json-documents"
    }
  ]
}
