Valkey: Search and JSON

Store and query JSON documents with the Valkey search module

👋 Welcome to the Stackhero documentation!

Stackhero offers a ready-to-use Valkey cloud solution that delivers numerous advantages, including:

  • Valkey Admin web UI included.
  • Unlimited message size and transfers.
  • One-click updates for easy maintenance.
  • Optimal performance and enhanced security thanks to a private, dedicated infrastructure.

Save time and make your life easier: it only takes 5 minutes to try Stackhero's Valkey cloud hosting solution!

Your Valkey instance on Stackhero comes with essential modules maintained by the Valkey project. These modules make Valkey much more than just a cache: you can store native JSON documents, index your data, and perform tag, numeric, text, and vector queries—all without having to manage a separate database.

Three modules are available, each included in your plan and licensed under the same permissive BSD 3-Clause license as Valkey:

  • JSON (JSON.* commands): Store and update structured JSON documents with JSONPath support. You can read or modify individual fields directly, so you never have to rewrite the entire document for minor changes.
  • Search (FT.* commands): Perform secondary indexing, tag, numeric, and full-text queries, as well as vector similarity search.
  • Bloom (BF.* commands): Use Bloom filters to efficiently answer "have I already seen this?" across large datasets, while using minimal memory.

Modules are disabled by default, so your existing service keeps its current behaviour until you enable them.

  1. Open your service on the Stackhero dashboard.
  2. Go to the service configuration.
  3. In the Modules section, select the modules you want to enable.
  4. Save your changes.

Valkey will restart to load the selected modules. This usually takes just a few seconds.

If you have stored data using a module (such as a JSON document or a Bloom filter), keep that module enabled. Valkey cannot read that data without the module that created it. Disabling the module would prevent your service from restarting successfully.

You can check which modules are currently loaded at any time:

valkey-cli -u "rediss://default:<yourPassword>@<XXXXXX>.stackhero-network.com:<PORT_TLS>" MODULE LIST

You can store structured documents natively as JSON, without serializing them into strings:

JSON.SET product:1 $ '{"name":"Espresso machine","brand":"Bianca","price":459,"stock":12}'

Reading or updating a single field is fast, atomic, and handled server-side:

JSON.GET product:1 $.price
# "[459]"

JSON.NUMINCRBY product:1 $.stock -1
# "[11]"

This approach eliminates the need to fetch and parse the entire document in your application for every update. For example, decrementing a stock counter is now a single atomic operation. No more race conditions or extra code.

The JSON.* command set matches the API of RedisJSON, so most Valkey and Redis clients work without changes.

Create an index once, and Valkey keeps it up to date for every key matching your prefix—even for keys written before the index was created:

FT.CREATE productsIndex
  ON HASH
  PREFIX 1 product:
  SCHEMA
    name TEXT
    brand TAG
    price NUMERIC

You can query your indexed data with powerful search commands:

# Exact tag match
FT.SEARCH productsIndex "@brand:{Bianca}"

# Numeric range
FT.SEARCH productsIndex "@price:[0 500]"

# Combined query
FT.SEARCH productsIndex "@brand:{Bianca} @price:[0 500]"

# Full-text search by name
FT.SEARCH productsIndex "espresso"

You can also run aggregations on the same index with FT.AGGREGATE. The FT.INFO command lets you check the index status, including the progress of background indexing jobs.

JSON documents are also indexable. When the JSON module is enabled, you can use ON JSON and JSONPath expressions in your schema.

Because Valkey uses the same protocol as Redis, any Redis client is compatible. You can use this example with the official node-redis client:

import { createClient, SCHEMA_FIELD_TYPE } from 'redis';

const client = createClient({ url: process.env.STACKHERO_VALKEY_URL_TLS });
await client.connect();

// Create the index once at startup
try {
  await client.ft.create(
    'productsIndex',
    {
      name: SCHEMA_FIELD_TYPE.TEXT,
      brand: SCHEMA_FIELD_TYPE.TAG,
      price: SCHEMA_FIELD_TYPE.NUMERIC
    },
    { ON: 'HASH', PREFIX: 'product:' }
  );
} catch (error) {
  if (!error.message.includes('Index already exists')) {
    throw error;
  }
}

await client.hSet('product:1', { name: 'Espresso machine', brand: 'Bianca', price: '459' });

const results = await client.ft.search('productsIndex', '@brand:{Bianca} @price:[0 500]');
console.log(results.total, results.documents);

await client.quit();

You can use the redis-py client to leverage Valkey search features:

import os
import redis
from redis.commands.search.field import TextField, TagField, NumericField
from redis.commands.search.index_definition import IndexDefinition, IndexType
from redis.commands.search.query import Query

r = redis.from_url(os.environ['STACKHERO_VALKEY_URL_TLS'], decode_responses=True)

# Create the index once at startup
try:
    r.ft('productsIndex').create_index(
        (TextField('name'), TagField('brand'), NumericField('price')),
        definition=IndexDefinition(prefix=['product:'], index_type=IndexType.HASH),
    )
except redis.ResponseError as error:
    if 'Index already exists' not in str(error):
        raise

r.hset('product:1', mapping={'name': 'Espresso machine', 'brand': 'Bianca', 'price': 459})

results = r.ft('productsIndex').search(Query('@brand:{Bianca} @price:[0 500]'))
print(results.total, results.docs)
  • The search module provides a focused set of commands: FT.CREATE, FT.SEARCH, FT.AGGREGATE, FT.INFO, FT.DROPINDEX, and FT._LIST. These efficiently cover tag, numeric, text, and vector queries. If your application requires advanced full-text features (such as spell checking, synonyms, or suggestion dictionaries), the Redis query engine offers more advanced capabilities.
  • Indexes are per-database and work within each database. For example, an index created in database 3 is only visible and queryable from database 3. This is an area where Valkey goes beyond Redis database selection, which only supports indexing on database 0.
  • Indexes use your plan's memory, just like your data. You can monitor used_memory in your Prometheus metrics after building an index.
  • Indexing existing keys runs in the background. Immediately after running FT.CREATE, search results may be incomplete for a few seconds while the index is being built.
  • All module commands are enabled. Every command from every module is available on your service, with no restrictions.

If you want to explore vector similarity, semantic search, or retrieval-augmented generation, check out the vector search guide.