From 2007e3d8891bb8733c34ceff7cba826760bddfc2 Mon Sep 17 00:00:00 2001
From: Arun Isaac
Date: Mon, 11 Jul 2022 00:43:59 +0530
Subject: web: server: Summarize number of results by type.

* tissue/web/server.scm (%css)[.search-results-statistics,
.search-results-statistics li, .search-results-statistics a,
.current-search-type]: New rules.
* tissue/web/server.scm (make-search-page): Summarize number of
results by type.
(query-string, matches): New functions.
(handler): Pass the number of matches by type to make-search-page.
* issues/add-links-to-filter-documents-by-type.gmi: Close issue.
---
 issues/add-links-to-filter-documents-by-type.gmi |   3 +
 tissue/web/server.scm                            | 107 ++++++++++++++++++++---
 2 files changed, 98 insertions(+), 12 deletions(-)

diff --git a/issues/add-links-to-filter-documents-by-type.gmi b/issues/add-links-to-filter-documents-by-type.gmi
index 533e0f7..095e5cf 100644
--- a/issues/add-links-to-filter-documents-by-type.gmi
+++ b/issues/add-links-to-filter-documents-by-type.gmi
@@ -3,3 +3,6 @@
 * tags: enhancement, web ui
 
 Currently, in the web UI, if the user wants to filter by document type (issues, other documents, etc.), they need to add terms such as type:issue or type:document to the search query. The casual user is not going to learn this search query syntax, and this is going to lead to a lot of confusion. It would be much better to add links to quickly filter by document type. These links will be graphical aids that any user can click with ease.
+
+This is implemented now.
+* closed
diff --git a/tissue/web/server.scm b/tissue/web/server.scm
index 39d63e2..21fbf4a 100644
--- a/tissue/web/server.scm
+++ b/tissue/web/server.scm
@@ -60,6 +60,17 @@ form { text-align: center; }
     padding: 0 0.2em;
 }
 
+.search-results-statistics {
+    list-style: none;
+    padding: 0;
+}
+.search-results-statistics li {
+    display: inline;
+    margin: 0.5em;
+}
+.search-results-statistics a { color: blue; }
+.current-search-type { font-weight: bold; }
+
 .search-results { padding: 0; }
 .search-result {
     list-style-type: none;
@@ -112,10 +123,24 @@ a.tag-chore {
     color: black;
 }")
 
-(define (make-search-page results query estimated-matches css)
-  "Return SXML for a page with search RESULTS produced for
-QUERY. ESTIMATED-MATCHES is the estimated number of matches. CSS is a
-URI to a stylesheet."
+(define* (make-search-page results query css
+                           #:key
+                           page-uri-path page-uri-parameters
+                           matches
+                           matched-open-issues matched-closed-issues
+                           matched-documents matched-commits
+                           current-search-type)
+  "Return SXML for a page with search RESULTS produced for QUERY.
+
+CSS is a URI to a stylesheet. PAGE-URI-PATH is the path part of the
+URI to the page. PAGE-URI-PARAMETERS is an association list of
+parameters in the query string of the URI of the page.
+
+MATCHES is the number of matches. MATCHED-OPEN-ISSUES,
+MATCHED-CLOSED-ISSUES, MATCHED-DOCUMENTS and MATCHED-COMMITS are
+respectively the number of open issues, closed issues, documents and
+commits matching the current query. CURRENT-SEARCH-TYPE is the type of
+document search results are being showed for."
   `(html
     (head
      (title "Tissue search")
@@ -131,6 +156,9 @@ URI to a stylesheet."
                      (name "query")
                      (value ,query)
                      (placeholder "Enter search query")))
+           (input (@ (type "hidden")
+                     (name "type")
+                     (value ,(symbol->string current-search-type))))
            (input (@ (type "submit") (value "Search"))))
      (details (@ (class "search-hint"))
               (summary "Hint")
@@ -156,9 +184,24 @@ operators "
                  (span (@ (class "search-filter"))
                        "OR")
                  "."))
-     (p "Found an estimated "
-        (strong ,(string-append (number->string estimated-matches))
-                " results"))
+     ,(let ((search-result-statistic
+             (lambda (search-type format-string matches)
+               `(li (a (@ (href ,(string-append
+                                  page-uri-path
+                                  "?"
+                                  (query-string
+                                   (acons "type" (symbol->string search-type)
+                                          (alist-delete "type" page-uri-parameters)))))
+                          ,@(if (eq? search-type current-search-type)
+                                '((class "current-search-type"))
+                                '()))
+                       ,(format #f format-string matches))))))
+        `(ul (@ (class "search-results-statistics"))
+             ,(search-result-statistic 'all "~a All" matches)
+             ,(search-result-statistic 'open-issue "~a open issues" matched-open-issues)
+             ,(search-result-statistic 'closed-issue "~a closed issues" matched-closed-issues)
+             ,(search-result-statistic 'document "~a documents" matched-documents)
+             ,(search-result-statistic 'commit "~a commits" matched-commits)))
      (ul (@ (class "search-results"))
          ,@results))))
 
@@ -173,6 +216,17 @@ operators "
            (string-split query #\&))
       '()))
 
+(define (query-string parameters)
+  "Return a query string for association list of PARAMETERS."
+  (string-join
+   (map (match-lambda
+          ((key . value)
+           (string-append (uri-encode key)
+                          "="
+                          (uri-encode value))))
+        parameters)
+   "&"))
+
 (define %mime-types
   '(("gif" image/gif)
     ("html" text/html)
@@ -185,6 +239,17 @@ operators "
     ("svg" image/svg+xml)
     ("txt" text/plain)))
 
+(define (matches db search-query filter)
+  "Return the number of matches in DB for SEARCH-QUERY filtering with
+FILTER query."
+  (MSet-get-matches-estimated
+   (enquire-mset
+    (enquire
+     db (new-Query (Query-OP-FILTER)
+                   (parse-query search-query)
+                   filter))
+    #:maximum-items (database-document-count db))))
+
 (define (handler request body hosts state-directory)
   "Handle web REQUEST with BODY and return two values---the response
 headers and body.
@@ -208,13 +273,24 @@ STATE-DIRECTORY."
       (cond
        ;; Search page
        ((member path (list "/" "/search"))
-        (let ((search-query (or (assoc-ref parameters "query")
-                                "")))
+        (let* ((search-query (or (assoc-ref parameters "query")
+                                 ""))
+               (search-type (match (assoc-ref parameters "type")
+                              ((or "open-issue" "closed-issue" "commit" "document")
+                               (string->symbol (assoc-ref parameters "type")))
+                              (_ 'all)))
+               (filter-alist `((open-issue . ,(parse-query "type:issue AND is:open"))
+                               (closed-issue . ,(parse-query "type:issue AND is:closed"))
+                               (commit . ,(parse-query "type:commit"))
+                               (document . ,(parse-query "type:document")))))
           (values '((content-type . (text/html)))
                   (sxml->html
                    (call-with-database (string-append state-directory "/" hostname "/xapian")
                      (lambda (db)
-                       (let ((mset (enquire-mset (enquire db (parse-query search-query))
+                       (let ((mset (enquire-mset (enquire db (new-Query (Query-OP-FILTER)
+                                                                        (parse-query search-query)
+                                                                        (or (assq-ref filter-alist search-type)
+                                                                            (Query-MatchAll))))
                                                  #:offset 0
                                                  #:maximum-items (database-document-count db))))
                          (make-search-page
@@ -228,8 +304,15 @@ STATE-DIRECTORY."
                                       '()
                                       mset))
                           search-query
-                          (MSet-get-matches-estimated mset)
-                          (assq-ref host-parameters 'css)))))))))
+                          (assq-ref host-parameters 'css)
+                          #:page-uri-path path
+                          #:page-uri-parameters parameters
+                          #:matches (matches db search-query (Query-MatchAll))
+                          #:matched-open-issues (matches db search-query (assq-ref filter-alist 'open-issue))
+                          #:matched-closed-issues (matches db search-query (assq-ref filter-alist 'closed-issue))
+                          #:matched-documents (matches db search-query (assq-ref filter-alist 'document))
+                          #:matched-commits (matches db search-query (assq-ref filter-alist 'commit))
+                          #:current-search-type search-type))))))))
        ;; Static files
        ((let ((file-path
                (find file-exists?
-- 
cgit v1.2.3