From 126b0bcad7fdc7239fca62abca7e480a8ff44a8f Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 04:45:20 -0500 Subject: [PATCH 1/7] try to deduplicate struct parsing code from table/{infer,tag}.go and serdes/objects.go --- astra/serdes/codecs.go | 4 +- astra/serdes/errors.go | 2 +- astra/serdes/helpers.go | 6 +- astra/serdes/objects.go | 192 ++++----------------- astra/table/infer.go | 116 +++---------- astra/table/tag.go | 18 -- internal/structutil/structutil.go | 220 +++++++++++++++++++++++++ internal/structutil/structutil_test.go | 97 +++++++++++ 8 files changed, 378 insertions(+), 277 deletions(-) create mode 100644 internal/structutil/structutil.go create mode 100644 internal/structutil/structutil_test.go diff --git a/astra/serdes/codecs.go b/astra/serdes/codecs.go index a4cb5f2..8e768d3 100644 --- a/astra/serdes/codecs.go +++ b/astra/serdes/codecs.go @@ -54,7 +54,7 @@ func (c DecodeCtx) syntaxErrorWrap(src []byte, msg string, err error) error { } func (c DecodeCtx) unmarshalTypeError(src []byte, t reflect.Type) error { - return &UnmarshalTypeError{Value: nextType(src), Type: t, Snippet: errorSnippet(src, c.Flags)} + return &UnmarshalTypeError{Value: nextJsonType(src), Type: t, Snippet: errorSnippet(src, c.Flags)} } func (c DecodeCtx) unmarshalValueTypeError(src []byte, t reflect.Type, value string) error { @@ -62,7 +62,7 @@ func (c DecodeCtx) unmarshalValueTypeError(src []byte, t reflect.Type, value str } func (c DecodeCtx) unmarshalTypeErrorWrap(src []byte, t reflect.Type, err error) error { - return &UnmarshalTypeError{Value: nextType(src), Type: t, Snippet: errorSnippet(src, c.Flags), Err: err} + return &UnmarshalTypeError{Value: nextJsonType(src), Type: t, Snippet: errorSnippet(src, c.Flags), Err: err} } func (c DecodeCtx) unmarshalValueTypeErrorWrap(src []byte, t reflect.Type, value string, err error) error { diff --git a/astra/serdes/errors.go b/astra/serdes/errors.go index b25aea1..bf3b092 100644 --- a/astra/serdes/errors.go +++ b/astra/serdes/errors.go @@ -338,7 +338,7 @@ func (e InvalidUnmarshalError) Error() string { return "serdes: Deserialize(nil " + e.Type.String() + ")" } -func nextType(src []byte) string { +func nextJsonType(src []byte) string { src = skipWS(src) if len(src) == 0 { return "EOF" diff --git a/astra/serdes/helpers.go b/astra/serdes/helpers.go index 914bdac..e125b43 100644 --- a/astra/serdes/helpers.go +++ b/astra/serdes/helpers.go @@ -39,7 +39,7 @@ func parseInt(ctx DecodeCtx, src []byte) ([]byte, int64, error) { } if end == 0 { - return src, 0, ctx.syntaxError(src, "expected int64 but found "+nextType(src)) + return src, 0, ctx.syntaxError(src, "expected int64 but found "+nextJsonType(src)) } num, err := strconv.ParseInt(unsafeString(src[:end]), 10, 64) @@ -57,7 +57,7 @@ func parseUint(ctx DecodeCtx, src []byte) ([]byte, uint64, error) { } if end == 0 { - return src, 0, ctx.syntaxError(src, "expected uint64 but found "+nextType(src)) + return src, 0, ctx.syntaxError(src, "expected uint64 but found "+nextJsonType(src)) } num, err := strconv.ParseUint(unsafeString(src[:end]), 10, 64) @@ -76,7 +76,7 @@ var floatChars = [256]uint8{ func parseFloat(ctx DecodeCtx, src []byte) ([]byte, float64, error) { src, numStr, err := parseNumber(ctx, src) if err != nil { - return src, 0, ctx.syntaxError(src, "expected float64 but found "+nextType(src)) + return src, 0, ctx.syntaxError(src, "expected float64 but found "+nextJsonType(src)) } f, err := strconv.ParseFloat(unsafeString(numStr), 64) diff --git a/astra/serdes/objects.go b/astra/serdes/objects.go index 35edc00..acc9e58 100644 --- a/astra/serdes/objects.go +++ b/astra/serdes/objects.go @@ -17,9 +17,10 @@ package serdes import ( "fmt" "reflect" - "sort" "strings" "unsafe" + + "github.com/datastax/astra-db-go/v2/internal/structutil" ) // Serdes @@ -188,7 +189,6 @@ type fieldInfo struct { typ reflect.Type codec codec offset uintptr - ord int meta jsonMeta empty func(unsafe.Pointer) bool } @@ -196,8 +196,6 @@ type fieldInfo struct { type jsonMeta struct { name string omitempty bool - ignored bool - tagged bool allowUnexported bool } @@ -239,189 +237,61 @@ func compileStructInfo(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr b } func compileStructFields(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr bool) ([]fieldInfo, error) { - type embeddedField struct { - ord int - offset uintptr - pointer bool - unexported bool - subtype *structInfo - subfield *fieldInfo + metas, err := structutil.GetFields(t) + if err != nil { + return nil, err } - topLevelNames := make(map[string]struct{}) - ambiguousNames := make(map[string]int) - ambiguousTags := make(map[string]int) - - fields := make([]fieldInfo, 0, t.NumField()) - embeddedFields := make([]embeddedField, 0, 5) - - for i := range t.NumField() { - f := t.Field(i) - - var ( - embedded = f.Anonymous - unexported = len(f.PkgPath) != 0 - ) - - meta := parseJsonMeta(f) - - if meta.ignored { - continue - } - - if unexported && !embedded && !meta.allowUnexported { - continue - } - - if embedded && !meta.tagged { // embedded w/out a tagged name - typ := f.Type - ptr := f.Type.Kind() == reflect.Ptr - - if ptr { - typ = typ.Elem() - } + fields := make([]fieldInfo, 0, len(metas)) - if typ.Kind() == reflect.Struct { - subtype, err := compileStructInfo(ctx, typ, seen, canAddr) - if err != nil { - return nil, err - } + for _, meta := range metas { + c := resolveCodec(ctx, meta.Field.Type, seen, canAddr) + c, offset := resolveCodecInEmbeddedFields(t, meta.Index, c, meta) - for j := range subtype.fields { - embeddedFields = append(embeddedFields, embeddedField{ - ord: i<<32 | j, - offset: f.Offset, - pointer: ptr, - unexported: unexported, - subtype: subtype, - subfield: &subtype.fields[j], - }) - } - - continue - } - - if unexported && !meta.allowUnexported { // ignore unallowed unexported non-struct types - continue - } + jm := jsonMeta{ + name: meta.Name, + omitempty: meta.OmitEmpty, + allowUnexported: meta.AllowUnexported, } - c := resolveCodec(ctx, f.Type, seen, canAddr) - fields = append(fields, fieldInfo{ codec: c, - offset: f.Offset, - meta: meta, - ord: i << 32, - typ: f.Type, - empty: emptyFuncFor(meta.omitempty, f.Type), + offset: offset, + meta: jm, + typ: meta.Field.Type, + empty: emptyFuncFor(meta.OmitEmpty, meta.Field.Type), }) - - // seeds the counters so embedded fields know they are secondary - topLevelNames[meta.name] = struct{}{} - ambiguousNames[meta.name]++ - ambiguousTags[meta.name]++ - } - - // first pass to count the number of fields w/ each name so we can resolve ambiguities in the next pass - for _, embfield := range embeddedFields { - ambiguousNames[embfield.subfield.meta.name]++ - if embfield.subfield.meta.tagged { - ambiguousTags[embfield.subfield.meta.name]++ - } - } - - for _, embfield := range embeddedFields { - subfield := *embfield.subfield - - switch resolveEmbeddedAmbiguity(subfield.meta, topLevelNames, ambiguousNames, ambiguousTags) { - case shadowed: - continue - case ambiguous: - return nil, &UnsupportedValueError{Msg: fmt.Sprintf("unresolvable ambiguity for field %q in struct %s", subfield.meta.name, t.String())} - case unambiguous: - // all good - } - - if embfield.pointer { - subfield.codec = mkEmbeddedStructPointerCodec(embfield.subtype.typ, embfield.unexported, subfield.meta.allowUnexported, subfield.offset, subfield.codec) - subfield.offset = embfield.offset - } else { - subfield.offset += embfield.offset - } - - // prevents dominant flags more than one level below the embedded one - subfield.meta.tagged = false - - // ensures order of the fields is the same is in the struct type - subfield.ord = embfield.ord - - fields = append(fields, subfield) } for i := range fields { fields[i].prefix = []byte(`,"` + fields[i].meta.name + `":`) } - sort.Slice(fields, func(i, j int) bool { return fields[i].ord < fields[j].ord }) - return fields, nil } -func parseJsonMeta(f reflect.StructField) jsonMeta { - var info jsonMeta - info.name = f.Name - - if parts := strings.Split(f.Tag.Get("json"), ","); len(parts) != 0 { - if len(parts[0]) > 0 { - info.name = parts[0] - info.tagged = true - } - - if info.name == "-" && len(parts) == 1 { - info.ignored = true - return info - } - - for _, opt := range parts[1:] { // TODO do we want to somehow warn if 'string' is used as an option since I don't want to support it? - switch opt { - case "omitempty": - info.omitempty = true - case "allowunexported": - info.allowUnexported = true - } - } - } - - return info -} - -type embeddedAmbiguity int +func resolveCodecInEmbeddedFields(parentType reflect.Type, indexPath []int, leafCodec codec, leafMeta structutil.FieldMeta) (codec, uintptr) { + f := parentType.Field(indexPath[0]) -const ( - unambiguous embeddedAmbiguity = iota - shadowed - ambiguous -) - -func resolveEmbeddedAmbiguity(meta jsonMeta, topLevelNames map[string]struct{}, nameCounts, tagCounts map[string]int) embeddedAmbiguity { - if _, exists := topLevelNames[meta.name]; exists { - return shadowed // top level field with the same name exists so ignore this embedded field + if len(indexPath) == 1 { + return leafCodec, f.Offset } - if nameCounts[meta.name] == 1 { - return unambiguous // no collisions so all good to go + embeddedType := f.Type + isEmbeddedStructPtr := embeddedType.Kind() == reflect.Pointer + if isEmbeddedStructPtr { + embeddedType = embeddedType.Elem() } - if tagCounts[meta.name] == 1 && meta.tagged { - return unambiguous // multiple fields with the same name, so the field with the tag wins - } + innerCodec, innerOffset := resolveCodecInEmbeddedFields(embeddedType, indexPath[1:], leafCodec, leafMeta) - if tagCounts[meta.name] != 1 { - return ambiguous // zero or multiple tags w/ the same name so we can't resolve anything + if isEmbeddedStructPtr { + unexported := len(f.PkgPath) != 0 + wrappedCodec := mkEmbeddedStructPointerCodec(f.Type.Elem(), unexported, leafMeta.AllowUnexported, innerOffset, innerCodec) + return wrappedCodec, f.Offset } - return shadowed // field collided and lost to a tagged field. + return innerCodec, innerOffset + f.Offset } // vendored from segmentio/encode/json diff --git a/astra/table/infer.go b/astra/table/infer.go index 5528552..ee87dc5 100644 --- a/astra/table/infer.go +++ b/astra/table/infer.go @@ -24,6 +24,7 @@ import ( "time" "github.com/datastax/astra-db-go/v2/astra/datatypes" + "github.com/datastax/astra-db-go/v2/internal/structutil" ) // Well-known reflect types for comparison in type mapping. @@ -48,11 +49,6 @@ type fieldData struct { fieldIdx int // position for stable ordering } -// collectFields iterates over the fields of a struct type (including promoted -// fields from embedded structs at any depth) and returns their processed -// metadata. Outer fields shadow embedded fields with the same column name, -// matching encoding/json promotion semantics. Two direct fields on the outer -// struct with the same column name are rejected as a user error. func collectFields(t reflect.Type) ([]fieldData, error) { for t.Kind() == reflect.Pointer { t = t.Elem() @@ -61,107 +57,43 @@ func collectFields(t reflect.Type) ([]fieldData, error) { return nil, fmt.Errorf("expected struct, got %s", t.Kind()) } + metas, err := structutil.GetFields(t) + if err != nil { + return nil, err + } + seen := make(map[string]bool) var result []fieldData - var queue []reflect.Type - // Direct fields on the outer struct. Duplicate column names here are a - // user error (two fields with the same json tag on the same struct). - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - if !f.IsExported() { - continue - } - if f.Anonymous { - if ft := derefStruct(f.Type); ft != nil { - queue = append(queue, ft) - } + for i, meta := range metas { + // Skip $-prefixed fields (API metadata: $similarity, $vector, $vectorize) + if strings.HasPrefix(meta.Name, "$") { continue } - fd, skip, err := processField(f, i) + + tag, err := parseAstraTag(meta.Field.Tag.Get("astra")) if err != nil { - return nil, err + return nil, fmt.Errorf("field %q: %w", meta.Field.Name, err) } - if skip { + + if tag.skip { continue } - if seen[fd.columnName] { - return nil, fmt.Errorf("duplicate column name %q", fd.columnName) - } - seen[fd.columnName] = true - result = append(result, fd) - } - // BFS over embedded types: outer-most level wins, nested embeds are - // descended into so promotion works at arbitrary depth. - baseOffset := t.NumField() - for len(queue) > 0 { - et := queue[0] - queue = queue[1:] - for i := 0; i < et.NumField(); i++ { - f := et.Field(i) - if !f.IsExported() { - continue - } - if f.Anonymous { - if ft := derefStruct(f.Type); ft != nil { - queue = append(queue, ft) - } - continue - } - fd, skip, err := processField(f, baseOffset+i) - if err != nil { - return nil, err - } - if skip || seen[fd.columnName] { - continue - } - seen[fd.columnName] = true - result = append(result, fd) + if seen[meta.Name] { + return nil, fmt.Errorf("duplicate column name %q", meta.Name) } - baseOffset += et.NumField() - } + seen[meta.Name] = true - return result, nil -} - -// derefStruct unwraps pointer types and returns the underlying struct type, -// or nil if t does not resolve to a struct. -func derefStruct(t reflect.Type) reflect.Type { - for t.Kind() == reflect.Pointer { - t = t.Elem() + result = append(result, fieldData{ + columnName: meta.Name, + goType: meta.Field.Type, + tag: tag, + fieldIdx: i, + }) } - if t.Kind() == reflect.Struct { - return t - } - return nil -} -// processField extracts fieldData from a single struct field. -func processField(f reflect.StructField, idx int) (fieldData, bool, error) { - name, include := columnName(f) - if !include { - return fieldData{}, true, nil - } - // Skip $-prefixed fields (API metadata: $similarity, $vector, $vectorize) - if strings.HasPrefix(name, "$") { - return fieldData{}, true, nil - } - - tag, err := parseAstraTag(f.Tag.Get("astra")) - if err != nil { - return fieldData{}, false, fmt.Errorf("field %q: %w", f.Name, err) - } - if tag.skip { - return fieldData{}, true, nil - } - - return fieldData{ - columnName: name, - goType: f.Type, - tag: tag, - fieldIdx: idx, - }, false, nil + return result, nil } // goTypeToColumn converts a Go reflect.Type to a table Column using tag metadata. diff --git a/astra/table/tag.go b/astra/table/tag.go index c17ff1e..e845e04 100644 --- a/astra/table/tag.go +++ b/astra/table/tag.go @@ -16,7 +16,6 @@ package table import ( "fmt" - "reflect" "strconv" "strings" ) @@ -129,20 +128,3 @@ func parseAstraTag(raw string) (tagInfo, error) { return info, nil } - -// columnName extracts the column name from a struct field's json tag, -// falling back to the field name if no json tag is present. -func columnName(f reflect.StructField) (name string, include bool) { - jt := f.Tag.Get("json") - if jt == "" { - return f.Name, true - } - name, _, _ = strings.Cut(jt, ",") - if name == "-" { - return "", false - } - if name == "" { - return f.Name, true - } - return name, true -} diff --git a/internal/structutil/structutil.go b/internal/structutil/structutil.go new file mode 100644 index 0000000..0024b40 --- /dev/null +++ b/internal/structutil/structutil.go @@ -0,0 +1,220 @@ +package structutil + +import ( + "fmt" + "reflect" + "sort" + "strings" +) + +// FieldMeta contains the fully parsed metadata for a single mapped field. +type FieldMeta struct { + Name string + OmitEmpty bool + AllowUnexported bool + + Field reflect.StructField + Index []int // The sequence of struct field indices to reach this field + + tagged bool +} + +type embeddedAmbiguity int + +const ( + unambiguous embeddedAmbiguity = iota + shadowed + ambiguous +) + +// parseJSONTag parses standard encoding/json style struct tags. +func parseJSONTag(f reflect.StructField) (name string, ignored, omitempty, tagged, allowUnexported bool) { + name = f.Name + + if parts := strings.Split(f.Tag.Get("json"), ","); len(parts) != 0 { + if len(parts[0]) > 0 { + name = parts[0] + tagged = true + } + + if name == "-" && len(parts) == 1 { + ignored = true + return + } + + for _, opt := range parts[1:] { + switch opt { + case "omitempty": + omitempty = true + case "allowunexported": + allowUnexported = true + } + } + } + + return +} + +func resolveEmbeddedAmbiguity(name string, tagged bool, topLevelNames map[string]struct{}, nameCounts, tagCounts map[string]int) embeddedAmbiguity { + if _, exists := topLevelNames[name]; exists { + return shadowed // top level field with the same name exists so ignore this embedded field + } + + if nameCounts[name] == 1 { + return unambiguous // no collisions so all good to go + } + + if tagCounts[name] == 1 && tagged { + return unambiguous // multiple fields with the same name, so the field with the tag wins + } + + if tagCounts[name] != 1 { + return ambiguous // zero or multiple tags w/ the same name so we can't resolve anything + } + + return shadowed // field collided and lost to a tagged field. +} + +// GetFields flattens a struct type, resolving embedded fields and name collisions. +// It follows encoding/json shadowing and ambiguity rules. +func GetFields(t reflect.Type) ([]FieldMeta, error) { + seen := make(map[reflect.Type]bool) + fields, err := getFieldsRecursive(t, seen, nil) + if err != nil { + return nil, err + } + + // Sort fields lexicographically by their Index to preserve struct definition order + sort.Slice(fields, func(i, j int) bool { + a, b := fields[i].Index, fields[j].Index + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + for k := 0; k < minLen; k++ { + if a[k] != b[k] { + return a[k] < b[k] + } + } + return len(a) < len(b) + }) + + return fields, nil +} + +func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []int) ([]FieldMeta, error) { + if t.Kind() == reflect.Pointer { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, nil + } + + if seen[t] { + return nil, nil // embedded cycle detected + } + seen[t] = true + defer delete(seen, t) + + type embeddedField struct { + subfield *FieldMeta + } + + topLevelNames := make(map[string]struct{}) + ambiguousNames := make(map[string]int) + ambiguousTags := make(map[string]int) + + fields := make([]FieldMeta, 0, t.NumField()) + var embeddedFields []embeddedField + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + embedded := f.Anonymous + unexported := len(f.PkgPath) != 0 + + name, ignored, omitempty, tagged, allowUnexported := parseJSONTag(f) + + if ignored { + continue + } + + if unexported && !embedded && !allowUnexported { + continue + } + + if embedded && !tagged { + typ := f.Type + ptr := f.Type.Kind() == reflect.Ptr + + if ptr { + typ = typ.Elem() + } + + if typ.Kind() == reflect.Struct { + idx := make([]int, len(basePath), len(basePath)+1) + copy(idx, basePath) + idx = append(idx, i) + + subtypeFields, err := getFieldsRecursive(typ, seen, idx) + if err != nil { + return nil, err + } + + for j := range subtypeFields { + embeddedFields = append(embeddedFields, embeddedField{ + subfield: &subtypeFields[j], + }) + } + continue + } + + if unexported && !allowUnexported { + continue + } + } + + idx := make([]int, len(basePath), len(basePath)+1) + copy(idx, basePath) + idx = append(idx, i) + + fields = append(fields, FieldMeta{ + Name: name, + OmitEmpty: omitempty, + AllowUnexported: allowUnexported, + Field: f, + Index: idx, + tagged: tagged, + }) + + topLevelNames[name] = struct{}{} + ambiguousNames[name]++ + ambiguousTags[name]++ + } + + for _, embfield := range embeddedFields { + ambiguousNames[embfield.subfield.Name]++ + if embfield.subfield.tagged { + ambiguousTags[embfield.subfield.Name]++ + } + } + + for _, embfield := range embeddedFields { + subfield := *embfield.subfield + + switch resolveEmbeddedAmbiguity(subfield.Name, subfield.tagged, topLevelNames, ambiguousNames, ambiguousTags) { + case shadowed: + continue + case ambiguous: + return nil, fmt.Errorf("unresolvable ambiguity for field %q in struct %s", subfield.Name, t.String()) + case unambiguous: + // all good + } + + // prevents dominant flags more than one level below the embedded one + subfield.tagged = false + + fields = append(fields, subfield) + } + + return fields, nil +} diff --git a/internal/structutil/structutil_test.go b/internal/structutil/structutil_test.go new file mode 100644 index 0000000..0e0ef8c --- /dev/null +++ b/internal/structutil/structutil_test.go @@ -0,0 +1,97 @@ +package structutil + +import ( + "reflect" + "testing" +) + +type E1 struct { + A string `json:"a"` + B string `json:"b"` +} + +type E2 struct { + B string `json:"b"` + C string `json:"c"` +} + +type TopShadow struct { + E1 + A string `json:"a"` // Shadows E1.A +} + +type TopAmbiguous struct { + E1 + E2 // Both E1 and E2 have a tagged "b" field -> unresolvable ambiguity +} + +type PtrCycle struct { + *PtrCycle + Val string `json:"val"` +} + +type Unexported struct { + Exported string + unexported string `json:"unexported,allowunexported"` + ignored string `json:"-"` +} + +func TestGetFields_Shadowing(t *testing.T) { + fields, err := GetFields(reflect.TypeOf(TopShadow{})) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if len(fields) != 2 { + t.Fatalf("expected 2 fields, got %d", len(fields)) + } + + if fields[0].Name != "b" || len(fields[0].Index) != 2 { + t.Errorf("expected first field 'b' from embedded E1, got %s with index %v", fields[0].Name, fields[0].Index) + } + + if fields[1].Name != "a" || len(fields[1].Index) != 1 { + t.Errorf("expected second field 'a' from top level, got %s with index %v", fields[1].Name, fields[1].Index) + } +} + +func TestGetFields_Ambiguous(t *testing.T) { + _, err := GetFields(reflect.TypeOf(TopAmbiguous{})) + if err == nil { + t.Fatal("expected error due to ambiguous field 'b', got nil") + } +} + +func TestGetFields_PtrCycle(t *testing.T) { + fields, err := GetFields(reflect.TypeOf(PtrCycle{})) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if len(fields) != 1 { + t.Fatalf("expected 1 field, got %d", len(fields)) + } + + if fields[0].Name != "val" { + t.Errorf("expected field 'val', got %s", fields[0].Name) + } +} + +func TestGetFields_Unexported(t *testing.T) { + fields, err := GetFields(reflect.TypeOf(Unexported{})) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if len(fields) != 2 { + t.Fatalf("expected 2 fields, got %d", len(fields)) + } + + if fields[0].Name != "Exported" { + t.Errorf("expected first field 'Exported', got %s", fields[0].Name) + } + + if fields[1].Name != "unexported" { + t.Errorf("expected second field 'unexported', got %s", fields[1].Name) + } +} From 5c1d26a926027c8c1ef54e4f275cc6b4c6c62d3f Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 17:11:11 -0500 Subject: [PATCH 2/7] add missing copyright --- internal/structutil/structutil.go | 14 ++++++++++++++ internal/structutil/structutil_test.go | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/internal/structutil/structutil.go b/internal/structutil/structutil.go index 0024b40..99798af 100644 --- a/internal/structutil/structutil.go +++ b/internal/structutil/structutil.go @@ -1,3 +1,17 @@ +// Copyright IBM Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package structutil import ( diff --git a/internal/structutil/structutil_test.go b/internal/structutil/structutil_test.go index 0e0ef8c..7e14fd1 100644 --- a/internal/structutil/structutil_test.go +++ b/internal/structutil/structutil_test.go @@ -1,3 +1,17 @@ +// Copyright IBM Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package structutil import ( From 26d6a7f507a083b92e12b0a175c2bd4dd643748a Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 17:50:13 -0500 Subject: [PATCH 3/7] work --- astra/serdes/objects.go | 35 ++--- astra/table/infer.go | 4 +- .../{structutil => reflectutil}/structutil.go | 134 +++++++++--------- .../structutil_test.go | 17 +-- 4 files changed, 88 insertions(+), 102 deletions(-) rename internal/{structutil => reflectutil}/structutil.go (90%) rename internal/{structutil => reflectutil}/structutil_test.go (85%) diff --git a/astra/serdes/objects.go b/astra/serdes/objects.go index acc9e58..40906b3 100644 --- a/astra/serdes/objects.go +++ b/astra/serdes/objects.go @@ -20,7 +20,7 @@ import ( "strings" "unsafe" - "github.com/datastax/astra-db-go/v2/internal/structutil" + "github.com/datastax/astra-db-go/v2/internal/reflectutil" ) // Serdes @@ -38,10 +38,10 @@ func mkStructCodec(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr bool) } } -func mkEmbeddedStructPointerCodec(t reflect.Type, unexported bool, allowed bool, offset uintptr, field codec) codec { +func mkEmbeddedStructPointerCodec(t reflect.Type, unexported bool, offset uintptr, field codec) codec { return codec{ mkEmbeddedStructPointerEncoder(field.encode), - mkEmbeddedStructPointerDecoder(t, unexported, allowed, offset, field.decode), + mkEmbeddedStructPointerDecoder(t, unexported, offset, field.decode), } } @@ -160,13 +160,13 @@ func mkStructDecoder(info *structInfo) decoder { } } -func mkEmbeddedStructPointerDecoder(t reflect.Type, unexported bool, allowed bool, offset uintptr, decode decoder) decoder { +func mkEmbeddedStructPointerDecoder(t reflect.Type, unexported bool, offset uintptr, decode decoder) decoder { return func(ctx DecodeCtx, src []byte, p unsafe.Pointer) ([]byte, error) { v := *(*unsafe.Pointer)(p) if v == nil { - if unexported && !allowed { - return nil, &UnsupportedValueError{Msg: fmt.Sprintf("cannot set embedded pointer to unexported struct without the \"allowunexported\" struct tag: %s", t)} + if unexported { + return nil, &UnsupportedValueError{Msg: fmt.Sprintf("cannot set embedded pointer to unexported struct: %s", t)} } v = unsafe.Pointer(reflect.New(t).Pointer()) *(*unsafe.Pointer)(p) = v @@ -194,9 +194,8 @@ type fieldInfo struct { } type jsonMeta struct { - name string - omitempty bool - allowUnexported bool + name string + omitempty bool } func (i structInfo) String() string { @@ -237,7 +236,7 @@ func compileStructInfo(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr b } func compileStructFields(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr bool) ([]fieldInfo, error) { - metas, err := structutil.GetFields(t) + metas, err := reflectutil.GetFields(t) if err != nil { return nil, err } @@ -249,9 +248,8 @@ func compileStructFields(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr c, offset := resolveCodecInEmbeddedFields(t, meta.Index, c, meta) jm := jsonMeta{ - name: meta.Name, - omitempty: meta.OmitEmpty, - allowUnexported: meta.AllowUnexported, + name: meta.Name, + omitempty: meta.OmitEmpty, } fields = append(fields, fieldInfo{ @@ -260,17 +258,14 @@ func compileStructFields(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr meta: jm, typ: meta.Field.Type, empty: emptyFuncFor(meta.OmitEmpty, meta.Field.Type), + prefix: []byte(`,"` + jm.name + `":`), }) } - - for i := range fields { - fields[i].prefix = []byte(`,"` + fields[i].meta.name + `":`) - } - + return fields, nil } -func resolveCodecInEmbeddedFields(parentType reflect.Type, indexPath []int, leafCodec codec, leafMeta structutil.FieldMeta) (codec, uintptr) { +func resolveCodecInEmbeddedFields(parentType reflect.Type, indexPath []int, leafCodec codec, leafMeta reflectutil.FieldMeta) (codec, uintptr) { f := parentType.Field(indexPath[0]) if len(indexPath) == 1 { @@ -287,7 +282,7 @@ func resolveCodecInEmbeddedFields(parentType reflect.Type, indexPath []int, leaf if isEmbeddedStructPtr { unexported := len(f.PkgPath) != 0 - wrappedCodec := mkEmbeddedStructPointerCodec(f.Type.Elem(), unexported, leafMeta.AllowUnexported, innerOffset, innerCodec) + wrappedCodec := mkEmbeddedStructPointerCodec(f.Type.Elem(), unexported, innerOffset, innerCodec) return wrappedCodec, f.Offset } diff --git a/astra/table/infer.go b/astra/table/infer.go index ee87dc5..f28ccf9 100644 --- a/astra/table/infer.go +++ b/astra/table/infer.go @@ -24,7 +24,7 @@ import ( "time" "github.com/datastax/astra-db-go/v2/astra/datatypes" - "github.com/datastax/astra-db-go/v2/internal/structutil" + "github.com/datastax/astra-db-go/v2/internal/reflectutil" ) // Well-known reflect types for comparison in type mapping. @@ -57,7 +57,7 @@ func collectFields(t reflect.Type) ([]fieldData, error) { return nil, fmt.Errorf("expected struct, got %s", t.Kind()) } - metas, err := structutil.GetFields(t) + metas, err := reflectutil.GetFields(t) if err != nil { return nil, err } diff --git a/internal/structutil/structutil.go b/internal/reflectutil/structutil.go similarity index 90% rename from internal/structutil/structutil.go rename to internal/reflectutil/structutil.go index 99798af..626cccf 100644 --- a/internal/structutil/structutil.go +++ b/internal/reflectutil/structutil.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package structutil +package reflectutil import ( "fmt" @@ -23,9 +23,8 @@ import ( // FieldMeta contains the fully parsed metadata for a single mapped field. type FieldMeta struct { - Name string - OmitEmpty bool - AllowUnexported bool + Name string + OmitEmpty bool Field reflect.StructField Index []int // The sequence of struct field indices to reach this field @@ -33,62 +32,6 @@ type FieldMeta struct { tagged bool } -type embeddedAmbiguity int - -const ( - unambiguous embeddedAmbiguity = iota - shadowed - ambiguous -) - -// parseJSONTag parses standard encoding/json style struct tags. -func parseJSONTag(f reflect.StructField) (name string, ignored, omitempty, tagged, allowUnexported bool) { - name = f.Name - - if parts := strings.Split(f.Tag.Get("json"), ","); len(parts) != 0 { - if len(parts[0]) > 0 { - name = parts[0] - tagged = true - } - - if name == "-" && len(parts) == 1 { - ignored = true - return - } - - for _, opt := range parts[1:] { - switch opt { - case "omitempty": - omitempty = true - case "allowunexported": - allowUnexported = true - } - } - } - - return -} - -func resolveEmbeddedAmbiguity(name string, tagged bool, topLevelNames map[string]struct{}, nameCounts, tagCounts map[string]int) embeddedAmbiguity { - if _, exists := topLevelNames[name]; exists { - return shadowed // top level field with the same name exists so ignore this embedded field - } - - if nameCounts[name] == 1 { - return unambiguous // no collisions so all good to go - } - - if tagCounts[name] == 1 && tagged { - return unambiguous // multiple fields with the same name, so the field with the tag wins - } - - if tagCounts[name] != 1 { - return ambiguous // zero or multiple tags w/ the same name so we can't resolve anything - } - - return shadowed // field collided and lost to a tagged field. -} - // GetFields flattens a struct type, resolving embedded fields and name collisions. // It follows encoding/json shadowing and ambiguity rules. func GetFields(t reflect.Type) ([]FieldMeta, error) { @@ -146,13 +89,13 @@ func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []i embedded := f.Anonymous unexported := len(f.PkgPath) != 0 - name, ignored, omitempty, tagged, allowUnexported := parseJSONTag(f) + name, ignored, omitempty, tagged := parseJSONTag(f) if ignored { continue } - if unexported && !embedded && !allowUnexported { + if unexported && !embedded { continue } @@ -182,7 +125,7 @@ func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []i continue } - if unexported && !allowUnexported { + if unexported { continue } } @@ -192,12 +135,11 @@ func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []i idx = append(idx, i) fields = append(fields, FieldMeta{ - Name: name, - OmitEmpty: omitempty, - AllowUnexported: allowUnexported, - Field: f, - Index: idx, - tagged: tagged, + Name: name, + OmitEmpty: omitempty, + Field: f, + Index: idx, + tagged: tagged, }) topLevelNames[name] = struct{}{} @@ -232,3 +174,57 @@ func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []i return fields, nil } + +// parseJSONTag parses standard encoding/json style struct tags. +func parseJSONTag(f reflect.StructField) (name string, ignored, omitempty, tagged bool) { + name = f.Name + + if parts := strings.Split(f.Tag.Get("json"), ","); len(parts) != 0 { + if len(parts[0]) > 0 { + name = parts[0] + tagged = true + } + + if name == "-" && len(parts) == 1 { + ignored = true + return + } + + for _, opt := range parts[1:] { + switch opt { + case "omitempty": + omitempty = true + } + } + } + + return +} + +type embeddedAmbiguity int + +const ( + unambiguous embeddedAmbiguity = iota + shadowed + ambiguous +) + +func resolveEmbeddedAmbiguity(name string, tagged bool, topLevelNames map[string]struct{}, nameCounts, tagCounts map[string]int) embeddedAmbiguity { + if _, exists := topLevelNames[name]; exists { + return shadowed // top level field with the same name exists so ignore this embedded field + } + + if nameCounts[name] == 1 { + return unambiguous // no collisions so all good to go + } + + if tagCounts[name] == 1 && tagged { + return unambiguous // multiple fields with the same name, so the field with the tag wins + } + + if tagCounts[name] != 1 { + return ambiguous // zero or multiple tags w/ the same name so we can't resolve anything + } + + return shadowed // field collided and lost to a tagged field. +} diff --git a/internal/structutil/structutil_test.go b/internal/reflectutil/structutil_test.go similarity index 85% rename from internal/structutil/structutil_test.go rename to internal/reflectutil/structutil_test.go index 7e14fd1..63562e9 100644 --- a/internal/structutil/structutil_test.go +++ b/internal/reflectutil/structutil_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package structutil +package reflectutil import ( "reflect" @@ -45,9 +45,8 @@ type PtrCycle struct { } type Unexported struct { - Exported string - unexported string `json:"unexported,allowunexported"` - ignored string `json:"-"` + Exported string + ignored string `json:"hi"` } func TestGetFields_Shadowing(t *testing.T) { @@ -97,15 +96,11 @@ func TestGetFields_Unexported(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - if len(fields) != 2 { - t.Fatalf("expected 2 fields, got %d", len(fields)) + if len(fields) != 1 { + t.Fatalf("expected 1 field, got %d", len(fields)) } if fields[0].Name != "Exported" { - t.Errorf("expected first field 'Exported', got %s", fields[0].Name) - } - - if fields[1].Name != "unexported" { - t.Errorf("expected second field 'unexported', got %s", fields[1].Name) + t.Errorf("expected field 'Exported', got %s", fields[0].Name) } } From 1215ec21d28c63cac89cef2dd2a07e890054d634 Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 18:21:59 -0500 Subject: [PATCH 4/7] little work --- astra/serdes/objects.go | 64 +++++++++----------- astra/table/infer.go | 80 ++++++++++++------------- internal/reflectutil/structutil.go | 35 ++++++----- internal/reflectutil/structutil_test.go | 16 ++--- 4 files changed, 93 insertions(+), 102 deletions(-) diff --git a/astra/serdes/objects.go b/astra/serdes/objects.go index 40906b3..e0d4531 100644 --- a/astra/serdes/objects.go +++ b/astra/serdes/objects.go @@ -55,7 +55,7 @@ func mkStructEncoder(info *structInfo) encoder { f := &info.fields[i] v := unsafe.Add(p, f.offset) - if f.meta.omitempty && f.empty(v) { + if f.omitempty && f.empty(v) { continue } @@ -68,7 +68,7 @@ func mkStructEncoder(info *structInfo) encoder { dst = append(dst, f.prefix...) } - ctx.fieldHint = extractFieldHint(f.meta.name) + ctx.fieldHint = extractFieldHint(f.name) var next []byte var err error @@ -79,7 +79,7 @@ func mkStructEncoder(info *structInfo) encoder { continue } - return dst[:start], wrapField(err, info.typ.Name(), f.meta.name) + return dst[:start], wrapField(err, info.typ.Name(), f.name) } dst = next } @@ -144,11 +144,11 @@ func mkStructDecoder(info *structInfo) decoder { f := &info.fields[fieldIdx] ptr := unsafe.Pointer(uintptr(p) + f.offset) - ctx.fieldHint = extractFieldHint(f.meta.name) + ctx.fieldHint = extractFieldHint(f.name) src, err = f.codec.decode(ctx, src, ptr) if err != nil { - return src, wrapField(err, info.typ.Name(), f.meta.name) + return src, wrapField(err, info.typ.Name(), f.name) } } else { src, err = skipValue(ctx, src) @@ -185,24 +185,20 @@ type structInfo struct { } type fieldInfo struct { - prefix []byte - typ reflect.Type - codec codec - offset uintptr - meta jsonMeta - empty func(unsafe.Pointer) bool -} - -type jsonMeta struct { + prefix []byte + typ reflect.Type + codec codec + offset uintptr name string omitempty bool + empty func(unsafe.Pointer) bool } func (i structInfo) String() string { var sb strings.Builder _, _ = fmt.Fprintf(&sb, "struct %s {", i.typ.String()) for _, f := range i.fields { - _, _ = fmt.Fprintf(&sb, "\n %s (offset: %d, type: %s, codec: %p)", f.meta.name, f.offset, f.typ.String(), f.codec.encode) + _, _ = fmt.Fprintf(&sb, "\n %s (offset: %d, type: %s, codec: %p)", f.name, f.offset, f.typ.String(), f.codec.encode) } sb.WriteString("\n}") return sb.String() @@ -229,40 +225,36 @@ func compileStructInfo(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr b for i := range fields { f := &fields[i] - info.offsets[f.meta.name] = i + info.offsets[f.name] = i } return info, nil } func compileStructFields(ctx codecCtx, t reflect.Type, seen seenStructs, canAddr bool) ([]fieldInfo, error) { - metas, err := reflectutil.GetFields(t) + fields, err := reflectutil.GetFlattenedFields(t) if err != nil { - return nil, err + return nil, &UnsupportedValueError{Msg: fmt.Sprintf("failed to compile struct %s: %v", t.String(), err)} } - fields := make([]fieldInfo, 0, len(metas)) + ret := make([]fieldInfo, 0, len(fields)) - for _, meta := range metas { - c := resolveCodec(ctx, meta.Field.Type, seen, canAddr) - c, offset := resolveCodecInEmbeddedFields(t, meta.Index, c, meta) + for _, f := range fields { + c := resolveCodec(ctx, f.Field.Type, seen, canAddr) + c, offset := resolveCodecInEmbeddedFields(t, f.Path, c, f) - jm := jsonMeta{ - name: meta.Name, - omitempty: meta.OmitEmpty, - } - - fields = append(fields, fieldInfo{ - codec: c, - offset: offset, - meta: jm, - typ: meta.Field.Type, - empty: emptyFuncFor(meta.OmitEmpty, meta.Field.Type), - prefix: []byte(`,"` + jm.name + `":`), + ret = append(ret, fieldInfo{ + codec: c, + offset: offset, + name: f.Name, + omitempty: f.OmitEmpty, + typ: f.Field.Type, + empty: emptyFuncFor(f.OmitEmpty, f.Field.Type), + prefix: []byte(`,"` + f.Name + `":`), }) } - - return fields, nil + + return ret, nil } func resolveCodecInEmbeddedFields(parentType reflect.Type, indexPath []int, leafCodec codec, leafMeta reflectutil.FieldMeta) (codec, uintptr) { diff --git a/astra/table/infer.go b/astra/table/infer.go index f28ccf9..ed299da 100644 --- a/astra/table/infer.go +++ b/astra/table/infer.go @@ -43,10 +43,10 @@ var ( // fieldData holds processed metadata about a single struct field. type fieldData struct { - columnName string - goType reflect.Type - tag tagInfo - fieldIdx int // position for stable ordering + name string + typ reflect.Type + tag tagInfo + ord int // position for stable ordering } func collectFields(t reflect.Type) ([]fieldData, error) { @@ -57,7 +57,7 @@ func collectFields(t reflect.Type) ([]fieldData, error) { return nil, fmt.Errorf("expected struct, got %s", t.Kind()) } - metas, err := reflectutil.GetFields(t) + fields, err := reflectutil.GetFlattenedFields(t) if err != nil { return nil, err } @@ -65,31 +65,31 @@ func collectFields(t reflect.Type) ([]fieldData, error) { seen := make(map[string]bool) var result []fieldData - for i, meta := range metas { + for i, f := range fields { // Skip $-prefixed fields (API metadata: $similarity, $vector, $vectorize) - if strings.HasPrefix(meta.Name, "$") { + if strings.HasPrefix(f.Name, "$") { continue } - tag, err := parseAstraTag(meta.Field.Tag.Get("astra")) + tag, err := parseAstraTag(f.Field.Tag.Get("astra")) if err != nil { - return nil, fmt.Errorf("field %q: %w", meta.Field.Name, err) + return nil, fmt.Errorf("field %q: %w", f.Field.Name, err) } if tag.skip { continue } - if seen[meta.Name] { - return nil, fmt.Errorf("duplicate column name %q", meta.Name) + if seen[f.Name] { + return nil, fmt.Errorf("duplicate column name %q", f.Name) } - seen[meta.Name] = true + seen[f.Name] = true result = append(result, fieldData{ - columnName: meta.Name, - goType: meta.Field.Type, - tag: tag, - fieldIdx: i, + name: f.Name, + typ: f.Field.Type, + tag: tag, + ord: i, }) } @@ -207,7 +207,7 @@ func goTypeToColumn(t reflect.Type, info tagInfo) (Column, error) { } // resolveTypeExpr walks a parsed typeExpr and produces a Column. It consults -// goType only at "infer" leaves and at container boundaries where the Go +// typ only at "infer" leaves and at container boundaries where the Go // type's shape (slice / map) governs how values will be marshaled. For // container leaves declared explicitly (e.g. set[ascii]), the declared leaf // wins and the Go element type is not consulted — callers are trusted to @@ -302,10 +302,10 @@ var scalarFactories = map[string]func() Column{ // keyField is used during primary key assembly. type keyField struct { - name string - ordinal int - idx int // struct field index for stable ordering - desc bool // clustering key: true = descending + name string + pkOrd int + fieldOrd int // struct field index for stable ordering + desc bool // clustering key: true = descending } // buildPrimaryKey assembles the PrimaryKey from collected partition and clustering key fields. @@ -317,7 +317,7 @@ func buildPrimaryKey(pks, cks []keyField) (PrimaryKey, error) { // Determine ordering strategy: all ordinals or all struct-order hasOrd, noOrd := false, false for _, pk := range pks { - if pk.ordinal > 0 { + if pk.pkOrd > 0 { hasOrd = true } else { noOrd = true @@ -328,12 +328,12 @@ func buildPrimaryKey(pks, cks []keyField) (PrimaryKey, error) { } if hasOrd { - sort.Slice(pks, func(i, j int) bool { return pks[i].ordinal < pks[j].ordinal }) + sort.Slice(pks, func(i, j int) bool { return pks[i].pkOrd < pks[j].pkOrd }) if err := validateOrdinals("partition key", pks); err != nil { return PrimaryKey{}, err } } else { - sort.Slice(pks, func(i, j int) bool { return pks[i].idx < pks[j].idx }) + sort.Slice(pks, func(i, j int) bool { return pks[i].fieldOrd < pks[j].fieldOrd }) } partitionBy := make([]string, len(pks)) @@ -343,7 +343,7 @@ func buildPrimaryKey(pks, cks []keyField) (PrimaryKey, error) { var partitionSort PartitionSort if len(cks) > 0 { - sort.Slice(cks, func(i, j int) bool { return cks[i].ordinal < cks[j].ordinal }) + sort.Slice(cks, func(i, j int) bool { return cks[i].pkOrd < cks[j].pkOrd }) if err := validateOrdinals("clustering key", cks); err != nil { return PrimaryKey{}, err } @@ -368,11 +368,11 @@ func buildPrimaryKey(pks, cks []keyField) (PrimaryKey, error) { // with no duplicates. func validateOrdinals(label string, keys []keyField) error { for i, k := range keys { - if i > 0 && k.ordinal == keys[i-1].ordinal { - return fmt.Errorf("duplicate %s ordinal %d: %q and %q", label, k.ordinal, keys[i-1].name, k.name) + if i > 0 && k.pkOrd == keys[i-1].pkOrd { + return fmt.Errorf("duplicate %s ordinal %d: %q and %q", label, k.pkOrd, keys[i-1].name, k.name) } - if k.ordinal != i+1 { - return fmt.Errorf("%s ordinals must be contiguous starting from 1 (expected %d, got %d for %q)", label, i+1, k.ordinal, k.name) + if k.pkOrd != i+1 { + return fmt.Errorf("%s ordinals must be contiguous starting from 1 (expected %d, got %d for %q)", label, i+1, k.pkOrd, k.name) } } return nil @@ -427,18 +427,18 @@ func Infer[T any]() (Definition, error) { columns := make(Columns, 0, len(fields)) var pks, cks []keyField - for _, fd := range fields { - col, err := goTypeToColumn(fd.goType, fd.tag) + for _, f := range fields { + col, err := goTypeToColumn(f.typ, f.tag) if err != nil { - return Definition{}, fmt.Errorf("table.Infer[%s]: field %q: %w", t.Name(), fd.columnName, err) + return Definition{}, fmt.Errorf("table.Infer[%s]: field %q: %w", t.Name(), f.name, err) } - columns = append(columns, NamedColumn{Name: fd.columnName, Column: col}) + columns = append(columns, NamedColumn{Name: f.name, Column: col}) - if fd.tag.isPK { - pks = append(pks, keyField{name: fd.columnName, ordinal: fd.tag.pkOrdinal, idx: fd.fieldIdx}) + if f.tag.isPK { + pks = append(pks, keyField{name: f.name, pkOrd: f.tag.pkOrdinal, fieldOrd: f.ord}) } - if fd.tag.isCK { - cks = append(cks, keyField{name: fd.columnName, ordinal: fd.tag.ckOrdinal, idx: fd.fieldIdx, desc: fd.tag.ckDescending}) + if f.tag.isCK { + cks = append(cks, keyField{name: f.name, pkOrd: f.tag.ckOrdinal, fieldOrd: f.ord, desc: f.tag.ckDescending}) } } @@ -482,11 +482,11 @@ func InferUDT[T any]() (UDTDefinition, error) { // TODO somehow try to check if columns := make(Columns, 0, len(fields)) for _, fd := range fields { - col, err := goTypeToColumn(fd.goType, fd.tag) + col, err := goTypeToColumn(fd.typ, fd.tag) if err != nil { - return UDTDefinition{}, fmt.Errorf("table.InferUDT[%s]: field %q: %w", t.Name(), fd.columnName, err) + return UDTDefinition{}, fmt.Errorf("table.InferUDT[%s]: field %q: %w", t.Name(), fd.name, err) } - columns = append(columns, NamedColumn{Name: fd.columnName, Column: col}) + columns = append(columns, NamedColumn{Name: fd.name, Column: col}) } return UDTDefinition{ diff --git a/internal/reflectutil/structutil.go b/internal/reflectutil/structutil.go index 626cccf..1bf139b 100644 --- a/internal/reflectutil/structutil.go +++ b/internal/reflectutil/structutil.go @@ -27,38 +27,36 @@ type FieldMeta struct { OmitEmpty bool Field reflect.StructField - Index []int // The sequence of struct field indices to reach this field + Path []int tagged bool } -// GetFields flattens a struct type, resolving embedded fields and name collisions. -// It follows encoding/json shadowing and ambiguity rules. -func GetFields(t reflect.Type) ([]FieldMeta, error) { +func GetFlattenedFields(t reflect.Type) ([]FieldMeta, error) { seen := make(map[reflect.Type]bool) fields, err := getFieldsRecursive(t, seen, nil) if err != nil { return nil, err } - // Sort fields lexicographically by their Index to preserve struct definition order sort.Slice(fields, func(i, j int) bool { - a, b := fields[i].Index, fields[j].Index - minLen := len(a) - if len(b) < minLen { - minLen = len(b) - } - for k := 0; k < minLen; k++ { - if a[k] != b[k] { - return a[k] < b[k] - } - } - return len(a) < len(b) + return fieldLessThan(fields[i].Path, fields[j].Path) }) return fields, nil } +func fieldLessThan(f1, f2 []int) bool { + minLen := min(len(f1), len(f2)) + + for k := 0; k < minLen; k++ { + if f1[k] != f2[k] { + return f1[k] < f2[k] + } + } + return len(f1) < len(f2) +} + func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []int) ([]FieldMeta, error) { if t.Kind() == reflect.Pointer { t = t.Elem() @@ -138,15 +136,17 @@ func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []i Name: name, OmitEmpty: omitempty, Field: f, - Index: idx, + Path: idx, tagged: tagged, }) + // seeds the counters so embedded fields know they are secondary topLevelNames[name] = struct{}{} ambiguousNames[name]++ ambiguousTags[name]++ } + // first pass to count the number of fields w/ each name so we can resolve ambiguities in the next pass for _, embfield := range embeddedFields { ambiguousNames[embfield.subfield.Name]++ if embfield.subfield.tagged { @@ -175,7 +175,6 @@ func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []i return fields, nil } -// parseJSONTag parses standard encoding/json style struct tags. func parseJSONTag(f reflect.StructField) (name string, ignored, omitempty, tagged bool) { name = f.Name diff --git a/internal/reflectutil/structutil_test.go b/internal/reflectutil/structutil_test.go index 63562e9..2cb43ae 100644 --- a/internal/reflectutil/structutil_test.go +++ b/internal/reflectutil/structutil_test.go @@ -50,7 +50,7 @@ type Unexported struct { } func TestGetFields_Shadowing(t *testing.T) { - fields, err := GetFields(reflect.TypeOf(TopShadow{})) + fields, err := GetFlattenedFields(reflect.TypeOf(TopShadow{})) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -59,24 +59,24 @@ func TestGetFields_Shadowing(t *testing.T) { t.Fatalf("expected 2 fields, got %d", len(fields)) } - if fields[0].Name != "b" || len(fields[0].Index) != 2 { - t.Errorf("expected first field 'b' from embedded E1, got %s with index %v", fields[0].Name, fields[0].Index) + if fields[0].Name != "b" || len(fields[0].Path) != 2 { + t.Errorf("expected first field 'b' from embedded E1, got %s with index %v", fields[0].Name, fields[0].Path) } - if fields[1].Name != "a" || len(fields[1].Index) != 1 { - t.Errorf("expected second field 'a' from top level, got %s with index %v", fields[1].Name, fields[1].Index) + if fields[1].Name != "a" || len(fields[1].Path) != 1 { + t.Errorf("expected second field 'a' from top level, got %s with index %v", fields[1].Name, fields[1].Path) } } func TestGetFields_Ambiguous(t *testing.T) { - _, err := GetFields(reflect.TypeOf(TopAmbiguous{})) + _, err := GetFlattenedFields(reflect.TypeOf(TopAmbiguous{})) if err == nil { t.Fatal("expected error due to ambiguous field 'b', got nil") } } func TestGetFields_PtrCycle(t *testing.T) { - fields, err := GetFields(reflect.TypeOf(PtrCycle{})) + fields, err := GetFlattenedFields(reflect.TypeOf(PtrCycle{})) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -91,7 +91,7 @@ func TestGetFields_PtrCycle(t *testing.T) { } func TestGetFields_Unexported(t *testing.T) { - fields, err := GetFields(reflect.TypeOf(Unexported{})) + fields, err := GetFlattenedFields(reflect.TypeOf(Unexported{})) if err != nil { t.Fatalf("unexpected error: %v", err) } From e23d48c93b07f22d64518c985289acf9f90f3efd Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 18:43:28 -0500 Subject: [PATCH 5/7] more --- astra/collection.go | 2 +- astra/common_impls.go | 2 +- astra/cursors/abstract_cursor.go | 2 +- astra/datatypes/date.go | 2 +- astra/datatypes/objectid.go | 2 +- astra/datatypes/time.go | 2 +- astra/datatypes/uuid.go | 2 +- astra/results/insert_result.go | 2 +- astra/table/infer.go | 13 +++---------- internal/reflectutil/ptr.go | 11 +++++++++++ internal/reflectutil/structutil.go | 4 +--- {astra/internal => internal}/utils/utils.go | 0 12 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 internal/reflectutil/ptr.go rename {astra/internal => internal}/utils/utils.go (100%) diff --git a/astra/collection.go b/astra/collection.go index 9ff8e1c..880206f 100644 --- a/astra/collection.go +++ b/astra/collection.go @@ -22,7 +22,7 @@ import ( "github.com/datastax/astra-db-go/v2/astra/cursors" "github.com/datastax/astra-db-go/v2/astra/filter" "github.com/datastax/astra-db-go/v2/astra/internal/command" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" "github.com/datastax/astra-db-go/v2/astra/options" "github.com/datastax/astra-db-go/v2/astra/ptr" "github.com/datastax/astra-db-go/v2/astra/results" diff --git a/astra/common_impls.go b/astra/common_impls.go index 5c2b4a3..739052a 100644 --- a/astra/common_impls.go +++ b/astra/common_impls.go @@ -24,7 +24,7 @@ import ( "github.com/datastax/astra-db-go/v2/astra/filter" "github.com/datastax/astra-db-go/v2/astra/internal/command" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" "github.com/datastax/astra-db-go/v2/astra/options" "github.com/datastax/astra-db-go/v2/astra/ptr" "github.com/datastax/astra-db-go/v2/astra/results" diff --git a/astra/cursors/abstract_cursor.go b/astra/cursors/abstract_cursor.go index 4d8aeb6..b27552e 100644 --- a/astra/cursors/abstract_cursor.go +++ b/astra/cursors/abstract_cursor.go @@ -22,7 +22,7 @@ import ( "reflect" "sync" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // ErrCursorClosed is returned when operations are attempted on a closed cursor. diff --git a/astra/datatypes/date.go b/astra/datatypes/date.go index 6b6e21b..78426ec 100644 --- a/astra/datatypes/date.go +++ b/astra/datatypes/date.go @@ -21,7 +21,7 @@ import ( "strings" "time" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // DateOnly represents a date (year, month, day) without a time component, diff --git a/astra/datatypes/objectid.go b/astra/datatypes/objectid.go index dedbd7c..7bba1c4 100644 --- a/astra/datatypes/objectid.go +++ b/astra/datatypes/objectid.go @@ -24,7 +24,7 @@ import ( "sync/atomic" "time" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // ObjectId represents an ObjectId that can be used as an _id in the DataAPI. diff --git a/astra/datatypes/time.go b/astra/datatypes/time.go index 02d95bf..67c66a5 100644 --- a/astra/datatypes/time.go +++ b/astra/datatypes/time.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // TimeOnly represents a time (hour, minute, second, nanosecond) without a date component, diff --git a/astra/datatypes/uuid.go b/astra/datatypes/uuid.go index 76304e9..beda48e 100644 --- a/astra/datatypes/uuid.go +++ b/astra/datatypes/uuid.go @@ -24,7 +24,7 @@ import ( "sync" "time" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // UUID represents a universally unique identifier with Data API JSON serialization. diff --git a/astra/results/insert_result.go b/astra/results/insert_result.go index 36517c3..8b047a2 100644 --- a/astra/results/insert_result.go +++ b/astra/results/insert_result.go @@ -18,7 +18,7 @@ import ( "encoding/json" "errors" - "github.com/datastax/astra-db-go/v2/astra/internal/utils" + "github.com/datastax/astra-db-go/v2/internal/utils" "github.com/datastax/astra-db-go/v2/astra/serdes" ) diff --git a/astra/table/infer.go b/astra/table/infer.go index ed299da..16f5976 100644 --- a/astra/table/infer.go +++ b/astra/table/infer.go @@ -50,9 +50,7 @@ type fieldData struct { } func collectFields(t reflect.Type) ([]fieldData, error) { - for t.Kind() == reflect.Pointer { - t = t.Elem() - } + t = reflectutil.UnwindPointerType(t) if t.Kind() != reflect.Struct { return nil, fmt.Errorf("expected struct, got %s", t.Kind()) } @@ -98,10 +96,7 @@ func collectFields(t reflect.Type) ([]fieldData, error) { // goTypeToColumn converts a Go reflect.Type to a table Column using tag metadata. func goTypeToColumn(t reflect.Type, info tagInfo) (Column, error) { - // Unwrap pointers - for t.Kind() == reflect.Pointer { - t = t.Elem() - } + t = reflectutil.UnwindPointerType(t) // jsonString — field is stored as a text column if info.isJSONString { @@ -213,9 +208,7 @@ func goTypeToColumn(t reflect.Type, info tagInfo) (Column, error) { // wins and the Go element type is not consulted — callers are trusted to // serialize values compatibly. func resolveTypeExpr(expr typeExpr, t reflect.Type, info tagInfo) (Column, error) { - for t.Kind() == reflect.Pointer { - t = t.Elem() - } + t = reflectutil.UnwindPointerType(t) switch expr.name { case "infer": diff --git a/internal/reflectutil/ptr.go b/internal/reflectutil/ptr.go new file mode 100644 index 0000000..708cfa0 --- /dev/null +++ b/internal/reflectutil/ptr.go @@ -0,0 +1,11 @@ +package reflectutil + +import "reflect" + +// UnwindPointerType recursively unwraps pointer types, returning the underlying type. +func UnwindPointerType(t reflect.Type) reflect.Type { + for t.Kind() == reflect.Pointer { + t = t.Elem() + } + return t +} diff --git a/internal/reflectutil/structutil.go b/internal/reflectutil/structutil.go index 1bf139b..2e33e2f 100644 --- a/internal/reflectutil/structutil.go +++ b/internal/reflectutil/structutil.go @@ -58,9 +58,7 @@ func fieldLessThan(f1, f2 []int) bool { } func getFieldsRecursive(t reflect.Type, seen map[reflect.Type]bool, basePath []int) ([]FieldMeta, error) { - if t.Kind() == reflect.Pointer { - t = t.Elem() - } + t = UnwindPointerType(t) if t.Kind() != reflect.Struct { return nil, nil } diff --git a/astra/internal/utils/utils.go b/internal/utils/utils.go similarity index 100% rename from astra/internal/utils/utils.go rename to internal/utils/utils.go From d64c65479f357bee01cce150d8eb7dc062011db6 Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 21:06:15 -0500 Subject: [PATCH 6/7] fix checks --- astra/collection.go | 2 +- astra/common_impls.go | 2 +- astra/results/insert_result.go | 2 +- internal/reflectutil/ptr.go | 14 ++++++++++++++ internal/testlib/misc.go | 2 -- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/astra/collection.go b/astra/collection.go index 880206f..e4deb82 100644 --- a/astra/collection.go +++ b/astra/collection.go @@ -22,12 +22,12 @@ import ( "github.com/datastax/astra-db-go/v2/astra/cursors" "github.com/datastax/astra-db-go/v2/astra/filter" "github.com/datastax/astra-db-go/v2/astra/internal/command" - "github.com/datastax/astra-db-go/v2/internal/utils" "github.com/datastax/astra-db-go/v2/astra/options" "github.com/datastax/astra-db-go/v2/astra/ptr" "github.com/datastax/astra-db-go/v2/astra/results" "github.com/datastax/astra-db-go/v2/astra/serdes" "github.com/datastax/astra-db-go/v2/astra/update" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // CollectionFilter is implemented by [filter.F] and [filter.Filter]. diff --git a/astra/common_impls.go b/astra/common_impls.go index 739052a..fd599de 100644 --- a/astra/common_impls.go +++ b/astra/common_impls.go @@ -24,12 +24,12 @@ import ( "github.com/datastax/astra-db-go/v2/astra/filter" "github.com/datastax/astra-db-go/v2/astra/internal/command" - "github.com/datastax/astra-db-go/v2/internal/utils" "github.com/datastax/astra-db-go/v2/astra/options" "github.com/datastax/astra-db-go/v2/astra/ptr" "github.com/datastax/astra-db-go/v2/astra/results" "github.com/datastax/astra-db-go/v2/astra/serdes" "github.com/datastax/astra-db-go/v2/astra/sort" + "github.com/datastax/astra-db-go/v2/internal/utils" ) type mkCmd = func(name string, payload any, opts ...options.APIOption) command.DataAPI diff --git a/astra/results/insert_result.go b/astra/results/insert_result.go index 8b047a2..b3305d1 100644 --- a/astra/results/insert_result.go +++ b/astra/results/insert_result.go @@ -18,8 +18,8 @@ import ( "encoding/json" "errors" - "github.com/datastax/astra-db-go/v2/internal/utils" "github.com/datastax/astra-db-go/v2/astra/serdes" + "github.com/datastax/astra-db-go/v2/internal/utils" ) // InsertOneResult represents the result of an insertOne operation. diff --git a/internal/reflectutil/ptr.go b/internal/reflectutil/ptr.go index 708cfa0..53df001 100644 --- a/internal/reflectutil/ptr.go +++ b/internal/reflectutil/ptr.go @@ -1,3 +1,17 @@ +// Copyright IBM Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package reflectutil import "reflect" diff --git a/internal/testlib/misc.go b/internal/testlib/misc.go index 3987207..75329aa 100644 --- a/internal/testlib/misc.go +++ b/internal/testlib/misc.go @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package testutil provides utilities for testing. As more patterns -// emerge, we can add more helpers. package testlib import ( From 0955604d4810af7cd95e509b43d21e83796593f0 Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 12 Jun 2026 23:57:43 -0500 Subject: [PATCH 7/7] work --- astra/admin.go | 2 +- astra/collection_test.go | 8 +- astra/db.go | 2 +- astra/db_admin_astra.go | 2 +- astra/db_test.go | 6 +- astra/options/api_options.go | 81 ++---- astra/options/builders_gen.go | 258 +++++++++--------- astra/options/collection_options.go | 24 +- astra/options/collection_options_test.go | 52 ++-- astra/options/index_options.go | 4 +- astra/options/options.go | 12 +- astra/options/options_test.go | 12 +- astra/options/table_options.go | 8 +- astra/results/embedding_provider_results.go | 8 +- astra/table.go | 2 +- astra/table_test.go | 2 +- integration/harness/prelude.go | 12 +- .../legacy/tests/collection_nested_tests.go | 2 +- integration/legacy/tests/collection_tests.go | 2 +- integration/legacy/tests/table_tests.go | 2 +- tools/gen-options/gen_options_test.go | 10 +- tools/gen-options/main.go | 27 +- 22 files changed, 244 insertions(+), 294 deletions(-) diff --git a/astra/admin.go b/astra/admin.go index bba7d6a..c20d691 100644 --- a/astra/admin.go +++ b/astra/admin.go @@ -520,7 +520,7 @@ func (a *AstraAdmin) awaitStatus(ctx context.Context, databaseID string, opts Aw case <-ctx.Done(): return ctx.Err() case <-ticker.C: - db, err := a.DatabaseInfo(ctx, databaseID, options.DatabaseInfo().SetAPIOptions(opts.APIOptions)) + db, err := a.DatabaseInfo(ctx, databaseID, options.DatabaseInfo().UpdateAPIOptions(opts.APIOptions)) if err != nil { return err } diff --git a/astra/collection_test.go b/astra/collection_test.go index 925042c..500e477 100644 --- a/astra/collection_test.go +++ b/astra/collection_test.go @@ -386,7 +386,7 @@ func TestDeleteManyAPIOptionsOverrideToken(t *testing.T) { _, err := coll.DeleteMany(ctx, filter.F{"x": 1}, options.CollectionDeleteMany(). - SetAPIOptions(options.API().SetToken("override-token")), + UpdateAPIOptions(options.API().SetToken("override-token")), ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -410,7 +410,7 @@ func TestDeleteOneAPIOptionsOverrideToken(t *testing.T) { _, err := coll.DeleteOne(ctx, filter.F{"x": 1}, options.CollectionDeleteOne(). - SetAPIOptions(options.API().SetToken("override-token")), + UpdateAPIOptions(options.API().SetToken("override-token")), ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -434,7 +434,7 @@ func TestUpdateOneAPIOptionsOverrideToken(t *testing.T) { _, err := coll.UpdateOne(ctx, filter.F{"x": 1}, update.Coll().Set("x", 2), options.CollectionUpdateOne(). - SetAPIOptions(options.API().SetToken("override-token")), + UpdateAPIOptions(options.API().SetToken("override-token")), ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -459,7 +459,7 @@ func TestResolveGeneralMethodTimeoutFromAPIOverride(t *testing.T) { // Set GeneralMethod timeout via APIOptions override _, err := coll.DeleteMany(ctx, filter.F{"status": "old"}, options.CollectionDeleteMany(). - SetAPIOptions( + UpdateAPIOptions( options.API().UpdateTimeout( options.Timeout().SetGeneralMethod(250*time.Millisecond), ), diff --git a/astra/db.go b/astra/db.go index 3fc1d98..3c46662 100644 --- a/astra/db.go +++ b/astra/db.go @@ -181,7 +181,7 @@ func (d *Db) Table(name string, opts ...options.GetTableOption) *Table { // // // With vector options // coll, err := db.CreateCollection(ctx, "my_collection", -// options.CreateCollection().SetVector(&options.VectorOptions{ +// options.CreateCollection().UpdateVector(&options.VectorOptions{ // Dimension: 1024, // Metric: "cosine", // }) diff --git a/astra/db_admin_astra.go b/astra/db_admin_astra.go index 47096d7..f58a701 100644 --- a/astra/db_admin_astra.go +++ b/astra/db_admin_astra.go @@ -95,7 +95,7 @@ func (a *AstraDatabaseAdmin) ListKeyspaces(ctx context.Context, opts ...options. return nil, err } - db, err := a.admin.DatabaseInfo(ctx, a.ID(), options.DatabaseInfo().SetAPIOptions(merged.APIOptions)) + db, err := a.admin.DatabaseInfo(ctx, a.ID(), options.DatabaseInfo().UpdateAPIOptions(merged.APIOptions)) if err != nil { return nil, err } diff --git a/astra/db_test.go b/astra/db_test.go index 9515e1c..209681e 100644 --- a/astra/db_test.go +++ b/astra/db_test.go @@ -95,7 +95,7 @@ func TestCollectionOptionsMarshal(t *testing.T) { }) t.Run("with vector", func(t *testing.T) { - opts := options.CreateCollection().SetVector(&options.VectorOptions{ + opts := options.CreateCollection().UpdateVector(&options.VectorOptions{ Dimension: ptr.To(1024), Metric: ptr.To("cosine"), }) @@ -112,11 +112,11 @@ func TestCollectionOptionsMarshal(t *testing.T) { }) t.Run("multiple builders merged", func(t *testing.T) { - opts := options.CreateCollection().SetVector(&options.VectorOptions{ + opts := options.CreateCollection().UpdateVector(&options.VectorOptions{ Dimension: ptr.To(512), Metric: ptr.To("euclidean"), }) - opts.SetVector(&options.VectorOptions{ + opts.UpdateVector(&options.VectorOptions{ Dimension: ptr.To(1024), Metric: ptr.To("cosine"), }) diff --git a/astra/options/api_options.go b/astra/options/api_options.go index 3126278..3038af3 100644 --- a/astra/options/api_options.go +++ b/astra/options/api_options.go @@ -78,11 +78,11 @@ type Caller struct { Version string } -// Headers is a custom map type that implements ShouldMerge to accumulate headers additively. +// Headers is a custom map type that implements shouldMerge to accumulate headers additively. type Headers map[string]string -// Merge implements ShouldMerge for Headers. -func (h Headers) Merge(other ShouldMerge) ShouldMerge { +// Merge implements shouldMerge for Headers. +func (h Headers) merge(other shouldMerge) shouldMerge { otherHeaders := other.(Headers) res := make(Headers, len(h)+len(otherHeaders)) maps.Copy(res, h) @@ -90,11 +90,11 @@ func (h Headers) Merge(other ShouldMerge) ShouldMerge { return res } -// Callers is a custom slice type that implements ShouldMerge to accumulate callers additively. +// Callers is a custom slice type that implements shouldMerge to accumulate callers additively. type Callers []Caller -// Merge implements ShouldMerge for Callers. -func (c Callers) Merge(other ShouldMerge) ShouldMerge { +// Merge implements shouldMerge for Callers. +func (c Callers) merge(other shouldMerge) shouldMerge { otherCallers := other.(Callers) res := make(Callers, 0, len(c)+len(otherCallers)) res = append(res, c...) @@ -118,41 +118,10 @@ type SerdesOptions struct { UseJSONUnmarshal *bool } -// Merge implements ShouldMerge for SerdesOptions. -func (o *SerdesOptions) Merge(other ShouldMerge) ShouldMerge { - otherSerdes := other.(*SerdesOptions) - result := &SerdesOptions{} - if o != nil { - *result = *o - } - if otherSerdes.TrustRawMessage != nil { - result.TrustRawMessage = otherSerdes.TrustRawMessage - } - if otherSerdes.SortMapKeys != nil { - result.SortMapKeys = otherSerdes.SortMapKeys - } - if otherSerdes.SerNoCache != nil { - result.SerNoCache = otherSerdes.SerNoCache - } - if otherSerdes.UseJSONMarshal != nil { - result.UseJSONMarshal = otherSerdes.UseJSONMarshal - } - if otherSerdes.SparseRows != nil { - result.SparseRows = otherSerdes.SparseRows - } - if otherSerdes.UseNumber != nil { - result.UseNumber = otherSerdes.UseNumber - } - if otherSerdes.DesNoCache != nil { - result.DesNoCache = otherSerdes.DesNoCache - } - if otherSerdes.ExtendedErrorContext != nil { - result.ExtendedErrorContext = otherSerdes.ExtendedErrorContext - } - if otherSerdes.UseJSONUnmarshal != nil { - result.UseJSONUnmarshal = otherSerdes.UseJSONUnmarshal - } - return result +// Merge implements shouldMerge for SerdesOptions. +func (o *SerdesOptions) merge(other shouldMerge) shouldMerge { + casted, _ := other.(*SerdesOptions) + return Merge[SerdesOptions](o, casted) } // GetSerFlags returns the aggregated serialization flags. @@ -211,26 +180,10 @@ type TimeoutOptions struct { GeneralMethod *time.Duration } -// Merge implements ShouldMerge for TimeoutOptions. -func (o *TimeoutOptions) Merge(other ShouldMerge) ShouldMerge { - otherTimeout := other.(*TimeoutOptions) - result := &TimeoutOptions{} - if o != nil { - *result = *o - } - if otherTimeout.Request != nil { - result.Request = otherTimeout.Request - } - if otherTimeout.Connection != nil { - result.Connection = otherTimeout.Connection - } - if otherTimeout.BulkOperation != nil { - result.BulkOperation = otherTimeout.BulkOperation - } - if otherTimeout.GeneralMethod != nil { - result.GeneralMethod = otherTimeout.GeneralMethod - } - return result +// Merge implements shouldMerge for TimeoutOptions. +func (o *TimeoutOptions) merge(other shouldMerge) shouldMerge { + casted, _ := other.(*TimeoutOptions) + return Merge[TimeoutOptions](o, casted) } // GetRequest returns the request timeout or 30 seconds if not set. @@ -444,3 +397,9 @@ func (o *APIOptions) GetDesFlags() serdes.DesFlags { } return o.Serdes.GetDesFlags() } + +// Merge implements shouldMerge for APIOptions. +func (o *APIOptions) merge(other shouldMerge) shouldMerge { + casted, _ := other.(*APIOptions) + return Merge[APIOptions](o, casted) +} diff --git a/astra/options/builders_gen.go b/astra/options/builders_gen.go index 67956cd..9df23df 100644 --- a/astra/options/builders_gen.go +++ b/astra/options/builders_gen.go @@ -163,7 +163,7 @@ func (b *apiOptionsBuilder) SetDataAPIBackend(v DataAPIBackend) *apiOptionsBuild // // Example using the fluent builder ([AlterTable]): // -// opts := options.AlterTable().SetAPIOptions(...) +// opts := options.AlterTable().UpdateAPIOptions(...) // // Example using a pointer to [AlterTableOptions] without the fluent builder: // @@ -191,10 +191,10 @@ func (b *alterTableOptionsBuilder) Setters() []func(*AlterTableOptions) { return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *alterTableOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *alterTableOptionsBuilder { +func (b *alterTableOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *alterTableOptionsBuilder { b.setters = append(b.setters, func(o *AlterTableOptions) { MergeInto(&o.APIOptions, v...) }) @@ -247,10 +247,10 @@ func (b *alterTypeOptionsBuilder) SetKeyspace(v string) *alterTypeOptionsBuilder return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *alterTypeOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *alterTypeOptionsBuilder { +func (b *alterTypeOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *alterTypeOptionsBuilder { b.setters = append(b.setters, func(o *AlterTypeOptions) { MergeInto(&o.APIOptions, v...) }) @@ -262,7 +262,7 @@ func (b *alterTypeOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *alter // // Example using the fluent builder ([CollectionCountDocuments]): // -// opts := options.CollectionCountDocuments().SetAPIOptions(...) +// opts := options.CollectionCountDocuments().UpdateAPIOptions(...) // // Example using a pointer to [CollectionCountDocumentsOptions] without the fluent builder: // @@ -290,10 +290,10 @@ func (b *collectionCountDocumentsOptionsBuilder) Setters() []func(*CollectionCou return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionCountDocumentsOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionCountDocumentsOptionsBuilder { +func (b *collectionCountDocumentsOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionCountDocumentsOptionsBuilder { b.setters = append(b.setters, func(o *CollectionCountDocumentsOptions) { MergeInto(&o.APIOptions, v...) }) @@ -388,10 +388,10 @@ func (b *collectionDeleteManyOptionsBuilder) SetTimeout(v time.Duration) *collec return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionDeleteManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionDeleteManyOptionsBuilder { +func (b *collectionDeleteManyOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionDeleteManyOptionsBuilder { b.setters = append(b.setters, func(o *CollectionDeleteManyOptions) { MergeInto(&o.APIOptions, v...) }) @@ -441,10 +441,10 @@ func (b *collectionDeleteOneOptionsBuilder) SetSort(v sort.Sortable) *collection return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionDeleteOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionDeleteOneOptionsBuilder { +func (b *collectionDeleteOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionDeleteOneOptionsBuilder { b.setters = append(b.setters, func(o *CollectionDeleteOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -456,7 +456,7 @@ func (b *collectionDeleteOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOption // // Example using the fluent builder ([CollectionEstimatedDocumentCount]): // -// opts := options.CollectionEstimatedDocumentCount().SetAPIOptions(...) +// opts := options.CollectionEstimatedDocumentCount().UpdateAPIOptions(...) // // Example using a pointer to [CollectionEstimatedDocumentCountOptions] without the fluent builder: // @@ -484,10 +484,10 @@ func (b *collectionEstimatedDocumentCountOptionsBuilder) Setters() []func(*Colle return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionEstimatedDocumentCountOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionEstimatedDocumentCountOptionsBuilder { +func (b *collectionEstimatedDocumentCountOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionEstimatedDocumentCountOptionsBuilder { b.setters = append(b.setters, func(o *CollectionEstimatedDocumentCountOptions) { MergeInto(&o.APIOptions, v...) }) @@ -600,10 +600,10 @@ func (b *collectionFindAndRerankOptionsBuilder) SetInitialPageState(v string) *c return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. -func (b *collectionFindAndRerankOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionFindAndRerankOptionsBuilder { +func (b *collectionFindAndRerankOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionFindAndRerankOptionsBuilder { b.setters = append(b.setters, func(o *CollectionFindAndRerankOptions) { MergeInto(&o.APIOptions, v...) }) @@ -661,10 +661,10 @@ func (b *collectionFindOneAndDeleteOptionsBuilder) SetProjection(v map[string]an return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionFindOneAndDeleteOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionFindOneAndDeleteOptionsBuilder { +func (b *collectionFindOneAndDeleteOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionFindOneAndDeleteOptionsBuilder { b.setters = append(b.setters, func(o *CollectionFindOneAndDeleteOptions) { MergeInto(&o.APIOptions, v...) }) @@ -741,10 +741,10 @@ func (b *collectionFindOneAndReplaceOptionsBuilder) SetReturnDocument(v ReturnDo return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionFindOneAndReplaceOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionFindOneAndReplaceOptionsBuilder { +func (b *collectionFindOneAndReplaceOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionFindOneAndReplaceOptionsBuilder { b.setters = append(b.setters, func(o *CollectionFindOneAndReplaceOptions) { MergeInto(&o.APIOptions, v...) }) @@ -821,10 +821,10 @@ func (b *collectionFindOneAndUpdateOptionsBuilder) SetReturnDocument(v ReturnDoc return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionFindOneAndUpdateOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionFindOneAndUpdateOptionsBuilder { +func (b *collectionFindOneAndUpdateOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionFindOneAndUpdateOptionsBuilder { b.setters = append(b.setters, func(o *CollectionFindOneAndUpdateOptions) { MergeInto(&o.APIOptions, v...) }) @@ -893,10 +893,10 @@ func (b *collectionFindOneOptionsBuilder) SetIncludeSimilarity(v bool) *collecti return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionFindOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionFindOneOptionsBuilder { +func (b *collectionFindOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionFindOneOptionsBuilder { b.setters = append(b.setters, func(o *CollectionFindOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1007,10 +1007,10 @@ func (b *collectionFindOptionsBuilder) SetInitialPageState(v string) *collection return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionFindOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionFindOptionsBuilder { +func (b *collectionFindOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionFindOptionsBuilder { b.setters = append(b.setters, func(o *CollectionFindOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1075,8 +1075,8 @@ func (b *collectionInsertManyOptionsBuilder) SetConcurrency(v int) *collectionIn return b } -// SetAPIOptions sets the APIOptions option. -func (b *collectionInsertManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionInsertManyOptionsBuilder { +// UpdateAPIOptions sets the APIOptions option. +func (b *collectionInsertManyOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionInsertManyOptionsBuilder { b.setters = append(b.setters, func(o *CollectionInsertManyOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1088,7 +1088,7 @@ func (b *collectionInsertManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptio // // Example using the fluent builder ([CollectionInsertOne]): // -// opts := options.CollectionInsertOne().SetAPIOptions(...) +// opts := options.CollectionInsertOne().UpdateAPIOptions(...) // // Example using a pointer to [CollectionInsertOneOptions] without the fluent builder: // @@ -1116,10 +1116,10 @@ func (b *collectionInsertOneOptionsBuilder) Setters() []func(*CollectionInsertOn return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionInsertOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionInsertOneOptionsBuilder { +func (b *collectionInsertOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionInsertOneOptionsBuilder { b.setters = append(b.setters, func(o *CollectionInsertOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1131,7 +1131,7 @@ func (b *collectionInsertOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOption // // Example using the fluent builder ([CollectionOptions]): // -// opts := options.CollectionOptions().SetAPIOptions(...) +// opts := options.CollectionOptions().UpdateAPIOptions(...) // // Example using a pointer to [CollectionOptionsOptions] without the fluent builder: // @@ -1159,10 +1159,10 @@ func (b *collectionOptionsOptionsBuilder) Setters() []func(*CollectionOptionsOpt return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionOptionsOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionOptionsOptionsBuilder { +func (b *collectionOptionsOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionOptionsOptionsBuilder { b.setters = append(b.setters, func(o *CollectionOptionsOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1221,10 +1221,10 @@ func (b *collectionReplaceOneOptionsBuilder) SetUpsert(v bool) *collectionReplac return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionReplaceOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionReplaceOneOptionsBuilder { +func (b *collectionReplaceOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionReplaceOneOptionsBuilder { b.setters = append(b.setters, func(o *CollectionReplaceOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1284,10 +1284,10 @@ func (b *collectionUpdateManyOptionsBuilder) SetTimeout(v time.Duration) *collec return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionUpdateManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionUpdateManyOptionsBuilder { +func (b *collectionUpdateManyOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionUpdateManyOptionsBuilder { b.setters = append(b.setters, func(o *CollectionUpdateManyOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1347,10 +1347,10 @@ func (b *collectionUpdateOneOptionsBuilder) SetUpsert(v bool) *collectionUpdateO return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *collectionUpdateOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *collectionUpdateOneOptionsBuilder { +func (b *collectionUpdateOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *collectionUpdateOneOptionsBuilder { b.setters = append(b.setters, func(o *CollectionUpdateOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1391,45 +1391,45 @@ func (b *createCollectionOptionsBuilder) Setters() []func(*CreateCollectionOptio return b.setters } -// SetDefaultId sets the DefaultId option. +// UpdateDefaultId sets the DefaultId option. // Settings for generating ids -func (b *createCollectionOptionsBuilder) SetDefaultId(v ...Builder[CollectionDefaultIdOptions]) *createCollectionOptionsBuilder { +func (b *createCollectionOptionsBuilder) UpdateDefaultId(v ...Builder[CollectionDefaultIdOptions]) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { MergeInto(&o.DefaultId, v...) }) return b } -// SetVector sets the Vector option. +// UpdateVector sets the Vector option. // Vector specifications for the collection -func (b *createCollectionOptionsBuilder) SetVector(v ...Builder[VectorOptions]) *createCollectionOptionsBuilder { +func (b *createCollectionOptionsBuilder) UpdateVector(v ...Builder[VectorOptions]) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { MergeInto(&o.Vector, v...) }) return b } -// SetIndexing sets the Indexing option. +// UpdateIndexing sets the Indexing option. // Overrides for document indexing -func (b *createCollectionOptionsBuilder) SetIndexing(v ...Builder[IndexingOptions]) *createCollectionOptionsBuilder { +func (b *createCollectionOptionsBuilder) UpdateIndexing(v ...Builder[IndexingOptions]) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { MergeInto(&o.Indexing, v...) }) return b } -// SetLexical sets the Lexical option. +// UpdateLexical sets the Lexical option. // Lexical analysis options for the collection -func (b *createCollectionOptionsBuilder) SetLexical(v ...Builder[LexicalOptions]) *createCollectionOptionsBuilder { +func (b *createCollectionOptionsBuilder) UpdateLexical(v ...Builder[LexicalOptions]) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { MergeInto(&o.Lexical, v...) }) return b } -// SetRerank sets the Rerank option. +// UpdateRerank sets the Rerank option. // Reranking options for the collection -func (b *createCollectionOptionsBuilder) SetRerank(v ...Builder[RerankOptions]) *createCollectionOptionsBuilder { +func (b *createCollectionOptionsBuilder) UpdateRerank(v ...Builder[RerankOptions]) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { MergeInto(&o.Rerank, v...) }) @@ -1448,10 +1448,10 @@ func (b *createCollectionOptionsBuilder) SetKeyspace(v string) *createCollection return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *createCollectionOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createCollectionOptionsBuilder { +func (b *createCollectionOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1521,10 +1521,10 @@ func (b *createDatabaseOptionsBuilder) SetPollInterval(v time.Duration) *createD return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→Admin hierarchy. -func (b *createDatabaseOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createDatabaseOptionsBuilder { +func (b *createDatabaseOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createDatabaseOptionsBuilder { b.setters = append(b.setters, func(o *CreateDatabaseOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1605,10 +1605,10 @@ func (b *createIndexOptionsBuilder) SetCaseSensitive(v bool) *createIndexOptions return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *createIndexOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createIndexOptionsBuilder { +func (b *createIndexOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createIndexOptionsBuilder { b.setters = append(b.setters, func(o *CreateIndexOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1689,10 +1689,10 @@ func (b *createKeyspaceOptionsBuilder) SetUpdateDbKeyspace(v bool) *createKeyspa return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *createKeyspaceOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createKeyspaceOptionsBuilder { +func (b *createKeyspaceOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createKeyspaceOptionsBuilder { b.setters = append(b.setters, func(o *CreateKeyspaceOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1755,10 +1755,10 @@ func (b *createTableOptionsBuilder) SetKeyspace(v string) *createTableOptionsBui return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *createTableOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createTableOptionsBuilder { +func (b *createTableOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createTableOptionsBuilder { b.setters = append(b.setters, func(o *CreateTableOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1809,10 +1809,10 @@ func (b *createTextIndexOptionsBuilder) SetIfNotExists(v bool) *createTextIndexO return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *createTextIndexOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createTextIndexOptionsBuilder { +func (b *createTextIndexOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createTextIndexOptionsBuilder { b.setters = append(b.setters, func(o *CreateTextIndexOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1875,10 +1875,10 @@ func (b *createTypeOptionsBuilder) SetKeyspace(v string) *createTypeOptionsBuild return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *createTypeOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createTypeOptionsBuilder { +func (b *createTypeOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createTypeOptionsBuilder { b.setters = append(b.setters, func(o *CreateTypeOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1954,10 +1954,10 @@ func (b *createVectorIndexOptionsBuilder) SetSourceModel(v string) *createVector return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *createVectorIndexOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *createVectorIndexOptionsBuilder { +func (b *createVectorIndexOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *createVectorIndexOptionsBuilder { b.setters = append(b.setters, func(o *CreateVectorIndexOptions) { MergeInto(&o.APIOptions, v...) }) @@ -1969,7 +1969,7 @@ func (b *createVectorIndexOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions] // // Example using the fluent builder ([DatabaseInfo]): // -// opts := options.DatabaseInfo().SetAPIOptions(...) +// opts := options.DatabaseInfo().UpdateAPIOptions(...) // // Example using a pointer to [DatabaseInfoOptions] without the fluent builder: // @@ -1997,10 +1997,10 @@ func (b *databaseInfoOptionsBuilder) Setters() []func(*DatabaseInfoOptions) { return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→Admin hierarchy. -func (b *databaseInfoOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *databaseInfoOptionsBuilder { +func (b *databaseInfoOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *databaseInfoOptionsBuilder { b.setters = append(b.setters, func(o *DatabaseInfoOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2053,10 +2053,10 @@ func (b *dropCollectionOptionsBuilder) SetKeyspace(v string) *dropCollectionOpti return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *dropCollectionOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *dropCollectionOptionsBuilder { +func (b *dropCollectionOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *dropCollectionOptionsBuilder { b.setters = append(b.setters, func(o *DropCollectionOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2117,10 +2117,10 @@ func (b *dropDatabaseOptionsBuilder) SetPollInterval(v time.Duration) *dropDatab return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→Admin hierarchy. -func (b *dropDatabaseOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *dropDatabaseOptionsBuilder { +func (b *dropDatabaseOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *dropDatabaseOptionsBuilder { b.setters = append(b.setters, func(o *DropDatabaseOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2181,10 +2181,10 @@ func (b *dropKeyspaceOptionsBuilder) SetPollInterval(v time.Duration) *dropKeysp return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *dropKeyspaceOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *dropKeyspaceOptionsBuilder { +func (b *dropKeyspaceOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *dropKeyspaceOptionsBuilder { b.setters = append(b.setters, func(o *DropKeyspaceOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2247,10 +2247,10 @@ func (b *dropTableIndexOptionsBuilder) SetKeyspace(v string) *dropTableIndexOpti return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *dropTableIndexOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *dropTableIndexOptionsBuilder { +func (b *dropTableIndexOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *dropTableIndexOptionsBuilder { b.setters = append(b.setters, func(o *DropTableIndexOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2313,10 +2313,10 @@ func (b *dropTableOptionsBuilder) SetKeyspace(v string) *dropTableOptionsBuilder return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *dropTableOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *dropTableOptionsBuilder { +func (b *dropTableOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *dropTableOptionsBuilder { b.setters = append(b.setters, func(o *DropTableOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2379,10 +2379,10 @@ func (b *dropTypeOptionsBuilder) SetKeyspace(v string) *dropTypeOptionsBuilder { return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *dropTypeOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *dropTypeOptionsBuilder { +func (b *dropTypeOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *dropTypeOptionsBuilder { b.setters = append(b.setters, func(o *DropTypeOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2433,10 +2433,10 @@ func (b *findAvailableRegionsOptionsBuilder) SetFilterByOrg(v bool) *findAvailab return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→Admin hierarchy. -func (b *findAvailableRegionsOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *findAvailableRegionsOptionsBuilder { +func (b *findAvailableRegionsOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *findAvailableRegionsOptionsBuilder { b.setters = append(b.setters, func(o *FindAvailableRegionsOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2513,10 +2513,10 @@ func (b *findEmbeddingProvidersOptionsBuilder) SetKeyspace(v string) *findEmbedd return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *findEmbeddingProvidersOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *findEmbeddingProvidersOptionsBuilder { +func (b *findEmbeddingProvidersOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *findEmbeddingProvidersOptionsBuilder { b.setters = append(b.setters, func(o *FindEmbeddingProvidersOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2569,10 +2569,10 @@ func (b *getCollectionOptionsBuilder) SetKeyspace(v string) *getCollectionOption return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Collection→Command hierarchy. -func (b *getCollectionOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *getCollectionOptionsBuilder { +func (b *getCollectionOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *getCollectionOptionsBuilder { b.setters = append(b.setters, func(o *GetCollectionOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2625,10 +2625,10 @@ func (b *getTableOptionsBuilder) SetKeyspace(v string) *getTableOptionsBuilder { return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *getTableOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *getTableOptionsBuilder { +func (b *getTableOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *getTableOptionsBuilder { b.setters = append(b.setters, func(o *GetTableOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2777,10 +2777,10 @@ func (b *listCollectionsOptionsBuilder) SetKeyspace(v string) *listCollectionsOp return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *listCollectionsOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *listCollectionsOptionsBuilder { +func (b *listCollectionsOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *listCollectionsOptionsBuilder { b.setters = append(b.setters, func(o *ListCollectionsOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2858,10 +2858,10 @@ func (b *listDatabasesOptionsBuilder) SetStartingAfter(v string) *listDatabasesO return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→Admin hierarchy. -func (b *listDatabasesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *listDatabasesOptionsBuilder { +func (b *listDatabasesOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *listDatabasesOptionsBuilder { b.setters = append(b.setters, func(o *ListDatabasesOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2873,7 +2873,7 @@ func (b *listDatabasesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *l // // Example using the fluent builder ([ListIndexes]): // -// opts := options.ListIndexes().SetAPIOptions(...) +// opts := options.ListIndexes().UpdateAPIOptions(...) // // Example using a pointer to [ListIndexesOptions] without the fluent builder: // @@ -2901,10 +2901,10 @@ func (b *listIndexesOptionsBuilder) Setters() []func(*ListIndexesOptions) { return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *listIndexesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *listIndexesOptionsBuilder { +func (b *listIndexesOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *listIndexesOptionsBuilder { b.setters = append(b.setters, func(o *ListIndexesOptions) { MergeInto(&o.APIOptions, v...) }) @@ -2916,7 +2916,7 @@ func (b *listIndexesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *lis // // Example using the fluent builder ([ListKeyspaces]): // -// opts := options.ListKeyspaces().SetAPIOptions(...) +// opts := options.ListKeyspaces().UpdateAPIOptions(...) // // Example using a pointer to [ListKeyspacesOptions] without the fluent builder: // @@ -2944,10 +2944,10 @@ func (b *listKeyspacesOptionsBuilder) Setters() []func(*ListKeyspacesOptions) { return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *listKeyspacesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *listKeyspacesOptionsBuilder { +func (b *listKeyspacesOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *listKeyspacesOptionsBuilder { b.setters = append(b.setters, func(o *ListKeyspacesOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3000,10 +3000,10 @@ func (b *listTablesOptionsBuilder) SetKeyspace(v string) *listTablesOptionsBuild return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *listTablesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *listTablesOptionsBuilder { +func (b *listTablesOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *listTablesOptionsBuilder { b.setters = append(b.setters, func(o *ListTablesOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3056,10 +3056,10 @@ func (b *listTypesOptionsBuilder) SetKeyspace(v string) *listTypesOptionsBuilder return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Command hierarchy. -func (b *listTypesOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *listTypesOptionsBuilder { +func (b *listTypesOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *listTypesOptionsBuilder { b.setters = append(b.setters, func(o *ListTypesOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3109,9 +3109,9 @@ func (b *rerankOptionsBuilder) SetEnabled(v bool) *rerankOptionsBuilder { return b } -// SetService sets the Service option. +// UpdateService sets the Service option. // Service configures the reranking service. -func (b *rerankOptionsBuilder) SetService(v ...Builder[RerankServiceOptions]) *rerankOptionsBuilder { +func (b *rerankOptionsBuilder) UpdateService(v ...Builder[RerankServiceOptions]) *rerankOptionsBuilder { b.setters = append(b.setters, func(o *RerankOptions) { MergeInto(&o.Service, v...) }) @@ -3302,7 +3302,7 @@ func (b *serdesOptionsBuilder) SetUseJSONUnmarshal(v bool) *serdesOptionsBuilder // // Example using the fluent builder ([TableDefinition]): // -// opts := options.TableDefinition().SetAPIOptions(...) +// opts := options.TableDefinition().UpdateAPIOptions(...) // // Example using a pointer to [TableDefinitionOptions] without the fluent builder: // @@ -3330,10 +3330,10 @@ func (b *tableDefinitionOptionsBuilder) Setters() []func(*TableDefinitionOptions return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableDefinitionOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableDefinitionOptionsBuilder { +func (b *tableDefinitionOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableDefinitionOptionsBuilder { b.setters = append(b.setters, func(o *TableDefinitionOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3345,7 +3345,7 @@ func (b *tableDefinitionOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) // // Example using the fluent builder ([TableDeleteMany]): // -// opts := options.TableDeleteMany().SetAPIOptions(...) +// opts := options.TableDeleteMany().UpdateAPIOptions(...) // // Example using a pointer to [TableDeleteManyOptions] without the fluent builder: // @@ -3373,10 +3373,10 @@ func (b *tableDeleteManyOptionsBuilder) Setters() []func(*TableDeleteManyOptions return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableDeleteManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableDeleteManyOptionsBuilder { +func (b *tableDeleteManyOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableDeleteManyOptionsBuilder { b.setters = append(b.setters, func(o *TableDeleteManyOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3388,7 +3388,7 @@ func (b *tableDeleteManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) // // Example using the fluent builder ([TableDeleteOne]): // -// opts := options.TableDeleteOne().SetAPIOptions(...) +// opts := options.TableDeleteOne().UpdateAPIOptions(...) // // Example using a pointer to [TableDeleteOneOptions] without the fluent builder: // @@ -3416,10 +3416,10 @@ func (b *tableDeleteOneOptionsBuilder) Setters() []func(*TableDeleteOneOptions) return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableDeleteOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableDeleteOneOptionsBuilder { +func (b *tableDeleteOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableDeleteOneOptionsBuilder { b.setters = append(b.setters, func(o *TableDeleteOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3488,10 +3488,10 @@ func (b *tableFindOneOptionsBuilder) SetIncludeSimilarity(v bool) *tableFindOneO return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableFindOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableFindOneOptionsBuilder { +func (b *tableFindOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableFindOneOptionsBuilder { b.setters = append(b.setters, func(o *TableFindOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3602,10 +3602,10 @@ func (b *tableFindOptionsBuilder) SetInitialPageState(v string) *tableFindOption return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableFindOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableFindOptionsBuilder { +func (b *tableFindOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableFindOptionsBuilder { b.setters = append(b.setters, func(o *TableFindOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3670,10 +3670,10 @@ func (b *tableInsertManyOptionsBuilder) SetConcurrency(v int) *tableInsertManyOp return b } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableInsertManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableInsertManyOptionsBuilder { +func (b *tableInsertManyOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableInsertManyOptionsBuilder { b.setters = append(b.setters, func(o *TableInsertManyOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3685,7 +3685,7 @@ func (b *tableInsertManyOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) // // Example using the fluent builder ([TableInsertOne]): // -// opts := options.TableInsertOne().SetAPIOptions(...) +// opts := options.TableInsertOne().UpdateAPIOptions(...) // // Example using a pointer to [TableInsertOneOptions] without the fluent builder: // @@ -3713,10 +3713,10 @@ func (b *tableInsertOneOptionsBuilder) Setters() []func(*TableInsertOneOptions) return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableInsertOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableInsertOneOptionsBuilder { +func (b *tableInsertOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableInsertOneOptionsBuilder { b.setters = append(b.setters, func(o *TableInsertOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3728,7 +3728,7 @@ func (b *tableInsertOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) * // // Example using the fluent builder ([TableUpdateOne]): // -// opts := options.TableUpdateOne().SetAPIOptions(...) +// opts := options.TableUpdateOne().UpdateAPIOptions(...) // // Example using a pointer to [TableUpdateOneOptions] without the fluent builder: // @@ -3756,10 +3756,10 @@ func (b *tableUpdateOneOptionsBuilder) Setters() []func(*TableUpdateOneOptions) return b.setters } -// SetAPIOptions sets the APIOptions option. +// UpdateAPIOptions sets the APIOptions option. // APIOptions overrides API-level settings (token, timeout, headers, etc.) // for this command. These are merged into the Client→DB→Table→Command hierarchy. -func (b *tableUpdateOneOptionsBuilder) SetAPIOptions(v ...Builder[APIOptions]) *tableUpdateOneOptionsBuilder { +func (b *tableUpdateOneOptionsBuilder) UpdateAPIOptions(v ...Builder[APIOptions]) *tableUpdateOneOptionsBuilder { b.setters = append(b.setters, func(o *TableUpdateOneOptions) { MergeInto(&o.APIOptions, v...) }) @@ -3892,9 +3892,9 @@ func (b *vectorOptionsBuilder) SetMetric(v string) *vectorOptionsBuilder { return b } -// SetService sets the Service option. +// UpdateService sets the Service option. // Service configures automatic vector embedding generation (vectorize). -func (b *vectorOptionsBuilder) SetService(v ...Builder[VectorServiceOptions]) *vectorOptionsBuilder { +func (b *vectorOptionsBuilder) UpdateService(v ...Builder[VectorServiceOptions]) *vectorOptionsBuilder { b.setters = append(b.setters, func(o *VectorOptions) { MergeInto(&o.Service, v...) }) diff --git a/astra/options/collection_options.go b/astra/options/collection_options.go index 7202e97..45dca2d 100644 --- a/astra/options/collection_options.go +++ b/astra/options/collection_options.go @@ -41,8 +41,8 @@ func (b *getCollectionOptionsBuilder) SetEmbeddingAPIKey(apiKey string) *getColl return b } -// SetRerankingAPIKey sets the API key to use for reranking generation for this collection. -func (b *getCollectionOptionsBuilder) SetRerankingAPIKey(apiKey string) *getCollectionOptionsBuilder { +// UpdateRerankingAPIKey sets the API key to use for reranking generation for this collection. +func (b *getCollectionOptionsBuilder) UpdateRerankingAPIKey(apiKey string) *getCollectionOptionsBuilder { b.setters = append(b.setters, func(o *GetCollectionOptions) { if o.APIOptions == nil { o.APIOptions = &APIOptions{} @@ -85,8 +85,8 @@ func (b *createCollectionOptionsBuilder) SetEmbeddingAPIKey(apiKey string) *crea return b } -// SetRerankingAPIKey sets the API key to use for reranking generation for this collection. -func (b *createCollectionOptionsBuilder) SetRerankingAPIKey(apiKey string) *createCollectionOptionsBuilder { +// UpdateRerankingAPIKey sets the API key to use for reranking generation for this collection. +func (b *createCollectionOptionsBuilder) UpdateRerankingAPIKey(apiKey string) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { if o.APIOptions == nil { o.APIOptions = &APIOptions{} @@ -96,9 +96,9 @@ func (b *createCollectionOptionsBuilder) SetRerankingAPIKey(apiKey string) *crea return b } -// SetIndexingAllow sets the list of field paths to index. Use "*" to index all fields. -// Mutually exclusive with SetIndexingDeny. -func (b *createCollectionOptionsBuilder) SetIndexingAllow(v ...string) *createCollectionOptionsBuilder { +// UpdateIndexingAllow sets the list of field paths to index. Use "*" to index all fields. +// Mutually exclusive with UpdateIndexingDeny. +func (b *createCollectionOptionsBuilder) UpdateIndexingAllow(v ...string) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { if o.Indexing == nil { o.Indexing = &IndexingOptions{} @@ -108,9 +108,9 @@ func (b *createCollectionOptionsBuilder) SetIndexingAllow(v ...string) *createCo return b } -// SetIndexingDeny sets the list of field paths to exclude from indexing. Use "*" to -// disable indexing entirely. Mutually exclusive with SetIndexingAllow. -func (b *createCollectionOptionsBuilder) SetIndexingDeny(v ...string) *createCollectionOptionsBuilder { +// UpdateIndexingDeny sets the list of field paths to exclude from indexing. Use "*" to +// disable indexing entirely. Mutually exclusive with UpdateIndexingAllow. +func (b *createCollectionOptionsBuilder) UpdateIndexingDeny(v ...string) *createCollectionOptionsBuilder { b.setters = append(b.setters, func(o *CreateCollectionOptions) { if o.Indexing == nil { o.Indexing = &IndexingOptions{} @@ -267,8 +267,8 @@ type RerankServiceOptions struct { Parameters map[string]any `json:"parameters,omitempty"` } -// SetAnalyzer sets the analyzer name for lexical search (e.g., "standard"). -func (b *lexicalOptionsBuilder) SetAnalyzer(v string) *lexicalOptionsBuilder { +// UpdateAnalyzer sets the analyzer name for lexical search (e.g., "standard"). +func (b *lexicalOptionsBuilder) UpdateAnalyzer(v string) *lexicalOptionsBuilder { b.setters = append(b.setters, func(o *LexicalOptions) { o.Analyzer = v }) diff --git a/astra/options/collection_options_test.go b/astra/options/collection_options_test.go index a148ecb..d05cedb 100644 --- a/astra/options/collection_options_test.go +++ b/astra/options/collection_options_test.go @@ -31,21 +31,21 @@ func TestIndexingOptionsValidation(t *testing.T) { }{ { name: "allow only", - opts: options.CreateCollection().SetIndexing(&options.IndexingOptions{ + opts: options.CreateCollection().UpdateIndexing(&options.IndexingOptions{ Allow: []string{"field1", "field2"}, }), wantErr: false, }, { name: "deny only", - opts: options.CreateCollection().SetIndexing(&options.IndexingOptions{ + opts: options.CreateCollection().UpdateIndexing(&options.IndexingOptions{ Deny: []string{"field3", "field4"}, }), wantErr: false, }, { name: "allow and deny", - opts: options.CreateCollection().SetIndexing(&options.IndexingOptions{ + opts: options.CreateCollection().UpdateIndexing(&options.IndexingOptions{ Allow: []string{"field1"}, Deny: []string{"field2"}, }), @@ -53,12 +53,12 @@ func TestIndexingOptionsValidation(t *testing.T) { }, { name: "fluent version with allow", - opts: options.CreateCollection().SetIndexingAllow("field1", "field2"), + opts: options.CreateCollection().UpdateIndexingAllow("field1", "field2"), wantErr: false, }, { name: "fluent version with allow and deny", - opts: options.CreateCollection().SetIndexingAllow("field1", "field2").SetIndexingDeny("field3", "field4"), + opts: options.CreateCollection().UpdateIndexingAllow("field1", "field2").UpdateIndexingDeny("field3", "field4"), wantErr: true, }, { @@ -104,8 +104,8 @@ func TestVectorServiceOptionsValidation(t *testing.T) { }{ { name: "both provider and modelName set", - opts: options.CreateCollection().SetVector( - options.Vector().SetDimension(1024).SetService( + opts: options.CreateCollection().UpdateVector( + options.Vector().SetDimension(1024).UpdateService( options.VectorService().SetProvider("openai").SetModelName("text-embedding-3-small"), ), ), @@ -113,8 +113,8 @@ func TestVectorServiceOptionsValidation(t *testing.T) { }, { name: "neither provider nor modelName set", - opts: options.CreateCollection().SetVector( - options.Vector().SetDimension(1024).SetService( + opts: options.CreateCollection().UpdateVector( + options.Vector().SetDimension(1024).UpdateService( options.VectorService(), ), ), @@ -122,8 +122,8 @@ func TestVectorServiceOptionsValidation(t *testing.T) { }, { name: "provider only", - opts: options.CreateCollection().SetVector( - options.Vector().SetDimension(1024).SetService( + opts: options.CreateCollection().UpdateVector( + options.Vector().SetDimension(1024).UpdateService( options.VectorService().SetProvider("openai"), ), ), @@ -131,8 +131,8 @@ func TestVectorServiceOptionsValidation(t *testing.T) { }, { name: "modelName only", - opts: options.CreateCollection().SetVector( - options.Vector().SetDimension(1024).SetService( + opts: options.CreateCollection().UpdateVector( + options.Vector().SetDimension(1024).UpdateService( options.VectorService().SetModelName("text-embedding-3-small"), ), ), @@ -140,7 +140,7 @@ func TestVectorServiceOptionsValidation(t *testing.T) { }, { name: "no service at all", - opts: options.CreateCollection().SetVector( + opts: options.CreateCollection().UpdateVector( options.Vector().SetDimension(1024), ), wantErr: false, @@ -216,11 +216,11 @@ func TestDeleteManyOptionsTimeoutNotSerialized(t *testing.T) { } } -func TestSetAPIOptionsBuilder(t *testing.T) { +func TestUpdateAPIOptionsBuilder(t *testing.T) { // Builder style opts, err := options.MergeAndValidate( options.CollectionDeleteMany(). - SetAPIOptions(options.API().SetToken("override-token")), + UpdateAPIOptions(options.API().SetToken("override-token")), ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -233,11 +233,11 @@ func TestSetAPIOptionsBuilder(t *testing.T) { } } -func TestSetAPIOptionsRawStruct(t *testing.T) { +func TestUpdateAPIOptionsRawStruct(t *testing.T) { token := "raw-token" opts, err := options.MergeAndValidate( options.CollectionDeleteMany(). - SetAPIOptions(&options.APIOptions{TokenProvider: options.NewStaticTokenProvider(token)}), + UpdateAPIOptions(&options.APIOptions{TokenProvider: options.NewStaticTokenProvider(token)}), ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -272,13 +272,13 @@ func TestAPIOptionsNotSerialized(t *testing.T) { } } -func TestSetAPIOptionsAllBuilders(t *testing.T) { - // Verify SetAPIOptions works on all 6 collection builders +func TestUpdateAPIOptionsAllBuilders(t *testing.T) { + // Verify UpdateAPIOptions works on all 6 collection builders token := "t" apiOpt := options.API().SetToken(token) // CollectionFind - f, err := options.MergeAndValidate(options.CollectionFind().SetAPIOptions(apiOpt)) + f, err := options.MergeAndValidate(options.CollectionFind().UpdateAPIOptions(apiOpt)) if err != nil { t.Fatalf("CollectionFind: %v", err) } @@ -290,7 +290,7 @@ func TestSetAPIOptionsAllBuilders(t *testing.T) { } // CollectionUpdateOne - u1, err := options.MergeAndValidate(options.CollectionUpdateOne().SetAPIOptions(apiOpt)) + u1, err := options.MergeAndValidate(options.CollectionUpdateOne().UpdateAPIOptions(apiOpt)) if err != nil { t.Fatalf("CollectionUpdateOne: %v", err) } @@ -302,7 +302,7 @@ func TestSetAPIOptionsAllBuilders(t *testing.T) { } // CollectionUpdateMany - um, err := options.MergeAndValidate(options.CollectionUpdateMany().SetAPIOptions(apiOpt)) + um, err := options.MergeAndValidate(options.CollectionUpdateMany().UpdateAPIOptions(apiOpt)) if err != nil { t.Fatalf("CollectionUpdateMany: %v", err) } @@ -314,7 +314,7 @@ func TestSetAPIOptionsAllBuilders(t *testing.T) { } // CollectionDeleteOne - d1, err := options.MergeAndValidate(options.CollectionDeleteOne().SetAPIOptions(apiOpt)) + d1, err := options.MergeAndValidate(options.CollectionDeleteOne().UpdateAPIOptions(apiOpt)) if err != nil { t.Fatalf("CollectionDeleteOne: %v", err) } @@ -326,7 +326,7 @@ func TestSetAPIOptionsAllBuilders(t *testing.T) { } // CollectionDeleteMany - dm, err := options.MergeAndValidate(options.CollectionDeleteMany().SetAPIOptions(apiOpt)) + dm, err := options.MergeAndValidate(options.CollectionDeleteMany().UpdateAPIOptions(apiOpt)) if err != nil { t.Fatalf("CollectionDeleteMany: %v", err) } @@ -338,7 +338,7 @@ func TestSetAPIOptionsAllBuilders(t *testing.T) { } // CollectionFindOneAndUpdate - fu, err := options.MergeAndValidate(options.CollectionFindOneAndUpdate().SetAPIOptions(apiOpt)) + fu, err := options.MergeAndValidate(options.CollectionFindOneAndUpdate().UpdateAPIOptions(apiOpt)) if err != nil { t.Fatalf("CollectionFindOneAndUpdate: %v", err) } diff --git a/astra/options/index_options.go b/astra/options/index_options.go index cca89c3..0e1738a 100644 --- a/astra/options/index_options.go +++ b/astra/options/index_options.go @@ -85,8 +85,8 @@ type CreateTextIndexOptions struct { APIOptions *APIOptions } -// SetAnalyzer sets the built-in analyzer to use for the text index (e.g. "standard", "simple", "whitespace", etc.) -func (b *createTextIndexOptionsBuilder) SetAnalyzer(v string) *createTextIndexOptionsBuilder { +// UpdateAnalyzer sets the built-in analyzer to use for the text index (e.g. "standard", "simple", "whitespace", etc.) +func (b *createTextIndexOptionsBuilder) UpdateAnalyzer(v string) *createTextIndexOptionsBuilder { b.setters = append(b.setters, func(o *CreateTextIndexOptions) { o.Analyzer = v }) diff --git a/astra/options/options.go b/astra/options/options.go index 3f62aae..224ac6c 100644 --- a/astra/options/options.go +++ b/astra/options/options.go @@ -40,12 +40,12 @@ type Builder[T any] interface { Setters() []func(*T) } -// ShouldMerge is implemented by options types whose pointer fields should +// shouldMerge is implemented by options types whose pointer fields should // be merged sub-field-by-sub-field rather than replaced wholesale when // encountered during a copyNonNilFields pass. // Implementations must handle nil receivers gracefully. -type ShouldMerge interface { - Merge(other ShouldMerge) ShouldMerge +type shouldMerge interface { + merge(other shouldMerge) shouldMerge } // NoopBuilder returns a [Builder] implementation that just copies @@ -131,9 +131,9 @@ func copyNonNilFields[T any](src, dst *T) { switch srcField.Kind() { case reflect.Pointer, reflect.Slice, reflect.Map, reflect.Interface, reflect.Func: if !srcField.IsNil() { - if sm, ok := srcField.Interface().(ShouldMerge); ok { - dstSM := dstVal.Field(i).Interface().(ShouldMerge) - dstVal.Field(i).Set(reflect.ValueOf(dstSM.Merge(sm))) + if sm, ok := srcField.Interface().(shouldMerge); ok { + dstSM := dstVal.Field(i).Interface().(shouldMerge) + dstVal.Field(i).Set(reflect.ValueOf(dstSM.merge(sm))) continue } dstVal.Field(i).Set(srcField) diff --git a/astra/options/options_test.go b/astra/options/options_test.go index de3c531..548b7e8 100644 --- a/astra/options/options_test.go +++ b/astra/options/options_test.go @@ -26,10 +26,10 @@ import ( ) func TestAdditiveMerging(t *testing.T) { - // Test that multiple SetAPIOptions calls merge additively + // Test that multiple UpdateAPIOptions calls merge additively opts := options.ListCollections(). - SetAPIOptions(options.API().SetToken("token1")). - SetAPIOptions(options.API().SetKeyspace("ks1")) + UpdateAPIOptions(options.API().SetToken("token1")). + UpdateAPIOptions(options.API().SetKeyspace("ks1")) merged := options.Merge(opts) @@ -68,7 +68,7 @@ func TestAdditiveHeaderMerging(t *testing.T) { func TestNestedInitialization(t *testing.T) { // Verifies that fields deep in a command struct correctly // initialize their container structs (APIOptions, TimeoutOptions). - opts := options.ListCollections().SetAPIOptions(options.API().SetRequestTimeout(45 * time.Second)) + opts := options.ListCollections().UpdateAPIOptions(options.API().SetRequestTimeout(45 * time.Second)) resolved := options.Merge(opts) @@ -98,8 +98,8 @@ func TestHierarchyInheritance(t *testing.T) { // 3. Collection adds a timeout and another header — use builder path for additive headers coll := db.Collection("my-coll", - options.GetCollection().SetAPIOptions(options.API().SetRequestTimeout(10*time.Second)), - options.GetCollection().SetAPIOptions(options.API().AddHeader("X-Coll", "true")), + options.GetCollection().UpdateAPIOptions(options.API().SetRequestTimeout(10*time.Second)), + options.GetCollection().UpdateAPIOptions(options.API().AddHeader("X-Coll", "true")), ) // 4. Resolve at the final level diff --git a/astra/options/table_options.go b/astra/options/table_options.go index b42482c..6e8b01b 100644 --- a/astra/options/table_options.go +++ b/astra/options/table_options.go @@ -34,8 +34,8 @@ func (b *getTableOptionsBuilder) SetEmbeddingAPIKey(apiKey string) *getTableOpti return b } -// SetRerankingAPIKey sets the API key to use for reranking generation for this table. -func (b *getTableOptionsBuilder) SetRerankingAPIKey(apiKey string) *getTableOptionsBuilder { +// UpdateRerankingAPIKey sets the API key to use for reranking generation for this table. +func (b *getTableOptionsBuilder) UpdateRerankingAPIKey(apiKey string) *getTableOptionsBuilder { b.setters = append(b.setters, func(o *GetTableOptions) { if o.APIOptions == nil { o.APIOptions = &APIOptions{} @@ -67,8 +67,8 @@ func (b *createTableOptionsBuilder) SetEmbeddingAPIKey(apiKey string) *createTab return b } -// SetRerankingAPIKey sets the API key to use for reranking generation for this table. -func (b *createTableOptionsBuilder) SetRerankingAPIKey(apiKey string) *createTableOptionsBuilder { +// UpdateRerankingAPIKey sets the API key to use for reranking generation for this table. +func (b *createTableOptionsBuilder) UpdateRerankingAPIKey(apiKey string) *createTableOptionsBuilder { b.setters = append(b.setters, func(o *CreateTableOptions) { if o.APIOptions == nil { o.APIOptions = &APIOptions{} diff --git a/astra/results/embedding_provider_results.go b/astra/results/embedding_provider_results.go index e82c1bb..d2e2fb0 100644 --- a/astra/results/embedding_provider_results.go +++ b/astra/results/embedding_provider_results.go @@ -87,8 +87,8 @@ type EmbeddingProviderInfo struct { // - "SHARED_SECRET": Authentication tied to a collection at collection creation time using the Astra KMS. // // _, err = db.CreateCollection(ctx, "my_coll", - // options.CreateCollection().SetVector( - // options.Vector().SetService( + // options.CreateCollection().UpdateVector( + // options.Vector().UpdateService( // options.VectorService(). // SetProvider("openai"). // SetModelName("text-embedding-3-small"). @@ -103,8 +103,8 @@ type EmbeddingProviderInfo struct { // No key or credential is needed when creating or using the collection. // // _, err = db.CreateCollection(ctx, "my_coll", - // options.CreateCollection().SetVector( - // options.Vector().SetService( + // options.CreateCollection().UpdateVector( + // options.Vector().UpdateService( // options.VectorService(). // SetProvider("nvidia"). // SetModelName("NV-Embed-QA"), diff --git a/astra/table.go b/astra/table.go index 90e70e9..bd59005 100644 --- a/astra/table.go +++ b/astra/table.go @@ -500,7 +500,7 @@ func createVectorIndexCommand(t *Table, name string, column string, opts ...opti // Example - with analyzer: // // err := tbl.CreateTextIndex(ctx, "content_idx", "content", -// options.CreateTextIndex().SetAnalyzer("standard")) +// options.CreateTextIndex().UpdateAnalyzer("standard")) // // Example - with ifNotExists: // diff --git a/astra/table_test.go b/astra/table_test.go index 57aee14..3948913 100644 --- a/astra/table_test.go +++ b/astra/table_test.go @@ -1025,7 +1025,7 @@ func TestTableUpdateOne_APIOptionsOverrideToken(t *testing.T) { err := tbl.UpdateOne(context.Background(), filter.F{"pk": 1}, update.Table().Set("x", 2), - options.TableUpdateOne().SetAPIOptions(options.API().SetToken("override-token")), + options.TableUpdateOne().UpdateAPIOptions(options.API().SetToken("override-token")), ) if err != nil { t.Fatalf("unexpected error: %v", err) diff --git a/integration/harness/prelude.go b/integration/harness/prelude.go index dc82d20..e6a8989 100644 --- a/integration/harness/prelude.go +++ b/integration/harness/prelude.go @@ -96,12 +96,12 @@ func startCreateCollections(db *astra.Db) { builder := options.CreateCollection().SetKeyspace(ks) if ks == TestKeyspaces[0] { - builder.SetVector(&options.VectorOptions{ + builder.UpdateVector(&options.VectorOptions{ Dimension: ptr.To(5), Metric: ptr.To("cosine"), }) } else { - builder.SetVector(&options.VectorOptions{ + builder.UpdateVector(&options.VectorOptions{ Dimension: ptr.To(1024), Service: &options.VectorServiceOptions{ Provider: ptr.To("openai"), @@ -113,7 +113,7 @@ func startCreateCollections(db *astra.Db) { coll, err := db.CreateCollection(Ctx, DefaultCollectionName, builder) testlib.PanicIfErr(err, "failed to create collection %s in keyspace %s", DefaultCollectionName, ks) - _, err = coll.DeleteMany(Ctx, filter.F{}, options.CollectionDeleteMany().SetAPIOptions(options.API().SetKeyspace(ks))) + _, err = coll.DeleteMany(Ctx, filter.F{}, options.CollectionDeleteMany().UpdateAPIOptions(options.API().SetKeyspace(ks))) testlib.PanicIfErr(err, "failed to clear collection %s in keyspace %s", DefaultCollectionName, ks) }(keyspace) } @@ -135,15 +135,15 @@ func startCreateTables(db *astra.Db) { tbl, err := db.CreateTable(Ctx, DefaultTableName, schema, options.CreateTable().SetIfNotExists(true).SetKeyspace(ks)) testlib.PanicIfErr(err, "failed to create table %s in keyspace %s", DefaultTableName, ks) - err = tbl.DeleteMany(Ctx, filter.F{}, options.TableDeleteMany().SetAPIOptions(options.API().SetKeyspace(ks))) + err = tbl.DeleteMany(Ctx, filter.F{}, options.TableDeleteMany().UpdateAPIOptions(options.API().SetKeyspace(ks))) testlib.PanicIfErr(err, "failed to clear table %s in keyspace %s", DefaultTableName, ks) if ks == TestKeyspaces[0] { - err = tbl.CreateVectorIndex(Ctx, fmt.Sprintf("vector_idx_%s", ks), "vector", options.CreateVectorIndex().SetMetric(options.MetricDotProduct).SetIfNotExists(true).SetAPIOptions(options.API().SetKeyspace(ks))) + err = tbl.CreateVectorIndex(Ctx, fmt.Sprintf("vector_idx_%s", ks), "vector", options.CreateVectorIndex().SetMetric(options.MetricDotProduct).SetIfNotExists(true).UpdateAPIOptions(options.API().SetKeyspace(ks))) testlib.PanicIfErr(err, "failed to create vector index in keyspace %s", ks) } - err = tbl.CreateIndex(Ctx, fmt.Sprintf("bigint_idx_%s", ks), "bigint", options.CreateIndex().SetIfNotExists(true).SetAPIOptions(options.API().SetKeyspace(ks))) + err = tbl.CreateIndex(Ctx, fmt.Sprintf("bigint_idx_%s", ks), "bigint", options.CreateIndex().SetIfNotExists(true).UpdateAPIOptions(options.API().SetKeyspace(ks))) testlib.PanicIfErr(err, "failed to create bigint index in keyspace %s", ks) }(keyspace) } diff --git a/integration/legacy/tests/collection_nested_tests.go b/integration/legacy/tests/collection_nested_tests.go index 702d336..44acf17 100644 --- a/integration/legacy/tests/collection_nested_tests.go +++ b/integration/legacy/tests/collection_nested_tests.go @@ -109,7 +109,7 @@ func getTestRestaurants() []Restaurant { func CollectionNestedCreate(e *harness.TestEnv) error { ctx := context.Background() db := e.DefaultDb() - _, err := db.CreateCollection(ctx, nestedCollectionName, options.CreateCollection().SetIndexingAllow("*")) + _, err := db.CreateCollection(ctx, nestedCollectionName, options.CreateCollection().UpdateIndexingAllow("*")) return err } diff --git a/integration/legacy/tests/collection_tests.go b/integration/legacy/tests/collection_tests.go index 8e5bd90..efb61ac 100644 --- a/integration/legacy/tests/collection_tests.go +++ b/integration/legacy/tests/collection_tests.go @@ -1240,7 +1240,7 @@ func CollectionVectorCreate(e *harness.TestEnv) error { // Create a collection with vector support _, err := db.CreateCollection(ctx, vectorCollectionName, - options.CreateCollection().SetVector(&options.VectorOptions{ + options.CreateCollection().UpdateVector(&options.VectorOptions{ Dimension: ptr.To(vectorDimension), Metric: ptr.To("cosine"), })) diff --git a/integration/legacy/tests/table_tests.go b/integration/legacy/tests/table_tests.go index 8f05660..6810011 100644 --- a/integration/legacy/tests/table_tests.go +++ b/integration/legacy/tests/table_tests.go @@ -223,7 +223,7 @@ func TableFind(e *harness.TestEnv) error { db := e.DefaultDb() warningHandlerRun := false - tbl := db.Table(tableName, options.GetTable().SetAPIOptions( + tbl := db.Table(tableName, options.GetTable().UpdateAPIOptions( options.API().SetWarningHandler(func(w results.Warning) { warningHandlerRun = true }), diff --git a/tools/gen-options/gen_options_test.go b/tools/gen-options/gen_options_test.go index 33bfb3a..704d699 100644 --- a/tools/gen-options/gen_options_test.go +++ b/tools/gen-options/gen_options_test.go @@ -68,7 +68,7 @@ const methodOnlyExample = `// CreateCollectionOption configures a CreateCollecti // // Example using the fluent builder ([CreateCollection]): // -// opts := options.CreateCollection().SetDefaultId(...) +// opts := options.CreateCollection().UpdateDefaultId(...) // // Example using a pointer to [CreateCollectionOptions] without the fluent builder: // @@ -101,7 +101,7 @@ func TestAliasExampleString(t *testing.T) { Alias: "CreateCollectionOption", Constructor: "CreateCollection", OptsType: "CreateCollectionOptions", - Method: "SetDefaultId", + Method: "UpdateDefaultId", }, want: methodOnlyExample, }, @@ -134,7 +134,7 @@ func TestPickAliasExample(t *testing.T) { { name: "picks first simple field", setters: []setterDef{ - {Method: "SetVector", Field: "Vector", IsVariadicBuilder: true}, + {Method: "UpdateVector", Field: "Vector", IsVariadicBuilder: true}, {Method: "SetBlocking", Field: "Blocking", ParamType: "bool"}, {Method: "SetLimit", Field: "Limit", ParamType: "int"}, }, @@ -149,9 +149,9 @@ func TestPickAliasExample(t *testing.T) { { name: "fallback to first setter when no simple field", setters: []setterDef{ - {Method: "SetVector", Field: "Vector", IsVariadicBuilder: true}, + {Method: "UpdateVector", Field: "Vector", IsVariadicBuilder: true}, }, - want: aliasDef{Method: "SetVector"}, + want: aliasDef{Method: "UpdateVector"}, }, { name: "empty setters", diff --git a/tools/gen-options/main.go b/tools/gen-options/main.go index e965073..e196812 100644 --- a/tools/gen-options/main.go +++ b/tools/gen-options/main.go @@ -21,7 +21,7 @@ // XxxOptionsBuilder struct, emits the builder struct definition, constructor, // Setters(), and all Set* methods. Also emits the options struct's Setters() method // and trivial Validate() stubs (when no hand-written Validate exists). -// Hand-written convenience methods (e.g. SetIndexingAllow) are left alone in their +// Hand-written convenience methods (e.g. UpdateIndexingAllow) are left alone in their // original files and simply layer on top of the generated setters. // // Usage (via go:generate in options/options.go): @@ -111,20 +111,20 @@ func load(dir string) (*loadedPkg, error) { } p := pkgs[0] - smIface, _ := shouldMergeInterface(p.Types) + smIface := shouldMergeInterface(p.Types) return &loadedPkg{name: p.Name, types: p.Types, shouldMerge: smIface, syntax: p.Syntax, fset: fset}, nil } -func shouldMergeInterface(pkg *types.Package) (*types.Interface, error) { - obj := pkg.Scope().Lookup("ShouldMerge") +func shouldMergeInterface(pkg *types.Package) *types.Interface { + obj := pkg.Scope().Lookup("shouldMerge") if obj == nil { - return nil, fmt.Errorf("ShouldMerge not found in package %q", pkg.Name()) + log.Fatalf("shouldMerge not found in package %q", pkg.Name()) } iface, ok := obj.Type().Underlying().(*types.Interface) if !ok { - return nil, fmt.Errorf("ShouldMerge is not an interface") + log.Fatalf("shouldMerge is not an interface") } - return iface, nil + return iface } // handWrittenTypes scans non-generated source files and returns a set of @@ -500,25 +500,16 @@ func setterForField(structName string, f *types.Var, shouldMerge *types.Interfac case *types.Pointer: named, isNamed := t.Elem().(*types.Named) - if shouldMerge != nil && types.Implements(t, shouldMerge) && isNamed { - return setterDef{ - Comment: comment, - Method: "Update" + f.Name(), - Field: f.Name(), - ParamType: fmt.Sprintf("Builder[%s]", named.Obj().Name()), - IsVariadicBuilder: true, - }, true - } // Nested Options child → variadic builder setter using Merge. isOption := false if isNamed { isOption = futureOptions[named.Obj().Name()] } - if isOption { + if isOption || (types.Implements(t, shouldMerge) && isNamed) { return setterDef{ Comment: comment, - Method: method, + Method: "Update" + f.Name(), Field: f.Name(), ParamType: fmt.Sprintf("Builder[%s]", named.Obj().Name()), IsVariadicBuilder: true,