package store import ( core "agent-system-core" ) // searchKnowledgePG:PG 档知识检索(设计 D16)。 // 单查询混合:tsvector('simple' 配置,词级)OR trigram LIKE 子串(中文兜底; // LIKE 由 pg_trgm GIN 索引加速)。排序:ts_rank 优先,命中时间次之。 func (s *Store) searchKnowledgePG(owner string, q core.RetrievalQuery) ([]core.RetrievalHit, error) { topK := q.TopK if topK <= 0 { topK = 5 } text := q.Text rows, err := s.db.Query(s.q(` SELECT k.id,k.owner,k.project_id,k.source,k.title,k.ref,k.chunk,k.created_at, ts_rank(k.tsv, websearch_to_tsquery('simple', ?)) AS r FROM knowledge k WHERE (k.owner=? OR k.owner='') AND (? = '' OR k.tsv @@ websearch_to_tsquery('simple', ?) OR k.chunk LIKE ? OR k.title LIKE ?) ORDER BY r DESC, k.created_at DESC LIMIT ?`), text, owner, text, text, "%"+text+"%", "%"+text+"%", topK*2) if err != nil { return nil, err } defer rows.Close() var out []core.RetrievalHit for rows.Next() { var d core.KnowledgeDoc var own, ca string var rank float64 if err := rows.Scan(&d.ID, &own, &d.ProjectID, &d.Source, &d.Title, &d.Ref, &d.Chunk, &ca, &rank); err != nil { return nil, err } d.Owner = own d.CreatedAt, _ = parseTime(ca) if !knowledgeVisible(owner, q, d) { continue } score := rank if score == 0 { score = 0.5 // 纯子串命中(trigram/中文兜底)给保底分 } out = append(out, core.RetrievalHit{Doc: d, Score: score, Kind: "fulltext"}) if len(out) >= topK { break } } return out, rows.Err() }