diff --git a/index/upside_down/reader.go b/index/upside_down/reader.go index 9e695cc2..81293104 100644 --- a/index/upside_down/reader.go +++ b/index/upside_down/reader.go @@ -239,12 +239,16 @@ func (r *UpsideDownCouchDocIDReader) Next() (index.IndexInternalID, error) { } func (r *UpsideDownCouchDocIDReader) Advance(docID index.IndexInternalID) (index.IndexInternalID, error) { - bir := NewBackIndexRow(docID, nil, nil) - r.iterator.Seek(bir.Key()) - key, val, valid := r.iterator.Current() - r.onlyPos = sort.SearchStrings(r.only, string(docID)) if r.onlyMode { + r.onlyPos = sort.SearchStrings(r.only, string(docID)) + if r.onlyPos >= len(r.only) { + // advanced to key after our last only key + return nil, nil + } + r.iterator.Seek(NewBackIndexRow([]byte(r.only[r.onlyPos]), nil, nil).Key()) + key, val, valid := r.iterator.Current() + var rv index.IndexInternalID for valid && r.onlyPos < len(r.only) { br, err := NewBackIndexRowKV(key, val) @@ -252,11 +256,16 @@ func (r *UpsideDownCouchDocIDReader) Advance(docID index.IndexInternalID) (index return nil, err } if !bytes.Equal(br.doc, []byte(r.only[r.onlyPos])) { - ok := r.nextOnly() - if !ok { + // the only key we seek'd to didn't exist + // now look for the closest key that did exist in only + r.onlyPos = sort.SearchStrings(r.only, string(br.doc)) + if r.onlyPos >= len(r.only) { + // advanced to key after our last only key return nil, nil } + // now seek to this new only key r.iterator.Seek(NewBackIndexRow([]byte(r.only[r.onlyPos]), nil, nil).Key()) + key, val, valid = r.iterator.Current() continue } else { rv = append([]byte(nil), br.doc...) @@ -271,6 +280,9 @@ func (r *UpsideDownCouchDocIDReader) Advance(docID index.IndexInternalID) (index return rv, nil } } else { + bir := NewBackIndexRow(docID, nil, nil) + r.iterator.Seek(bir.Key()) + key, val, valid := r.iterator.Current() if valid { br, err := NewBackIndexRowKV(key, val) if err != nil { diff --git a/index/upside_down/reader_test.go b/index/upside_down/reader_test.go index 5ca42cf7..333de998 100644 --- a/index/upside_down/reader_test.go +++ b/index/upside_down/reader_test.go @@ -306,3 +306,219 @@ func TestCrashBadBackIndexRow(t *testing.T) { t.Fatal(err) } } + +func TestIndexDocIdOnlyReader(t *testing.T) { + defer func() { + err := DestroyTest() + if err != nil { + t.Fatal(err) + } + }() + + analysisQueue := index.NewAnalysisQueue(1) + idx, err := NewUpsideDownCouch(boltdb.Name, boltTestConfig, 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) + } + }() + + doc := document.NewDocument("1") + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + doc = document.NewDocument("3") + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + doc = document.NewDocument("5") + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + doc = document.NewDocument("7") + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + doc = document.NewDocument("9") + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + indexReader, err := idx.Reader() + if err != nil { + t.Error(err) + } + defer func() { + err := indexReader.Close() + if err != nil { + t.Error(err) + } + }() + + onlyIds := []string{"1", "5", "9"} + reader, err := indexReader.DocIDReaderOnly(onlyIds) + if err != nil { + t.Errorf("Error accessing doc id reader: %v", err) + } + defer func() { + err := reader.Close() + if err != nil { + t.Fatal(err) + } + }() + + id, err := reader.Next() + count := uint64(0) + for id != nil { + count++ + id, err = reader.Next() + } + if count != 3 { + t.Errorf("expected 3, got %d", count) + } + + // try it again, but jump + reader2, err := indexReader.DocIDReaderOnly(onlyIds) + if err != nil { + t.Errorf("Error accessing doc id reader: %v", err) + } + defer func() { + err := reader2.Close() + if err != nil { + t.Error(err) + } + }() + + id, err = reader2.Advance(index.IndexInternalID("5")) + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("5")) { + t.Errorf("expected to find id '5', got '%s'", id) + } + + id, err = reader2.Advance(index.IndexInternalID("a")) + if err != nil { + t.Error(err) + } + if id != nil { + t.Errorf("expected to find id '', got '%s'", id) + } + + // some keys aren't actually there + onlyIds = []string{"0", "2", "4", "5", "6", "8", "a"} + reader3, err := indexReader.DocIDReaderOnly(onlyIds) + if err != nil { + t.Errorf("Error accessing doc id reader: %v", err) + } + defer func() { + err := reader3.Close() + if err != nil { + t.Error(err) + } + }() + + id, err = reader3.Next() + count = uint64(0) + for id != nil { + count++ + id, err = reader3.Next() + } + if count != 1 { + t.Errorf("expected 1, got %d", count) + } + + // mix advance and next + onlyIds = []string{"0", "1", "3", "5", "6", "9"} + reader4, err := indexReader.DocIDReaderOnly(onlyIds) + if err != nil { + t.Errorf("Error accessing doc id reader: %v", err) + } + defer func() { + err := reader4.Close() + if err != nil { + t.Error(err) + } + }() + + // first key is "1" + id, err = reader4.Next() + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("1")) { + t.Errorf("expected to find id '1', got '%s'", id) + } + + // advancing to key we dont have gives next + id, err = reader4.Advance(index.IndexInternalID("2")) + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("3")) { + t.Errorf("expected to find id '3', got '%s'", id) + } + + // next after advance works + id, err = reader4.Next() + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("5")) { + t.Errorf("expected to find id '5', got '%s'", id) + } + + // advancing to key we do have works + id, err = reader4.Advance(index.IndexInternalID("9")) + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("9")) { + t.Errorf("expected to find id '9', got '%s'", id) + } + + // advance backwards at end + id, err = reader4.Advance(index.IndexInternalID("4")) + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("5")) { + t.Errorf("expected to find id '5', got '%s'", id) + } + + // next after advance works + id, err = reader4.Next() + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("9")) { + t.Errorf("expected to find id '9', got '%s'", id) + } + + // advance backwards to key that exists, but not in only set + id, err = reader4.Advance(index.IndexInternalID("7")) + if err != nil { + t.Error(err) + } + if !id.Equals(index.IndexInternalID("9")) { + t.Errorf("expected to find id '9', got '%s'", id) + } + +}