diff options
| author | unwox <me@unwox.com> | 2024-10-15 14:48:16 +0600 |
|---|---|---|
| committer | unwox <me@unwox.com> | 2024-10-15 14:49:36 +0600 |
| commit | 1204496efa2fcd495bd74ba8ca249b7f082f3ba5 (patch) | |
| tree | 57f908c7bae9ff1194ccb68328ec59ef5be00872 /bin/serve.fnl | |
| parent | 1520d83acccdcad2d7f87aec073b48d5f4995bf6 (diff) | |
WIP try to fix spelling mistakes in search querie
currently works very slowly and uses a lot of CPU
Diffstat (limited to 'bin/serve.fnl')
| -rw-r--r-- | bin/serve.fnl | 237 |
1 files changed, 155 insertions, 82 deletions
diff --git a/bin/serve.fnl b/bin/serve.fnl index 4d982c2..4cbb905 100644 --- a/bin/serve.fnl +++ b/bin/serve.fnl @@ -9,6 +9,9 @@ (local json (require :vendor.json)) (local array (require :lib.array)) (local str (require :lib.string)) +(local texts (require :texts)) +(local spellfix (require :spellfix)) +(local {: must} (require :lib.utils)) (local ozchai (require :parser.ozchai)) (local ipuer (require :parser.ipuer)) @@ -17,7 +20,7 @@ (when _G.unpack (tset table :unpack _G.unpack)) -(local db (luna.db.open "file:var/db.sqlite?_journal=WAL&_sync=NORMAL")) +(local db (must (luna.db.open "file:var/db.sqlite?_journal=WAL&_sync=NORMAL"))) (local query-synonyms { "шэн" "шен" @@ -36,9 +39,66 @@ (string.gsub """ "\"") (string.gsub "&" "&")))) +(fn get-query-string [query key] + (if (and query + (. query key) + (. query key 1) + (< 0 (# (. query key 1)))) + (. query key 1) + nil)) + +(fn get-query-number [query key] + (if (and query + (. query key) + (. query key 1) + (< 0 (# (. query key 1)))) + (tonumber (. query key 1)) + nil)) + +(fn collect-form [params] + {:query (str.trim (or (get-query-string params "query") "")) + :tags (filter #(~= "" $2) (or params.tags [])) + :min-price (get-query-number params "min-price") + :max-price (get-query-number params "max-price") + :price-per (= "on" (get-query-string params "price-per"))}) + +(fn form-empty? [form] + (and + (= "" form.query) + (= (# form.tags) 0) + (not form.min-price) + (not form.max-price) + ;; price-per is intentionally left out since it must not trigger search + ;; by itself + )) + +(fn form->path [page form] + (.. "?page=" (tostring page) + (if (not (str.empty? form.query)) + (.. "&query=" form.query) + "") + (if (< 0 (# form.tags)) + (.. "&" + (array.join + (array.flatten + (map (fn [_ tag] (.. "tags=" tag)) + form.tags)) + "&")) + "") + (if (and form.min-price (< 0 form.min-price)) + (.. "&min-price=" (tostring form.min-price)) + "") + (if (and form.max-price (< 0 form.max-price)) + (.. "&max-price=" (tostring form.max-price)) + "") + (if form.price-per + "&price-per=on" + ""))) + (fn random-products [limit] (assert (< 0 limit) "limit must be > 0") - (luna.db.query* + (must + (luna.db.query* db "SELECT site, title, @@ -52,17 +112,18 @@ FROM products ORDER BY RANDOM() LIMIT ?" - [limit])) + [limit]))) (fn all-tags [] (map (fn [_ v] (. v 1)) - (luna.db.query + (must + (luna.db.query db "SELECT title FROM tags ORDER BY creation_time" - []))) + [])))) -(fn query-products [page query tags] +(fn query-products [{: query : tags : min-price : max-price : price-per} page] (local tags (or tags [])) (var where-conds []) @@ -81,42 +142,59 @@ (.. q "*"))) (str.split query)) " AND "))) - (local where-sql (array.join where-conds "\nAND ")) + (when (and min-price (< 0 min-price)) + (if price-per + (table.insert where-conds "products.price_per >= ?") + (table.insert where-conds "products.price >= ?")) + (table.insert where-vars min-price)) + (when (and max-price (< 0 max-price)) + (if price-per + (table.insert where-conds "products.price_per <= ?") + (table.insert where-conds "products.price <= ?")) + (table.insert where-vars max-price)) + + (local where-sql + (if (< 0 (# where-conds)) + (.. "AND " (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)) + (must + (luna.db.query + db + (string.format + "SELECT count(*) + FROM search + INNER JOIN products ON search.fid = products.url + LEFT JOIN product_tags ON product_tags.product = search.fid + WHERE search.`table` = 'products' + %s" where-sql) + where-vars))) {:results + (must (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)])) + 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' + %s + ORDER BY rank + LIMIT 48 OFFSET ?" where-sql) + (array.concat where-vars [(* (- page 1) 48)]))) :total (if (< 0 (# total)) (. total 1 1) 0)}) @@ -170,48 +248,39 @@ ;; FIXME: security issue [:small {} [:NO-ESCAPE (unescape (str.truncate product.description 200))]]]) -(fn paginator-template [query tags page limit total] +(fn paginator-template [form 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 (make-url (- page 1) query tags)} "←"] + [:a {:href (form->path (- page 1) form)} "←"] "") (faccumulate [res [:span {}] i 1 last-page] (do (table.insert - res [:a {:href (make-url i query tags) + res [:a {:href (form->path i form) :class (if (= page i) "paginator-active" "")} (tostring i)]) res)) (if (< page last-page) - [:a {:href (make-url (+ page 1) query tags)} "→"] + [:a {:href (form->path (+ page 1) form)} "→"] "")] [:div {} "Всего результатов: " [:strong {} (string.format "%d" total)]]] "")) -(fn aside-template [query tags paginator] +(fn aside-template [form paginator] [:aside {:class "aside"} [:div {:class "aside-content"} - (if (or (~= query "") (< 0 (# tags))) + (if (not (form-empty? form)) [:a {:href "/" :style "display: block;"} [:img {:class "logo" :src "static/logo.svg" :alt "Логотип meicha.ru" :title "Логотип meicha.ru"}]] [:img {:class "logo" :src "static/logo.svg" :alt "Логотип meicha.ru" :title "Логотип meicha.ru"}]) [:form {:class "form"} - [:input {:type :search :name :query :value query + [:input {:type :search :name "query" :value form.query :autofocus true :placeholder "Поисковый запрос"}] [:div {:class "form-tags"} [:select {:class "form-tag" :name "tags"} @@ -220,14 +289,28 @@ (map (fn [_ tag] [:option {:value tag - :selected (if (array.contains tags tag) "selected" nil)} + :selected (if (array.contains form.tags tag) + "selected" nil)} tag]) (all-tags)))]] + [:div {:class "form-price"} + [:input {:type :number :name :min-price :min "1" + :placeholder "От ₽" :value (tostring form.min-price)}] + [:input {:type :number :name :max-price :min "1" + :placeholder "До ₽" :value (tostring form.max-price)}]] + [:div {:class "form-price-per"} + [:input {:type :checkbox :id "price-per" :name "price-per" + :checked (if form.price-per "checked" nil)}] + [:label {:for "price-per"} "цена за грамм"]] [:button {:type :submit} "Искать"]] paginator]]) -(fn base-template [query tags page total ...] - (local paginator (paginator-template query tags page 48 total)) +(fn base-template [form page total items] + (local paginator (paginator-template form page 48 total)) + (local spellfix-suggestion + (if (and (not (str.empty? form.query)) items (< 0 (# items))) + nil + (spellfix.guess form.query))) [:html {:lang "en"} [:head {} [:meta {:charset "UTF-8"}] @@ -236,44 +319,34 @@ [:body {} [:div {:class "container"} [:div {:class "content"} - (aside-template query tags paginator) + (aside-template form paginator) [:section {} - [:div {:class "list"} ...] + (if (< 0 (# items)) + [:div {:class "list"} + (table.unpack (map #(item-template $2) items))] + (if spellfix-suggestion + [:NO-ESCAPE + (string.format texts.no-results-with-suggestion + spellfix-suggestion + spellfix-suggestion)] + texts.no-results)) [:footer {} paginator]]]]]]) -(fn get-query-string [query key] - (if (and query - (. query key) - (. query key 1) - (< 0 (# (. query key 1)))) - (. query key 1) - nil)) - -(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 (get-query-number query "page") 1) - search (str.trim (or (get-query-string query "query") "")) - tags (filter #(~= "" $2) (or query.tags [])) + form (collect-form query) {: results : total} - (if (or (~= "" search) (< 0 (# tags))) - (query-products page search tags) + (if (not (form-empty? form)) + (query-products form page) {:total 48 :results (random-products 48)})] (values 200 headers (html.render - (base-template - search tags page total - (table.unpack (map #(item-template $2) results))) + (base-template form page total results) true))) (values 404 {} "not found"))) -(luna.router.route "GET /" root-handler) -(luna.router.static "GET /static/" "static/") +(must (luna.router.route "GET /" root-handler)) +(must (luna.router.static "GET /static/" "static/")) |
