diff --git a/mapping/document.go b/mapping/document.go index 847326e41..5d70af912 100644 --- a/mapping/document.go +++ b/mapping/document.go @@ -112,6 +112,17 @@ func (dm *DocumentMapping) analyzerNameForPath(path string) string { return "" } +// synonymSourceForPath attempts to first find the field +// described by this path, then returns the analyzer +// configured for that field +func (dm *DocumentMapping) synonymSourceForPath(path string) string { + field := dm.fieldDescribedByPath(path) + if field != nil { + return field.SynonymSource + } + return "" +} + func (dm *DocumentMapping) fieldDescribedByPath(path string) *FieldMapping { pathElements := decodePath(path) if len(pathElements) > 1 { diff --git a/mapping/field.go b/mapping/field.go index 5c064fddd..ad5b4f424 100644 --- a/mapping/field.go +++ b/mapping/field.go @@ -80,6 +80,8 @@ type FieldMapping struct { // Applicable to vector fields only - optimization string VectorIndexOptimizedFor string `json:"vector_index_optimized_for,omitempty"` + + SynonymSource string `json:"synonym_source,omitempty"` } // NewTextFieldMapping returns a default field mapping for text diff --git a/mapping/index.go b/mapping/index.go index fe8c96713..e7387d58a 100644 --- a/mapping/index.go +++ b/mapping/index.go @@ -457,3 +457,25 @@ func (im *IndexMappingImpl) FieldMappingForPath(path string) FieldMapping { func (im *IndexMappingImpl) DefaultSearchField() string { return im.DefaultField } + +func (im *IndexMappingImpl) SynonymSourceForPath(path string) string { + // first we look for explicit mapping on the field + for _, docMapping := range im.TypeMapping { + synonymSource := docMapping.synonymSourceForPath(path) + if synonymSource != "" { + return synonymSource + } + } + + // now try the default mapping + pathMapping, _ := im.DefaultMapping.documentMappingForPath(path) + if pathMapping != nil { + if len(pathMapping.Fields) > 0 { + if pathMapping.Fields[0].SynonymSource != "" { + return pathMapping.Fields[0].SynonymSource + } + } + } + + return "" +} diff --git a/pre_search.go b/pre_search.go index c8c55bfbc..646d25199 100644 --- a/pre_search.go +++ b/pre_search.go @@ -26,6 +26,8 @@ type preSearchResultProcessor interface { finalize(*SearchResult) } +// ----------------------------------------------------------------------------- +// KNN preSearchResultProcessor for handling KNN presearch results type knnPreSearchResultProcessor struct { addFn func(sr *SearchResult, indexName string) finalizeFn func(sr *SearchResult) @@ -44,16 +46,77 @@ func (k *knnPreSearchResultProcessor) finalize(sr *SearchResult) { } // ----------------------------------------------------------------------------- +// Synonym preSearchResultProcessor for handling Synonym presearch results +type synonymPreSearchResultProcessor struct { + addFn func(sr *SearchResult, indexName string) + finalizeFn func(sr *SearchResult) +} -func finalizePreSearchResult(req *SearchRequest, preSearchResult *SearchResult) { - if requestHasKNN(req) { - preSearchResult.Hits = finalizeKNNResults(req, preSearchResult.Hits) +func (s *synonymPreSearchResultProcessor) add(sr *SearchResult, indexName string) { + if s.addFn != nil { + s.addFn(sr, indexName) } } +func (s *synonymPreSearchResultProcessor) finalize(sr *SearchResult) { + if s.finalizeFn != nil { + s.finalizeFn(sr) + } +} + +// ----------------------------------------------------------------------------- +// Master struct that can hold any number of presearch result processors +type compositePreSearchResultProcessor struct { + presearchResultProcessors []preSearchResultProcessor +} + +// Implements the add method, which forwards to all the internal processors +func (m *compositePreSearchResultProcessor) add(sr *SearchResult, indexName string) { + for _, p := range m.presearchResultProcessors { + p.add(sr, indexName) + } +} + +// Implements the finalize method, which forwards to all the internal processors +func (m *compositePreSearchResultProcessor) finalize(sr *SearchResult) { + for _, p := range m.presearchResultProcessors { + p.finalize(sr) + } +} + +// ----------------------------------------------------------------------------- +// Function to create the appropriate preSearchResultProcessor(s) func createPreSearchResultProcessor(req *SearchRequest) preSearchResultProcessor { + var processors []preSearchResultProcessor + // Add KNN processor if the request has KNN + if requestHasKNN(req) { + if knnProcessor := newKnnPreSearchResultProcessor(req); knnProcessor != nil { + processors = append(processors, knnProcessor) + } + } + // Add Synonym processor if the request has Synonym + if requestHasSynonym(req) { + if synonymProcessor := newSynonymPreSearchResultProcessor(req); synonymProcessor != nil { + processors = append(processors, synonymProcessor) + } + } + // Return based on the number of processors, optimizing for the common case of 1 processor + // If there are no processors, return nil + switch len(processors) { + case 0: + return nil + case 1: + return processors[0] + default: + return &compositePreSearchResultProcessor{ + presearchResultProcessors: processors, + } + } +} + +// ----------------------------------------------------------------------------- +func finalizePreSearchResult(req *SearchRequest, preSearchResult *SearchResult) { if requestHasKNN(req) { - return newKnnPreSearchResultProcessor(req) + preSearchResult.Hits = finalizeKNNResults(req, preSearchResult.Hits) } - return &knnPreSearchResultProcessor{} // equivalent to nil }