226 lines
5.1 KiB
Go
226 lines
5.1 KiB
Go
package model_es
|
||
|
||
import (
|
||
"bytes"
|
||
"catface/app/global/variable"
|
||
"catface/app/model"
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"github.com/elastic/go-elasticsearch/v8"
|
||
"github.com/elastic/go-elasticsearch/v8/esapi"
|
||
)
|
||
|
||
func CreateEncounterESFactory(encounter *model.Encounter) *Encounter {
|
||
if encounter == nil { // UPDATE 这样写好丑。
|
||
return &Encounter{}
|
||
}
|
||
|
||
// 我把数值绑定到了工厂创建当中。
|
||
return &Encounter{
|
||
Id: encounter.Id,
|
||
Title: encounter.Title,
|
||
Content: encounter.Content,
|
||
Tags: encounter.TagsSlice, // TODO 暂时没有对此字段的查询。
|
||
}
|
||
}
|
||
|
||
// INFO 存储能够作为索引存在的数据。
|
||
type Encounter struct {
|
||
Id int64 `json:"id"`
|
||
Title string `json:"title"`
|
||
Content string `json:"content"`
|
||
Tags []string `json:"tags"`
|
||
}
|
||
|
||
func (e *Encounter) IndexName() string {
|
||
return "catface_encounters"
|
||
}
|
||
|
||
func (e *Encounter) InsertDocument() error {
|
||
ctx := context.Background()
|
||
|
||
// 将结构体转换为 JSON 字符串
|
||
data, err := json.Marshal(e)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 创建请求
|
||
req := esapi.IndexRequest{
|
||
Index: e.IndexName(),
|
||
DocumentID: fmt.Sprintf("%d", e.Id),
|
||
Body: bytes.NewReader(data),
|
||
Refresh: "true",
|
||
}
|
||
|
||
// 发送请求
|
||
res, err := req.Do(ctx, variable.ElasticClient)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer res.Body.Close()
|
||
|
||
if res.IsError() {
|
||
var e map[string]interface{}
|
||
if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
|
||
return fmt.Errorf("error parsing the response body: %s", err)
|
||
} else {
|
||
return fmt.Errorf("[%s] %s: %s",
|
||
res.Status(),
|
||
e["error"].(map[string]interface{})["type"],
|
||
e["error"].(map[string]interface{})["reason"],
|
||
)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// TODO 改正,仿 Insert
|
||
func (e *Encounter) UpdateDocument(client *elasticsearch.Client, encounter *Encounter) error {
|
||
ctx := context.Background()
|
||
|
||
// 将结构体转换为 JSON 字符串
|
||
data, err := json.Marshal(map[string]interface{}{
|
||
"doc": encounter,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 创建请求
|
||
req := esapi.UpdateRequest{
|
||
Index: encounter.IndexName(),
|
||
DocumentID: fmt.Sprintf("%d", encounter.Id),
|
||
Body: bytes.NewReader(data),
|
||
Refresh: "true",
|
||
}
|
||
|
||
// 发送请求
|
||
res, err := req.Do(ctx, client)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer res.Body.Close()
|
||
|
||
if res.IsError() {
|
||
var e map[string]interface{}
|
||
if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
|
||
return fmt.Errorf("error parsing the response body: %s", err)
|
||
} else {
|
||
return fmt.Errorf("[%s] %s: %s",
|
||
res.Status(),
|
||
e["error"].(map[string]interface{})["type"],
|
||
e["error"].(map[string]interface{})["reason"],
|
||
)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
/**
|
||
* @description: 粗略地包含各种关键词匹配,
|
||
* @param {*elasticsearch.Client} client
|
||
* @param {string} query
|
||
* @return {*} 对应 Encounter 的 id,然后交给 MySQL 来查询详细的信息?
|
||
*/
|
||
func (e *Encounter) QueryDocumentsMatchAll(query string) ([]int64, error) {
|
||
ctx := context.Background()
|
||
|
||
// 创建查询请求
|
||
req := esapi.SearchRequest{ // UPDATE 同时实现查询高亮?
|
||
Index: []string{e.IndexName()},
|
||
Body: strings.NewReader(fmt.Sprintf(`{
|
||
"_source": ["id"],
|
||
"query": {
|
||
"bool": {
|
||
"should": [
|
||
{
|
||
"match": {
|
||
"title": "%s"
|
||
}
|
||
},
|
||
{
|
||
"match": {
|
||
"content": "%s"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}`, query, query)),
|
||
}
|
||
|
||
// 发送请求
|
||
res, err := req.Do(ctx, variable.ElasticClient)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer res.Body.Close()
|
||
|
||
if res.IsError() {
|
||
var e map[string]interface{}
|
||
if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
|
||
return nil, fmt.Errorf("error parsing the response body: %s", err)
|
||
} else {
|
||
return nil, fmt.Errorf("[%s] %s: %s",
|
||
res.Status(),
|
||
e["error"].(map[string]interface{})["type"],
|
||
e["error"].(map[string]interface{})["reason"],
|
||
)
|
||
}
|
||
}
|
||
|
||
// 解析响应
|
||
var r map[string]interface{}
|
||
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 提取命中结果
|
||
hits, ok := r["hits"].(map[string]interface{})["hits"].([]interface{})
|
||
if !ok {
|
||
return nil, fmt.Errorf("error extracting hits from response")
|
||
}
|
||
|
||
fmt.Println(hits)
|
||
|
||
// 转换为 id 切片
|
||
var ids []int64
|
||
for _, hit := range hits {
|
||
hitMap := hit.(map[string]interface{})["_source"].(map[string]interface{})
|
||
id := int64(hitMap["id"].(float64))
|
||
ids = append(ids, id)
|
||
}
|
||
|
||
return ids, nil
|
||
|
||
// // 转换为 Encounter 切片
|
||
// var encounters []*Encounter
|
||
// for _, hit := range hits {
|
||
// hitMap := hit.(map[string]interface{})
|
||
// source := hitMap["_source"].(map[string]interface{})
|
||
|
||
// // TIP 将 []interface{} 转换为 []string
|
||
// tagsInterface := source["tags"].([]interface{})
|
||
// tags := make([]string, len(tagsInterface))
|
||
// for i, tag := range tagsInterface {
|
||
// tags[i] = tag.(string)
|
||
// }
|
||
|
||
// encounter := &Encounter{
|
||
// Id: int64(source["id"].(float64)),
|
||
// Title: source["title"].(string),
|
||
// Content: source["content"].(string),
|
||
// Tags: tags,
|
||
// }
|
||
// encounters = append(encounters, encounter)
|
||
// }
|
||
|
||
// return encounters, nil
|
||
}
|