add auth
This commit is contained in:
+44
-10
@@ -147,6 +147,12 @@ func (h *SearchHandler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
// `exact=1` (or "true") disables typo tolerance + prefix expansion so the
|
||||
// query only matches literal keywords. Stopword filtering still applies —
|
||||
// "letter to mukunda" with exact=1 still drops "letter"/"to" so the search
|
||||
// is effectively the literal word "mukunda" with no fuzzy variants.
|
||||
exact := parseBoolParam(r.URL.Query().Get("exact"))
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
var results []searchResultItem
|
||||
@@ -154,13 +160,13 @@ func (h *SearchHandler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if searchType == "all" {
|
||||
// Search all 9 collections
|
||||
results, totalFound = h.multiSearch(ctx, nil, query, page, limit)
|
||||
results, totalFound = h.multiSearch(ctx, nil, query, page, limit, exact)
|
||||
} else if len(requestedTypes) == 1 {
|
||||
// Single collection — use singleSearch for efficiency
|
||||
results, totalFound = h.singleSearch(ctx, requestedTypes[0], query, page, limit)
|
||||
results, totalFound = h.singleSearch(ctx, requestedTypes[0], query, page, limit, exact)
|
||||
} else {
|
||||
// Multiple collections — use multiSearch with a subset
|
||||
results, totalFound = h.multiSearch(ctx, requestedTypes, query, page, limit)
|
||||
results, totalFound = h.multiSearch(ctx, requestedTypes, query, page, limit, exact)
|
||||
}
|
||||
|
||||
totalPages := int(math.Ceil(float64(totalFound) / float64(limit)))
|
||||
@@ -181,8 +187,10 @@ func (h *SearchHandler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
// multiSearch searches across all 9 collections using Typesense multi_search,
|
||||
// merges results by text_match score, and returns a single paginated list.
|
||||
// multiSearch searches across multiple collections using Typesense multi_search.
|
||||
// If collections is nil, searches all 9 collections.
|
||||
func (h *SearchHandler) multiSearch(ctx context.Context, collections []string, query string, page, limit int) ([]searchResultItem, int) {
|
||||
// If collections is nil, searches all 9 collections. When `exact` is true,
|
||||
// typo tolerance and prefix expansion are turned off so only literal-keyword
|
||||
// matches come back.
|
||||
func (h *SearchHandler) multiSearch(ctx context.Context, collections []string, query string, page, limit int, exact bool) ([]searchResultItem, int) {
|
||||
if collections == nil {
|
||||
collections = []string{
|
||||
"verses", "prose", "letters", "transcriptions", "glossary",
|
||||
@@ -193,6 +201,7 @@ func (h *SearchHandler) multiSearch(ctx context.Context, collections []string, q
|
||||
exhaustive := true
|
||||
prioritizeExact := true
|
||||
prioritizeTokenPos := true
|
||||
prefix, numTypos := fuzzinessKnobs(exact)
|
||||
searches := make([]api.MultiSearchCollectionParameters, 0, len(collections))
|
||||
for _, col := range collections {
|
||||
queryBy := collectionQueryByFields[col]
|
||||
@@ -205,8 +214,8 @@ func (h *SearchHandler) multiSearch(ctx context.Context, collections []string, q
|
||||
QueryBy: stringPtr(queryBy),
|
||||
PerPage: &perPage,
|
||||
Page: &pg,
|
||||
Prefix: stringPtr("true"),
|
||||
NumTypos: stringPtr("2"),
|
||||
Prefix: stringPtr(prefix),
|
||||
NumTypos: stringPtr(numTypos),
|
||||
ExhaustiveSearch: &exhaustive,
|
||||
Stopwords: stringPtr(searchStopwordsSet),
|
||||
// Boost docs whose tokens are exact matches and appear early in the field
|
||||
@@ -264,7 +273,7 @@ func (h *SearchHandler) multiSearch(ctx context.Context, collections []string, q
|
||||
}
|
||||
|
||||
// singleSearch searches a single collection.
|
||||
func (h *SearchHandler) singleSearch(ctx context.Context, collection, query string, page, limit int) ([]searchResultItem, int) {
|
||||
func (h *SearchHandler) singleSearch(ctx context.Context, collection, query string, page, limit int, exact bool) ([]searchResultItem, int) {
|
||||
queryBy, ok := collectionQueryByFields[collection]
|
||||
if !ok {
|
||||
return []searchResultItem{}, 0
|
||||
@@ -273,13 +282,14 @@ func (h *SearchHandler) singleSearch(ctx context.Context, collection, query stri
|
||||
exhaustive := true
|
||||
prioritizeExact := true
|
||||
prioritizeTokenPos := true
|
||||
prefix, numTypos := fuzzinessKnobs(exact)
|
||||
params := &api.SearchCollectionParams{
|
||||
Q: stringPtr(query),
|
||||
QueryBy: stringPtr(queryBy),
|
||||
Page: &page,
|
||||
PerPage: &limit,
|
||||
Prefix: stringPtr("true"),
|
||||
NumTypos: stringPtr("2"),
|
||||
Prefix: stringPtr(prefix),
|
||||
NumTypos: stringPtr(numTypos),
|
||||
ExhaustiveSearch: &exhaustive,
|
||||
Stopwords: stringPtr(searchStopwordsSet),
|
||||
// Boost docs whose tokens are exact matches and appear early in the field
|
||||
@@ -534,3 +544,27 @@ func parseIntParam(s string, defaultVal int) int {
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// parseBoolParam returns true for "1", "true", "yes", "on" (case-insensitive).
|
||||
// Everything else (including the empty string) is false.
|
||||
func parseBoolParam(s string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(s)) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// fuzzinessKnobs maps the high-level `exact` flag to the two Typesense params
|
||||
// that actually disable fuzzy matching:
|
||||
//
|
||||
// exact=false → prefix=true, num_typos=2 (default forgiving search)
|
||||
// exact=true → prefix=false, num_typos=0 (literal-keyword only)
|
||||
//
|
||||
// Stopword filtering is left unchanged in both modes.
|
||||
func fuzzinessKnobs(exact bool) (prefix, numTypos string) {
|
||||
if exact {
|
||||
return "false", "0"
|
||||
}
|
||||
return "true", "2"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user