diff --git a/search/Search Index.go b/search/Search Index.go index 112431f..fa43768 100644 --- a/search/Search Index.go +++ b/search/Search Index.go @@ -17,11 +17,17 @@ import ( "github.com/google/uuid" ) +// A search selector is a term that discovers a file. +type SearchSelector struct { + Word string // Normalized version of the word + Hash []byte // Hash of the word + ExactSearch bool // Indicates this is an exact search term, for example a full filename. +} + // SearchIndexRecord identifies a hash to a given file type SearchIndexRecord struct { - // input data - Word string - Hash []byte + // List of selectors that found the result. Multiple keywords may find the same file. + Selectors []SearchSelector // result data FileID uuid.UUID @@ -180,7 +186,7 @@ func (index *SearchIndexStore) UnindexHash(fileID uuid.UUID, hash []byte) (err e } // LookupHash returns all index records stored for the hash. -func (index *SearchIndexStore) LookupHash(hash []byte) (records []SearchIndexRecord, err error) { +func (index *SearchIndexStore) LookupHash(selector SearchSelector, resultMap map[uuid.UUID]*SearchIndexRecord) (err error) { if index.Database == nil { return } @@ -188,21 +194,25 @@ func (index *SearchIndexStore) LookupHash(hash []byte) (records []SearchIndexRec index.RLock() defer index.RUnlock() - raw, found := index.Database.Get(hash) + raw, found := index.Database.Get(selector.Hash) if !found { - return nil, nil + return nil } else if len(raw)%indexRecordSize != 0 { // check if record is corrupt - return nil, errors.New("corrupt index record") + return errors.New("corrupt index record") } for offset := 0; offset < len(raw); offset += indexRecordSize { if record := decodeIndexRecord(raw[offset : offset+indexRecordSize]); record != nil { - record.Hash = hash - records = append(records, *record) + if existingRecord, ok := resultMap[record.FileID]; ok { + existingRecord.Selectors = append(existingRecord.Selectors, selector) + } else { + record.Selectors = []SearchSelector{selector} + resultMap[record.FileID] = record + } } } - return records, nil + return nil } // ---- index and reverse index code ---- diff --git a/search/Search Term.go b/search/Search Term.go index d982fbf..4f54df0 100644 --- a/search/Search Term.go +++ b/search/Search Term.go @@ -6,6 +6,8 @@ Author: Peter Kleissner package search +import "github.com/google/uuid" + func (index *SearchIndexStore) Search(term string) (results []SearchIndexRecord) { termS, isExact, _ := sanitizeInputTerm(term) @@ -13,16 +15,23 @@ func (index *SearchIndexStore) Search(term string) (results []SearchIndexRecord) return } + resultMap := make(map[uuid.UUID]*SearchIndexRecord) + resultMapToSlice := func() (results []SearchIndexRecord) { + for _, result := range resultMap { + results = append(results, *result) + } + return results + } + // start with exact search hashExact := hashWord(termS) if hashExact != nil { - records, _ := index.LookupHash(hashExact) - results = append(results, setSearchIndexFields(records, termS)...) + index.LookupHash(SearchSelector{Hash: hashExact, Word: termS, ExactSearch: true}, resultMap) } // exact search only? if isExact { - return + return resultMapToSlice() } // break up the term into hashes @@ -35,17 +44,8 @@ func (index *SearchIndexStore) Search(term string) (results []SearchIndexRecord) // look up the hashes! for hash, keyword := range hashes { - records, _ := index.LookupHash(hash[:]) - results = append(results, setSearchIndexFields(records, keyword)...) + index.LookupHash(SearchSelector{Hash: hash[:], Word: keyword}, resultMap) } - return -} - -// setSearchIndexFields sets certain fields in the index record for adding context in the output -func setSearchIndexFields(records []SearchIndexRecord, word string) []SearchIndexRecord { - for n := range records { - records[n].Word = word - } - return records + return resultMapToSlice() }