Files
Dominik Schmidt 903ef4521b feat(search): search facets by word too, whole values only by opt-out
SharePoint's default for a text property is word breaking, so ours is too:
every keyword field gets the _words sibling unless it opts out with
NoWordBreaker, which now carries SharePoint's polarity as well. Artist,
album, camera model and the other facets match by word like name and title;
tags and favorites stay one label, ids, paths and the mime type one value.
2026-08-31 13:40:42 +02:00

58 lines
2.3 KiB
Go

// Package mapping builds search index mappings for bleve and OpenSearch from
// a Go struct via reflection. Field names come from json tags; the caller
// provides overrides for fields that need a specific type or analyzer.
package mapping
// Field type constants used in FieldOpts.Type. An empty Type means the type
// is inferred from the Go field via reflection.
const (
TypeKeyword = "keyword"
TypeFulltext = "fulltext"
TypePath = "path"
TypeWildcard = "wildcard"
TypeNumeric = "numeric"
TypeDatetime = "datetime"
TypeBool = "bool"
TypeObject = "object"
TypeGeopoint = "geopoint"
)
// LowercaseSuffix names the lowercased sibling of a keyword/path field.
const LowercaseSuffix = "_lowercase"
// WordsSuffix names the word-broken sibling of a keyword field.
const WordsSuffix = "_words"
// WordsAnalyzer names the analyzer both engines register for the words sibling.
const WordsAnalyzer = "words"
// FieldOpts overrides the default type inference for a struct field. Keys in
// the override map are json-tag names (e.g. "Name", "location", "audio.artist"),
// not Go field names.
type FieldOpts struct {
// Type is one of the Type* constants. Empty means "infer from Go type".
Type string
// CaseInsensitive additionally indexes a lowercased <name>_lowercase sibling
// for case-insensitive search; the case-preserved base is always indexed.
// On by default for keyword/path fields, KQL searches case-insensitively;
// false opts a field out (ids, paths).
CaseInsensitive *bool
// NoWordBreaker is SharePoint's switch, with its default: a keyword field
// additionally indexes a <name>_words sibling split into lowercased words
// (no stemming), so a single word matches a value that contains it,
// "report" finds "Report.txt"; true opts a field out and leaves it one
// whole value (tags, ids, paths). The base stays the whole value for
// returning and aggregating; wildcards and whole-value matches use the
// _lowercase sibling. Keyword only.
NoWordBreaker *bool
// IncludeInAll controls bleve's _all field inclusion. Nil means "use the
// bleve default for this field type". Has no effect on OpenSearch.
IncludeInAll *bool
}
func (o FieldOpts) caseInsensitive() bool { return o.CaseInsensitive == nil || *o.CaseInsensitive }
func (o FieldOpts) wordBroken() bool { return o.NoWordBreaker == nil || !*o.NoWordBreaker }