CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-redis

A modern, high performance Redis client for Node.js with full TypeScript support and all Redis Stack modules

Pending
Overview
Eval results
Files

search-indexing.mddocs/

Search and Indexing

Full-text search, indexing, and aggregation capabilities using RediSearch for complex query operations on Redis data. Provides powerful search features including full-text search, numeric filtering, geo-spatial queries, and advanced aggregation.

Capabilities

Index Management

Operations for creating, managing, and configuring search indexes.

/**
 * Create a search index with schema definition
 * @param index - Name of the index
 * @param schema - Index schema definition
 * @param options - Optional index configuration
 * @returns 'OK'
 */
function create(index: string, schema: RediSearchSchema, options?: FtCreateOptions): Promise<SimpleStringReply<'OK'>>;

/**
 * Get information about a search index
 * @param index - Name of the index
 * @returns Array containing index information
 */
function info(index: string): Promise<ArrayReply>;

/**
 * Drop a search index
 * @param index - Name of the index
 * @param options - Optional drop configuration
 * @returns 'OK'
 */
function dropIndex(index: string, options?: FtDropIndexOptions): Promise<SimpleStringReply<'OK'>>;

/**
 * Alter an existing index by adding new fields
 * @param index - Name of the index
 * @param schema - New fields to add to the schema
 * @returns 'OK'
 */
function alter(index: string, schema: RediSearchSchema): Promise<SimpleStringReply<'OK'>>;

/**
 * List all search indexes
 * @returns Array of index names
 */
function _list(): Promise<ArrayReply<BlobStringReply>>;

interface RediSearchSchema {
  [fieldName: string]: SchemaFieldDefinition;
}

interface SchemaFieldDefinition {
  /** Field type */
  type: SchemaFieldType;
  /** Field is sortable */
  SORTABLE?: boolean;
  /** Field cannot be searched */
  NOINDEX?: boolean;
  /** Store original value for highlighting */
  NOSTEM?: boolean;
  /** Text field specific options */
  WEIGHT?: number;
  PHONETIC?: SchemaTextFieldPhonetic;
  /** Numeric/geo field options */
  SEPARATOR?: string;
  /** Vector field options */
  ALGORITHM?: SchemaVectorFieldAlgorithm;
  DIM?: number;
  DISTANCE_METRIC?: 'L2' | 'IP' | 'COSINE';
}

type SchemaFieldType = 'TEXT' | 'NUMERIC' | 'GEO' | 'TAG' | 'VECTOR';
type SchemaTextFieldPhonetic = 'dm:en' | 'dm:fr' | 'fm';
type SchemaVectorFieldAlgorithm = 'FLAT' | 'HNSW';

interface FtCreateOptions {
  /** Key prefix for indexed documents */
  PREFIX?: string | string[];
  /** Custom key filter expression */
  FILTER?: string;
  /** Default language for text fields */
  LANGUAGE?: RediSearchLanguage;
  /** Default field for searches */
  LANGUAGE_FIELD?: string;
  /** Default score for documents */
  SCORE?: number;
  /** Field containing document score */
  SCORE_FIELD?: string;
  /** Field containing document payload */
  PAYLOAD_FIELD?: string;
  /** Maximum number of text records */
  MAXTEXTFIELDS?: boolean;
  /** Disable stemming */
  NOSTEM?: boolean;
  /** Disable stop words */
  NOFREQS?: boolean;
  /** Disable term offset vectors */
  NOOFFSETS?: boolean;
  /** Number of background indexing threads */
  NOHL?: boolean;
  /** Custom stop words */
  STOPWORDS?: string[];
}

interface FtDropIndexOptions {
  /** Also delete the documents */
  DD?: boolean;
}

type RediSearchLanguage = 'arabic' | 'danish' | 'dutch' | 'english' | 'finnish' | 'french' | 'german' | 'hungarian' | 'italian' | 'norwegian' | 'portuguese' | 'romanian' | 'russian' | 'spanish' | 'swedish' | 'turkish' | 'chinese';

Usage Examples:

import { createClient } from "redis";

const client = createClient();
await client.connect();

// Create a search index for products
await client.ft.create("products", {
  name: { type: 'TEXT', SORTABLE: true },
  description: { type: 'TEXT' },
  price: { type: 'NUMERIC', SORTABLE: true },
  category: { type: 'TAG', SORTABLE: true },
  location: { type: 'GEO' },
  tags: { type: 'TAG', SEPARATOR: ',' }
}, {
  PREFIX: 'product:',
  LANGUAGE: 'english'
});

// Get index information
const indexInfo = await client.ft.info("products");
console.log("Index info:", indexInfo);

// List all indexes
const indexes = await client.ft._list();
console.log("Available indexes:", indexes);

// Add new field to existing index
await client.ft.alter("products", {
  brand: { type: 'TAG', SORTABLE: true }
});

// Drop index (keeping documents)
await client.ft.dropIndex("products");

// Drop index and delete documents
await client.ft.dropIndex("products", { DD: true });

Search Operations

Core search functionality with query parsing and result formatting.

/**
 * Search the index with a query
 * @param index - Name of the index
 * @param query - Search query string
 * @param options - Optional search parameters
 * @returns Search results with documents and metadata
 */
function search(index: string, query: string, options?: FtSearchOptions): Promise<SearchReply>;

/**
 * Search without returning document content
 * @param index - Name of the index  
 * @param query - Search query string
 * @param options - Optional search parameters
 * @returns Search results with document IDs only
 */
function searchNoContent(index: string, query: string, options?: FtSearchOptions): Promise<SearchReply>;

/**
 * Explain query execution plan
 * @param index - Name of the index
 * @param query - Search query string
 * @param options - Optional query options
 * @returns Query execution plan
 */
function explain(index: string, query: string, options?: FtExplainOptions): Promise<BlobStringReply>;

/**
 * Profile search query performance
 * @param index - Name of the index
 * @param query - Search query string
 * @param options - Optional search parameters
 * @returns Profiling information with search results
 */
function profileSearch(index: string, query: string, options?: FtSearchOptions): Promise<ArrayReply>;

interface FtSearchOptions {
  /** Return only document IDs */
  NOCONTENT?: boolean;
  /** Include term frequency information */
  WITHSCORES?: boolean;
  /** Include payload information */
  WITHPAYLOADS?: boolean;
  /** Sort by field */
  SORTBY?: {
    BY: string;
    DIRECTION?: 'ASC' | 'DESC';
  };
  /** Limit results */
  LIMIT?: {
    from: number;
    size: number;
  };
  /** Specific fields to return */
  RETURN?: string[];
  /** Highlight matching terms */
  HIGHLIGHT?: {
    FIELDS?: string[];
    TAGS?: {
      open: string;
      close: string;
    };
  };
  /** Summarize fields */
  SUMMARIZE?: {
    FIELDS?: string[];
    FRAGS?: number;
    LEN?: number;
    SEPARATOR?: string;
  };
  /** Default language */
  LANGUAGE?: RediSearchLanguage;
  /** Expression filter */
  FILTER?: string;
  /** Geographic filter */
  GEOFILTER?: {
    field: string;
    lon: number;
    lat: number;
    radius: number;
    unit: 'm' | 'km' | 'mi' | 'ft';
  };
  /** Include term position information */
  INKEYS?: string[];
  /** Include tag values */
  INFIELDS?: string[];
  /** Return raw document values */
  RETURN_RAW?: boolean;
  /** Disable stemming */
  NOSTEM?: boolean;
  /** Custom scoring function */
  SCORER?: string;
}

interface SearchReply {
  /** Total number of results */
  total: number;
  /** Array of document results */
  documents: SearchDocument[];
}

interface SearchDocument {
  /** Document ID */
  id: string;
  /** Document score */
  score?: number;
  /** Document payload */
  payload?: string;
  /** Document fields */
  value: Record<string, string>;
}

Usage Examples:

// Add some sample data first
await client.hSet("product:1", {
  name: "Smartphone",
  description: "Latest Android smartphone with great camera",
  price: "599",
  category: "electronics",
  tags: "mobile,android,camera"
});

await client.hSet("product:2", {
  name: "Laptop",
  description: "High-performance laptop for developers",
  price: "1299",
  category: "computers",
  tags: "laptop,development,performance"
});

// Basic search
const results = await client.ft.search("products", "smartphone");
console.log(`Found ${results.total} results:`, results.documents);

// Search with options
const searchResults = await client.ft.search("products", "camera OR laptop", {
  WITHSCORES: true,
  SORTBY: { BY: "price", DIRECTION: "ASC" },
  LIMIT: { from: 0, size: 10 },
  HIGHLIGHT: {
    FIELDS: ["name", "description"],
    TAGS: { open: "<mark>", close: "</mark>" }
  }
});

// Filtered search
const electronicsResults = await client.ft.search("products", "*", {
  FILTER: "@category:{electronics}",
  SORTBY: { BY: "price", DIRECTION: "DESC" }
});

// Price range search
const priceResults = await client.ft.search("products", "@price:[500 1000]");

// Geographic search (if location field exists)
const nearbyResults = await client.ft.search("products", "*", {
  GEOFILTER: {
    field: "location",
    lon: -122.4194,
    lat: 37.7749,
    radius: 10,
    unit: "km"
  }
});

// Get search explanation
const explanation = await client.ft.explain("products", "smartphone camera");
console.log("Query plan:", explanation);

Aggregation Operations

Advanced aggregation operations for complex data analysis and reporting.

/**
 * Perform aggregation query on search index
 * @param index - Name of the index
 * @param query - Search query string
 * @param options - Aggregation pipeline steps
 * @returns Aggregation results
 */
function aggregate(index: string, query: string, ...options: FtAggregateStep[]): Promise<ArrayReply>;

/**
 * Perform aggregation with cursor for large result sets
 * @param index - Name of the index
 * @param query - Search query string  
 * @param options - Aggregation options with cursor configuration
 * @returns Aggregation results with cursor
 */
function aggregateWithCursor(index: string, query: string, options?: FtAggregateWithCursorOptions): Promise<ArrayReply>;

/**
 * Profile aggregation query performance
 * @param index - Name of the index
 * @param query - Search query string
 * @param options - Aggregation pipeline steps
 * @returns Profiling information with aggregation results
 */
function profileAggregate(index: string, query: string, ...options: FtAggregateStep[]): Promise<ArrayReply>;

type FtAggregateStep = 
  | { type: 'GROUPBY'; fields: string[]; reducers: FtAggregateGroupByReducer[] }
  | { type: 'SORTBY'; fields: Array<{ field: string; direction?: 'ASC' | 'DESC' }> }
  | { type: 'APPLY'; expression: string; as: string }
  | { type: 'LIMIT'; offset: number; count: number }
  | { type: 'FILTER'; expression: string };

type FtAggregateGroupByReducer =
  | { type: 'COUNT'; as?: string }
  | { type: 'COUNT_DISTINCT'; field: string; as?: string }
  | { type: 'SUM'; field: string; as?: string }
  | { type: 'MIN'; field: string; as?: string }
  | { type: 'MAX'; field: string; as?: string }
  | { type: 'AVG'; field: string; as?: string }
  | { type: 'STDDEV'; field: string; as?: string }
  | { type: 'TOLIST'; field: string; as?: string };

interface FtAggregateWithCursorOptions {
  /** Cursor read size */
  WITHCURSOR?: {
    COUNT?: number;
    MAXIDLE?: number;
  };
  /** Aggregation steps */
  steps?: FtAggregateStep[];
}

Usage Examples:

// Group by category and count
const categoryStats = await client.ft.aggregate("products", "*",
  {
    type: 'GROUPBY',
    fields: ['@category'],
    reducers: [
      { type: 'COUNT', as: 'count' },
      { type: 'AVG', field: '@price', as: 'avg_price' },
      { type: 'MIN', field: '@price', as: 'min_price' },
      { type: 'MAX', field: '@price', as: 'max_price' }
    ]
  },
  {
    type: 'SORTBY',
    fields: [{ field: '@count', direction: 'DESC' }]
  }
);

// Price range analysis
const priceRanges = await client.ft.aggregate("products", "*",
  {
    type: 'APPLY',
    expression: 'floor(@price/100)*100',
    as: 'price_range'
  },
  {
    type: 'GROUPBY',
    fields: ['@price_range'],
    reducers: [
      { type: 'COUNT', as: 'products_in_range' }
    ]
  },
  {
    type: 'SORTBY',
    fields: [{ field: '@price_range', direction: 'ASC' }]
  }
);

// Complex aggregation with multiple steps
const complexAgg = await client.ft.aggregate("products", "electronics",
  {
    type: 'FILTER',
    expression: '@price > 100'
  },
  {
    type: 'APPLY',
    expression: '@price * 0.1',
    as: 'tax'
  },
  {
    type: 'GROUPBY',
    fields: ['@category'],
    reducers: [
      { type: 'COUNT', as: 'count' },
      { type: 'SUM', field: '@price', as: 'total_price' },
      { type: 'SUM', field: '@tax', as: 'total_tax' }
    ]
  },
  {
    type: 'LIMIT',
    offset: 0,
    count: 10
  }
);

Cursor Operations

Cursor-based operations for handling large result sets efficiently.

/**
 * Read from aggregation cursor
 * @param index - Name of the index
 * @param cursor - Cursor ID
 * @param options - Read options
 * @returns Next batch of results
 */
function cursorRead(index: string, cursor: number, options?: FtCursorReadOptions): Promise<ArrayReply>;

/**
 * Delete aggregation cursor
 * @param index - Name of the index
 * @param cursor - Cursor ID
 * @returns 'OK'
 */
function cursorDel(index: string, cursor: number): Promise<SimpleStringReply<'OK'>>;

interface FtCursorReadOptions {
  /** Number of results to read */
  COUNT?: number;
}

Usage Examples:

// Create aggregation with cursor
const cursorResult = await client.ft.aggregateWithCursor("products", "*", {
  WITHCURSOR: { COUNT: 100, MAXIDLE: 300000 },
  steps: [
    {
      type: 'GROUPBY',
      fields: ['@category'],
      reducers: [{ type: 'COUNT', as: 'count' }]
    }
  ]
});

// Extract cursor ID from result
const cursorId = cursorResult[cursorResult.length - 1];

// Read more results
const moreResults = await client.ft.cursorRead("products", cursorId, { COUNT: 50 });

// Clean up cursor
await client.ft.cursorDel("products", cursorId);

Suggestion Operations

Auto-complete and suggestion functionality.

/**
 * Add suggestion to auto-complete dictionary
 * @param key - Dictionary key
 * @param string - Suggestion string
 * @param score - Suggestion score
 * @param options - Optional payload and increment behavior
 * @returns Number of suggestions added
 */
function sugAdd(key: string, string: string, score: number, options?: FtSugAddOptions): Promise<NumberReply>;

/**
 * Get suggestions from auto-complete dictionary
 * @param key - Dictionary key
 * @param prefix - Prefix to match
 * @param options - Optional parameters for fuzzy matching and limits
 * @returns Array of matching suggestions
 */
function sugGet(key: string, prefix: string, options?: FtSugGetOptions): Promise<ArrayReply<BlobStringReply>>;

/**
 * Get suggestions with scores
 * @param key - Dictionary key
 * @param prefix - Prefix to match
 * @param options - Optional parameters
 * @returns Array of suggestions with scores
 */
function sugGetWithScores(key: string, prefix: string, options?: FtSugGetOptions): Promise<ArrayReply>;

/**
 * Delete suggestion from dictionary
 * @param key - Dictionary key
 * @param string - Suggestion string to delete
 * @returns 1 if deleted, 0 if not found
 */
function sugDel(key: string, string: string): Promise<BooleanReply>;

/**
 * Get number of suggestions in dictionary
 * @param key - Dictionary key
 * @returns Number of suggestions
 */
function sugLen(key: string): Promise<NumberReply>;

interface FtSugAddOptions {
  /** Increment score if suggestion exists */
  INCR?: boolean;
  /** Optional payload data */
  PAYLOAD?: string;
}

interface FtSugGetOptions {
  /** Enable fuzzy matching */
  FUZZY?: boolean;
  /** Maximum number of results */
  MAX?: number;
  /** Include scores in results */
  WITHSCORES?: boolean;
  /** Include payloads in results */
  WITHPAYLOADS?: boolean;
}

Usage Examples:

// Add suggestions to auto-complete
await client.ft.sugAdd("product_suggestions", "smartphone", 1.0);
await client.ft.sugAdd("product_suggestions", "smart tv", 0.8);
await client.ft.sugAdd("product_suggestions", "smart watch", 0.9, { PAYLOAD: "category:wearable" });

// Get suggestions
const suggestions = await client.ft.sugGet("product_suggestions", "smart", { MAX: 5 });
// ["smartphone", "smart watch", "smart tv"]

// Get suggestions with scores
const withScores = await client.ft.sugGetWithScores("product_suggestions", "smart", { MAX: 3 });
// ["smartphone", "1", "smart watch", "0.9", "smart tv", "0.8"]

// Fuzzy matching
const fuzzy = await client.ft.sugGet("product_suggestions", "smar", { FUZZY: true, MAX: 3 });

// Get suggestion count
const count = await client.ft.sugLen("product_suggestions"); // 3

// Delete suggestion
await client.ft.sugDel("product_suggestions", "smart tv");

Dictionary and Synonym Management

Operations for managing dictionaries and synonyms.

/**
 * Add terms to dictionary
 * @param dictionary - Dictionary name
 * @param terms - Terms to add
 * @returns Number of new terms added
 */
function dictAdd(dictionary: string, ...terms: string[]): Promise<NumberReply>;

/**
 * Delete terms from dictionary
 * @param dictionary - Dictionary name
 * @param terms - Terms to delete
 * @returns Number of terms deleted
 */
function dictDel(dictionary: string, ...terms: string[]): Promise<NumberReply>;

/**
 * Dump all terms in dictionary
 * @param dictionary - Dictionary name
 * @returns Array of all terms in dictionary
 */
function dictDump(dictionary: string): Promise<ArrayReply<BlobStringReply>>;

/**
 * Update synonym group
 * @param index - Index name
 * @param groupId - Synonym group ID
 * @param terms - Terms in the synonym group
 * @param options - Optional skip initial scan
 * @returns 'OK'
 */
function synUpdate(index: string, groupId: string, terms: string[], options?: { SKIPINITIALSCAN?: boolean }): Promise<SimpleStringReply<'OK'>>;

/**
 * Dump synonym groups
 * @param index - Index name
 * @returns Array of synonym group information
 */
function synDump(index: string): Promise<ArrayReply>;

Usage Examples:

// Dictionary management
await client.ft.dictAdd("stopwords", "the", "a", "an", "and", "or", "but");
const stopwordCount = await client.ft.dictAdd("stopwords", "in", "on", "at"); // Returns count of new words

// View dictionary contents
const allStopwords = await client.ft.dictDump("stopwords");

// Remove words from dictionary
await client.ft.dictDel("stopwords", "but", "or");

// Synonym management
await client.ft.synUpdate("products", "smartphones", [
  "smartphone", "mobile phone", "cell phone", "mobile device"
]);

await client.ft.synUpdate("products", "laptops", [
  "laptop", "notebook", "portable computer"
]);

// View synonyms
const synonyms = await client.ft.synDump("products");

Alias Management

Index alias operations for flexible index management.

/**
 * Add alias for an index
 * @param alias - Alias name
 * @param index - Target index name
 * @returns 'OK'
 */
function aliasAdd(alias: string, index: string): Promise<SimpleStringReply<'OK'>>;

/**
 * Delete an alias
 * @param alias - Alias name to delete
 * @returns 'OK'
 */
function aliasDel(alias: string): Promise<SimpleStringReply<'OK'>>;

/**
 * Update alias to point to different index
 * @param alias - Alias name
 * @param index - New target index name
 * @returns 'OK'  
 */
function aliasUpdate(alias: string, index: string): Promise<SimpleStringReply<'OK'>>;

Usage Examples:

// Create alias for current index
await client.ft.aliasAdd("current_products", "products_v1");

// Use alias in searches (same as using index name)
const results = await client.ft.search("current_products", "smartphone");

// Update alias to point to new index version
await client.ft.aliasUpdate("current_products", "products_v2");

// Delete alias when no longer needed
await client.ft.aliasDel("current_products");

Search Query Syntax

RediSearch supports powerful query syntax for complex searches:

// Query syntax examples:
"hello world"           // Full-text search for both terms
"hello | world"         // OR search
"hello -world"          // Exclude term
"\"hello world\""       // Exact phrase
"hello*"                // Prefix search
"%hello%"               // Fuzzy search
"@title:hello"          // Field-specific search
"@price:[100 200]"      // Numeric range
"@location:[lat lon radius unit]" // Geographic filter
"@tags:{electronics|computers}"   // Tag filter
"(@title:hello) (@price:[100 200])" // Complex query

Constants and Enums

const REDISEARCH_LANGUAGE = {
  ARABIC: 'arabic',
  DANISH: 'danish', 
  DUTCH: 'dutch',
  ENGLISH: 'english',
  FINNISH: 'finnish',
  FRENCH: 'french',
  GERMAN: 'german',
  HUNGARIAN: 'hungarian',
  ITALIAN: 'italian',
  NORWEGIAN: 'norwegian',
  PORTUGUESE: 'portuguese',
  ROMANIAN: 'romanian',
  RUSSIAN: 'russian',
  SPANISH: 'spanish',
  SWEDISH: 'swedish',
  TURKISH: 'turkish',
  CHINESE: 'chinese'
} as const;

const SCHEMA_FIELD_TYPE = {
  TEXT: 'TEXT',
  NUMERIC: 'NUMERIC', 
  GEO: 'GEO',
  TAG: 'TAG',
  VECTOR: 'VECTOR'
} as const;

const FT_AGGREGATE_GROUP_BY_REDUCERS = {
  COUNT: 'COUNT',
  COUNT_DISTINCT: 'COUNT_DISTINCT',
  SUM: 'SUM',
  MIN: 'MIN',
  MAX: 'MAX',
  AVG: 'AVG',
  STDDEV: 'STDDEV',
  TOLIST: 'TOLIST'
} as const;

Install with Tessl CLI

npx tessl i tessl/npm-redis

docs

bloom-filters.md

client-management.md

index.md

json-operations.md

redis-commands.md

search-indexing.md

time-series.md

tile.json