search.arc 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. ; Search extension for News
  2. ; Copyright (C) 2017 Pelle Hjek
  3. ;
  4. ; This program is free software: you can redistribute it and/or modify
  5. ; it under the terms of the GNU Affero General Public License as published by
  6. ; the Free Software Foundation, either version 3 of the License, or
  7. ; (at your option) any later version.
  8. ;
  9. ; This program is distributed in the hope that it will be useful,
  10. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ; GNU Affero General Public License for more details.
  13. ;
  14. ; You should have received a copy of the GNU Affero General Public License
  15. ; along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. (newsop search (query in by for)
  17. (search-page user query in by for))
  18. (= searches* (table))
  19. (deftem search
  20. query ""
  21. in ""
  22. by ""
  23. for "")
  24. (def search-bar (user (o label) (o query) (o in "all") (o by "popularity") (o for "all time"))
  25. ; TODO: Search cached pages
  26. ; TODO: Only show advanced fields on search page
  27. ; TODO: prefilled searches is only enabled for logged-in users,
  28. ; because if searches are stored as the 'nil' user,
  29. ; then users who are not logged in may accidentally see each others searches!
  30. ; A solution to this might have to involve cookies for anonymous users.
  31. (let lastsearch (aif (and (profile user)
  32. (is label "search")
  33. (searches* user))
  34. it (inst 'search))
  35. (tag (form action "search" class "search")
  36. (pr "Search: ")
  37. (input 'query lastsearch!query 18 )
  38. (when t ;(is label "search") ;advanced
  39. (spanclass "advanced"
  40. (pr " in ")
  41. (menu 'in '("all"
  42. "stories"
  43. "comments") lastsearch!in)
  44. (pr " by ")
  45. (menu 'by '("popularity"
  46. "date") lastsearch!by)
  47. (pr " for ")
  48. (menu 'for '("all time"
  49. "last 24h"
  50. "past week"
  51. "past month"
  52. "past year") lastsearch!for))))))
  53. (def sort-by (by items)
  54. (sort
  55. (case by "popularity" (fn (a b) (errsafe:> (a 'score) (b 'score)))
  56. "date" (fn (a b) (errsafe:> (a 'time) (b 'time))))
  57. items))
  58. (def keep-for (for items)
  59. (keep
  60. (case for "all time" idfn
  61. "last 24h" [< (days-since _!time) 1]
  62. "past week" [< (days-since _!time) 7]
  63. "past month" [< (days-since _!time) 30]
  64. "past year" [< (days-since _!time) 365])
  65. items))
  66. (def items-in (in)
  67. (apply join (case in "all" (list stories* comments*)
  68. "stories" (list stories*)
  69. "comments" (list comments*))))
  70. (def search-page (user query (o in "all") (o by "popularity") (o for "all time"))
  71. (= (searches* user) (inst 'search 'query query 'in in 'by by 'for for))
  72. (withs (results (sort-by by (keep-for for (search (items-in in) tokens.query)))
  73. title (+ "Search results for \"" query "\" (" (len results) " results)")
  74. label "search"
  75. here (search-url query in by for))
  76. (listpage user (msec) results label title here nil)))
  77. (def search-url (query (o in) (o by) (o for))
  78. (+ "search?query=" query
  79. (if in (+ "&in=" in))
  80. (if by (+ "&by=" by))
  81. (if for (+ "&for=" for))))
  82. (def search (stories query)
  83. (keep [match-all? _ query] stories))
  84. (def match-all? (story query)
  85. (all idfn (map [match? story _] query)))
  86. (def match? (story pat)
  87. (some [match-ignoring-case? (string story._) pat] '(title url by text)))
  88. (def match-ignoring-case? (s pat)
  89. (re-match (re:string "(?i:" pat ")") s))