guix package: '--search' sorts by relevance.
* guix/scripts/package.scm (find-packages-by-description): Rewrite to compute a score based on the number of regexps matched and the number of matches for each regexp. Sort according to this score and return it as a second value. (process-query) <'search>: Capture the two return values of 'find-packages-by-description'. Pass #:extra-fields to 'package->recutils'. * doc/guix.texi (Invoking guix package): Mention relevance, give an example.
This commit is contained in:
parent
4ee79f22f5
commit
4e863eb35f
|
@ -1854,7 +1854,7 @@ availability of packages:
|
||||||
@itemx -s @var{regexp}
|
@itemx -s @var{regexp}
|
||||||
@cindex searching for packages
|
@cindex searching for packages
|
||||||
List the available packages whose name, synopsis, or description matches
|
List the available packages whose name, synopsis, or description matches
|
||||||
@var{regexp}. Print all the metadata of matching packages in
|
@var{regexp}, sorted by relevance. Print all the metadata of matching packages in
|
||||||
@code{recutils} format (@pxref{Top, GNU recutils databases,, recutils,
|
@code{recutils} format (@pxref{Top, GNU recutils databases,, recutils,
|
||||||
GNU recutils manual}).
|
GNU recutils manual}).
|
||||||
|
|
||||||
|
@ -1862,12 +1862,18 @@ This allows specific fields to be extracted using the @command{recsel}
|
||||||
command, for instance:
|
command, for instance:
|
||||||
|
|
||||||
@example
|
@example
|
||||||
$ guix package -s malloc | recsel -p name,version
|
$ guix package -s malloc | recsel -p name,version,relevance
|
||||||
|
name: jemalloc
|
||||||
|
version: 4.5.0
|
||||||
|
relevance: 6
|
||||||
|
|
||||||
name: glibc
|
name: glibc
|
||||||
version: 2.17
|
version: 2.25
|
||||||
|
relevance: 1
|
||||||
|
|
||||||
name: libgc
|
name: libgc
|
||||||
version: 7.2alpha6
|
version: 7.6.0
|
||||||
|
relevance: 1
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
Similarly, to show the name of all the packages available under the
|
Similarly, to show the name of all the packages available under the
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#:select (directory-exists? mkdir-p))
|
#:select (directory-exists? mkdir-p))
|
||||||
#:use-module (ice-9 format)
|
#:use-module (ice-9 format)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (ice-9 regex)
|
||||||
#:use-module (ice-9 vlist)
|
#:use-module (ice-9 vlist)
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-11)
|
#:use-module (srfi srfi-11)
|
||||||
|
@ -238,32 +239,45 @@ specified in MANIFEST, a manifest object."
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(define (find-packages-by-description regexps)
|
(define (find-packages-by-description regexps)
|
||||||
"Return the list of packages whose name matches one of REGEXPS, or whose
|
"Return two values: the list of packages whose name, synopsis, or
|
||||||
synopsis or description matches all of REGEXPS."
|
description matches at least one of REGEXPS sorted by relevance, and the list
|
||||||
(define version<? (negate version>=?))
|
of relevance scores."
|
||||||
|
(define (score str)
|
||||||
|
(let ((counts (filter-map (lambda (regexp)
|
||||||
|
(match (regexp-exec regexp str)
|
||||||
|
(#f #f)
|
||||||
|
(m (match:count m))))
|
||||||
|
regexps)))
|
||||||
|
;; Compute a score that's proportional to the number of regexps matched
|
||||||
|
;; and to the number of matches for each regexp.
|
||||||
|
(* (length counts) (reduce + 0 counts))))
|
||||||
|
|
||||||
(define (matches-all? str)
|
(define (package-score package)
|
||||||
(every (cut regexp-exec <> str) regexps))
|
(+ (* 3 (score (package-name package)))
|
||||||
|
(* 2 (match (package-synopsis package)
|
||||||
|
((? string? str) (score (P_ str)))
|
||||||
|
(#f 0)))
|
||||||
|
(match (package-description package)
|
||||||
|
((? string? str) (score (P_ str)))
|
||||||
|
(#f 0))))
|
||||||
|
|
||||||
(define (matches-one? str)
|
(let ((matches (fold-packages (lambda (package result)
|
||||||
(find (cut regexp-exec <> str) regexps))
|
(match (package-score package)
|
||||||
|
((? zero?)
|
||||||
(sort
|
result)
|
||||||
(fold-packages (lambda (package result)
|
(score
|
||||||
(if (or (matches-one? (package-name package))
|
(cons (list package score) result))))
|
||||||
(and=> (package-synopsis package)
|
'())))
|
||||||
(compose matches-all? P_))
|
(unzip2 (sort matches
|
||||||
(and=> (package-description package)
|
(lambda (m1 m2)
|
||||||
(compose matches-all? P_)))
|
(match m1
|
||||||
(cons package result)
|
((package1 score1)
|
||||||
result))
|
(match m2
|
||||||
'())
|
((package2 score2)
|
||||||
(lambda (p1 p2)
|
(if (= score1 score2)
|
||||||
(case (string-compare (package-name p1) (package-name p2)
|
(string>? (package-full-name package1)
|
||||||
(const '<) (const '=) (const '>))
|
(package-full-name package2))
|
||||||
((=) (version<? (package-version p1) (package-version p2)))
|
(> score1 score2)))))))))))
|
||||||
((<) #t)
|
|
||||||
(else #f)))))
|
|
||||||
|
|
||||||
(define (transaction-upgrade-entry entry transaction)
|
(define (transaction-upgrade-entry entry transaction)
|
||||||
"Return a variant of TRANSACTION that accounts for the upgrade of ENTRY, a
|
"Return a variant of TRANSACTION that accounts for the upgrade of ENTRY, a
|
||||||
|
@ -752,8 +766,14 @@ processed, #f otherwise."
|
||||||
opts))
|
opts))
|
||||||
(regexps (map (cut make-regexp* <> regexp/icase) patterns)))
|
(regexps (map (cut make-regexp* <> regexp/icase) patterns)))
|
||||||
(leave-on-EPIPE
|
(leave-on-EPIPE
|
||||||
(for-each (cute package->recutils <> (current-output-port))
|
(let-values (((packages scores)
|
||||||
(find-packages-by-description regexps)))
|
(find-packages-by-description regexps)))
|
||||||
|
(for-each (lambda (package score)
|
||||||
|
(package->recutils package (current-output-port)
|
||||||
|
#:extra-fields
|
||||||
|
`((relevance . ,score))))
|
||||||
|
packages
|
||||||
|
scores)))
|
||||||
#t))
|
#t))
|
||||||
|
|
||||||
(('show requested-name)
|
(('show requested-name)
|
||||||
|
|
Loading…
Reference in New Issue