feat: 新增index/mapping/document/search接口
This commit is contained in:
187
operations/index/search.go
Normal file
187
operations/index/search.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// Package index provides index-level operations for OpenSearch.
|
||||
package index
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"es-demo/client"
|
||||
|
||||
"github.com/opensearch-project/opensearch-go/v2/opensearchapi"
|
||||
)
|
||||
|
||||
// SearchQuery represents a search query configuration.
|
||||
type SearchQuery struct {
|
||||
// Query is the search query DSL
|
||||
Query map[string]any `json:"query,omitempty"`
|
||||
|
||||
// Size is the maximum number of hits to return (default: 10)
|
||||
Size int `json:"size,omitempty"`
|
||||
|
||||
// From is the starting offset (for pagination)
|
||||
From int `json:"from,omitempty"`
|
||||
|
||||
// Sort defines the sort order
|
||||
Sort []map[string]any `json:"sort,omitempty"`
|
||||
|
||||
// Source defines which fields to return
|
||||
Source any `json:"_source,omitempty"`
|
||||
}
|
||||
|
||||
// SearchResult represents search results.
|
||||
type SearchResult struct {
|
||||
Took int64 `json:"took"`
|
||||
Hits struct {
|
||||
Total struct {
|
||||
Value int64 `json:"value"`
|
||||
Relation string `json:"relation"`
|
||||
} `json:"total"`
|
||||
MaxScore *float64 `json:"max_score"`
|
||||
Hits []Hit `json:"hits"`
|
||||
} `json:"hits"`
|
||||
}
|
||||
|
||||
// Hit represents a single search result hit.
|
||||
type Hit struct {
|
||||
Index string `json:"_index"`
|
||||
ID string `json:"_id"`
|
||||
Score *float64 `json:"_score"`
|
||||
Source map[string]any `json:"_source"`
|
||||
}
|
||||
|
||||
// Search performs a search query on an index.
|
||||
func Search(ctx context.Context, c *client.Client, indexName string, query *SearchQuery) (*SearchResult, error) {
|
||||
if indexName == "" {
|
||||
return nil, ErrInvalidIndexName
|
||||
}
|
||||
|
||||
if query == nil {
|
||||
query = &SearchQuery{}
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if query.Size == 0 {
|
||||
query.Size = 10
|
||||
}
|
||||
|
||||
data, err := json.Marshal(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal search query: %w", err)
|
||||
}
|
||||
|
||||
req := opensearchapi.SearchRequest{
|
||||
Index: []string{indexName},
|
||||
Body: bytes.NewReader(data),
|
||||
}
|
||||
|
||||
res, err := req.Do(ctx, c.GetClient())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute search request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := res.Body.Close(); closeErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to close response body: %v\n", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if res.IsError() {
|
||||
bodyBytes, _ := io.ReadAll(res.Body)
|
||||
return nil, fmt.Errorf("search failed with status %s: %s", res.Status(), string(bodyBytes))
|
||||
}
|
||||
|
||||
var result SearchResult
|
||||
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode search result: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// MatchQuery creates a match query for full-text search.
|
||||
func MatchQuery(field string, value any) map[string]any {
|
||||
return map[string]any{
|
||||
"match": map[string]any{
|
||||
field: value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TermQuery creates a term query for exact matching.
|
||||
func TermQuery(field string, value any) map[string]any {
|
||||
return map[string]any{
|
||||
"term": map[string]any{
|
||||
field: value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RangeQuery creates a range query.
|
||||
func RangeQuery(field string, gte, lte any) map[string]any {
|
||||
rangeMap := make(map[string]any)
|
||||
if gte != nil {
|
||||
rangeMap["gte"] = gte
|
||||
}
|
||||
if lte != nil {
|
||||
rangeMap["lte"] = lte
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"range": map[string]any{
|
||||
field: rangeMap,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BoolQuery creates a bool query for combining multiple queries.
|
||||
type BoolQuery struct {
|
||||
Must []map[string]any `json:"must,omitempty"`
|
||||
Should []map[string]any `json:"should,omitempty"`
|
||||
MustNot []map[string]any `json:"must_not,omitempty"`
|
||||
Filter []map[string]any `json:"filter,omitempty"`
|
||||
}
|
||||
|
||||
// ToBoolQuery converts BoolQuery to query DSL.
|
||||
func (b *BoolQuery) ToBoolQuery() map[string]any {
|
||||
return map[string]any{
|
||||
"bool": b,
|
||||
}
|
||||
}
|
||||
|
||||
// MatchAllQuery creates a match_all query.
|
||||
func MatchAllQuery() map[string]any {
|
||||
return map[string]any{
|
||||
"match_all": map[string]any{},
|
||||
}
|
||||
}
|
||||
|
||||
// MultiMatchQuery creates a multi_match query for searching across multiple fields.
|
||||
func MultiMatchQuery(value any, fields ...string) map[string]any {
|
||||
return map[string]any{
|
||||
"multi_match": map[string]any{
|
||||
"query": value,
|
||||
"fields": fields,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WildcardQuery creates a wildcard query.
|
||||
func WildcardQuery(field string, value string) map[string]any {
|
||||
return map[string]any{
|
||||
"wildcard": map[string]any{
|
||||
field: value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// PrefixQuery creates a prefix query.
|
||||
func PrefixQuery(field string, value string) map[string]any {
|
||||
return map[string]any{
|
||||
"prefix": map[string]any{
|
||||
field: value,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user