diff options
| author | unwox <me@unwox.com> | 2024-10-10 15:03:06 +0600 |
|---|---|---|
| committer | unwox <me@unwox.com> | 2024-10-10 15:03:06 +0600 |
| commit | 240aeb84855a153764830186d5f4e7f22873561c (patch) | |
| tree | 0cda41c18d327edb9691249ac8d6e5a44fa9c91e /bin | |
| parent | 9d1b193640c039f7300b44dd30def266cbc31a85 (diff) | |
implement filtering by tags
Diffstat (limited to 'bin')
| -rw-r--r-- | bin/serve.fnl | 200 |
1 files changed, 129 insertions, 71 deletions
diff --git a/bin/serve.fnl b/bin/serve.fnl index d4dbf4c..63e4a3b 100644 --- a/bin/serve.fnl +++ b/bin/serve.fnl @@ -1,4 +1,4 @@ -(import-macros {: map : reduce} :lib.macro) +(import-macros {: map : reduce : filter} :lib.macro) (tset package :path (.. package.path ";./vendor/lpeglj/?.lua")) @@ -34,6 +34,91 @@ (string.gsub """ "\"") (string.gsub "&" "&")))) +(fn random-products [limit] + (assert (< 0 limit) "limit must be > 0") + (luna.db.query* + db + "SELECT site, + title, + description, + image, + url, + price, + weight, + price_per AS \"price-per\", + year + FROM products + ORDER BY RANDOM() + LIMIT ?" + [limit])) + +(fn all-tags [] + (map + (fn [_ v] (. v 1)) + (luna.db.query + db + "SELECT title FROM tags ORDER BY creation_time" + []))) + +(fn query-products [page query tags] + (local tags (or tags [])) + + (var where-conds []) + (var where-vars []) + (when (< 0 (# tags)) + (each [_ tag (pairs tags)] + (table.insert where-conds "product_tags.tag = ?") + (table.insert where-vars tag))) + (when (and query (< 0 (# query))) + (table.insert where-conds "search.title MATCH ?") + (table.insert where-vars + (array.join + (map (fn [_ q] + (if (. query-synonyms q) + (.. "(" q "* OR " (. query-synonyms q) "*)") + (.. q "*"))) + (str.split query)) + " "))) + (local where-sql (array.join where-conds "\nAND ")) + + (local total + (luna.db.query + db + (string.format + "SELECT count(*) + FROM search + LEFT JOIN product_tags ON product_tags.product = search.fid + WHERE search.`table` = 'products' + AND %s" where-sql) + where-vars)) + + {:results + (luna.db.query* + db + (string.format + "SELECT highlight(search, 0, '<i>', '</i>') AS \"title\", + products.site, + products.description, + products.image, + products.url, + products.price, + products.weight, + products.price_per AS \"price-per\", + products.year, + products.archived, + products.creation_time AS \"creation-time\" + FROM search + INNER JOIN products ON search.fid = products.url + LEFT JOIN product_tags ON product_tags.product = products.url + WHERE search.`table` = 'products' + AND %s + ORDER BY rank + LIMIT 48 OFFSET ?" where-sql) + (array.concat where-vars [(* (- page 1) 48)])) + :total (if (< 0 (# total)) + (. total 1 1) + 0)}) + (fn site-name-template [name] (if (= name "ipuer") @@ -83,24 +168,33 @@ ;; FIXME: security issue [:small {} [:NO-ESCAPE (unescape (str.truncate product.description 200))]]]) -(fn paginator-template [query page limit total] +(fn paginator-template [query tags page limit total] (local last-page (math.ceil (/ total limit))) + (fn make-url [page query tags] + (.. "?page=" (tostring page) + "&query=" query + "&" + (array.join + (array.flatten + (map (fn [_ tag] (.. "tags=" tag)) + tags)) + "&"))) (if (< limit total) [:div {:class "paginator"} [:div {:class "paginator-numbers"} (if (< 1 page) - [:a {:href (.. "?page=" (- page 1) "&query=" query)} "<"] + [:a {:href (make-url (- page 1) query tags)} "←"] "") (faccumulate [res [:span {}] i 1 last-page] (do (table.insert - res [:a {:href (.. "?page=" i "&query=" query) + res [:a {:href (make-url i query tags) :class (if (= page i) "paginator-active" "")} (tostring i)]) res)) (if (< page last-page) - [:a {:href (.. "?page=" (+ page 1) "&query=" query)} ">"] + [:a {:href (make-url (+ page 1) query tags)} "→"] "")] [:div {} "Всего результатов: " [:strong {} (string.format "%d" total)]]] "")) @@ -108,7 +202,7 @@ (fn aside-template [query tags paginator] [:aside {:class "aside"} [:div {:class "aside-content"} - (if (~= query "") + (if (or (~= query "") (< 0 (# tags))) [:a {:href "/" :style "display: block;"} [:img {:class "logo" :src "static/logo.svg" :alt "Логотип meicha.ru" :title "Логотип meicha.ru"}]] @@ -117,11 +211,21 @@ [:form {:class "form"} [:input {:type :search :name :query :value query :autofocus true :placeholder "Поисковый запрос"}] + [:div {:class "form-tags"} + [:select {:class "form-tag" :name "tags"} + [:option {:value ""} "~ Категория ~"] + (table.unpack + (map + (fn [_ tag] + [:option {:value tag + :selected (if (array.contains tags tag) "selected" nil)} + tag]) + (all-tags)))]] [:button {:type :submit} "Искать"]] paginator]]) (fn base-template [query tags page total ...] - (local paginator (paginator-template query page 48 total)) + (local paginator (paginator-template query tags page 48 total)) [:html {:lang "en"} [:head {} [:meta {:charset "UTF-8"}] @@ -135,76 +239,30 @@ [:div {:class "list"} ...] [:footer {} paginator]]]]]]) -(fn random-products [limit] - (assert (< 0 limit) "limit must be > 0") - (luna.db.query* - db - "SELECT site, - title, - description, - image, - url, - price, - weight, - price_per AS \"price-per\", - year - FROM products - ORDER BY RANDOM() - LIMIT ?" - [limit])) - -(fn query-products [page query sorters] - (local query - (table.concat - (map (fn [_ q] - (if (. query-synonyms q) - (.. "(" q "* OR " (. query-synonyms q) "*)") - (.. q "*"))) - (str.split query)) - " ")) - (local total - (luna.db.query - db - "SELECT count(*) - FROM search - WHERE search.`table` = 'products' - AND search.title MATCH ?" - [query])) +(fn get-query-string [query key] + (if (and query + (. query key) + (. query key 1) + (< 0 (# (. query key 1)))) + (. query key 1) + nil)) - {:results - (luna.db.query* - db - "SELECT highlight(search, 0, '<i>', '</i>') AS \"title\", - products.site, - products.description, - products.image, - products.url, - products.price, - products.weight, - products.price_per AS \"price-per\", - products.year, - products.archived, - products.creation_time AS \"creation-time\" - FROM search - INNER JOIN products ON search.fid = products.url - WHERE search.`table` = 'products' - AND search.title MATCH ? - ORDER BY rank - LIMIT 48 OFFSET ?" - [query (* (- page 1) 48)]) - :total (if (< 0 (# total)) - (. total 1 1) - 0)}) +(fn get-query-number [query key] + (if (and query + (. query key) + (. query key 1)) + (tonumber (. query key 1)) + nil)) (fn root-handler [{: path : query}] (if (= path "/") (let [headers {:content-type "text/html"} - page (or (tonumber query.page) 1) - search (str.trim (or query.query "")) - tags (or query.tags []) + page (or (get-query-number query "page") 1) + search (str.trim (or (get-query-string query "query") "")) + tags (filter #(~= "" $2) (or query.tags [])) {: results : total} - (if (~= "" search) - (query-products page search) + (if (or (~= "" search) (< 0 (# tags))) + (query-products page search tags) {:total 48 :results (random-products 48)})] (values 200 headers |
