mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-16 00:41:44 +01:00
900ac62251
This is a large and complex PR, so let me explain in detail its changes. First, I had to create new index mappings for Bleve and ElasticSerach as the current ones do not support search by filename. This requires Gitea to recreate the code search indexes (I do not know if this is a breaking change, but I feel it deserves a heads-up). I've used [this approach](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-pathhierarchy-tokenizer.html) to model the filename index. It allows us to efficiently search for both the full path and the name of a file. Bleve, however, does not support this out-of-box, so I had to code a brand new [token filter](https://blevesearch.com/docs/Token-Filters/) to generate the search terms. I also did an overhaul in the `indexer_test.go` file. It now asserts the order of the expected results (this is important since matches based on the name of a file are more relevant than those based on its content). I've added new test scenarios that deal with searching by filename. They use a new repo included in the Gitea fixture. The screenshot below depicts how Gitea shows the search results. It shows results based on content in the same way as the current version does. In matches based on the filename, the first seven lines of the file contents are shown (BTW, this is how GitHub does it). ![image](https://github.com/user-attachments/assets/9d938d86-1a8d-4f89-8644-1921a473e858) Resolves #32096 --------- Signed-off-by: Bruno Sofiato <bruno.sofiato@gmail.com>
87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package bleve
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"github.com/blevesearch/bleve/v2"
|
|
"github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode"
|
|
"github.com/blevesearch/bleve/v2/index/upsidedown"
|
|
"github.com/ethantkoenig/rupture"
|
|
)
|
|
|
|
const (
|
|
maxFuzziness = 2
|
|
)
|
|
|
|
// openIndexer open the index at the specified path, checking for metadata
|
|
// updates and bleve version updates. If index needs to be created (or
|
|
// re-created), returns (nil, nil)
|
|
func openIndexer(path string, latestVersion int) (bleve.Index, int, error) {
|
|
_, err := os.Stat(path)
|
|
if err != nil && os.IsNotExist(err) {
|
|
return nil, 0, nil
|
|
} else if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
metadata, err := rupture.ReadIndexMetadata(path)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if metadata.Version < latestVersion {
|
|
// the indexer is using a previous version, so we should delete it and
|
|
// re-populate
|
|
return nil, metadata.Version, util.RemoveAll(path)
|
|
}
|
|
|
|
index, err := bleve.Open(path)
|
|
if err != nil {
|
|
if errors.Is(err, upsidedown.IncompatibleVersion) {
|
|
log.Warn("Indexer was built with a previous version of bleve, deleting and rebuilding")
|
|
return nil, 0, util.RemoveAll(path)
|
|
}
|
|
return nil, 0, err
|
|
}
|
|
|
|
return index, 0, nil
|
|
}
|
|
|
|
// This method test the GuessFuzzinessByKeyword method. The fuzziness is based on the levenshtein distance and determines how many chars
|
|
// may be different on two string and they still be considered equivalent.
|
|
// Given a phrasse, its shortest word determines its fuzziness. If a phrase uses CJK (eg: `갃갃갃` `啊啊啊`), the fuzziness is zero.
|
|
func GuessFuzzinessByKeyword(s string) int {
|
|
tokenizer := unicode.NewUnicodeTokenizer()
|
|
tokens := tokenizer.Tokenize([]byte(s))
|
|
|
|
if len(tokens) > 0 {
|
|
fuzziness := maxFuzziness
|
|
|
|
for _, token := range tokens {
|
|
fuzziness = min(fuzziness, guessFuzzinessByKeyword(string(token.Term)))
|
|
}
|
|
|
|
return fuzziness
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func guessFuzzinessByKeyword(s string) int {
|
|
// according to https://github.com/blevesearch/bleve/issues/1563, the supported max fuzziness is 2
|
|
// magic number 4 was chosen to determine the levenshtein distance per each character of a keyword
|
|
// BUT, when using CJK (eg: `갃갃갃` `啊啊啊`), it mismatches a lot.
|
|
for _, r := range s {
|
|
if r >= 128 {
|
|
return 0
|
|
}
|
|
}
|
|
return min(maxFuzziness, len(s)/4)
|
|
}
|