Files
es-demo/operations/index/mapping.go

125 lines
3.3 KiB
Go

// 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"
)
// FieldMapping represents a field mapping configuration.
type FieldMapping struct {
Type string `json:"type"`
Fields map[string]any `json:"fields,omitempty"`
// Common field options
Index *bool `json:"index,omitempty"`
Store *bool `json:"store,omitempty"`
Analyzer string `json:"analyzer,omitempty"`
Format string `json:"format,omitempty"`
IgnoreAbove int `json:"ignore_above,omitempty"`
}
// PutMapping updates the mapping for an index by adding new fields.
// Note: Existing field mappings cannot be changed in OpenSearch.
func PutMapping(ctx context.Context, c *client.Client, indexName string, properties map[string]FieldMapping) error {
if indexName == "" {
return ErrInvalidIndexName
}
if len(properties) == 0 {
return fmt.Errorf("properties cannot be empty")
}
// Build mapping request
mappingBody := map[string]any{
"properties": properties,
}
data, err := json.Marshal(mappingBody)
if err != nil {
return fmt.Errorf("failed to marshal mapping: %w", err)
}
req := opensearchapi.IndicesPutMappingRequest{
Index: []string{indexName},
Body: bytes.NewReader(data),
}
res, err := req.Do(ctx, c.GetClient())
if err != nil {
return fmt.Errorf("failed to execute put mapping 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 fmt.Errorf("put mapping failed with status %s: %s", res.Status(), string(bodyBytes))
}
return nil
}
// GetMapping retrieves the mapping for an index.
func GetMapping(ctx context.Context, c *client.Client, indexName string) (map[string]any, error) {
if indexName == "" {
return nil, ErrInvalidIndexName
}
req := opensearchapi.IndicesGetMappingRequest{
Index: []string{indexName},
}
res, err := req.Do(ctx, c.GetClient())
if err != nil {
return nil, fmt.Errorf("failed to execute get mapping 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.StatusCode == 404 {
return nil, ErrIndexNotFound
}
if res.IsError() {
bodyBytes, _ := io.ReadAll(res.Body)
return nil, fmt.Errorf("get mapping failed with status %s: %s", res.Status(), string(bodyBytes))
}
var response map[string]struct {
Mappings map[string]any `json:"mappings"`
}
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode mapping response: %w", err)
}
indexData, exists := response[indexName]
if !exists {
return nil, ErrIndexNotFound
}
return indexData.Mappings, nil
}
// AddField adds a new field to the index mapping.
// This is a convenience wrapper around PutMapping for adding a single field.
func AddField(ctx context.Context, c *client.Client, indexName string, fieldName string, mapping FieldMapping) error {
return PutMapping(ctx, c, indexName, map[string]FieldMapping{
fieldName: mapping,
})
}