github.com/m3db/m3@v1.5.0/src/m3ninx/index/segment/builder/builder_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package builder 22 23 import ( 24 "fmt" 25 "sort" 26 "testing" 27 "unsafe" 28 29 "github.com/m3db/m3/src/m3ninx/doc" 30 "github.com/m3db/m3/src/m3ninx/index" 31 "github.com/m3db/m3/src/m3ninx/index/segment" 32 33 "github.com/stretchr/testify/require" 34 ) 35 36 var ( 37 testOptions = NewOptions() 38 39 testDocuments = []doc.Metadata{ 40 { 41 Fields: []doc.Field{ 42 { 43 Name: []byte("fruit"), 44 Value: []byte("banana"), 45 }, 46 { 47 Name: []byte("color"), 48 Value: []byte("yellow"), 49 }, 50 }, 51 }, 52 { 53 Fields: []doc.Field{ 54 { 55 Name: []byte("fruit"), 56 Value: []byte("apple"), 57 }, 58 { 59 Name: []byte("color"), 60 Value: []byte("red"), 61 }, 62 }, 63 }, 64 { 65 ID: []byte("42"), 66 Fields: []doc.Field{ 67 { 68 Name: []byte("fruit"), 69 Value: []byte("pineapple"), 70 }, 71 { 72 Name: []byte("color"), 73 Value: []byte("yellow"), 74 }, 75 }, 76 }, 77 } 78 ) 79 80 func TestBuilderFields(t *testing.T) { 81 builder, err := NewBuilderFromDocuments(testOptions) 82 require.NoError(t, err) 83 defer func() { 84 require.NoError(t, builder.Close()) 85 }() 86 87 for i := 0; i < 10; i++ { 88 builder.Reset() 89 90 knownsFields := map[string]struct{}{} 91 for _, d := range testDocuments { 92 for _, f := range d.Fields { 93 knownsFields[string(f.Name)] = struct{}{} 94 } 95 _, err = builder.Insert(d) 96 require.NoError(t, err) 97 } 98 99 fieldsIter, err := builder.FieldsPostingsList() 100 require.NoError(t, err) 101 102 fields := toSlice(t, fieldsIter) 103 for _, f := range fields { 104 delete(knownsFields, string(f)) 105 } 106 require.Empty(t, knownsFields) 107 } 108 } 109 110 func TestBuilderTerms(t *testing.T) { 111 builder, err := NewBuilderFromDocuments(testOptions) 112 require.NoError(t, err) 113 defer func() { 114 require.NoError(t, builder.Close()) 115 }() 116 117 for i := 0; i < 10; i++ { 118 builder.Reset() 119 120 knownsFields := map[string]map[string]struct{}{} 121 for _, d := range testDocuments { 122 for _, f := range d.Fields { 123 knownVals, ok := knownsFields[string(f.Name)] 124 if !ok { 125 knownVals = make(map[string]struct{}) 126 knownsFields[string(f.Name)] = knownVals 127 } 128 knownVals[string(f.Value)] = struct{}{} 129 } 130 _, err = builder.Insert(d) 131 require.NoError(t, err) 132 } 133 134 for field, expectedTerms := range knownsFields { 135 termsIter, err := builder.Terms([]byte(field)) 136 require.NoError(t, err) 137 terms := toTermPostings(t, termsIter) 138 for term := range terms { 139 delete(expectedTerms, term) 140 } 141 require.Empty(t, expectedTerms) 142 } 143 } 144 } 145 146 // Test that calling Insert(...) API returns correct concrete errors 147 // instead of partial batch error type. 148 func TestBuilderInsertDuplicateReturnsErrDuplicateID(t *testing.T) { 149 builder, err := NewBuilderFromDocuments(testOptions) 150 require.NoError(t, err) 151 defer func() { 152 require.NoError(t, builder.Close()) 153 }() 154 155 _, err = builder.Insert(testDocuments[2]) 156 require.NoError(t, err) 157 158 _, err = builder.Insert(testDocuments[2]) 159 require.Error(t, err) 160 require.Equal(t, index.ErrDuplicateID, err) 161 } 162 163 func toSlice(t *testing.T, iter segment.FieldsPostingsListIterator) [][]byte { 164 elems := [][]byte{} 165 for iter.Next() { 166 b, _ := iter.Current() 167 elems = append(elems, b) 168 } 169 require.NoError(t, iter.Err()) 170 require.NoError(t, iter.Close()) 171 return elems 172 } 173 174 type termPostings map[string][]int 175 176 func toTermPostings(t *testing.T, iter segment.TermsIterator) termPostings { 177 elems := make(termPostings) 178 for iter.Next() { 179 term, postings := iter.Current() 180 _, exists := elems[string(term)] 181 require.False(t, exists) 182 183 values := []int{} 184 it := postings.Iterator() 185 for it.Next() { 186 values = append(values, int(it.Current())) 187 } 188 sort.Sort(sort.IntSlice(values)) 189 190 require.NoError(t, it.Err()) 191 require.NoError(t, it.Close()) 192 193 elems[string(term)] = values 194 } 195 require.NoError(t, iter.Err()) 196 require.NoError(t, iter.Close()) 197 return elems 198 } 199 200 // nolint: unused 201 func printBuilder(t *testing.T, b segment.Builder) { 202 fmt.Printf("print builder %x\n", unsafe.Pointer(b.(*builder))) 203 fieldsIter, err := b.FieldsPostingsList() 204 require.NoError(t, err) 205 for fieldsIter.Next() { 206 curr, _ := fieldsIter.Current() 207 fmt.Printf("builder field: %v\n", string(curr)) 208 termsIter, err := b.Terms(curr) 209 require.NoError(t, err) 210 for termsIter.Next() { 211 term, postings := termsIter.Current() 212 postingsIter := postings.Iterator() 213 for postingsIter.Next() { 214 posting := postingsIter.Current() 215 fmt.Printf("builder term: %s, doc: %d\n", string(term), posting) 216 } 217 } 218 } 219 fmt.Println() 220 }