Go中如何在ElasticSearch搜索结果中包含嵌套Document对象
问题解决:让ElasticSearch搜索结果包含关联的Document对象
问题根源
- 序列化忽略字段:
AdSpecializedMachinery的Document字段标记了json:"-",导致将数据同步到ElasticSearch时,该字段不会被序列化存入ES,自然无法从搜索结果中获取。 - 索引结构缺失:即使取消
json:"-",若ES索引未配置嵌套类型,数组形式的Document数据会被扁平化,无法正确解析。
解决方案
步骤1:调整模型序列化标签
修改AdSpecializedMachinery中的Document字段,去掉json:"-"标签,确保数据能被序列化到ES:
// 原字段 Document []Document `json:"-" gorm:"many2many:ad_specialized_machineries_documents;"` // 修改后 Document []Document `json:"documents" gorm:"many2many:ad_specialized_machineries_documents;"`
- 保留
gorm标签维持数据库多对多关联逻辑,新增json:"documents"让字段能被JSON序列化。 - 需重新同步所有
AdSpecializedMachinery数据到ES,确保新的documents字段被存入索引。
步骤2:配置ES索引的嵌套映射
因为Document是对象数组,需将ES索引中的documents字段设置为nested类型,避免数组扁平化:
PUT /index_ad_sm/_mapping { "properties": { "documents": { "type": "nested", "properties": { "id": {"type": "integer"}, "title": {"type": "text"}, "shareLink": {"type": "text"} // 按需添加其他需要索引的Document字段 } } } }
步骤3:确保搜索返回嵌套字段
默认情况下ES会返回所有存储的字段,若需明确指定,可在查询中添加_source配置:
query := map[string]interface{}{ "_source": []string{"*", "documents"}, // 指定返回所有字段及嵌套的documents "query": map[string]interface{}{ "multi_match": map[string]interface{}{ "query": match, "fuzziness": "AUTO", "fields": []interface{}{"name", "description"}, }, }, }
步骤4:验证解析逻辑
确保parseHint函数能正确解析嵌套的documents数组。如果使用标准json.Unmarshal,只要结构体的json标签与ES返回的字段名匹配,即可自动映射到Document字段。
备选方案:Document存储在独立ES索引
若Document数据量较大或需单独管理,可在搜索到广告后批量查询关联的Document:
- 修改Document模型:添加关联广告ID字段
type Document struct { // 原有字段... AdID int `json:"ad_id" gorm:"index"` // 新增关联广告ID }
- 批量查询关联Document:
// 在SearchSimple获取ads后执行: // 提取所有广告ID adIDs := make([]int, len(ads)) for i, ad := range ads { adIDs[i] = ad.ID } // 构造Document查询 var docBuf bytes.Buffer docQuery := map[string]interface{}{ "query": map[string]interface{}{ "terms": map[string]interface{}{"ad_id": adIDs}, }, } if err := json.NewEncoder(&docBuf).Encode(docQuery); err != nil { return nil, fmt.Errorf("encode doc query failed: %w", err) } // 执行查询 docRes, err := es.client.Search( es.client.Search.WithContext(ctx), es.client.Search.WithIndex("documents"), // Document的ES索引名 es.client.Search.WithBody(&docBuf), ) if err != nil { return nil, fmt.Errorf("search documents failed: %w", err) } defer docRes.Body.Close() // 解析Document并匹配到对应广告 documents, err := parseHint[model.Document](docRes.Body) if err != nil { return nil, fmt.Errorf("parse documents failed: %w", err) } adDocMap := make(map[int][]model.Document) for _, doc := range documents { adDocMap[doc.AdID] = append(adDocMap[doc.AdID], doc) } for i := range ads { ads[i].Document = adDocMap[ads[i].ID] }
内容的提问来源于stack exchange,提问作者Мерей Мурсальбеков
相关产品推荐
相关产品推荐

