Files
halo-dark-mode-plugin/.agents/skills/halo-plugin-dev/references/server-search.md
T
Serendipity edbf78f236 feat: 初始化 Halo 暗色模式插件
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试
- Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面
- 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等)
- 设计文档和调查文档
- Halo 插件/主题开发 Agent Skills

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 21:03:00 +08:00

4.5 KiB

Search Integration

Integrate custom extensions with Halo's built-in search engine.

Source: HaloDocument | HaloDocumentsProvider | SearchEngine

HaloDocumentsProvider

Implement this extension point to make your custom Extension searchable.

@Component
public class MyDocumentProvider implements HaloDocumentsProvider {

    private final ReactiveExtensionClient client;

    public MyDocumentProvider(ReactiveExtensionClient client) {
        this.client = client;
    }

    @Override
    public Flux<HaloDocument> fetchAll() {
        return client.listAll(MyExtension.class, new ListOptions(), Sort.unsorted())
            .filter(ext -> ext.getMetadata().getDeletionTimestamp() == null)
            .map(this::convertToDocument);
    }

    @Override
    public String getType() {
        return "myextension.mygroup.halo.run";
    }

    private HaloDocument convertToDocument(MyExtension ext) {
        var doc = new HaloDocument();
        doc.setId(ext.getMetadata().getName());
        doc.setMetadataName(ext.getMetadata().getName());
        doc.setTitle(ext.getSpec().getTitle());
        doc.setDescription(ext.getSpec().getDescription());
        doc.setContent(stripHtml(ext.getSpec().getContent()));
        doc.setOwnerName(ext.getSpec().getOwner());
        doc.setCreationTimestamp(ext.getMetadata().getCreationTimestamp());
        doc.setUpdateTimestamp(ext.getMetadata().getCreationTimestamp());
        doc.setPermalink("/my-extensions/" + ext.getSpec().getSlug());
        doc.setType(getType());
        doc.setPublished(true);
        doc.setRecycled(false);
        doc.setExposed(true);
        return doc;
    }
}

Search Events

Publish events to add/remove documents from the search index:

// Add or update documents
applicationEventPublisher.publishEvent(
    new HaloDocumentAddRequestEvent(this, List.of(haloDoc)));

// Delete documents by ID
applicationEventPublisher.publishEvent(
    new HaloDocumentDeleteRequestEvent(this, List.of("doc-id-1", "doc-id-2")));

// Rebuild entire index (e.g., on plugin config change)
applicationEventPublisher.publishEvent(
    new HaloDocumentRebuildRequestEvent(this));

SearchEngine (Replacing Default)

To replace Halo's default search engine (e.g., with Meilisearch):

@Component
public class MeilisearchEngine implements SearchEngine {

    @Override
    public boolean available() {
        return meilisearchClient != null && meilisearchClient.isHealthy();
    }

    @Override
    public void addOrUpdate(Iterable<HaloDocument> docs) {
        // Batch index documents
    }

    @Override
    public void deleteDocument(Iterable<String> ids) {
        // Batch delete by ID
    }

    @Override
    public void deleteAll() {
        // Clear index
    }

    @Override
    public SearchResult search(SearchOption option) {
        // Execute search query
    }
}

HaloDocument Fields

Field Description Required
id Global unique document ID Yes
metadataName Extension metadata name Yes
title Document title Yes
content Plain text content (no HTML) Yes
description Short description No
permalink URL path Yes
type Document type identifier Yes
ownerName Owner metadata name Yes
categories Category metadata names No
tags Tag metadata names No
published Whether published Yes
recycled Whether in recycle bin Yes
exposed Whether publicly visible Yes
creationTimestamp / updateTimestamp Timestamps Yes