syntax = "proto3";package types;message Keyword {string Field = 1; string Word = 2;
}message Document {string Id = 1; uint64 IntId = 2; uint64 BitsFeature = 3; repeated Keyword Keywords = 4; bytes Bytes = 5;
}
type Document struct {Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"`IntId uint64 `protobuf:"varint,2,opt,name=IntId,proto3" json:"IntId,omitempty"`BitsFeature uint64 `protobuf:"varint,3,opt,name=BitsFeature,proto3" json:"BitsFeature,omitempty"`Keywords []*Keyword `protobuf:"bytes,4,rep,name=Keywords,proto3" json:"Keywords,omitempty"`Bytes []byte `protobuf:"bytes,5,opt,name=Bytes,proto3" json:"Bytes,omitempty"`
}

import "github.com/atopos31/go-velisearch/types"
type IRverseIndexer interface {Add(doc types.Document) Delete(IntId uint64, keyword *types.Keyword) Search(query *types.TermQuery, onFlag uint64, offFlag uint64, orFlags []uint64) []string
}
import ("runtime""sync""github.com/atopos31/go-velisearch/types""github.com/atopos31/go-velisearch/util""github.com/huandu/skiplist"farmhash "github.com/leemcloughlin/gofarmhash"
)var _ IRverseIndexer = (*SkipListInvertedIndexer)(nil)type SkipListInvertedIndexer struct {table *util.ConcurrentHashMap locks []sync.RWMutex
}type SkipListValue struct {Id string BitsFeature uint64
}func NewSkipListInvertedIndexer(docNumEstimate int) *SkipListInvertedIndexer {return &SkipListInvertedIndexer{table: util.NewConcurrentHashMap(runtime.NumCPU(), docNumEstimate),locks: make([]sync.RWMutex, 1000),}
}func (indexer *SkipListInvertedIndexer) Add(doc types.Document) {for _, keyword := range doc.Keywords {Key := keyword.ToString()lock := indexer.getLock(Key)skipListValue := SkipListValue{Id: doc.Id,BitsFeature: doc.BitsFeature,}lock.Lock()if value, exist := indexer.table.Get(Key); exist {list := value.(*skiplist.SkipList)list.Set(doc.IntId, skipListValue)} else {list := skiplist.New(skiplist.Uint64)list.Set(doc.IntId, skipListValue)indexer.table.Set(Key, list)}lock.Unlock()}
}func (indexer *SkipListInvertedIndexer) getLock(key string) *sync.RWMutex {n := int(farmhash.Hash32WithSeed([]byte(key), 0))return &indexer.locks[n%len(indexer.locks)]
}func (indexer *SkipListInvertedIndexer) Delete(IntId uint64, keyword *types.Keyword) {Key := keyword.ToString()lock := indexer.getLock(Key)lock.Lock()defer lock.Unlock()if value, exist := indexer.table.Get(Key); exist {list := value.(*skiplist.SkipList)list.Remove(IntId)}
}
func (kw Keyword) ToString() string {if len(kw.Word) > 0 {return kw.Field + "\001" + kw.Word}return ""
}