Search & Discovery

Enable site-wide full-text search across plugin content with Postgres or Elasticsearch ranking engines.

VitNode includes a unified site-wide search and discovery engine. Searchable records across all plugins are projected into the core_search_index table, powering /search and /discover.

Single table filtering

This guide covers site-wide search. For search boxes on individual tables, see Search your tables.

Quick start

1. Indexing on Create / Update

Index or update an item from any Hono route handler via c.get("search"):

await c.get("search").index({
  itemType: "article",
  itemId: article.id,
  title: article.title,
  content: article.content, // HTML automatically stripped to plain text
  url: `/articles/${article.slug}`,
  authorId: article.authorId,
  createdAt: article.createdAt,
})

2. Removing from Search Index

When an item is deleted, remove it from the index:

await c.get("search").delete("article", article.id)

Rebuild Indexers

To allow admins to re-index all historical content from the AdminCP, register a search indexer in your plugin's config.api.ts:

plugins/blog/src/api/indexers/post.indexer.ts
import { buildSearchIndexer } from "@vitnode/core/api/lib/search"
import { blog_posts } from "@/database/posts"

export const postSearchIndexer = buildSearchIndexer({
  itemType: "article",
  totalCount: async (c) => {
    return await c.get("db").$count(blog_posts)
  },
  batch: async (c, { limit, offset }) => {
    const posts = await c.get("db").select().from(blog_posts).limit(limit).offset(offset)

    return posts.map((post) => ({
      itemType: "article",
      itemId: post.id,
      title: post.title,
      content: post.content,
      url: `/articles/${post.slug}`,
      authorId: post.authorId,
      createdAt: post.createdAt,
    }))
  },
})

Register it in config.api.ts:

plugins/blog/src/config.api.ts
export const blogApiPlugin = () =>
  buildApiPlugin({
    pluginId: "blog",
    searchIndexers: [postSearchIndexer], 
  })

Pluggable Search Engines

VitNode supports two search engines:

  1. PostgreSQL Full-Text Search (Default):
    • Uses native tsvector + GIN indexes with weighted ranking (title weighted above content).
    • Zero additional infrastructure required.
  2. Elasticsearch (@vitnode/elasticsearch):
    • Offloads indexing and search to an Elasticsearch or OpenSearch cluster.
    • Unlocks fuzzy matching, phrase boosts, and decay scoring.

AdminCP Search Management

Manage search status at Core → Advanced → Search (/admin/core/advanced/search):

  • View indexed record counts across all collections.
  • Trigger background rebuilds of specific collections or the entire site.

Search Document Reference

Prop

Type

Learn More