Repository: blevesearch/bleve (go)
Issue: term field reader stats out of sync when advancing backwards
The scorch implementation of `TermFieldReader` `Advance()` has special handling for the case when it is advancing backwards.  Essentially it cheats, and just creates a new `TermFieldReader`, and advances that forward.  This works, but the stats tracking is not handled correctly, and this leads to the `TotTermSearchersStarted` and `TotTermSearchersFinished` stats to get out of sync.
Pull request description: fix stats handling of term field reader advancing backwards
The current implementation creates a new term field reader,
which replaces the original one, this causes the stats
tracking start/finished to get out of sync.

This fixes the issue by recording the original term field
reader as finished when replacing with the new one.

fixes #1415

Patch:
diff --git a/index/scorch/snapshot_index_tfr.go b/index/scorch/snapshot_index_tfr.go
index 5d56f194..61537fc4 100644
--- a/index/scorch/snapshot_index_tfr.go
+++ b/index/scorch/snapshot_index_tfr.go
@@ -133,6 +133,8 @@ func (i *IndexSnapshotTermFieldReader) Advance(ID index.IndexInternalID, preAllo
 		if err != nil {
 			return nil, err
 		}
+		// close the current term field reader before replacing it with a new one
+		_ = i.Close()
 		*i = *(i2.(*IndexSnapshotTermFieldReader))
 	}
 	num, err := docInternalToNumber(ID)


Test patch:
diff --git a/index/scorch/scorch_test.go b/index/scorch/scorch_test.go
index 5a9ef40c..e22fafa2 100644
--- a/index/scorch/scorch_test.go
+++ b/index/scorch/scorch_test.go
@@ -2380,3 +2380,88 @@ func TestCancelIndexForceMerge(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+func TestIndexSeekBackwardsStats(t *testing.T) {
+	cfg := CreateConfig("TestIndexOpenReopen")
+	err := InitTest(cfg)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		err := DestroyTest(cfg)
+		if err != nil {
+			t.Log(err)
+		}
+	}()
+
+	analysisQueue := index.NewAnalysisQueue(1)
+	idx, err := NewScorch(Name, cfg, analysisQueue)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = idx.Open()
+	if err != nil {
+		t.Errorf("error opening index: %v", err)
+	}
+	defer func() {
+		err := idx.Close()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	// insert a doc
+	doc := document.NewDocument("1")
+	doc.AddField(document.NewTextField("name", []uint64{}, []byte("cat")))
+	err = idx.Update(doc)
+	if err != nil {
+		t.Fatalf("error updating index: %v", err)
+	}
+
+	// insert another doc
+	doc = document.NewDocument("2")
+	doc.AddField(document.NewTextField("name", []uint64{}, []byte("cat")))
+	err = idx.Update(doc)
+	if err != nil {
+		t.Fatalf("error updating index: %v", err)
+	}
+
+	reader, err := idx.Reader()
+	if err != nil {
+		t.Fatalf("error getting index reader: %v", err)
+	}
+	defer reader.Close()
+
+	tfr, err := reader.TermFieldReader([]byte("cat"), "name", false, false, false)
+	if err != nil {
+		t.Fatalf("error getting term field readyer for name/cat: %v", err)
+	}
+
+	tfdFirst, err := tfr.Next(nil)
+	if err != nil {
+		t.Fatalf("error getting first tfd: %v", err)
+	}
+
+	_, err = tfr.Next(nil)
+	if err != nil {
+		t.Fatalf("error getting second tfd: %v", err)
+	}
+
+	// seek backwards to the first
+	_, err = tfr.Advance(tfdFirst.ID, nil)
+	if err != nil {
+		t.Fatalf("error adancing backwards: %v", err)
+	}
+
+	err = tfr.Close()
+	if err != nil {
+		t.Fatalf("error closing term field reader: %v", err)
+	}
+
+
+	if idx.(*Scorch).stats.TotTermSearchersStarted != idx.(*Scorch).stats.TotTermSearchersFinished {
+		t.Errorf("expected term searchers started %d to equal term searchers finished %d",
+			idx.(*Scorch).stats.TotTermSearchersStarted,
+			idx.(*Scorch).stats.TotTermSearchersFinished)
+	}
+}