Repository: blevesearch/bleve
Pull request #1928 (2023-12-05 04:50:27)

Fix scoring for match all query
Currently, match all queries used to return 1.0 as the score even when score = "none".
This PR fixes that by ensuring they return 0.0 in such a case and adding a UT for verification.


Patch:
diff --git a/search/scorer/scorer_constant.go b/search/scorer/scorer_constant.go
index fc36fd5b..10190bd8 100644
--- a/search/scorer/scorer_constant.go
+++ b/search/scorer/scorer_constant.go
@@ -37,6 +37,7 @@ type ConstantScorer struct {
 	queryNorm              float64
 	queryWeight            float64
 	queryWeightExplanation *search.Explanation
+	includeScore           bool
 }
 
 func (s *ConstantScorer) Size() int {
@@ -51,10 +52,11 @@ func (s *ConstantScorer) Size() int {
 
 func NewConstantScorer(constant float64, boost float64, options search.SearcherOptions) *ConstantScorer {
 	rv := ConstantScorer{
-		options:     options,
-		queryWeight: 1.0,
-		constant:    constant,
-		boost:       boost,
+		options:      options,
+		queryWeight:  1.0,
+		constant:     constant,
+		boost:        boost,
+		includeScore: options.Score != "none",
 	}
 
 	return &rv
@@ -92,35 +94,38 @@ func (s *ConstantScorer) SetQueryNorm(qnorm float64) {
 func (s *ConstantScorer) Score(ctx *search.SearchContext, id index.IndexInternalID) *search.DocumentMatch {
 	var scoreExplanation *search.Explanation
 
-	score := s.constant
+	rv := ctx.DocumentMatchPool.Get()
+	rv.IndexInternalID = id
 
-	if s.options.Explain {
-		scoreExplanation = &search.Explanation{
-			Value:   score,
-			Message: fmt.Sprintf("ConstantScore()"),
-		}
-	}
+	if s.includeScore {
+		score := s.constant
 
-	// if the query weight isn't 1, multiply
-	if s.queryWeight != 1.0 {
-		score = score * s.queryWeight
 		if s.options.Explain {
-			childExplanations := make([]*search.Explanation, 2)
-			childExplanations[0] = s.queryWeightExplanation
-			childExplanations[1] = scoreExplanation
 			scoreExplanation = &search.Explanation{
-				Value:    score,
-				Message:  fmt.Sprintf("weight(^%f), product of:", s.boost),
-				Children: childExplanations,
+				Value:   score,
+				Message: fmt.Sprintf("ConstantScore()"),
 			}
 		}
-	}
 
-	rv := ctx.DocumentMatchPool.Get()
-	rv.IndexInternalID = id
-	rv.Score = score
-	if s.options.Explain {
-		rv.Expl = scoreExplanation
+		// if the query weight isn't 1, multiply
+		if s.queryWeight != 1.0 {
+			score = score * s.queryWeight
+			if s.options.Explain {
+				childExplanations := make([]*search.Explanation, 2)
+				childExplanations[0] = s.queryWeightExplanation
+				childExplanations[1] = scoreExplanation
+				scoreExplanation = &search.Explanation{
+					Value:    score,
+					Message:  fmt.Sprintf("weight(^%f), product of:", s.boost),
+					Children: childExplanations,
+				}
+			}
+		}
+
+		rv.Score = score
+		if s.options.Explain {
+			rv.Expl = scoreExplanation
+		}
 	}
 
 	return rv


Test patch:
diff --git a/search_test.go b/search_test.go
index 815cb998..c0b8b0c1 100644
--- a/search_test.go
+++ b/search_test.go
@@ -127,6 +127,70 @@ func TestSortedFacetedQuery(t *testing.T) {
 	}
 }
 
+func TestMatchAllScorer(t *testing.T) {
+	tmpIndexPath := createTmpIndexPath(t)
+	defer cleanupTmpIndexPath(t, tmpIndexPath)
+
+	indexMapping := NewIndexMapping()
+	indexMapping.TypeField = "type"
+	indexMapping.DefaultAnalyzer = "en"
+	documentMapping := NewDocumentMapping()
+
+	contentFieldMapping := NewTextFieldMapping()
+	contentFieldMapping.Index = true
+	contentFieldMapping.Store = true
+	documentMapping.AddFieldMappingsAt("content", contentFieldMapping)
+
+	index, err := New(tmpIndexPath, indexMapping)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		err := index.Close()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	index.Index("1", map[string]interface{}{
+		"country": "india",
+		"content": "k",
+	})
+	index.Index("2", map[string]interface{}{
+		"country": "india",
+		"content": "l",
+	})
+	index.Index("3", map[string]interface{}{
+		"country": "india",
+		"content": "k",
+	})
+
+	d, err := index.DocCount()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if d != 3 {
+		t.Errorf("expected 3, got %d", d)
+	}
+
+	searchRequest := NewSearchRequest(NewMatchAllQuery())
+	searchRequest.Score = "none"
+	searchResults, err := index.Search(searchRequest)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if searchResults.Total != 3 {
+		t.Fatalf("expected all the 3 docs in the index, got %v", searchResults.Total)
+	}
+
+	for _, hit := range searchResults.Hits {
+		if hit.Score != 0.0 {
+			t.Fatalf("expected 0 score since score = none, got %v", hit.Score)
+		}
+	}
+}
+
 func TestSearchResultString(t *testing.T) {
 
 	tests := []struct {