Skip to content

Commit 62ad148

Browse files
committed
MB-57888: New apis for index update
1 parent 88f6374 commit 62ad148

File tree

6 files changed

+235
-68
lines changed

6 files changed

+235
-68
lines changed

index.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,24 @@ func OpenUsing(path string, runtimeConfig map[string]interface{}) (Index, error)
329329
return openIndexUsing(path, runtimeConfig)
330330
}
331331

332+
// Update index at the specified path, must exist.
333+
// The mapping used when created will be overwritten by the mapping provided
334+
// for all Index/Search operations.
335+
// Throws an error without any changes to the index if an unupdatable mapping is provided
336+
func Update(path string, newParams string) (Index, error) {
337+
return updateIndexUsing(path, nil, newParams)
338+
}
339+
340+
// UpdateUsing index at the specified path, must exist.
341+
// The mapping used when created will be overwritten by the mapping provided
342+
// for all Index/Search operations.
343+
// The provided runtimeConfig can override settings
344+
// persisted when the kvstore was created.
345+
// Throws an error without any changes to the index if an unupdatable mapping is provided
346+
func UpdateUsing(path string, runtimeConfig map[string]interface{}, newParams string) (Index, error) {
347+
return updateIndexUsing(path, runtimeConfig, newParams)
348+
}
349+
332350
// Builder is a limited interface, used to build indexes in an offline mode.
333351
// Items cannot be updated or deleted, and the caller MUST ensure a document is
334352
// indexed only once.

index/scorch/persister.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ func (s *Scorch) loadSnapshot(snapshot *bolt.Bucket) (*IndexSnapshot, error) {
859859
segmentBucket := snapshot.Bucket(k)
860860
if segmentBucket == nil {
861861
_ = rv.DecRef()
862-
return nil, fmt.Errorf("segment key, but bucket missing % x", k)
862+
return nil, fmt.Errorf("segment key, but bucket missing %x", k)
863863
}
864864
segmentSnapshot, err := s.loadSegment(segmentBucket)
865865
if err != nil {

index/scorch/scorch.go

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const Version uint8 = 2
3737

3838
var ErrClosed = fmt.Errorf("scorch closed")
3939

40-
var mappingInternalKey = []byte("_mapping")
40+
var MappingInternalKey = []byte("_mapping")
4141

4242
type Scorch struct {
4343
nextSegmentID uint64
@@ -886,15 +886,27 @@ func (s *Scorch) FireIndexEvent() {
886886
s.fireEvent(EventKindIndexStart, 0)
887887
}
888888

889+
// Updates bolt db with the given field info. Existing field info already in bolt
890+
// will be merged before persisting. The index mapping is also overwritted both
891+
// in bolt as well as the index snapshot
889892
func (s *Scorch) UpdateFields(fieldInfo map[string]*index.FieldInfo, mappingBytes []byte) error {
890-
err := s.updateBolt(fieldInfo, mappingBytes)
893+
// Switch from pointer to value to marshal into a json for storage
894+
updatedFields := make(map[string]index.FieldInfo)
895+
for field, info := range fieldInfo {
896+
updatedFields[field] = *info
897+
}
898+
err := s.updateBolt(updatedFields, mappingBytes)
891899
if err != nil {
892900
return err
893901
}
902+
s.root.m.Lock()
903+
s.root.updatedFields = updatedFields
904+
s.root.m.Unlock()
894905
return nil
895906
}
896907

897-
func (s *Scorch) updateBolt(fieldInfo map[string]*index.FieldInfo, mappingBytes []byte) error {
908+
// Merge and update deleted field info and rewrite index mapping
909+
func (s *Scorch) updateBolt(fieldInfo map[string]index.FieldInfo, mappingBytes []byte) error {
898910
return s.rootBolt.Update(func(tx *bolt.Tx) error {
899911
snapshots := tx.Bucket(boltSnapshotsBucket)
900912
if snapshots == nil {
@@ -910,20 +922,20 @@ func (s *Scorch) updateBolt(fieldInfo map[string]*index.FieldInfo, mappingBytes
910922
}
911923
snapshot := snapshots.Bucket(k)
912924
cc := snapshot.Cursor()
913-
for kk, _ := cc.First(); kk != nil; kk, _ = c.Next() {
914-
if k[0] == boltInternalKey[0] {
915-
internalBucket := snapshot.Bucket(k)
925+
for kk, _ := cc.First(); kk != nil; kk, _ = cc.Next() {
926+
if kk[0] == boltInternalKey[0] {
927+
internalBucket := snapshot.Bucket(kk)
916928
if internalBucket == nil {
917-
return fmt.Errorf("segment key, but bucket missing % x", k)
929+
return fmt.Errorf("segment key, but bucket missing %x", kk)
918930
}
919-
err = internalBucket.Put(mappingInternalKey, mappingBytes)
931+
err = internalBucket.Put(MappingInternalKey, mappingBytes)
920932
if err != nil {
921933
return err
922934
}
923-
} else if k[0] != boltMetaDataKey[0] {
924-
segmentBucket := snapshot.Bucket(k)
935+
} else if kk[0] != boltMetaDataKey[0] {
936+
segmentBucket := snapshot.Bucket(kk)
925937
if segmentBucket == nil {
926-
return fmt.Errorf("segment key, but bucket missing % x", k)
938+
return fmt.Errorf("segment key, but bucket missing %x", kk)
927939
}
928940
var updatedFields map[string]index.FieldInfo
929941
updatedFieldBytes := segmentBucket.Get(boltUpdatedFieldsKey)
@@ -932,11 +944,11 @@ func (s *Scorch) updateBolt(fieldInfo map[string]*index.FieldInfo, mappingBytes
932944
if err != nil {
933945
return fmt.Errorf("error reading updated field bytes: %v", err)
934946
}
947+
for field, info := range fieldInfo {
948+
updatedFields[field] = info
949+
}
935950
} else {
936-
updatedFields = make(map[string]index.FieldInfo)
937-
}
938-
for field, info := range fieldInfo {
939-
updatedFields[field] = *info
951+
updatedFields = fieldInfo
940952
}
941953
b, err := json.Marshal(updatedFields)
942954
if err != nil {
@@ -949,7 +961,6 @@ func (s *Scorch) updateBolt(fieldInfo map[string]*index.FieldInfo, mappingBytes
949961
}
950962
}
951963
}
952-
953964
return nil
954965
})
955966
}

index/scorch/snapshot_index.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,12 @@ func (is *IndexSnapshot) Document(id string) (rv index.Document, err error) {
466466
// Keeping that TODO for now until we have a cleaner way.
467467
rvd.StoredFieldsSize += uint64(len(val))
468468

469+
// Skip fields that are supposed to have deleted store values
469470
if info, ok := is.updatedFields[name]; ok &&
470471
(info.All || info.Store) {
471472
return true
472473
}
474+
473475
// copy value, array positions to preserve them beyond the scope of this callback
474476
value := append([]byte(nil), val...)
475477
arrayPos := append([]uint64(nil), pos...)
@@ -612,6 +614,8 @@ func (is *IndexSnapshot) TermFieldReader(ctx context.Context, term []byte, field
612614

613615
var dict segment.TermDictionary
614616
var err error
617+
618+
// Skip fields that are supposed to have no indexing
615619
if info, ok := is.updatedFields[field]; ok &&
616620
(info.Index || info.All) {
617621
dict = nil
@@ -621,6 +625,7 @@ func (is *IndexSnapshot) TermFieldReader(ctx context.Context, term []byte, field
621625
if err != nil {
622626
return nil, err
623627
}
628+
624629
if dictStats, ok := dict.(segment.DiskStatsReporter); ok {
625630
bytesRead := dictStats.BytesRead()
626631
rv.incrementBytesRead(bytesRead)
@@ -765,6 +770,7 @@ func (is *IndexSnapshot) documentVisitFieldTermsOnSegment(
765770
}
766771
}
767772

773+
// Filter out fields that are supposed to have no doc values
768774
var filteredFields []string
769775
for _, field := range vFields {
770776
if info, ok := is.updatedFields[field]; ok &&
@@ -797,8 +803,8 @@ func (is *IndexSnapshot) documentVisitFieldTermsOnSegment(
797803
}
798804
}
799805

800-
if ssvOk && ssv != nil && len(vFields) > 0 {
801-
dvs, err = ssv.VisitDocValues(localDocNum, fields, visitor, dvs)
806+
if ssvOk && ssv != nil && len(filteredFields) > 0 {
807+
dvs, err = ssv.VisitDocValues(localDocNum, filteredFields, visitor, dvs)
802808
if err != nil {
803809
return nil, nil, err
804810
}

index_impl.go

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ type indexImpl struct {
5757

5858
const storePath = "store"
5959

60-
var mappingInternalKey = []byte("_mapping")
61-
6260
const SearchQueryStartCallbackKey = "_search_query_start_callback_key"
6361
const SearchQueryEndCallbackKey = "_search_query_end_callback_key"
6462

@@ -129,7 +127,7 @@ func newIndexUsing(path string, mapping mapping.IndexMapping, indexType string,
129127
if err != nil {
130128
return nil, err
131129
}
132-
err = rv.i.SetInternal(mappingInternalKey, mappingBytes)
130+
err = rv.i.SetInternal(scorch.MappingInternalKey, mappingBytes)
133131
if err != nil {
134132
return nil, err
135133
}
@@ -164,25 +162,110 @@ func openIndexUsing(path string, runtimeConfig map[string]interface{}) (rv *inde
164162
storeConfig = map[string]interface{}{}
165163
}
166164

165+
storeConfig["path"] = indexStorePath(path)
166+
storeConfig["create_if_missing"] = false
167+
storeConfig["error_if_exists"] = false
168+
for rck, rcv := range runtimeConfig {
169+
storeConfig[rck] = rcv
170+
}
171+
172+
// open the index
173+
indexTypeConstructor := registry.IndexTypeConstructorByName(rv.meta.IndexType)
174+
if indexTypeConstructor == nil {
175+
return nil, ErrorUnknownIndexType
176+
}
177+
178+
rv.i, err = indexTypeConstructor(rv.meta.Storage, storeConfig, Config.analysisQueue)
179+
if err != nil {
180+
return nil, err
181+
}
182+
err = rv.i.Open()
183+
if err != nil {
184+
return nil, err
185+
}
186+
defer func(rv *indexImpl) {
187+
if !rv.open {
188+
rv.i.Close()
189+
}
190+
}(rv)
191+
192+
// now load the mapping
193+
indexReader, err := rv.i.Reader()
194+
if err != nil {
195+
return nil, err
196+
}
197+
defer func() {
198+
if cerr := indexReader.Close(); cerr != nil && err == nil {
199+
err = cerr
200+
}
201+
}()
202+
203+
mappingBytes, err := indexReader.GetInternal(scorch.MappingInternalKey)
204+
if err != nil {
205+
return nil, err
206+
}
207+
208+
var im *mapping.IndexMappingImpl
209+
err = util.UnmarshalJSON(mappingBytes, &im)
210+
if err != nil {
211+
return nil, fmt.Errorf("error parsing mapping JSON: %v\nmapping contents:\n%s", err, string(mappingBytes))
212+
}
213+
214+
// mark the index as open
215+
rv.mutex.Lock()
216+
defer rv.mutex.Unlock()
217+
rv.open = true
218+
219+
// validate the mapping
220+
err = im.Validate()
221+
if err != nil {
222+
// note even if the mapping is invalid
223+
// we still return an open usable index
224+
return rv, err
225+
}
226+
227+
rv.m = im
228+
indexStats.Register(rv)
229+
return rv, err
230+
}
231+
232+
func updateIndexUsing(path string, runtimeConfig map[string]interface{}, newParams string) (rv *indexImpl, err error) {
233+
rv = &indexImpl{
234+
path: path,
235+
name: path,
236+
}
237+
rv.stats = &IndexStat{i: rv}
238+
239+
rv.meta, err = openIndexMeta(path)
240+
if err != nil {
241+
return nil, err
242+
}
243+
244+
// backwards compatibility if index type is missing
245+
if rv.meta.IndexType == "" {
246+
rv.meta.IndexType = upsidedown.Name
247+
}
248+
249+
storeConfig := rv.meta.Config
250+
if storeConfig == nil {
251+
storeConfig = map[string]interface{}{}
252+
}
253+
167254
var um *mapping.IndexMappingImpl
168-
var umBytes []byte
255+
256+
if len(newParams) == 0 {
257+
return nil, fmt.Errorf(("updated mapping is empty"))
258+
}
259+
260+
err = util.UnmarshalJSON([]byte(newParams), &um)
261+
if err != nil {
262+
return nil, fmt.Errorf("error parsing updated mapping JSON: %v\nmapping contents:\n%s", err, newParams)
263+
}
169264

170265
storeConfig["path"] = indexStorePath(path)
171266
storeConfig["create_if_missing"] = false
172267
storeConfig["error_if_exists"] = false
173268
for rck, rcv := range runtimeConfig {
174-
if rck == "mapping" {
175-
if val, ok := rcv.([]byte); ok {
176-
err = util.UnmarshalJSON(val, &um)
177-
if err != nil {
178-
return nil, fmt.Errorf("error parsing updated mapping JSON: %v\nmapping contents:\n%s", err, val)
179-
}
180-
umBytes = val
181-
} else {
182-
return nil, fmt.Errorf("error typecasting updated mapping JSON\nmapping contents: %v", rcv)
183-
}
184-
continue
185-
}
186269
storeConfig[rck] = rcv
187270
}
188271

@@ -196,6 +279,7 @@ func openIndexUsing(path string, runtimeConfig map[string]interface{}) (rv *inde
196279
if err != nil {
197280
return nil, err
198281
}
282+
199283
err = rv.i.Open()
200284
if err != nil {
201285
return nil, err
@@ -217,7 +301,7 @@ func openIndexUsing(path string, runtimeConfig map[string]interface{}) (rv *inde
217301
}
218302
}()
219303

220-
mappingBytes, err := indexReader.GetInternal(mappingInternalKey)
304+
mappingBytes, err := indexReader.GetInternal(scorch.MappingInternalKey)
221305
if err != nil {
222306
return nil, err
223307
}
@@ -241,6 +325,7 @@ func openIndexUsing(path string, runtimeConfig map[string]interface{}) (rv *inde
241325
return rv, err
242326
}
243327

328+
// Validate and update the index with the new mapping
244329
if um != nil {
245330
ui, ok := rv.i.(index.UpdateIndex)
246331
if !ok {
@@ -252,15 +337,16 @@ func openIndexUsing(path string, runtimeConfig map[string]interface{}) (rv *inde
252337
return rv, err
253338
}
254339

255-
fieldInfo, err := deletedFields(im, um)
340+
fieldInfo, err := DeletedFields(im, um)
256341
if err != nil {
257342
return rv, err
258343
}
259344

260-
err = ui.UpdateFields(fieldInfo, umBytes)
345+
err = ui.UpdateFields(fieldInfo, []byte(newParams))
261346
if err != nil {
262347
return rv, err
263348
}
349+
im = um
264350
}
265351

266352
rv.m = im

0 commit comments

Comments
 (0)