Documentation
¶
Overview ¶
Package search 提供全文搜索。
切词在 Go 侧完成(中日韩按二元组,见 tokenize.go),结果存进独立的 post_search 表, 检索走 GIN 索引——全程不依赖任何 PostgreSQL 扩展,装机即用。
索引由后台对账维护:按 posts.updated_at 找出落后的行重建,不在内容模块的写路径上挂钩子。 对外只暴露 Searcher 接口,将来换成 meilisearch 一类外部引擎时调用方不动。
Index ¶
- Constants
- func TSQuery(text string) string
- func Tokenize(text string) []string
- type Doc
- type Handler
- type Hit
- type Module
- type Params
- type Result
- type Searcher
- type Service
- func (s *Service) Reindex(ctx context.Context) (int, error)
- func (s *Service) Run(ctx context.Context)
- func (s *Service) Search(ctx context.Context, params *Params) (*Result, error)
- func (s *Service) SearchPostIDs(ctx context.Context, query string, limit, offset int) (ids []int64, total int, err error)
- func (s *Service) Stats(ctx context.Context) (Stats, error)
- type Stats
- type Store
- func (s *Store) Index(ctx context.Context, row *Doc) error
- func (s *Store) MarkAllStale(ctx context.Context) (int, error)
- func (s *Store) Search(ctx context.Context, tsquery string, types []string, limit, offset int) ([]Hit, int, error)
- func (s *Store) Stale(ctx context.Context, limit int) ([]Doc, error)
- func (s *Store) Stats(ctx context.Context) (Stats, error)
Constants ¶
const Name = "search"
Name 是模块名,也是迁移版本表的后缀(goose_db_version_search)。
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Doc ¶
type Doc struct {
ID int64 `bun:"id"`
Title string `bun:"title"`
Excerpt string `bun:"excerpt"`
Content string `bun:"content"`
UpdatedAt time.Time `bun:"updated_at"`
}
Doc 是一条待索引的内容。
type Hit ¶
type Hit struct {
ID int64 `bun:"id" json:"id"`
Type string `bun:"type" json:"type" enum:"post,page"`
Title string `bun:"title" json:"title"`
Slug string `bun:"slug" json:"slug"`
Excerpt string `bun:"excerpt" json:"excerpt"`
PublishedAt *time.Time `bun:"published_at" json:"publishedAt"`
Score float64 `bun:"score" json:"score" doc:"相关度,只在同一次查询内可比"`
}
Hit 是一条搜索结果。
type Module ¶
type Module struct {
// contains filtered or unexported fields
}
Module 是搜索模块。
type Params ¶
type Params struct {
// Query 是用户输入的原始关键词。
Query string
// Types 限定内容类型(post / page);为空表示不限。
Types []string
Page api.PageParams
}
Params 是一次搜索的入参。
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service 是基于 PostgreSQL tsvector 的搜索实现。
func NewService ¶
NewService 构造 Service。
func (*Service) Run ¶
Run 持续把过期的索引补齐,直到 ctx 取消。
不在内容模块的写路径上挂钩子,而是按 posts.updated_at 对账:写路径有七八条 (创建、改稿、发布、撤回、进回收站、恢复、定时发布扫描……),将来还会加, 漏挂一处的表现是「那部分内容永远搜不到」且没有任何报错。对账则天然覆盖全部写法, 连批量导入与直接改库都跟得上,代价只是最多 reconcileInterval 的延迟。
func (*Service) SearchPostIDs ¶
func (s *Service) SearchPostIDs(ctx context.Context, query string, limit, offset int) ( ids []int64, total int, err error, )
SearchPostIDs 按相关度返回文章 ID 与总数,供主题前台取回完整视图。
实现 theme.Searcher:前台只要「哪些、什么顺序」,作者与分类标签由主题自己补齐。 固定只搜文章——搜索页展示的是文章流,独立页面(关于、联系我们)混进去只会干扰; 要搜页面的调用方走 Public 接口的 type 参数。
type Stats ¶
type Stats struct {
Total int `json:"total" doc:"内容总数"`
Indexed int `json:"indexed" doc:"已建索引的条数"`
Pending int `json:"pending" doc:"索引落后于内容、等待重建的条数"`
}
Stats 是索引的进度快照。
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store 负责索引的读写。
func (*Store) Index ¶
Index 为一条内容写入索引。
indexed_at 写的是**这次读到的** updated_at 而不是 now():若内容在读出之后又被改过, 它的 updated_at 会更大,这一行仍然算过期,下一轮自然会重建。写 now() 则会把 那次修改吞掉,索引从此停在旧内容上。
func (*Store) MarkAllStale ¶
MarkAllStale 清空索引,返回待重建的条数。切词规则改动后用它整体重建。
func (*Store) Search ¶
func (s *Store) Search(ctx context.Context, tsquery string, types []string, limit, offset int) ( []Hit, int, error, )
Search 按相关度分页返回命中的内容,只包含前台可见的条目。
tsquery 由调用方给出(见 TSQuery),空串时不应走到这里。