From 4d53db9fc8b7bc6eae5e6e2703b89df305d161d3 Mon Sep 17 00:00:00 2001 From: Marty Schoch Date: Fri, 15 Aug 2014 09:39:41 -0400 Subject: [PATCH] fixed bug with internal get/set/delete, added tests --- index/upside_down/upside_down.go | 182 +++++---- index/upside_down/upside_down_test.go | 515 +++++++++++++++++++++++++- 2 files changed, 594 insertions(+), 103 deletions(-) diff --git a/index/upside_down/upside_down.go b/index/upside_down/upside_down.go index 6ab7bd07..1769b668 100644 --- a/index/upside_down/upside_down.go +++ b/index/upside_down/upside_down.go @@ -506,27 +506,6 @@ func (udc *UpsideDownCouch) backIndexRowsForBatch(batch index.Batch) (map[string return rv, nil } -func (udc *UpsideDownCouch) Dump() { - it := udc.store.Iterator([]byte{0}) - defer it.Close() - key, val, valid := it.Current() - for valid { - - row, err := ParseFromKeyValue(key, val) - if err != nil { - fmt.Printf("error parsing key/value: %v", err) - return - } - if row != nil { - fmt.Printf("%v\n", row) - fmt.Printf("Key: % -100x\nValue: % -100x\n\n", key, val) - } - - it.Next() - key, val, valid = it.Current() - } -} - func (udc *UpsideDownCouch) Fields() ([]string, error) { rv := make([]string, 0) it := udc.store.Iterator([]byte{'f'}) @@ -553,71 +532,6 @@ func (udc *UpsideDownCouch) Fields() ([]string, error) { return rv, nil } -func (udc *UpsideDownCouch) DumpFields() { - it := udc.store.Iterator([]byte{'f'}) - defer it.Close() - key, val, valid := it.Current() - for valid { - if !bytes.HasPrefix(key, []byte{'f'}) { - break - } - - row, err := ParseFromKeyValue(key, val) - if err != nil { - fmt.Printf("error parsing key/value: %v", err) - return - } - if row != nil { - fmt.Printf("%v\n", row) - fmt.Printf("Key: % -100x\nValue: % -100x\n\n", key, val) - } - - it.Next() - key, val, valid = it.Current() - } -} - -type keyset [][]byte - -func (k keyset) Len() int { return len(k) } -func (k keyset) Swap(i, j int) { k[i], k[j] = k[j], k[i] } -func (k keyset) Less(i, j int) bool { return bytes.Compare(k[i], k[j]) < 0 } - -// DumpDoc returns all rows in the index related to this doc id -func (udc *UpsideDownCouch) DumpDoc(id string) ([]interface{}, error) { - rv := make([]interface{}, 0) - back, err := udc.backIndexRowForDoc(id) - if err != nil { - return nil, err - } - keys := make(keyset, 0) - for _, stored := range back.storedFields { - sr := NewStoredRow(id, stored, 'x', []byte{}) - key := sr.Key() - keys = append(keys, key) - } - for _, entry := range back.entries { - tfr := NewTermFrequencyRow(entry.term, entry.field, id, 0, 0) - key := tfr.Key() - keys = append(keys, key) - } - sort.Sort(keys) - - for _, key := range keys { - value, err := udc.store.Get(key) - if err != nil { - return nil, err - } - row, err := ParseFromKeyValue(key, value) - if err != nil { - return nil, err - } - rv = append(rv, row) - } - - return rv, nil -} - func (udc *UpsideDownCouch) TermFieldReader(term []byte, fieldName string) (index.TermFieldReader, error) { fieldIndex, fieldExists := udc.fieldIndexes[fieldName] if fieldExists { @@ -793,17 +707,97 @@ func (udc *UpsideDownCouch) SetInternal(key, val []byte) error { } func (udc *UpsideDownCouch) GetInternal(key []byte) ([]byte, error) { - internalRow, err := NewInternalRowKV(key, nil) - if err != nil { - return nil, err - } + internalRow := NewInternalRow(key, nil) return udc.store.Get(internalRow.Key()) } func (udc *UpsideDownCouch) DeleteInternal(key []byte) error { - internalRow, err := NewInternalRowKV(key, nil) - if err != nil { - return err - } + internalRow := NewInternalRow(key, nil) return udc.store.Delete(internalRow.Key()) } + +func (udc *UpsideDownCouch) Dump() { + it := udc.store.Iterator([]byte{0}) + defer it.Close() + key, val, valid := it.Current() + for valid { + + row, err := ParseFromKeyValue(key, val) + if err != nil { + fmt.Printf("error parsing key/value: %v", err) + return + } + if row != nil { + fmt.Printf("%v\n", row) + fmt.Printf("Key: % -100x\nValue: % -100x\n\n", key, val) + } + + it.Next() + key, val, valid = it.Current() + } +} + +func (udc *UpsideDownCouch) DumpFields() { + it := udc.store.Iterator([]byte{'f'}) + defer it.Close() + key, val, valid := it.Current() + for valid { + if !bytes.HasPrefix(key, []byte{'f'}) { + break + } + + row, err := ParseFromKeyValue(key, val) + if err != nil { + fmt.Printf("error parsing key/value: %v", err) + return + } + if row != nil { + fmt.Printf("%v\n", row) + fmt.Printf("Key: % -100x\nValue: % -100x\n\n", key, val) + } + + it.Next() + key, val, valid = it.Current() + } +} + +type keyset [][]byte + +func (k keyset) Len() int { return len(k) } +func (k keyset) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k keyset) Less(i, j int) bool { return bytes.Compare(k[i], k[j]) < 0 } + +// DumpDoc returns all rows in the index related to this doc id +func (udc *UpsideDownCouch) DumpDoc(id string) ([]interface{}, error) { + rv := make([]interface{}, 0) + back, err := udc.backIndexRowForDoc(id) + if err != nil { + return nil, err + } + keys := make(keyset, 0) + for _, stored := range back.storedFields { + sr := NewStoredRow(id, stored, 'x', []byte{}) + key := sr.Key() + keys = append(keys, key) + } + for _, entry := range back.entries { + tfr := NewTermFrequencyRow(entry.term, entry.field, id, 0, 0) + key := tfr.Key() + keys = append(keys, key) + } + sort.Sort(keys) + + for _, key := range keys { + value, err := udc.store.Get(key) + if err != nil { + return nil, err + } + row, err := ParseFromKeyValue(key, value) + if err != nil { + return nil, err + } + rv = append(rv, row) + } + + return rv, nil +} diff --git a/index/upside_down/upside_down_test.go b/index/upside_down/upside_down_test.go index 6a3d0ee8..ea24b842 100644 --- a/index/upside_down/upside_down_test.go +++ b/index/upside_down/upside_down_test.go @@ -10,13 +10,16 @@ package upside_down import ( "os" + "reflect" "regexp" "testing" + "time" "github.com/couchbaselabs/bleve/analysis" "github.com/couchbaselabs/bleve/analysis/tokenizers/regexp_tokenizer" "github.com/couchbaselabs/bleve/document" - "github.com/couchbaselabs/bleve/index/store/gouchstore" + "github.com/couchbaselabs/bleve/index" + "github.com/couchbaselabs/bleve/index/store/leveldb" ) var testAnalyzer = &analysis.Analyzer{ @@ -26,7 +29,7 @@ var testAnalyzer = &analysis.Analyzer{ func TestIndexOpenReopen(t *testing.T) { defer os.RemoveAll("test") - store, err := gouchstore.Open("test") + store, err := leveldb.Open("test", true) idx := NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -49,7 +52,7 @@ func TestIndexOpenReopen(t *testing.T) { // now close it idx.Close() - store, err = gouchstore.Open("test") + store, err = leveldb.Open("test", true) idx = NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -63,7 +66,7 @@ func TestIndexOpenReopen(t *testing.T) { func TestIndexInsert(t *testing.T) { defer os.RemoveAll("test") - store, err := gouchstore.Open("test") + store, err := leveldb.Open("test", true) idx := NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -101,7 +104,7 @@ func TestIndexInsert(t *testing.T) { func TestIndexInsertThenDelete(t *testing.T) { defer os.RemoveAll("test") - store, err := gouchstore.Open("test") + store, err := leveldb.Open("test", true) idx := NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -169,7 +172,7 @@ func TestIndexInsertThenDelete(t *testing.T) { func TestIndexInsertThenUpdate(t *testing.T) { defer os.RemoveAll("test") - store, err := gouchstore.Open("test") + store, err := leveldb.Open("test", true) idx := NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -218,7 +221,7 @@ func TestIndexInsertThenUpdate(t *testing.T) { func TestIndexInsertMultiple(t *testing.T) { defer os.RemoveAll("test") - store, err := gouchstore.Open("test") + store, err := leveldb.Open("test", true) idx := NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -252,12 +255,13 @@ func TestIndexInsertMultiple(t *testing.T) { // close and reopen and and one more to testing counting works correctly idx.Close() - store, err = gouchstore.Open("test") + store, err = leveldb.Open("test", true) idx = NewUpsideDownCouch(store) err = idx.Open() if err != nil { t.Errorf("error opening index: %v", err) } + defer idx.Close() doc = document.NewDocument("3") doc.AddField(document.NewTextField("name", []byte("test"))) @@ -276,7 +280,10 @@ func TestIndexInsertMultiple(t *testing.T) { func TestIndexInsertWithStore(t *testing.T) { defer os.RemoveAll("test") - store, err := gouchstore.Open("test") + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } idx := NewUpsideDownCouch(store) err = idx.Open() if err != nil { @@ -326,3 +333,493 @@ func TestIndexInsertWithStore(t *testing.T) { t.Errorf("expected field content 'test', got '%s'", string(textField.Value())) } } + +func TestIndexInternalCRUD(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + // get something that doesnt exist yet + val, err := idx.GetInternal([]byte("key")) + if err != nil { + t.Error(err) + } + if val != nil { + t.Errorf("expected nil, got %s", val) + } + + // set + err = idx.SetInternal([]byte("key"), []byte("abc")) + if err != nil { + t.Error(err) + } + + // get + val, err = idx.GetInternal([]byte("key")) + if err != nil { + t.Error(err) + } + if string(val) != "abc" { + t.Errorf("expected %s, got '%s'", "abc", val) + } + + // delete + err = idx.DeleteInternal([]byte("key")) + if err != nil { + t.Error(err) + } + + // get again + val, err = idx.GetInternal([]byte("key")) + if err != nil { + t.Error(err) + } + if val != nil { + t.Errorf("expected nil, got %s", val) + } +} + +func TestIndexBatch(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + var expectedCount uint64 = 0 + + // first create 2 docs the old fashioned way + doc := document.NewDocument("1") + doc.AddField(document.NewTextField("name", []byte("test"))) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + expectedCount += 1 + + doc = document.NewDocument("2") + doc.AddField(document.NewTextField("name", []byte("test2"))) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + expectedCount += 1 + + // now create a batch which does 3 things + // insert new doc + // update existing doc + // delete existing doc + // net document count change 0 + + batch := make(index.Batch, 0) + doc = document.NewDocument("3") + doc.AddField(document.NewTextField("name", []byte("test3"))) + batch["3"] = doc + doc = document.NewDocument("2") + doc.AddField(document.NewTextField("name", []byte("test2updated"))) + batch["2"] = doc + batch["1"] = nil + + err = idx.Batch(batch) + if err != nil { + t.Error(err) + } + + docCount := idx.DocCount() + if docCount != expectedCount { + t.Errorf("Expected document count to be %d got %d", expectedCount, docCount) + } + + docIdReader, err := idx.DocIdReader("", "") + if err != nil { + t.Error(err) + } + docIds := make([]string, 0) + docId, err := docIdReader.Next() + for docId != "" && err == nil { + docIds = append(docIds, docId) + docId, err = docIdReader.Next() + } + if err != nil { + t.Error(err) + } + expectedDocIds := []string{"2", "3"} + if !reflect.DeepEqual(docIds, expectedDocIds) { + t.Errorf("expected ids: %v, got ids: %v", expectedDocIds, docIds) + } +} + +func TestIndexInsertUpdateDeleteWithMultipleTypesStored(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + var expectedCount uint64 = 0 + docCount := idx.DocCount() + if docCount != expectedCount { + t.Errorf("Expected document count to be %d got %d", expectedCount, docCount) + } + + doc := document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewNumericFieldWithIndexingOptions("age", 35.99, document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewDateTimeFieldWithIndexingOptions("unixEpoch", time.Unix(0, 0), document.INDEX_FIELD|document.STORE_FIELD)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + expectedCount += 1 + + docCount = idx.DocCount() + if docCount != expectedCount { + t.Errorf("Expected document count to be %d got %d", expectedCount, docCount) + } + + // should have 72 rows + // 1 for version + // 3 for schema fields + // 1 for text term + // 16 for numeric terms + // 16 for date terms + // 3 for the stored field + // 1 for the text term count + // 16 for numeric term counts + // 16 for date term counts + // 1 for the back index entry + expectedLength := uint64(1 + 3 + 1 + (64 / document.DEFAULT_PRECISION_STEP) + (64 / document.DEFAULT_PRECISION_STEP) + 3 + 1 + (64 / document.DEFAULT_PRECISION_STEP) + (64 / document.DEFAULT_PRECISION_STEP) + 1) + rowCount := idx.rowCount() + if rowCount != expectedLength { + t.Errorf("expected %d rows, got: %d", expectedLength, rowCount) + } + + storedDoc, err := idx.Document("1") + if err != nil { + t.Error(err) + } + + if len(storedDoc.Fields) != 3 { + t.Errorf("expected 3 stored field, got %d", len(storedDoc.Fields)) + } + textField, ok := storedDoc.Fields[0].(*document.TextField) + if !ok { + t.Errorf("expected text field") + } + if string(textField.Value()) != "test" { + t.Errorf("expected field content 'test', got '%s'", string(textField.Value())) + } + numField, ok := storedDoc.Fields[1].(*document.NumericField) + if !ok { + t.Errorf("expected numeric field") + } + numFieldNumer, err := numField.Number() + if err != nil { + t.Error(err) + } else { + if numFieldNumer != 35.99 { + t.Errorf("expeted numeric value 35.99, got %f", numFieldNumer) + } + } + dateField, ok := storedDoc.Fields[2].(*document.DateTimeField) + if !ok { + t.Errorf("expected date field") + } + dateFieldDate, err := dateField.DateTime() + if err != nil { + t.Error(err) + } else { + if dateFieldDate != time.Unix(0, 0) { + t.Errorf("expected date value unix epoch, got %v", dateFieldDate) + } + } + + // now update the document, but omit one of the fields + doc = document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("testup"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewNumericFieldWithIndexingOptions("age", 36.99, document.INDEX_FIELD|document.STORE_FIELD)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + // expected doc count shouldn't have changed + docCount = idx.DocCount() + if docCount != expectedCount { + t.Errorf("Expected document count to be %d got %d", expectedCount, docCount) + } + + // should only get 2 fields back now though + storedDoc, err = idx.Document("1") + if err != nil { + t.Error(err) + } + + if len(storedDoc.Fields) != 2 { + t.Errorf("expected 3 stored field, got %d", len(storedDoc.Fields)) + } + textField, ok = storedDoc.Fields[0].(*document.TextField) + if !ok { + t.Errorf("expected text field") + } + if string(textField.Value()) != "testup" { + t.Errorf("expected field content 'testup', got '%s'", string(textField.Value())) + } + numField, ok = storedDoc.Fields[1].(*document.NumericField) + if !ok { + t.Errorf("expected numeric field") + } + numFieldNumer, err = numField.Number() + if err != nil { + t.Error(err) + } else { + if numFieldNumer != 36.99 { + t.Errorf("expeted numeric value 36.99, got %f", numFieldNumer) + } + } + + // now delete the document + err = idx.Delete("1") + expectedCount-- + + // expected doc count shouldn't have changed + docCount = idx.DocCount() + if docCount != expectedCount { + t.Errorf("Expected document count to be %d got %d", expectedCount, docCount) + } +} + +func TestIndexInsertFields(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + doc := document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewNumericFieldWithIndexingOptions("age", 35.99, document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewDateTimeFieldWithIndexingOptions("unixEpoch", time.Unix(0, 0), document.INDEX_FIELD|document.STORE_FIELD)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + fields, err := idx.Fields() + if err != nil { + t.Error(err) + } else { + expectedFields := []string{"name", "age", "unixEpoch"} + if !reflect.DeepEqual(fields, expectedFields) { + t.Errorf("expected fields: %v, got %v", expectedFields, fields) + } + } + +} + +func TestIndexUpdateComposites(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + doc := document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewTextFieldWithIndexingOptions("title", []byte("mister"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewCompositeFieldWithIndexingOptions("_all", true, nil, nil, document.INDEX_FIELD)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + // should have 72 rows + // 1 for version + // 3 for schema fields + // 4 for text term + // 2 for the stored field + // 4 for the text term count + // 1 for the back index entry + expectedLength := uint64(1 + 3 + 4 + 2 + 4 + 1) + rowCount := idx.rowCount() + if rowCount != expectedLength { + t.Errorf("expected %d rows, got: %d", expectedLength, rowCount) + } + + // now lets update it + doc = document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("testupdated"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewTextFieldWithIndexingOptions("title", []byte("misterupdated"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewCompositeFieldWithIndexingOptions("_all", true, nil, nil, document.INDEX_FIELD)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + // make sure new values are in index + storedDoc, err := idx.Document("1") + if err != nil { + t.Error(err) + } + if len(storedDoc.Fields) != 2 { + t.Errorf("expected 2 stored field, got %d", len(storedDoc.Fields)) + } + textField, ok := storedDoc.Fields[0].(*document.TextField) + if !ok { + t.Errorf("expected text field") + } + if string(textField.Value()) != "testupdated" { + t.Errorf("expected field content 'test', got '%s'", string(textField.Value())) + } + + // should have the same row count as before + rowCount = idx.rowCount() + if rowCount != expectedLength { + t.Errorf("expected %d rows, got: %d", expectedLength, rowCount) + } +} + +func TestIndexFieldsMisc(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + doc := document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD)) + doc.AddField(document.NewTextFieldWithIndexingOptions("title", []byte("mister"), document.INDEX_FIELD|document.STORE_FIELD)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + fieldName1 := idx.fieldIndexToName(1) + if fieldName1 != "name" { + t.Errorf("expected field named 'name', got '%s'", fieldName1) + } + fieldName2 := idx.fieldIndexToName(2) + if fieldName2 != "title" { + t.Errorf("expected field named 'title', got '%s'", fieldName2) + } + fieldName3 := idx.fieldIndexToName(3) + if fieldName3 != "" { + t.Errorf("expected field named '', got '%s'", fieldName3) + } + +} + +func TestIndexTermReaderCompositeFields(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + doc := document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD|document.INCLUDE_TERM_VECTORS)) + doc.AddField(document.NewTextFieldWithIndexingOptions("title", []byte("mister"), document.INDEX_FIELD|document.STORE_FIELD|document.INCLUDE_TERM_VECTORS)) + doc.AddField(document.NewCompositeFieldWithIndexingOptions("_all", true, nil, nil, document.INDEX_FIELD|document.INCLUDE_TERM_VECTORS)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + termFieldReader, err := idx.TermFieldReader([]byte("mister"), "_all") + if err != nil { + t.Error(err) + } + + tfd, err := termFieldReader.Next() + for tfd != nil && err == nil { + if tfd.ID != "1" { + t.Errorf("expected to find document id 1") + } + tfd, err = termFieldReader.Next() + } + if err != nil { + t.Error(err) + } +} + +func TestIndexDocumentFieldTerms(t *testing.T) { + defer os.RemoveAll("test") + + store, err := leveldb.Open("test", true) + if err != nil { + t.Error(err) + } + idx := NewUpsideDownCouch(store) + err = idx.Open() + if err != nil { + t.Errorf("error opening index: %v", err) + } + defer idx.Close() + + doc := document.NewDocument("1") + doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD|document.INCLUDE_TERM_VECTORS)) + doc.AddField(document.NewTextFieldWithIndexingOptions("title", []byte("mister"), document.INDEX_FIELD|document.STORE_FIELD|document.INCLUDE_TERM_VECTORS)) + doc.AddField(document.NewCompositeFieldWithIndexingOptions("_all", true, nil, nil, document.INDEX_FIELD|document.INCLUDE_TERM_VECTORS)) + err = idx.Update(doc) + if err != nil { + t.Errorf("Error updating index: %v", err) + } + + fieldTerms, err := idx.DocumentFieldTerms("1") + if err != nil { + t.Error(err) + } + expectedFieldTerms := index.FieldTerms{ + "name": []string{"test"}, + "title": []string{"mister"}, + "_all": []string{"test", "mister"}, + } + if !reflect.DeepEqual(fieldTerms, expectedFieldTerms) { + t.Errorf("expected field terms: %#v, got: %#v", expectedFieldTerms, fieldTerms) + } +}