更新搜索引擎
This commit is contained in:
9
controllers/search/close.go
Normal file
9
controllers/search/close.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package search
|
||||
|
||||
// 程序退出时关闭索引(可选)
|
||||
func CloseIndex() error {
|
||||
if globalIndex != nil {
|
||||
return globalIndex.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
39
controllers/search/init.go
Normal file
39
controllers/search/init.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 初始化索引(程序启动时调用一次)
|
||||
func InitIndex() error {
|
||||
// 检查索引目录是否存在(Bleve v2 方式)
|
||||
_, err := os.Stat("data")
|
||||
exists := !os.IsNotExist(err)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
// 除了"不存在"之外的其他错误(如权限问题)
|
||||
return fmt.Errorf("检查索引目录失败: %v", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
// 打开已有索引
|
||||
globalIndex, err = bleve.Open("data")
|
||||
if err != nil {
|
||||
return fmt.Errorf("打开索引失败: %v", err)
|
||||
}
|
||||
log.Println("成功打开已有索引")
|
||||
} else {
|
||||
// 创建新索引
|
||||
mapping := bleve.NewIndexMapping()
|
||||
globalIndex, err = bleve.New("data", mapping)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建索引失败: %v", err)
|
||||
}
|
||||
log.Println("成功创建新索引")
|
||||
|
||||
// 初始化文档(略)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
98
controllers/search/insert.go
Normal file
98
controllers/search/insert.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"log"
|
||||
"toutoukan/model/article"
|
||||
)
|
||||
|
||||
// InsertDocument 插入单条文档到索引
|
||||
func InsertDocument(c *gin.Context) {
|
||||
// 检查索引是否初始化
|
||||
if globalIndex == nil {
|
||||
c.JSON(500, gin.H{"error": "索引未初始化"})
|
||||
return
|
||||
}
|
||||
|
||||
// 绑定请求体中的文档数据
|
||||
var doc article.Document
|
||||
if err := c.ShouldBindJSON(&doc); err != nil {
|
||||
c.JSON(400, gin.H{"error": "请求格式错误: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文档必填字段
|
||||
if doc.Title == "" && doc.Body == "" {
|
||||
c.JSON(400, gin.H{"error": "文档ID不能为空,且标题和内容不能同时为空"})
|
||||
return
|
||||
}
|
||||
|
||||
docID := uuid.New().String()
|
||||
|
||||
// 将文档插入索引
|
||||
if err := globalIndex.Index(docID, doc); err != nil {
|
||||
log.Printf("插入文档失败: %v", err)
|
||||
c.JSON(500, gin.H{"error": "插入文档到索引失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 插入成功响应
|
||||
c.JSON(200, gin.H{
|
||||
"message": "文档插入成功",
|
||||
"doc_id": docID,
|
||||
})
|
||||
}
|
||||
|
||||
// BatchInsertDocuments 批量插入文档到索引
|
||||
//func BatchInsertDocuments(c *gin.Context) {
|
||||
// // 检查索引是否初始化
|
||||
// if globalIndex == nil {
|
||||
// c.JSON(500, gin.H{"error": "索引未初始化"})
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // 绑定请求体中的文档数组
|
||||
// var docs []article.Document
|
||||
// if err := c.ShouldBindJSON(&docs); err != nil {
|
||||
// c.JSON(400, gin.H{"error": "请求格式错误: " + err.Error()})
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // 验证批量文档
|
||||
// if len(docs) == 0 {
|
||||
// c.JSON(400, gin.H{"error": "文档列表不能为空"})
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // 批量插入文档
|
||||
// successCount := 0
|
||||
// failures := make(map[string]string)
|
||||
//
|
||||
// for _, doc := range docs {
|
||||
// if doc.ID == "" || (doc.Title == "" && doc.Body == "") {
|
||||
// failures[doc.ID] = "ID不能为空或内容为空"
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// if err := globalIndex.Index(doc.ID, doc); err != nil {
|
||||
// failures[doc.ID] = err.Error()
|
||||
// continue
|
||||
// }
|
||||
// successCount++
|
||||
// }
|
||||
//
|
||||
// // 批量插入结果响应
|
||||
// response := gin.H{
|
||||
// "total": len(docs),
|
||||
// "success": successCount,
|
||||
// "failed": len(failures),
|
||||
// "message": fmt.Sprintf("批量插入完成,成功%d条,失败%d条", successCount, len(failures)),
|
||||
// }
|
||||
//
|
||||
// if len(failures) > 0 {
|
||||
// response["failures"] = failures
|
||||
// }
|
||||
//
|
||||
// c.JSON(200, response)
|
||||
//}
|
||||
85
controllers/search/search.go
Normal file
85
controllers/search/search.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"toutoukan/model/article"
|
||||
)
|
||||
|
||||
// 全局索引变量,避免重复创建
|
||||
var globalIndex bleve.Index
|
||||
|
||||
func DataSearch(c *gin.Context) {
|
||||
if globalIndex == nil {
|
||||
c.JSON(500, gin.H{"error": "索引未初始化,请先调用InitIndex()"})
|
||||
return
|
||||
}
|
||||
|
||||
keyword := c.Query("keyword")
|
||||
if keyword == "" {
|
||||
c.JSON(400, gin.H{"error": "请提供搜索关键词(?keyword=xxx)"})
|
||||
return
|
||||
}
|
||||
|
||||
query := bleve.NewQueryStringQuery(`title:"` + keyword + `" OR body:"` + keyword + `"`)
|
||||
searchRequest := bleve.NewSearchRequest(query)
|
||||
|
||||
// 只返回必要字段减少资源消耗
|
||||
searchRequest.Fields = []string{"title", "body"}
|
||||
|
||||
searchResult, err := globalIndex.Search(searchRequest)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": "搜索失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
ID string `json:"id"`
|
||||
Score float64 `json:"score"`
|
||||
Doc *article.Document `json:"doc"`
|
||||
}
|
||||
var results []Result
|
||||
|
||||
for _, hit := range searchResult.Hits {
|
||||
doc := &article.Document{}
|
||||
hasField := false
|
||||
|
||||
// 宽松的字段处理逻辑
|
||||
if title, ok := hit.Fields["title"].(string); ok {
|
||||
doc.Title = title
|
||||
hasField = true
|
||||
} else {
|
||||
log.Printf("文档 %s 缺少title字段", hit.ID)
|
||||
}
|
||||
|
||||
if body, ok := hit.Fields["body"].(string); ok {
|
||||
doc.Body = body
|
||||
hasField = true
|
||||
} else {
|
||||
log.Printf("文档 %s 缺少body字段", hit.ID)
|
||||
}
|
||||
|
||||
// 至少有一个字段存在才包含结果
|
||||
if hasField {
|
||||
results = append(results, Result{
|
||||
ID: hit.ID,
|
||||
Score: hit.Score,
|
||||
Doc: doc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 添加调试信息
|
||||
if len(results) == 0 {
|
||||
log.Printf("关键词'%s'搜索返回%d个文档,但有效结果为0", keyword, searchResult.Total)
|
||||
} else {
|
||||
log.Printf("关键词'%s'搜索返回%d个文档", keyword, searchResult.Total)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"total": len(results), // 返回实际有效结果数
|
||||
"took": searchResult.Took,
|
||||
"result": results,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user