mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
feat(search): update search pattern matching rules (#1257)
Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
@@ -897,7 +897,7 @@ func (bdw *DBWrapper) SearchRepos(ctx context.Context, searchText string, filter
|
||||
return err
|
||||
}
|
||||
|
||||
if score := common.ScoreRepoName(searchText, string(repoName)); score != -1 {
|
||||
if rank := common.RankRepoName(searchText, string(repoName)); rank != -1 {
|
||||
var (
|
||||
// specific values used for sorting that need to be calculated based on all manifests from the repo
|
||||
repoDownloads = 0
|
||||
@@ -1006,7 +1006,7 @@ func (bdw *DBWrapper) SearchRepos(ctx context.Context, searchText string, filter
|
||||
|
||||
pageFinder.Add(repodb.DetailedRepoMeta{
|
||||
RepoMeta: repoMeta,
|
||||
Score: score,
|
||||
Rank: rank,
|
||||
Downloads: repoDownloads,
|
||||
UpdateTime: repoLastUpdated,
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
// by iterating the manifests, etc.)
|
||||
type DetailedRepoMeta struct {
|
||||
RepoMeta RepoMetadata
|
||||
Score int
|
||||
Rank int
|
||||
Downloads int
|
||||
UpdateTime time.Time
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func SortByAlphabeticDsc(pageBuffer []DetailedRepoMeta) func(i, j int) bool {
|
||||
|
||||
func SortByRelevance(pageBuffer []DetailedRepoMeta) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
return pageBuffer[i].Score < pageBuffer[j].Score
|
||||
return pageBuffer[i].Rank < pageBuffer[j].Rank
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,21 @@ func ValidateRepoReferenceInput(repo, reference string, manifestDigest godigest.
|
||||
return nil
|
||||
}
|
||||
|
||||
func ScoreRepoName(searchText string, repoName string) int {
|
||||
// These constants are meant used to describe how high or low in rank a match is.
|
||||
// Note that the "higher rank" relates to a lower number so ranks are sorted in a
|
||||
// ascending order.
|
||||
const (
|
||||
lowPriority = 100
|
||||
mediumPriority = 10
|
||||
highPriority = 1
|
||||
perfectMatchPriority = 0
|
||||
)
|
||||
|
||||
// RankRepoName associates a rank to a given repoName given a searchText.
|
||||
// The imporance of the value grows inversly proportional to the int value it has.
|
||||
// For example: rank(1) > rank(10) > rank(100)...
|
||||
func RankRepoName(searchText string, repoName string) int {
|
||||
searchText = strings.Trim(searchText, "/")
|
||||
searchTextSlice := strings.Split(searchText, "/")
|
||||
repoNameSlice := strings.Split(repoName, "/")
|
||||
|
||||
@@ -70,37 +84,60 @@ func ScoreRepoName(searchText string, repoName string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
if searchText == repoName {
|
||||
return perfectMatchPriority
|
||||
}
|
||||
|
||||
// searchText containst just 1 diretory name
|
||||
if len(searchTextSlice) == 1 {
|
||||
// check if it maches first or last name in path
|
||||
if index := strings.Index(repoNameSlice[len(repoNameSlice)-1], searchTextSlice[0]); index != -1 {
|
||||
return index + 1
|
||||
lastNameInRepoPath := repoNameSlice[len(repoNameSlice)-1]
|
||||
|
||||
// searchText: "bar" | repoName: "foo/bar" lastNameInRepoPath: "bar"
|
||||
if index := strings.Index(lastNameInRepoPath, searchText); index != -1 {
|
||||
return (index + 1) * highPriority
|
||||
}
|
||||
|
||||
// we'll make repos that match the first name in path less important than matching the last name in path
|
||||
if index := strings.Index(repoNameSlice[0], searchTextSlice[0]); index != -1 {
|
||||
return (index + 1) * 10
|
||||
firstNameInRepoPath := repoNameSlice[0]
|
||||
|
||||
// searchText: "foo" | repoName: "foo/bar" firstNameInRepoPath: "foo"
|
||||
if index := strings.Index(firstNameInRepoPath, searchText); index != -1 {
|
||||
return (index + 1) * mediumPriority
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
if len(searchTextSlice) < len(repoNameSlice) &&
|
||||
strings.HasPrefix(repoName, searchText) {
|
||||
return 1
|
||||
}
|
||||
foundPrefixInRepoName := true
|
||||
|
||||
// searchText and repoName match perfectly up until the last name in path
|
||||
// searchText: "foo/bar/rep" | repoName: "foo/bar/baz/repo" foundPrefixInRepoName: true
|
||||
// searchText: "foo/baz/rep" | repoName: "foo/bar/baz/repo" foundPrefixInRepoName: false
|
||||
for i := 0; i < len(searchTextSlice)-1; i++ {
|
||||
if searchTextSlice[i] != repoNameSlice[i] {
|
||||
return -1
|
||||
foundPrefixInRepoName = false
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// check the last
|
||||
if index := strings.Index(repoNameSlice[len(repoNameSlice)-1], searchTextSlice[len(searchTextSlice)-1]); index != -1 {
|
||||
return (index + 1)
|
||||
if foundPrefixInRepoName {
|
||||
lastNameInRepoPath := repoNameSlice[len(repoNameSlice)-1]
|
||||
lastNameInSearchText := searchTextSlice[len(searchTextSlice)-1]
|
||||
|
||||
// searchText: "foo/bar/epo" | repoName: "foo/bar/baz/repo" -> Index(repo, epo) = 1
|
||||
if index := strings.Index(lastNameInRepoPath, lastNameInSearchText); index != -1 {
|
||||
return (index + 1) * highPriority
|
||||
}
|
||||
}
|
||||
|
||||
// searchText: "foo/bar/b" | repoName: "foo/bar/baz/repo"
|
||||
if strings.HasPrefix(repoName, searchText) {
|
||||
return mediumPriority
|
||||
}
|
||||
|
||||
// searchText: "bar/ba" | repoName: "foo/bar/baz/repo"
|
||||
if index := strings.Index(repoName, searchText); index != -1 {
|
||||
return (index + 1) * lowPriority
|
||||
}
|
||||
|
||||
// no match
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
@@ -829,7 +829,7 @@ func (dwr *DBWrapper) SearchRepos(ctx context.Context, searchText string, filter
|
||||
continue
|
||||
}
|
||||
|
||||
if score := common.ScoreRepoName(searchText, repoMeta.Name); score != -1 {
|
||||
if rank := common.RankRepoName(searchText, repoMeta.Name); rank != -1 {
|
||||
var (
|
||||
// specific values used for sorting that need to be calculated based on all manifests from the repo
|
||||
repoDownloads = 0
|
||||
@@ -935,7 +935,7 @@ func (dwr *DBWrapper) SearchRepos(ctx context.Context, searchText string, filter
|
||||
|
||||
pageFinder.Add(repodb.DetailedRepoMeta{
|
||||
RepoMeta: repoMeta,
|
||||
Score: score,
|
||||
Rank: rank,
|
||||
Downloads: repoDownloads,
|
||||
UpdateTime: repoLastUpdated,
|
||||
})
|
||||
|
||||
@@ -1962,17 +1962,22 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) {
|
||||
|
||||
func TestRelevanceSorting(t *testing.T) {
|
||||
Convey("Test Relevance Sorting", t, func() {
|
||||
So(common.ScoreRepoName("alpine", "alpine"), ShouldEqual, 1)
|
||||
So(common.ScoreRepoName("test/alpine", "alpine"), ShouldEqual, -1)
|
||||
So(common.ScoreRepoName("alpine", "test/alpine"), ShouldEqual, 1)
|
||||
So(common.ScoreRepoName("test", "test/alpine"), ShouldEqual, 10)
|
||||
So(common.ScoreRepoName("pine", "test/alpine"), ShouldEqual, 3)
|
||||
So(common.ScoreRepoName("pine", "alpine/alpine"), ShouldEqual, 3)
|
||||
So(common.ScoreRepoName("pine", "alpine/test"), ShouldEqual, 30)
|
||||
So(common.ScoreRepoName("test/pine", "alpine"), ShouldEqual, -1)
|
||||
So(common.ScoreRepoName("repo/test", "repo/test/alpine"), ShouldEqual, 1)
|
||||
So(common.ScoreRepoName("repo/test/golang", "repo/test2/alpine"), ShouldEqual, -1)
|
||||
So(common.ScoreRepoName("repo/test/pine", "repo/test/alpine"), ShouldEqual, 3)
|
||||
So(common.RankRepoName("alpine", "alpine"), ShouldEqual, 0)
|
||||
So(common.RankRepoName("test/alpine", "test/alpine"), ShouldEqual, 0)
|
||||
So(common.RankRepoName("test/alpine", "alpine"), ShouldEqual, -1)
|
||||
So(common.RankRepoName("alpine", "test/alpine"), ShouldEqual, 1)
|
||||
So(common.RankRepoName("test", "test/alpine"), ShouldEqual, 10)
|
||||
So(common.RankRepoName("pine", "test/alpine"), ShouldEqual, 3)
|
||||
So(common.RankRepoName("pine", "alpine/alpine"), ShouldEqual, 3)
|
||||
So(common.RankRepoName("pine", "alpine/test"), ShouldEqual, 30)
|
||||
So(common.RankRepoName("test/pine", "alpine"), ShouldEqual, -1)
|
||||
So(common.RankRepoName("repo/test", "repo/test/alpine"), ShouldEqual, 10)
|
||||
So(common.RankRepoName("repo/test/golang", "repo/test2/alpine"), ShouldEqual, -1)
|
||||
So(common.RankRepoName("repo/test/pine", "repo/test/alpine"), ShouldEqual, 3)
|
||||
So(common.RankRepoName("debian", "c3/debian/base-amd64"), ShouldEqual, 400)
|
||||
So(common.RankRepoName("debian/base-amd64", "c3/debian/base-amd64"), ShouldEqual, 400)
|
||||
So(common.RankRepoName("debian/base-amd64", "c3/aux/debian/base-amd64"), ShouldEqual, 800)
|
||||
So(common.RankRepoName("aux/debian", "c3/aux/debian/base-amd64"), ShouldEqual, 400)
|
||||
|
||||
Convey("Integration", func() {
|
||||
filePath := path.Join(t.TempDir(), "repo.db")
|
||||
|
||||
Reference in New Issue
Block a user