Browse Source

feat(search): improve exact match for separate source (#2044)

* unify query builder

* remove uneeded code

* reorganize code
Jo 2 years ago
parent
commit
d13bdb0ce1
10 changed files with 163 additions and 630 deletions
  1. 1 1
      cmd.go
  2. 5 13
      main.go
  3. 89 0
      pkg/query/metric.go
  4. 56 62
      pkg/query/mixed_sources.go
  5. 3 3
      pkg/query/mixed_sources_test.go
  6. 0 174
      pkg/query/source.go
  7. 8 15
      pkg/query/source_test.go
  8. 0 112
      pkg/query/types.go
  9. 0 249
      pkg/query/types_test.go
  10. 1 1
      query.go

+ 1 - 1
cmd.go

@@ -395,7 +395,7 @@ func displayNumberMenu(ctx context.Context, cfg *settings.Configuration, pkgS []
 ) error {
 	queryBuilder.Execute(ctx, dbExecutor, pkgS)
 
-	if err := queryBuilder.Results(os.Stdout, dbExecutor, query.NumberMenu); err != nil {
+	if err := queryBuilder.Results(dbExecutor, query.NumberMenu); err != nil {
 		return err
 	}
 

+ 5 - 13
main.go

@@ -115,19 +115,11 @@ func main() {
 
 	cfg.Runtime = runtime
 
-	if cfg.SeparateSources {
-		cfg.Runtime.QueryBuilder = query.NewSourceQueryBuilder(
-			cfg.Runtime.AURCache,
-			cfg.Runtime.Logger.Child("querybuilder"), cfg.SortBy,
-			cfg.Mode, cfg.SearchBy, cfg.BottomUp,
-			cfg.SingleLineResults)
-	} else {
-		cfg.Runtime.QueryBuilder = query.NewMixedSourceQueryBuilder(
-			cfg.Runtime.AURCache,
-			cfg.Runtime.Logger.Child("mixed.querybuilder"), cfg.SortBy,
-			cfg.Mode, cfg.SearchBy,
-			cfg.BottomUp, cfg.SingleLineResults)
-	}
+	cfg.Runtime.QueryBuilder = query.NewSourceQueryBuilder(
+		cfg.Runtime.AURCache,
+		cfg.Runtime.Logger.Child("mixed.querybuilder"), cfg.SortBy,
+		cfg.Mode, cfg.SearchBy,
+		cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources)
 
 	var useColor bool
 

+ 89 - 0
pkg/query/metric.go

@@ -0,0 +1,89 @@
+package query
+
+import (
+	"hash/fnv"
+	"strings"
+
+	"github.com/adrg/strutil"
+)
+
+const minVotes = 30
+
+// TODO: Add support for Popularity and LastModified
+func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
+	return 1 - (minVotes / (minVotes + float64(pkg.votes)))
+}
+
+func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
+	if v, ok := a.distanceCache[pkg.name]; ok {
+		return v
+	}
+
+	if strings.EqualFold(pkg.name, a.search) {
+		return 1.0
+	}
+
+	sim := strutil.Similarity(pkg.name, a.search, a.metric)
+
+	for _, prov := range pkg.provides {
+		// If the package provides search, it's a perfect match
+		// AUR packages don't populate provides
+		candidate := strutil.Similarity(prov, a.search, a.metric) * 0.80
+		if candidate > sim {
+			sim = candidate
+		}
+	}
+
+	simDesc := strutil.Similarity(pkg.description, a.search, a.metric)
+
+	// slightly overweight sync sources by always giving them max popularity
+	popularity := 1.0
+	if pkg.source == sourceAUR {
+		popularity = a.aurSortByMetric(pkg)
+	}
+
+	sim = sim*0.5 + simDesc*0.2 + popularity*0.3
+
+	a.distanceCache[pkg.name] = sim
+
+	return sim
+}
+
+func (a *abstractResults) separateSourceScore(source string, score float64) float64 {
+	if !a.separateSources {
+		return 0
+	}
+
+	if score == 1.0 {
+		return 50
+	}
+
+	switch source {
+	case sourceAUR:
+		return 0
+	case "core":
+		return 40
+	case "extra":
+		return 30
+	case "community":
+		return 20
+	case "multilib":
+		return 10
+	}
+
+	if v, ok := a.separateSourceCache[source]; ok {
+		return v
+	}
+
+	h := fnv.New32a()
+	h.Write([]byte(source))
+	sourceScore := float64(int(h.Sum32())%9 + 2)
+	a.separateSourceCache[source] = sourceScore
+
+	return sourceScore
+}
+
+func (a *abstractResults) calculateMetric(pkg *abstractResult) float64 {
+	score := a.GetMetric(pkg)
+	return a.separateSourceScore(pkg.source, score) + score
+}

+ 56 - 62
pkg/query/mixed_sources.go

@@ -2,11 +2,10 @@ package query
 
 import (
 	"context"
-	"fmt"
-	"io"
 	"sort"
 	"strconv"
 	"strings"
+	"unicode"
 
 	"github.com/Jguer/aur"
 	"github.com/Jguer/go-alpm/v2"
@@ -26,11 +25,11 @@ const sourceAUR = "aur"
 type Builder interface {
 	Len() int
 	Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string)
-	Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error
+	Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error
 	GetTargets(include, exclude intrange.IntRanges, otherExclude stringset.StringSet) ([]string, error)
 }
 
-type MixedSourceQueryBuilder struct {
+type SourceQueryBuilder struct {
 	results           []abstractResult
 	sortBy            string
 	searchBy          string
@@ -38,12 +37,13 @@ type MixedSourceQueryBuilder struct {
 	queryMap          map[string]map[string]interface{}
 	bottomUp          bool
 	singleLineResults bool
+	separateSources   bool
 
 	aurClient aur.QueryClient
 	logger    *text.Logger
 }
 
-func NewMixedSourceQueryBuilder(
+func NewSourceQueryBuilder(
 	aurClient aur.QueryClient,
 	logger *text.Logger,
 	sortBy string,
@@ -51,8 +51,9 @@ func NewMixedSourceQueryBuilder(
 	searchBy string,
 	bottomUp,
 	singleLineResults bool,
-) *MixedSourceQueryBuilder {
-	return &MixedSourceQueryBuilder{
+	separateSources bool,
+) *SourceQueryBuilder {
+	return &SourceQueryBuilder{
 		aurClient:         aurClient,
 		logger:            logger,
 		bottomUp:          bottomUp,
@@ -60,6 +61,7 @@ func NewMixedSourceQueryBuilder(
 		targetMode:        targetMode,
 		searchBy:          searchBy,
 		singleLineResults: singleLineResults,
+		separateSources:   separateSources,
 		queryMap:          map[string]map[string]interface{}{},
 		results:           make([]abstractResult, 0, 100),
 	}
@@ -74,57 +76,26 @@ type abstractResult struct {
 }
 
 type abstractResults struct {
-	results       []abstractResult
-	search        string
-	distanceCache map[string]float64
-	bottomUp      bool
-	metric        strutil.StringMetric
+	results         []abstractResult
+	search          string
+	bottomUp        bool
+	metric          strutil.StringMetric
+	separateSources bool
+	sortBy          string
+
+	distanceCache       map[string]float64
+	separateSourceCache map[string]float64
 }
 
 func (a *abstractResults) Len() int      { return len(a.results) }
 func (a *abstractResults) Swap(i, j int) { a.results[i], a.results[j] = a.results[j], a.results[i] }
 
-func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
-	if v, ok := a.distanceCache[pkg.name]; ok {
-		return v
-	}
-
-	if strings.EqualFold(pkg.name, a.search) {
-		return 1.0
-	}
-
-	sim := strutil.Similarity(pkg.name, a.search, a.metric)
-
-	for _, prov := range pkg.provides {
-		// If the package provides search, it's a perfect match
-		// AUR packages don't populate provides
-		candidate := strutil.Similarity(prov, a.search, a.metric)
-		if candidate > sim {
-			sim = candidate
-		}
-	}
-
-	simDesc := strutil.Similarity(pkg.description, a.search, a.metric)
-
-	// slightly overweight sync sources by always giving them max popularity
-	popularity := 1.0
-	if pkg.source == sourceAUR {
-		popularity = 1 - (30 / (30 + float64(pkg.votes)))
-	}
-
-	sim = sim*0.5 + simDesc*0.2 + popularity*0.3
-
-	a.distanceCache[pkg.name] = sim
-
-	return sim
-}
-
 func (a *abstractResults) Less(i, j int) bool {
 	pkgA := a.results[i]
 	pkgB := a.results[j]
 
-	simA := a.GetMetric(&pkgA)
-	simB := a.GetMetric(&pkgB)
+	simA := a.calculateMetric(&pkgA)
+	simB := a.calculateMetric(&pkgB)
 
 	if a.bottomUp {
 		return simA < simB
@@ -133,7 +104,7 @@ func (a *abstractResults) Less(i, j int) bool {
 	return simA > simB
 }
 
-func (s *MixedSourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) {
+func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) {
 	var aurErr error
 
 	pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
@@ -143,15 +114,18 @@ func (s *MixedSourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Exe
 	}
 
 	sortableResults := &abstractResults{
-		results:       []abstractResult{},
-		search:        strings.Join(pkgS, ""),
-		distanceCache: map[string]float64{},
-		bottomUp:      s.bottomUp,
-		metric:        metric,
+		results:             []abstractResult{},
+		search:              strings.Join(pkgS, ""),
+		bottomUp:            s.bottomUp,
+		metric:              metric,
+		separateSources:     s.separateSources,
+		sortBy:              s.sortBy,
+		distanceCache:       map[string]float64{},
+		separateSourceCache: map[string]float64{},
 	}
 
 	if s.targetMode.AtLeastAUR() {
-		var aurResults aurQuery
+		var aurResults []aur.Pkg
 		aurResults, aurErr = queryAUR(ctx, s.aurClient, pkgS, s.searchBy)
 		dbName := sourceAUR
 
@@ -160,6 +134,10 @@ func (s *MixedSourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Exe
 				s.queryMap[dbName] = map[string]interface{}{}
 			}
 
+			if !matchesSearch(&aurResults[i], pkgS) {
+				continue
+			}
+
 			s.queryMap[dbName][aurResults[i].Name] = aurResults[i]
 
 			sortableResults.results = append(sortableResults.results, abstractResult{
@@ -213,10 +191,10 @@ func (s *MixedSourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Exe
 	}
 }
 
-func (s *MixedSourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
+func (s *SourceQueryBuilder) Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
 	for i := range s.results {
 		if verboseSearch == Minimal {
-			_, _ = fmt.Fprintln(w, s.results[i].name)
+			s.logger.Println(s.results[i].name)
 			continue
 		}
 
@@ -239,17 +217,17 @@ func (s *MixedSourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, v
 			toPrint += syncPkgSearchString(syncPkg, dbExecutor, s.singleLineResults)
 		}
 
-		fmt.Fprintln(w, toPrint)
+		s.logger.Println(toPrint)
 	}
 
 	return nil
 }
 
-func (s *MixedSourceQueryBuilder) Len() int {
+func (s *SourceQueryBuilder) Len() int {
 	return len(s.results)
 }
 
-func (s *MixedSourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
+func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
 	otherExclude stringset.StringSet,
 ) ([]string, error) {
 	var (
@@ -259,7 +237,6 @@ func (s *MixedSourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges
 	)
 
 	for i := 0; i <= s.Len(); i++ {
-		// FIXME: this is probably broken
 		target := i - 1
 		if s.bottomUp {
 			target = lenRes - i
@@ -272,3 +249,20 @@ func (s *MixedSourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges
 
 	return targets, nil
 }
+
+func matchesSearch(pkg *aur.Pkg, terms []string) bool {
+	for _, pkgN := range terms {
+		if strings.IndexFunc(pkgN, unicode.IsSymbol) != -1 {
+			return true
+		}
+		name := strings.ToLower(pkg.Name)
+		desc := strings.ToLower(pkg.Description)
+		targ := strings.ToLower(pkgN)
+
+		if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
+			return false
+		}
+	}
+
+	return true
+}

+ 3 - 3
pkg/query/mixed_sources_test.go

@@ -38,9 +38,9 @@ func TestMixedSourceQueryBuilder(t *testing.T) {
 			client, err := rpc.NewClient(rpc.WithHTTPClient(&mockDoer{}))
 
 			w := &strings.Builder{}
-			queryBuilder := NewMixedSourceQueryBuilder(client,
+			queryBuilder := NewSourceQueryBuilder(client,
 				text.NewLogger(w, strings.NewReader(""), false, "test"),
-				"votes", parser.ModeAny, "", tc.bottomUp, false)
+				"votes", parser.ModeAny, "", tc.bottomUp, false, false)
 			search := []string{"linux"}
 			mockStore := &mockDB{}
 
@@ -59,7 +59,7 @@ func TestMixedSourceQueryBuilder(t *testing.T) {
 				assert.Equal(t, "linux", queryBuilder.results[0].name)
 			}
 
-			queryBuilder.Results(w, mockStore, Detailed)
+			queryBuilder.Results(mockStore, Detailed)
 
 			wString := w.String()
 			require.GreaterOrEqual(t, len(wString), 1, wString)

+ 0 - 174
pkg/query/source.go

@@ -2,21 +2,9 @@ package query
 
 import (
 	"context"
-	"io"
-	"sort"
-	"strings"
-	"unicode"
 
 	"github.com/Jguer/aur"
-	"github.com/Jguer/go-alpm/v2"
 	"github.com/hashicorp/go-multierror"
-	"github.com/leonelquinteros/gotext"
-
-	"github.com/Jguer/yay/v12/pkg/db"
-	"github.com/Jguer/yay/v12/pkg/intrange"
-	"github.com/Jguer/yay/v12/pkg/settings/parser"
-	"github.com/Jguer/yay/v12/pkg/stringset"
-	"github.com/Jguer/yay/v12/pkg/text"
 )
 
 type SearchVerbosity int
@@ -28,168 +16,6 @@ const (
 	Minimal
 )
 
-type SourceQueryBuilder struct {
-	repoQuery
-	aurQuery
-
-	sortBy            string
-	searchBy          string
-	targetMode        parser.TargetMode
-	bottomUp          bool
-	singleLineResults bool
-
-	aurCache aur.QueryClient
-	logger   *text.Logger
-}
-
-func NewSourceQueryBuilder(
-	aurCache aur.QueryClient,
-	logger *text.Logger,
-	sortBy string,
-	targetMode parser.TargetMode,
-	searchBy string,
-	bottomUp,
-	singleLineResults bool,
-) *SourceQueryBuilder {
-	return &SourceQueryBuilder{
-		aurCache:          aurCache,
-		logger:            logger,
-		repoQuery:         []alpm.IPackage{},
-		aurQuery:          []aur.Pkg{},
-		bottomUp:          bottomUp,
-		sortBy:            sortBy,
-		targetMode:        targetMode,
-		searchBy:          searchBy,
-		singleLineResults: singleLineResults,
-	}
-}
-
-func (s *SourceQueryBuilder) Execute(ctx context.Context,
-	dbExecutor db.Executor,
-	pkgS []string,
-) {
-	var aurErr error
-
-	pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
-
-	if s.targetMode.AtLeastAUR() {
-		s.aurQuery, aurErr = queryAUR(ctx, s.aurCache, pkgS, s.searchBy)
-		s.aurQuery = filterAURResults(pkgS, s.aurQuery)
-
-		sort.Sort(aurSortable{aurQuery: s.aurQuery, sortBy: s.sortBy, bottomUp: s.bottomUp})
-	}
-
-	if s.targetMode.AtLeastRepo() {
-		s.repoQuery = repoQuery(dbExecutor.SyncPackages(pkgS...))
-
-		if s.bottomUp {
-			s.Reverse()
-		}
-	}
-
-	if aurErr != nil && len(s.repoQuery) != 0 {
-		s.logger.Errorln(ErrAURSearch{inner: aurErr})
-		s.logger.Warnln(gotext.Get("Showing repo packages only"))
-	}
-}
-
-func (s *SourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
-	if s.aurQuery == nil || s.repoQuery == nil {
-		return ErrNoQuery{}
-	}
-
-	if s.bottomUp {
-		if s.targetMode.AtLeastAUR() {
-			s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
-		}
-
-		if s.targetMode.AtLeastRepo() {
-			s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
-		}
-	} else {
-		if s.targetMode.AtLeastRepo() {
-			s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
-		}
-
-		if s.targetMode.AtLeastAUR() {
-			s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
-		}
-	}
-
-	return nil
-}
-
-func (s *SourceQueryBuilder) Len() int {
-	return len(s.repoQuery) + len(s.aurQuery)
-}
-
-func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
-	otherExclude stringset.StringSet,
-) ([]string, error) {
-	isInclude := len(exclude) == 0 && len(otherExclude) == 0
-
-	var targets []string
-
-	for i, pkg := range s.repoQuery {
-		var target int
-
-		if s.bottomUp {
-			target = len(s.repoQuery) - i
-		} else {
-			target = i + 1
-		}
-
-		if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
-			targets = append(targets, pkg.DB().Name()+"/"+pkg.Name())
-		}
-	}
-
-	for i := range s.aurQuery {
-		var target int
-
-		if s.bottomUp {
-			target = len(s.aurQuery) - i + len(s.repoQuery)
-		} else {
-			target = i + 1 + len(s.repoQuery)
-		}
-
-		if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
-			targets = append(targets, "aur/"+s.aurQuery[i].Name)
-		}
-	}
-
-	return targets, nil
-}
-
-// filter AUR results to remove strings that don't contain all of the search terms.
-func filterAURResults(pkgS []string, results []aur.Pkg) []aur.Pkg {
-	aurPkgs := make([]aur.Pkg, 0, len(results))
-	matchesSearchTerms := func(pkg *aur.Pkg, terms []string) bool {
-		for _, pkgN := range terms {
-			if strings.IndexFunc(pkgN, unicode.IsSymbol) != -1 {
-				return true
-			}
-			name := strings.ToLower(pkg.Name)
-			desc := strings.ToLower(pkg.Description)
-			targ := strings.ToLower(pkgN)
-
-			if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
-				return false
-			}
-		}
-
-		return true
-	}
-
-	for i := range results {
-		if matchesSearchTerms(&results[i], pkgS) {
-			aurPkgs = append(aurPkgs, results[i])
-		}
-	}
-
-	return aurPkgs
-}
-
 // queryAUR searches AUR and narrows based on subarguments.
 func queryAUR(ctx context.Context,
 	aurClient aur.QueryClient,

+ 8 - 15
pkg/query/source_test.go

@@ -5,7 +5,6 @@ import (
 	"context"
 	"io"
 	"net/http"
-	"strings"
 	"testing"
 
 	"github.com/Jguer/aur/rpc"
@@ -112,30 +111,24 @@ func TestSourceQueryBuilder(t *testing.T) {
 
 			queryBuilder := NewSourceQueryBuilder(client,
 				text.NewLogger(io.Discard, bytes.NewBufferString(""), false, "test"),
-				"votes", parser.ModeAny, "", tc.bottomUp, false)
+				"votes", parser.ModeAny, "", tc.bottomUp, false, true)
 			search := []string{"linux"}
 			mockStore := &mockDB{}
 
 			queryBuilder.Execute(context.Background(), mockStore, search)
-			assert.Len(t, queryBuilder.aurQuery, 1)
-			assert.Len(t, queryBuilder.repoQuery, 2)
 			assert.Equal(t, 3, queryBuilder.Len())
-			assert.Equal(t, "linux-ck", queryBuilder.aurQuery[0].Name)
 
 			if tc.bottomUp {
-				assert.Equal(t, "linux-zen", queryBuilder.repoQuery[0].Name())
-				assert.Equal(t, "linux", queryBuilder.repoQuery[1].Name())
+				assert.Equal(t, "linux-ck", queryBuilder.results[0].name)
+				assert.Equal(t, "linux-zen", queryBuilder.results[1].name)
+				assert.Equal(t, "linux", queryBuilder.results[2].name)
 			} else {
-				assert.Equal(t, "linux-zen", queryBuilder.repoQuery[1].Name())
-				assert.Equal(t, "linux", queryBuilder.repoQuery[0].Name())
+				assert.Equal(t, "linux-ck", queryBuilder.results[2].name)
+				assert.Equal(t, "linux-zen", queryBuilder.results[1].name)
+				assert.Equal(t, "linux", queryBuilder.results[0].name)
 			}
 
-			w := &strings.Builder{}
-			queryBuilder.Results(w, mockStore, Detailed)
-
-			wString := w.String()
-			require.GreaterOrEqual(t, len(wString), 1)
-			assert.Equal(t, tc.want, wString)
+			queryBuilder.Results(mockStore, Detailed)
 		})
 	}
 }

+ 0 - 112
pkg/query/types.go

@@ -2,7 +2,6 @@ package query
 
 import (
 	"fmt"
-	"io"
 	"strconv"
 
 	"github.com/Jguer/aur"
@@ -13,64 +12,6 @@ import (
 	"github.com/Jguer/yay/v12/pkg/text"
 )
 
-type (
-	aurQuery  []aur.Pkg       // Query is a collection of Results.
-	repoQuery []alpm.IPackage // Query holds the results of a repository search.
-)
-
-type aurSortable struct {
-	aurQuery
-	sortBy   string
-	bottomUp bool
-}
-
-func (r repoQuery) Reverse() {
-	for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
-		r[i], r[j] = r[j], r[i]
-	}
-}
-
-func (r repoQuery) Less(i, j int) bool {
-	return text.LessRunes([]rune(r[i].Name()), []rune(r[j].Name()))
-}
-
-func (q aurSortable) Len() int {
-	return len(q.aurQuery)
-}
-
-func (q aurSortable) Less(i, j int) bool {
-	var result bool
-
-	switch q.sortBy {
-	case "votes":
-		result = q.aurQuery[i].NumVotes > q.aurQuery[j].NumVotes
-	case "popularity":
-		result = q.aurQuery[i].Popularity > q.aurQuery[j].Popularity
-	case "name":
-		result = text.LessRunes([]rune(q.aurQuery[i].Name), []rune(q.aurQuery[j].Name))
-	case "base":
-		result = text.LessRunes([]rune(q.aurQuery[i].PackageBase), []rune(q.aurQuery[j].PackageBase))
-	case "submitted":
-		result = q.aurQuery[i].FirstSubmitted < q.aurQuery[j].FirstSubmitted
-	case "modified":
-		result = q.aurQuery[i].LastModified < q.aurQuery[j].LastModified
-	case "id":
-		result = q.aurQuery[i].ID < q.aurQuery[j].ID
-	case "baseid":
-		result = q.aurQuery[i].PackageBaseID < q.aurQuery[j].PackageBaseID
-	}
-
-	if q.bottomUp {
-		return !result
-	}
-
-	return result
-}
-
-func (q aurSortable) Swap(i, j int) {
-	q.aurQuery[i], q.aurQuery[j] = q.aurQuery[j], q.aurQuery[i]
-}
-
 func getSearchBy(value string) aur.By {
 	switch value {
 	case "name":
@@ -104,36 +45,6 @@ func getSearchBy(value string) aur.By {
 	}
 }
 
-// PrintSearch handles printing search results in a given format.
-func (q aurQuery) printSearch(
-	w io.Writer,
-	start int,
-	dbExecutor db.Executor,
-	searchMode SearchVerbosity,
-	bottomUp,
-	singleLineResults bool,
-) {
-	for i := range q {
-		if searchMode == Minimal {
-			_, _ = fmt.Fprintln(w, q[i].Name)
-			continue
-		}
-
-		var toprint string
-
-		if searchMode == NumberMenu {
-			if bottomUp {
-				toprint += text.Magenta(strconv.Itoa(len(q)+start-i-1) + " ")
-			} else {
-				toprint += text.Magenta(strconv.Itoa(start+i) + " ")
-			}
-		}
-
-		toprint += aurPkgSearchString(&q[i], dbExecutor, singleLineResults)
-		_, _ = fmt.Fprintln(w, toprint)
-	}
-}
-
 func aurPkgSearchString(
 	pkg *aur.Pkg,
 	dbExecutor db.Executor,
@@ -172,29 +83,6 @@ func aurPkgSearchString(
 }
 
 // PrintSearch receives a RepoSearch type and outputs pretty text.
-func (r repoQuery) printSearch(w io.Writer, dbExecutor db.Executor, searchMode SearchVerbosity, bottomUp, singleLineResults bool) {
-	for i, res := range r {
-		if searchMode == Minimal {
-			_, _ = fmt.Fprintln(w, res.Name())
-			continue
-		}
-
-		var toprint string
-
-		if searchMode == NumberMenu {
-			if bottomUp {
-				toprint += text.Magenta(strconv.Itoa(len(r)-i) + " ")
-			} else {
-				toprint += text.Magenta(strconv.Itoa(i+1) + " ")
-			}
-		}
-
-		toprint += syncPkgSearchString(res, dbExecutor, singleLineResults)
-		_, _ = fmt.Fprintln(w, toprint)
-	}
-}
-
-// PrintSearch receives a RepoSearch type and outputs pretty text.
 func syncPkgSearchString(pkg alpm.IPackage, dbExecutor db.Executor, singleLineResults bool) string {
 	toPrint := text.Bold(text.ColorHash(pkg.DB().Name())) + "/" + text.Bold(pkg.Name()) +
 		" " + text.Cyan(pkg.Version()) +

+ 0 - 249
pkg/query/types_test.go

@@ -1,249 +0,0 @@
-package query
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/Jguer/yay/v12/pkg/db/mock"
-	"github.com/Jguer/yay/v12/pkg/text"
-
-	"github.com/Jguer/aur"
-)
-
-var (
-	pkgA = aur.Pkg{
-		Name:        "package-a",
-		Version:     "1.0.0",
-		Description: "Package A description",
-		Maintainer:  "Package A Maintainer",
-	}
-	pkgARepo = &mock.Package{
-		PName:        pkgA.Name,
-		PVersion:     pkgA.Version,
-		PDescription: pkgA.Description,
-		PSize:        1,
-		PISize:       1,
-		PDB:          mock.NewDB("dba"),
-	}
-
-	pkgB = aur.Pkg{
-		Name:        "package-b",
-		Version:     "1.0.0",
-		Description: "Package B description",
-		Maintainer:  "Package B Maintainer",
-	}
-	pkgBRepo = &mock.Package{
-		PName:        pkgB.Name,
-		PVersion:     pkgB.Version,
-		PDescription: pkgB.Description,
-		PSize:        1,
-		PISize:       1,
-		PDB:          mock.NewDB("dbb"),
-	}
-)
-
-func Test_aurQuery_printSearch(t *testing.T) {
-	type args struct {
-		searchMode        SearchVerbosity
-		singleLineResults bool
-	}
-	tests := []struct {
-		name     string
-		q        aurQuery
-		args     args
-		useColor bool
-		want     string
-	}{
-		{
-			name: "AUR,Minimal,NoColor",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode: Minimal,
-			},
-			want: "package-a\npackage-b\n",
-		},
-		{
-			name: "AUR,DoubleLine,NumberMenu,NoColor",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode:        NumberMenu,
-				singleLineResults: false,
-			},
-			want: "1 aur/package-a 1.0.0 (+0 0.00) \n    Package A description\n2 aur/package-b 1.0.0 (+0 0.00) \n    Package B description\n",
-		},
-		{
-			name: "AUR,SingleLine,NumberMenu,NoColor",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode:        NumberMenu,
-				singleLineResults: true,
-			},
-			want: "1 aur/package-a 1.0.0 (+0 0.00) \tPackage A description\n2 aur/package-b 1.0.0 (+0 0.00) \tPackage B description\n",
-		},
-		{
-			name: "AUR,DoubleLine,Detailed,NoColor",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: false,
-			},
-			want: "aur/package-a 1.0.0 (+0 0.00) \n    Package A description\naur/package-b 1.0.0 (+0 0.00) \n    Package B description\n",
-		},
-		{
-			name: "AUR,SingleLine,Detailed,NoColor",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: true,
-			},
-			want: "aur/package-a 1.0.0 (+0 0.00) \tPackage A description\naur/package-b 1.0.0 (+0 0.00) \tPackage B description\n",
-		},
-		{
-			name: "AUR,DoubleLine,Detailed,Color",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: false,
-			},
-			useColor: true,
-			want:     "\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mpackage-a\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (+0\x1b[0m \x1b[1m0.00) \x1b[0m\n    Package A description\n\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mpackage-b\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (+0\x1b[0m \x1b[1m0.00) \x1b[0m\n    Package B description\n",
-		},
-		{
-			name: "AUR,SingleLine,Detailed,Color",
-			q:    aurQuery{pkgA, pkgB},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: true,
-			},
-			useColor: true,
-			want:     "\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mpackage-a\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (+0\x1b[0m \x1b[1m0.00) \x1b[0m\tPackage A description\n\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mpackage-b\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (+0\x1b[0m \x1b[1m0.00) \x1b[0m\tPackage B description\n",
-		},
-		{
-			name: "AUR,NoPackages",
-			q:    aurQuery{},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: true,
-			},
-			useColor: true,
-			want:     "",
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			w := &strings.Builder{}
-			executor := &mock.DBExecutor{LocalPackageFn: func(string) mock.IPackage { return nil }}
-			text.UseColor = tt.useColor
-
-			// Fire
-			tt.q.printSearch(w, 1, executor, tt.args.searchMode, false, tt.args.singleLineResults)
-
-			got := w.String()
-			assert.Equal(t, tt.want, got)
-		})
-	}
-}
-
-func Test_repoQuery_printSearch(t *testing.T) {
-	type args struct {
-		searchMode        SearchVerbosity
-		singleLineResults bool
-	}
-	tests := []struct {
-		name     string
-		q        repoQuery
-		args     args
-		useColor bool
-		want     string
-	}{
-		{
-			name: "REPO,Minimal,NoColor",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode: Minimal,
-			},
-			want: "package-a\npackage-b\n",
-		},
-		{
-			name: "REPO,DoubleLine,NumberMenu,NoColor",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode:        NumberMenu,
-				singleLineResults: false,
-			},
-			want: "1 dba/package-a 1.0.0 (1.0 B 1.0 B) \n    Package A description\n2 dbb/package-b 1.0.0 (1.0 B 1.0 B) \n    Package B description\n",
-		},
-		{
-			name: "REPO,SingleLine,NumberMenu,NoColor",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode:        NumberMenu,
-				singleLineResults: true,
-			},
-			want: "1 dba/package-a 1.0.0 (1.0 B 1.0 B) \tPackage A description\n2 dbb/package-b 1.0.0 (1.0 B 1.0 B) \tPackage B description\n",
-		},
-		{
-			name: "REPO,DoubleLine,Detailed,NoColor",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: false,
-			},
-			want: "dba/package-a 1.0.0 (1.0 B 1.0 B) \n    Package A description\ndbb/package-b 1.0.0 (1.0 B 1.0 B) \n    Package B description\n",
-		},
-		{
-			name: "REPO,SingleLine,Detailed,NoColor",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: true,
-			},
-			want: "dba/package-a 1.0.0 (1.0 B 1.0 B) \tPackage A description\ndbb/package-b 1.0.0 (1.0 B 1.0 B) \tPackage B description\n",
-		},
-		{
-			name: "AUR,DoubleLine,Detailed,Color",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: false,
-			},
-			useColor: true,
-			want:     "\x1b[1m\x1b[35mdba\x1b[0m\x1b[0m/\x1b[1mpackage-a\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n    Package A description\n\x1b[1m\x1b[36mdbb\x1b[0m\x1b[0m/\x1b[1mpackage-b\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n    Package B description\n",
-		},
-		{
-			name: "REPO,SingleLine,Detailed,Color",
-			q:    repoQuery{pkgARepo, pkgBRepo},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: true,
-			},
-			useColor: true,
-			want:     "\x1b[1m\x1b[35mdba\x1b[0m\x1b[0m/\x1b[1mpackage-a\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\tPackage A description\n\x1b[1m\x1b[36mdbb\x1b[0m\x1b[0m/\x1b[1mpackage-b\x1b[0m \x1b[36m1.0.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\tPackage B description\n",
-		},
-		{
-			name: "REPO,NoPackages",
-			q:    repoQuery{},
-			args: args{
-				searchMode:        Detailed,
-				singleLineResults: true,
-			},
-			useColor: true,
-			want:     "",
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			w := &strings.Builder{}
-			executor := &mock.DBExecutor{LocalPackageFn: func(string) mock.IPackage { return nil }}
-			text.UseColor = tt.useColor
-
-			// Fire
-			tt.q.printSearch(w, executor, tt.args.searchMode, false, tt.args.singleLineResults)
-
-			got := w.String()
-			assert.Equal(t, tt.want, got)
-		})
-	}
-}

+ 1 - 1
query.go

@@ -29,7 +29,7 @@ func syncSearch(ctx context.Context, pkgS []string,
 		searchMode = query.Detailed
 	}
 
-	return queryBuilder.Results(os.Stdout, dbExecutor, searchMode)
+	return queryBuilder.Results(dbExecutor, searchMode)
 }
 
 // SyncInfo serves as a pacman -Si for repo packages and AUR packages.