{
  "schema_version": 2,
  "id": "develop/clients/nodejs/queryjson",
  "title": "Index and query documents",
  "url": "https://redis.io/docs/latest/develop/clients/nodejs/queryjson/",
  "summary": "Learn how to use Redis Search with JSON and hash documents.",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-10-16T16:25:49+01:00",
  "page_type": "content",
  "content_hash": "853a498216b091ad0dc90220b854b876b07ddc1e4f4dc08a2c55ff388ddf12b2",
  "sections": [
    {
      "id": "initialize",
      "title": "Initialize",
      "role": "content",
      "text": "Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) client library if you\nhaven't already done so.\n\nAdd the following dependencies:\n\nFoundational: Import required modules for Redis client and query operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "create-data",
      "title": "Create data",
      "role": "content",
      "text": "Create some test data to add to your database. The example data shown\nbelow is compatible with both JSON and hash objects.\n\nFoundational: Define sample data structures for indexing and querying examples\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "add-the-index",
      "title": "Add the index",
      "role": "content",
      "text": "Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/nodejs/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to Redis for query operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nCreate an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFirst, drop any existing index to avoid a collision. (The callback is required\nto avoid an error if the index doesn't already exist.)\n\nFoundational: Drop an existing search index safely using error handling to avoid collisions\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThen create the index:\n\nFoundational: Create a search index for JSON documents with field definitions and aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "add-the-data",
      "title": "Add the data",
      "role": "content",
      "text": "Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them. Note that placing\nthe commands in a `Promise.all()` call is an easy way to create a\n[pipeline](https://redis.io/docs/latest/develop/clients/nodejs/transpipe),\nwhich is more efficient than sending the commands individually.\n\nFoundational: Add JSON documents with indexed key prefixes using Promise.all() for efficient pipelining\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "query-the-data",
      "title": "Query the data",
      "role": "content",
      "text": "You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery data: Execute a full-text search combined with numeric range filtering on indexed JSON documents\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nRestrict query results: Project specific fields in query results to reduce data transfer and improve performance\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregate query results: Use aggregation queries to group and count results by field values for analytics\n\n**Difficulty:** Advanced\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"
    },
    {
      "id": "differences-with-hash-documents",
      "title": "Differences with hash documents",
      "role": "content",
      "text": "Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must use `HASH` for the `ON` option\nwhen you create the index. The code below shows these changes with\na new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples.\n\nFirst, drop any existing index to avoid a collision.\n\nFoundational: Drop an existing search index safely using error handling to avoid collisions\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThen create the new index:\n\nFoundational: Create a search index for hash documents with HASH type specification\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nYou use [`hSet()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`json.set()`](https://redis.io/docs/latest/commands/json.set),\nbut the same flat `userX` objects work equally well with either\nhash or JSON:\n\nFoundational: Add hash documents with indexed key prefixes using hSet() for automatic indexing\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The format of the result is\nalso the same:\n\nQuery data: Execute the same full-text query pattern on hash documents as on JSON documents\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 hash 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 as compact hashes using HIMPORT PREPARE and pipelined HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_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. The query returns\nthe same result, although the fields can appear in a different order.\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": "more-information",
      "title": "More information",
      "role": "content",
      "text": "See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."
    }
  ],
  "examples": [
    {
      "id": "initialize-ex0",
      "language": "javascript",
      "code": "import {\n    createClient,\n    SCHEMA_FIELD_TYPE,\n    FT_AGGREGATE_GROUP_BY_REDUCERS,\n    FT_AGGREGATE_STEPS,\n} from 'redis';",
      "section_id": "initialize"
    },
    {
      "id": "create-data-ex0",
      "language": "javascript",
      "code": "const user1 = {\n    name: 'Paul John',\n    email: 'paul.john@example.com',\n    age: 42,\n    city: 'London'\n};\n\nconst user2 = {\n    name: 'Eden Zamir',\n    email: 'eden.zamir@example.com',\n    age: 29,\n    city: 'Tel Aviv'\n};\n\nconst user3 = {\n    name: 'Paul Zamir',\n    email: 'paul.zamir@example.com',\n    age: 35,\n    city: 'Tel Aviv'\n};",
      "section_id": "create-data"
    },
    {
      "id": "add-the-index-ex0",
      "language": "javascript",
      "code": "const client = await createClient();\nawait client.connect();",
      "section_id": "add-the-index"
    },
    {
      "id": "add-the-index-ex1",
      "language": "javascript",
      "code": "await client.ft.dropIndex('idx:users', { DD: true }).then(() => {}, () => {});",
      "section_id": "add-the-index"
    },
    {
      "id": "add-the-index-ex2",
      "language": "javascript",
      "code": "await client.ft.create('idx:users', {\n    '$.name': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n        AS: 'name'\n    },\n    '$.city': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n        AS: 'city'\n    },\n    '$.age': {\n        type: SCHEMA_FIELD_TYPE.NUMERIC,\n        AS: 'age'\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'user:'\n});",
      "section_id": "add-the-index"
    },
    {
      "id": "add-the-data-ex0",
      "language": "javascript",
      "code": "const [user1Reply, user2Reply, user3Reply] = await Promise.all([\n    client.json.set('user:1', '$', user1),\n    client.json.set('user:2', '$', user2),\n    client.json.set('user:3', '$', user3)\n]);",
      "section_id": "add-the-data"
    },
    {
      "id": "query-the-data-ex0",
      "language": "javascript",
      "code": "let findPaulResult = await client.ft.search('idx:users', 'Paul @age:[30 40]');\n\nconsole.log(findPaulResult.total); // >>> 1\n\nfindPaulResult.documents.forEach(doc => {\n    console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`);\n});\n// >>> ID: user:3, name: Paul Zamir, age: 35",
      "section_id": "query-the-data"
    },
    {
      "id": "query-the-data-ex1",
      "language": "javascript",
      "code": "let citiesResult = await client.ft.search('idx:users', '*',{\n    RETURN: 'city'\n});\n\nconsole.log(citiesResult.total); // >>> 3\n\ncitiesResult.documents.forEach(cityDoc => {\n    console.log(cityDoc.value);\n});\n// >>> { city: 'London' }\n// >>> { city: 'Tel Aviv' }\n// >>> { city: 'Tel Aviv' }",
      "section_id": "query-the-data"
    },
    {
      "id": "query-the-data-ex2",
      "language": "javascript",
      "code": "let aggResult = await client.ft.aggregate('idx:users', '*', {\n    STEPS: [{\n        type: FT_AGGREGATE_STEPS.GROUPBY,\n        properties: '@city',\n        REDUCE: [{\n            type: FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT,\n            AS: 'count'\n        }]\n    }]\n});\n\nconsole.log(aggResult.total); // >>> 2\n\naggResult.results.forEach(result => {\n    console.log(`${result.city} - ${result.count}`);\n});\n// >>> London - 1\n// >>> Tel Aviv - 2",
      "section_id": "query-the-data"
    },
    {
      "id": "differences-with-hash-documents-ex0",
      "language": "javascript",
      "code": "await client.ft.dropIndex('hash-idx:users', { DD: true }).then(() => {}, () => {});",
      "section_id": "differences-with-hash-documents"
    },
    {
      "id": "differences-with-hash-documents-ex1",
      "language": "javascript",
      "code": "await client.ft.create('hash-idx:users', {\n    'name': {\n        type: SCHEMA_FIELD_TYPE.TEXT\n    },\n    'city': {\n        type: SCHEMA_FIELD_TYPE.TEXT\n    },\n    'age': {\n        type: SCHEMA_FIELD_TYPE.NUMERIC\n    }\n}, {\n    ON: 'HASH',\n    PREFIX: 'huser:'\n});",
      "section_id": "differences-with-hash-documents"
    },
    {
      "id": "differences-with-hash-documents-ex2",
      "language": "javascript",
      "code": "const [huser1Reply, huser2Reply, huser3Reply] = await Promise.all([\n    client.hSet('huser:1', user1),\n    client.hSet('huser:2', user2),\n    client.hSet('huser:3', user3)\n]);",
      "section_id": "differences-with-hash-documents"
    },
    {
      "id": "differences-with-hash-documents-ex3",
      "language": "javascript",
      "code": "let findPaulHashResult = await client.ft.search(\n    'hash-idx:users', 'Paul @age:[30 40]'\n);\n\nconsole.log(findPaulHashResult.total); // >>> 1\n\nfindPaulHashResult.documents.forEach(doc => {\n    console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`);\n});\n// >>> ID: huser:3, name: Paul Zamir, age: 35",
      "section_id": "differences-with-hash-documents"
    },
    {
      "id": "bulk-import-with-himport-ex0",
      "language": "javascript",
      "code": "const fields = ['name', 'email', 'age', 'city'];\nawait client.hImportPrepare('huser', fields);\n\nconst himportReplies = await Promise.all(\n    [['huser:1', user1], ['huser:2', user2], ['huser:3', user3]].map(\n        ([key, user]) => client.hImportSet(\n            key, 'huser', fields.map(field => String(user[field]))\n        )\n    )\n);",
      "section_id": "bulk-import-with-himport"
    }
  ]
}
