diff --git a/buildSrc/src/main/groovy/DownloadSpecsPlugin.groovy b/buildSrc/src/main/groovy/DownloadSpecsPlugin.groovy index d4e71f0..338388c 100644 --- a/buildSrc/src/main/groovy/DownloadSpecsPlugin.groovy +++ b/buildSrc/src/main/groovy/DownloadSpecsPlugin.groovy @@ -68,11 +68,46 @@ class DownloadSpecsPlugin implements Plugin { if (content.contains(allOfBlock)) { content = content.replace(allOfBlock, replacement) - file.text = content println('Successfully patched SynonymItemSchema') } else { println('Warning: SynonymItemSchema allOf block not found, skipping patch') } + + // Patch: Add rerank_hybrid_matches to SearchParameters + // The server accepts this parameter for all search endpoints (single and multi), + // but the spec only defines it on MultiSearchCollectionParameters. + println('Patching OpenAPI spec: adding rerank_hybrid_matches to SearchParameters') + String searchParamsAnchor = '''\ + conversation_id: + description: > + The Id of a previous conversation to continue, this tells Typesense to include prior context when communicating with the LLM. + type: string + + MultiSearchParameters:''' + + String searchParamsReplacement = '''\ + conversation_id: + description: > + The Id of a previous conversation to continue, this tells Typesense to include prior context when communicating with the LLM. + type: string + rerank_hybrid_matches: + type: boolean + description: > + When true, computes both text match and vector distance scores for all matches in hybrid search. + Documents found only through keyword search will get a vector distance score, and + documents found only through vector search will get a text match score. + default: false + + MultiSearchParameters:''' + + if (content.contains(searchParamsAnchor)) { + content = content.replace(searchParamsAnchor, searchParamsReplacement) + println('Successfully added rerank_hybrid_matches to SearchParameters') + } else { + println('Warning: SearchParameters anchor not found, skipping rerank_hybrid_matches patch') + } + + file.text = content } } } diff --git a/src/test/java/org/typesense/api/SearchParametersRerankHybridTest.java b/src/test/java/org/typesense/api/SearchParametersRerankHybridTest.java new file mode 100644 index 0000000..cad1a17 --- /dev/null +++ b/src/test/java/org/typesense/api/SearchParametersRerankHybridTest.java @@ -0,0 +1,55 @@ +package org.typesense.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.typesense.model.SearchParameters; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Bug: rerank_hybrid_matches was only available on MultiSearchCollectionParameters, + * forcing users to use multi_search even for single collection hybrid search. + * The Typesense server accepts this parameter for all search endpoints. + */ +class SearchParametersRerankHybridTest { + + private final ObjectMapper mapper = new ObjectMapper(); + + @Test + void testRerankHybridMatchesDefaultsToFalseAndSerializes() { + SearchParameters params = new SearchParameters(); + assertFalse(params.isRerankHybridMatches()); + + Map map = mapper.convertValue(params, Map.class); + assertEquals(false, map.get("rerank_hybrid_matches")); + } + + @Test + void testRerankHybridMatchesTrueSerializesToCorrectJsonKey() { + SearchParameters params = new SearchParameters() + .q("search term") + .queryBy("title") + .rerankHybridMatches(true); + + assertTrue(params.isRerankHybridMatches()); + + Map map = mapper.convertValue(params, Map.class); + assertTrue(map.containsKey("rerank_hybrid_matches"), + "Serialized map must contain 'rerank_hybrid_matches' key for Typesense server compatibility"); + assertTrue((Boolean) map.get("rerank_hybrid_matches")); + } + + @Test + void testRerankHybridMatchesFalseIsIncludedInSerialization() { + SearchParameters params = new SearchParameters() + .q("*") + .rerankHybridMatches(false); + + Map map = mapper.convertValue(params, Map.class); + assertFalse((Boolean) map.get("rerank_hybrid_matches")); + } +}