> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Blog Search

This recipe demonstrates building a full-text blog search with highlighted snippets,
phrase matching, and date filtering.

### Schema Design

The schema prioritizes full-text search capabilities with date-based filtering:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    import { Redis, s } from "@upstash/redis";

    const redis = Redis.fromEnv();

    const articles = await redis.search.createIndex({
      name: "articles",
      dataType: "hash",
      prefix: "article:",
      schema: s.object({
        // Full-text searchable content
        title: s.string(),
        body: s.string(),
        summary: s.string(),

        // Author name without stemming
        author: s.string().noStem(),

        // Date fields for filtering and sorting
        publishedAt: s.date().fast(),
        updatedAt: s.date().fast(),

        // Status for draft/published filtering
        published: s.boolean(),

        // View count for popularity sorting
        viewCount: s.number("U64"),
      }),
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    from upstash_redis import Redis

    redis = Redis.from_env()

    articles = redis.search.create_index(
        name="articles",
        data_type="hash",
        prefix="article:",
        schema={
            # Full-text searchable content
            "title": "TEXT",
            "body": "TEXT",
            "summary": "TEXT",

            # Author name without stemming
            "author": {"type": "TEXT", "nostem": True},

            # Date fields for filtering and sorting
            "publishedAt": {"type": "DATE", "fast": True},
            "updatedAt": {"type": "DATE", "fast": True},

            # Status for draft/published filtering
            "published": "BOOL",

            # View count for popularity sorting
            "viewCount": "U64",
        },
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    SEARCH.CREATE articles ON HASH PREFIX 1 article: SCHEMA title TEXT body TEXT summary TEXT author TEXT NOSTEM publishedAt DATE FAST updatedAt DATE FAST published BOOL viewCount U64 FAST
    ```
  </Tab>
</Tabs>

### Sample Data

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    await redis.hset("article:1", {
      title: "Getting Started with Redis Search",
      body: "Redis Search provides powerful full-text search capabilities directly in Redis. In this tutorial, we'll explore how to create indexes, define schemas, and write queries. Full-text search allows you to find documents based on their content rather than just their keys. This is essential for building search features in modern applications.",
      summary: "Learn how to add full-text search to your Redis application with practical examples.",
      author: "Jane Smith",
      tags: "redis,search,tutorial",
      publishedAt: "2024-03-15T10:00:00Z",
      updatedAt: "2024-03-15T10:00:00Z",
      published: "true",
      viewCount: "1542",
    });

    await redis.hset("article:2", {
      title: "Advanced Query Techniques for Search",
      body: "Once you've mastered the basics, it's time to explore advanced query techniques. Boolean operators let you combine conditions with AND, OR, and NOT logic. Phrase matching ensures words appear in sequence. Fuzzy matching handles typos gracefully. Together, these features enable sophisticated search experiences.",
      summary: "Master boolean operators, phrase matching, and fuzzy search for better results.",
      author: "John Doe",
      tags: "redis,search,advanced",
      publishedAt: "2024-03-20T14:30:00Z",
      updatedAt: "2024-03-22T09:15:00Z",
      published: "true",
      viewCount: "892",
    });

    await redis.hset("article:3", {
      title: "Building Real-Time Search with Redis",
      body: "Real-time search requires instant indexing and low-latency queries. Redis excels at both. When you write data to Redis, the search index updates automatically. Queries execute in milliseconds, even with millions of documents. This makes Redis ideal for applications where search results must reflect the latest data.",
      summary: "Build search features that update instantly as data changes.",
      author: "Jane Smith",
      tags: "redis,real-time,performance",
      publishedAt: "2024-03-25T08:00:00Z",
      updatedAt: "2024-03-25T08:00:00Z",
      published: "true",
      viewCount: "2103",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    redis.hset("article:1", {
        "title": "Getting Started with Redis Search",
        "body": "Redis Search provides powerful full-text search capabilities directly in Redis. In this tutorial, we'll explore how to create indexes, define schemas, and write queries. Full-text search allows you to find documents based on their content rather than just their keys. This is essential for building search features in modern applications.",
        "summary": "Learn how to add full-text search to your Redis application with practical examples.",
        "author": "Jane Smith",
        "tags": "redis,search,tutorial",
        "publishedAt": "2024-03-15T10:00:00Z",
        "updatedAt": "2024-03-15T10:00:00Z",
        "published": "true",
        "viewCount": "1542",
    })

    redis.hset("article:2", {
        "title": "Advanced Query Techniques for Search",
        "body": "Once you've mastered the basics, it's time to explore advanced query techniques. Boolean operators let you combine conditions with AND, OR, and NOT logic. Phrase matching ensures words appear in sequence. Fuzzy matching handles typos gracefully. Together, these features enable sophisticated search experiences.",
        "summary": "Master boolean operators, phrase matching, and fuzzy search for better results.",
        "author": "John Doe",
        "tags": "redis,search,advanced",
        "publishedAt": "2024-03-20T14:30:00Z",
        "updatedAt": "2024-03-22T09:15:00Z",
        "published": "true",
        "viewCount": "892",
    })

    redis.hset("article:3", {
        "title": "Building Real-Time Search with Redis",
        "body": "Real-time search requires instant indexing and low-latency queries. Redis excels at both. When you write data to Redis, the search index updates automatically. Queries execute in milliseconds, even with millions of documents. This makes Redis ideal for applications where search results must reflect the latest data.",
        "summary": "Build search features that update instantly as data changes.",
        "author": "Jane Smith",
        "tags": "redis,real-time,performance",
        "publishedAt": "2024-03-25T08:00:00Z",
        "updatedAt": "2024-03-25T08:00:00Z",
        "published": "true",
        "viewCount": "2103",
    })
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    HSET article:1 title "Getting Started with Redis Search" body "Redis Search provides powerful full-text search capabilities directly in Redis. In this tutorial, we will explore how to create indexes, define schemas, and write queries. Full-text search allows you to find documents based on their content rather than just their keys. This is essential for building search features in modern applications." summary "Learn how to add full-text search to your Redis application with practical examples." author "Jane Smith" tags "redis,search,tutorial" publishedAt "2024-03-15T10:00:00Z" updatedAt "2024-03-15T10:00:00Z" published "true" viewCount "1542"

    HSET article:2 title "Advanced Query Techniques for Search" body "Once you have mastered the basics, it is time to explore advanced query techniques. Boolean operators let you combine conditions with AND, OR, and NOT logic. Phrase matching ensures words appear in sequence. Fuzzy matching handles typos gracefully. Together, these features enable sophisticated search experiences." summary "Master boolean operators, phrase matching, and fuzzy search for better results." author "John Doe" tags "redis,search,advanced" publishedAt "2024-03-20T14:30:00Z" updatedAt "2024-03-22T09:15:00Z" published "true" viewCount "892"

    HSET article:3 title "Building Real-Time Search with Redis" body "Real-time search requires instant indexing and low-latency queries. Redis excels at both. When you write data to Redis, the search index updates automatically. Queries execute in milliseconds, even with millions of documents. This makes Redis ideal for applications where search results must reflect the latest data." summary "Build search features that update instantly as data changes." author "Jane Smith" tags "redis,real-time,performance" publishedAt "2024-03-25T08:00:00Z" updatedAt "2024-03-25T08:00:00Z" published "true" viewCount "2103"
    ```
  </Tab>
</Tabs>

### Waiting for Indexing

Index updates are batched for performance, so newly added data may not appear in search results immediately.
Use `SEARCH.WAITINDEXING` to ensure all pending updates are processed before querying:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    await articles.waitIndexing();
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    articles.wait_indexing()
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    SEARCH.WAITINDEXING articles
    ```
  </Tab>
</Tabs>

### Basic Full-Text Search

Smart matching handles natural language queries across title and body:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Search across title and body
    const results = await articles.query({
      filter: {
        $should: [
          { title: "redis search" },
          { body: "redis search" },
        ],
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Search across title and body
    results = articles.query(
        filter={
            "$should": [
                {"title": "redis search"},
                {"body": "redis search"},
            ],
        },
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    SEARCH.QUERY articles '{"$should": [{"title": "redis search"}, {"body": "redis search"}]}'
    ```
  </Tab>
</Tabs>

### Search with Highlighted Results

Highlighting shows users why each result matched their query:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Search with highlighted matches in title and body
    const results = await articles.query({
      filter: {
        $should: [
          { title: "full-text search" },
          { body: "full-text search" },
        ],
      },
      highlight: {
        fields: ["title", "body"],
      },
    });

    // Results include highlighted text like:
    // "Redis Search provides powerful <em>full-text</em> <em>search</em> capabilities..."
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Search with highlighted matches in title and body
    results = articles.query(
        filter={
            "$should": [
                {"title": "full-text search"},
                {"body": "full-text search"},
            ],
        },
        highlight={"fields": ["title", "body"]},
    )

    # Results include highlighted text like:
    # "Redis Search provides powerful <em>full-text</em> <em>search</em> capabilities..."
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    SEARCH.QUERY articles '{"$should": [{"title": "full-text search"}, {"body": "full-text search"}]}' HIGHLIGHT FIELDS 2 title body
    ```
  </Tab>
</Tabs>

### Custom Highlight Tags

Use custom tags for different rendering contexts:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Markdown-style highlighting
    const results = await articles.query({
      filter: {
        body: "redis",
      },
      highlight: {
        fields: ["body"],
        preTag: "**",
        postTag: "**",
      },
    });

    // HTML with custom class
    const htmlResults = await articles.query({
      filter: {
        body: "redis",
      },
      highlight: {
        fields: ["body"],
        preTag: "<mark class='search-match'>",
        postTag: "</mark>",
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Markdown-style highlighting
    results = articles.query(
        filter={"body": "redis"},
        highlight={"fields": ["body"], "preTag": "**", "postTag": "**"},
    )

    # HTML with custom class
    html_results = articles.query(
        filter={"body": "redis"},
        highlight={"fields": ["body"], "preTag": "<mark class='search-match'>", "postTag": "</mark>"},
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    # Markdown-style highlighting
    SEARCH.QUERY articles '{"body": "redis"}' HIGHLIGHT FIELDS 1 body TAGS ** **

    # HTML with custom class
    SEARCH.QUERY articles '{"body": "redis"}' HIGHLIGHT FIELDS 1 body TAGS "<mark class='search-match'>" "</mark>"
    ```
  </Tab>
</Tabs>

### Exact Phrase Search

Find articles containing exact phrases using double quotes or the `$phrase` operator:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Using double quotes for exact phrase
    const results = await articles.query({
      filter: {
        body: "\"full-text search\"",
      },
    });

    // Using $phrase operator
    const phraseResults = await articles.query({
      filter: {
        body: {
          $phrase: "boolean operators",
        },
      },
    });

    // Phrase with slop (allow words between)
    // Matches "search results" or "search the results" or "search for better results"
    const slopResults = await articles.query({
      filter: {
        body: {
          $phrase: {
            value: "search results",
            slop: 3,
          },
        },
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Using double quotes for exact phrase
    results = articles.query(
        filter={"body": '"full-text search"'},
    )

    # Using $phrase operator
    phrase_results = articles.query(
        filter={"body": {"$phrase": "boolean operators"}},
    )

    # Phrase with slop (allow words between)
    # Matches "search results" or "search the results" or "search for better results"
    slop_results = articles.query(
        filter={
            "body": {
                "$phrase": {
                    "value": "search results",
                    "slop": 3,
                },
            },
        },
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    # Using double quotes for exact phrase
    SEARCH.QUERY articles '{"body": "\"full-text search\""}'

    # Using $phrase operator
    SEARCH.QUERY articles '{"body": {"$phrase": "boolean operators"}}'

    # Phrase with slop
    SEARCH.QUERY articles '{"body": {"$phrase": {"value": "search results", "slop": 3}}}'
    ```
  </Tab>
</Tabs>

### Filter by Author

Find all articles by a specific author:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // All articles by Jane Smith
    const results = await articles.query({
      filter: {
        author: "Jane Smith",
        published: true,
      },
      orderBy: {
        publishedAt: "DESC",
      },
    });

    // Search within a specific author's articles
    const authorSearch = await articles.query({
      filter: {
        $must: {
          author: "Jane Smith",
          body: "redis",
        },
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # All articles by Jane Smith
    results = articles.query(
        filter={"author": "Jane Smith", "published": True},
        order_by={"publishedAt": "DESC"},
    )

    # Search within a specific author's articles
    author_search = articles.query(
        filter={
            "$must": {
                "author": "Jane Smith",
                "body": "redis",
            },
        },
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    # All articles by Jane Smith
    SEARCH.QUERY articles '{"author": "Jane Smith", "published": true}' ORDERBY publishedAt DESC

    # Search within a specific author's articles
    SEARCH.QUERY articles '{"$must": {"author": "Jane Smith", "body": "redis"}}'
    ```
  </Tab>
</Tabs>

### Date Range Queries

Find articles published within a specific time period:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Articles from a specific month
    const marchArticles = await articles.query({
      filter: {
        publishedAt: {
          $gte: "2026-01-01T00:00:00Z",
          $lt: "2026-02-01T00:00:00Z",
        },
      },
      orderBy: {
        publishedAt: "DESC",
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Articles from a specific month
    march_articles = articles.query(
        filter={
            "publishedAt": {
                "$gte": "2026-01-01T00:00:00Z",
                "$lt": "2026-02-01T00:00:00Z",
            },
        },
        order_by={"publishedAt": "DESC"},
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    # Articles from a specific month
    SEARCH.QUERY articles '{"publishedAt": {"$gte": "2026-01-01T00:00:00Z", "$lt": "2026-02-01T00:00:00Z"}}' ORDERBY publishedAt DESC
    ```
  </Tab>
</Tabs>

### Popular Articles

Sort by view count to find popular content:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Most popular articles
    const popular = await articles.query({
      filter: {
        published: true,
      },
      orderBy: {
        viewCount: "DESC",
      },
      limit: 10,
    });

    // Popular articles about a topic
    const popularRedis = await articles.query({
      filter: {
        $must: {
          body: "redis",
          published: true,
        },
      },
      orderBy: {
        viewCount: "DESC",
      },
      limit: 5,
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Most popular articles
    popular = articles.query(
        filter={"published": True},
        order_by={"viewCount": "DESC"},
        limit=10,
    )

    # Popular articles about a topic
    popular_redis = articles.query(
        filter={
            "$must": {
                "body": "redis",
                "published": True,
            },
        },
        order_by={"viewCount": "DESC"},
        limit=5,
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    # Most popular articles
    SEARCH.QUERY articles '{"published": true}' ORDERBY viewCount DESC LIMIT 10

    # Popular articles about a topic
    SEARCH.QUERY articles '{"$must": {"body": "redis", "published": true}}' ORDERBY viewCount DESC LIMIT 5
    ```
  </Tab>
</Tabs>

### Boosting Title Matches

Prioritize matches in the title over body text:

<Tabs>
  <Tab title="TypeScript">
    ```ts theme={"system"}
    // Boost title matches for better relevance
    const results = await articles.query({
      filter: {
        $should: [
          { title: "redis search", $boost: 5.0 }, // Title matches score 5x higher
          { body: "redis search" },
          { summary: "redis search", $boost: 2.0 },
        ],
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    # Boost title matches for better relevance
    results = articles.query(
        filter={
            "$should": [
                {"title": "redis search", "$boost": 5.0},  # Title matches score 5x higher
                {"body": "redis search"},
                {"summary": "redis search", "$boost": 2.0},
            ],
        },
    )
    ```
  </Tab>

  <Tab title="Redis CLI">
    ```bash theme={"system"}
    SEARCH.QUERY articles '{"$should": [{"title": "redis search", "$boost": 5.0}, {"body": "redis search"}, {"summary": "redis search", "$boost": 2.0}]}'
    ```
  </Tab>
</Tabs>

### Key Takeaways

* Hash storage works well for flat document structures like blog articles
* Use highlighting to show users why results matched their query
* Boost title matches over body text for better relevance
* Use `$phrase` with `slop` for flexible phrase matching
* Combine date ranges with text search for temporal filtering
* Mark `viewCount` as `FAST` to enable popularity sorting
* Filter drafts using `published: true` in `$must` conditions
