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.

Quick start

Index when a record changes

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,
})

Delete removed records

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

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

Register a rebuild indexer

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.

For setup, credentials, configuration, and the first rebuild, follow the Elasticsearch tutorial.


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