Skip to content

Commit f34c4ac

Browse files
authored
quick fix sort issue (#87)
* quick fix to sort issue It has been observed that in some cases the computed sort value for a DocumentMatch becomes corrupted. The problem has been traced back to the doc values uncompressed slices, but for now a quick fix is proposed to copy the bytes associate with the sort key, ensuring that no other doc values operations can corrupt them.
1 parent 7fbf27b commit f34c4ac

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

index_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,3 +1738,83 @@ func TestCrudWithNoMMap(t *testing.T) {
17381738
}
17391739
}
17401740
}
1741+
1742+
// TestBug87 reproduces a situation in which a search matches several documents
1743+
// and we compare the document's stored value for the _id field, with the
1744+
// document's sort value. The sort value should be the same _id, but
1745+
// comes from the doc values storage.
1746+
// In this case, because doc values were loaded from multiple chunks, an
1747+
// "uncompressed" buffer is reused. Incorrect use of of these doc values
1748+
// bytes in computed sort values may lead to incorrect sort order and other
1749+
// undesired behavior.
1750+
func TestBug87(t *testing.T) {
1751+
tmpIndexPath := createTmpIndexPath(t)
1752+
defer cleanupTmpIndexPath(t, tmpIndexPath)
1753+
1754+
config := DefaultConfig(tmpIndexPath)
1755+
indexWriter, err := OpenWriter(config)
1756+
if err != nil {
1757+
t.Fatal(err)
1758+
}
1759+
defer func() {
1760+
err = indexWriter.Close()
1761+
if err != nil {
1762+
t.Fatal(err)
1763+
}
1764+
}()
1765+
1766+
// create 1025 documents in a batch
1767+
// this should require more than one chunk in doc values
1768+
batch := NewBatch()
1769+
for i := 0; i < 1025; i++ {
1770+
doc := NewDocument(fmt.Sprintf("%d", i)).
1771+
AddField(NewTextField("name", "marty").Sortable())
1772+
batch.Update(doc.ID(), doc)
1773+
}
1774+
1775+
err = indexWriter.Batch(batch)
1776+
if err != nil {
1777+
t.Error(err)
1778+
}
1779+
1780+
reader, err := indexWriter.Reader()
1781+
if err != nil {
1782+
t.Fatal(err)
1783+
}
1784+
defer func() {
1785+
err = reader.Close()
1786+
if err != nil {
1787+
t.Fatal(err)
1788+
}
1789+
}()
1790+
1791+
q := NewTermQuery("marty").SetField("name")
1792+
req := NewTopNSearch(2000, q).SortBy([]string{"_id"})
1793+
1794+
dmi, err := reader.Search(context.Background(), req)
1795+
if err != nil {
1796+
t.Fatal(err)
1797+
}
1798+
1799+
next, err := dmi.Next()
1800+
for err == nil && next != nil {
1801+
var id string
1802+
err = next.VisitStoredFields(func(field string, value []byte) bool {
1803+
if field == "_id" {
1804+
id = string(value)
1805+
return false
1806+
}
1807+
return true
1808+
})
1809+
if err != nil {
1810+
t.Fatal(err)
1811+
}
1812+
if string(next.SortValue[0]) != id {
1813+
t.Fatalf("expected id '%s' to match sort value '%s'", id, string(next.SortValue[0]))
1814+
}
1815+
next, err = dmi.Next()
1816+
}
1817+
if err != nil {
1818+
t.Fatal(err)
1819+
}
1820+
}

search/sort.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ func (o SortOrder) Reverse() {
4343

4444
func (o SortOrder) Compute(match *DocumentMatch) {
4545
for _, sort := range o {
46-
match.SortValue = append(match.SortValue, sort.Value(match))
46+
sortVal := sort.Value(match)
47+
sortValCopy := make([]byte, len(sortVal))
48+
copy(sortValCopy, sortVal)
49+
match.SortValue = append(match.SortValue, sortValCopy)
4750
}
4851
}
4952

0 commit comments

Comments
 (0)