From b09834f98d062ff1cbcd44554da807bf5c88a4bf Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Mon, 10 Aug 2026 18:01:28 +0200 Subject: [PATCH] fix(search): reject CaseInsensitive on non-keyword/path fields CaseInsensitive routes queries to a _lowercase sibling that is only generated for keyword/path fields, so marking any other type CaseInsensitive would silently match nothing. Validate now rejects it up front. --- services/search/pkg/mapping/validate.go | 23 +++++++++++++++----- services/search/pkg/mapping/validate_test.go | 10 +++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/services/search/pkg/mapping/validate.go b/services/search/pkg/mapping/validate.go index 761309992a..f0a8e462e2 100644 --- a/services/search/pkg/mapping/validate.go +++ b/services/search/pkg/mapping/validate.go @@ -17,17 +17,28 @@ func Validate(t reflect.Type, overrides map[string]FieldOpts) error { return nil } names := collectNames(t, "") - var unknown []string - for k := range overrides { + var unknown, miscased []string + for k, opts := range overrides { if _, ok := names[k]; !ok { unknown = append(unknown, k) + continue + } + // CaseInsensitive routes queries to a _lowercase sibling, which is + // only generated for keyword/path fields; on any other type the query + // would target a non-existent field and silently match nothing. + if opts.caseInsensitive() && !isCasedType(opts) { + miscased = append(miscased, k) } } - if len(unknown) == 0 { - return nil + if len(unknown) > 0 { + sort.Strings(unknown) + return fmt.Errorf("mapping: unknown override keys: %s", strings.Join(unknown, ", ")) } - sort.Strings(unknown) - return fmt.Errorf("mapping: unknown override keys: %s", strings.Join(unknown, ", ")) + if len(miscased) > 0 { + sort.Strings(miscased) + return fmt.Errorf("mapping: CaseInsensitive is only valid on keyword/path fields: %s", strings.Join(miscased, ", ")) + } + return nil } func collectNames(t reflect.Type, prefix string) map[string]struct{} { diff --git a/services/search/pkg/mapping/validate_test.go b/services/search/pkg/mapping/validate_test.go index 3b15f39caf..69a96b0a01 100644 --- a/services/search/pkg/mapping/validate_test.go +++ b/services/search/pkg/mapping/validate_test.go @@ -44,4 +44,14 @@ var _ = Describe("Validate", func() { It("accepts empty overrides", func() { Expect(Validate(reflect.TypeFor[sample](), nil)).To(Succeed()) }) + + It("rejects CaseInsensitive on a non-keyword/path field", func() { + True := true + err := Validate(reflect.TypeFor[sample](), map[string]FieldOpts{ + "Name": {Type: TypeFulltext, CaseInsensitive: &True}, + }) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Name")) + Expect(err.Error()).To(ContainSubstring("CaseInsensitive")) + }) })