feat: add fulltext search with ?q= parameter
Filter posts by case-insensitive match in title and body. Extracted filter_by_lang and search_posts helpers to keep complexity in check.
This commit is contained in:
@@ -30,6 +30,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- CSRF protection on `POST /admin/preview`.
|
- CSRF protection on `POST /admin/preview`.
|
||||||
- Symlink-safe containment in `Store#media_file` via `File.realpath`.
|
- Symlink-safe containment in `Store#media_file` via `File.realpath`.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Scheduled publishing:** a `publish_at` frontmatter field hides posts from the
|
||||||
|
public API and feeds until their publication date arrives. The admin form
|
||||||
|
includes a Publish at date picker.
|
||||||
|
- **JSON Feed:** `GET /api/volumen/feed.json` serves a jsonfeed.org v1.1 feed
|
||||||
|
with `content_html` and `date_published` per item.
|
||||||
|
- **Fulltext search:** `GET /api/volumen/posts?q=…` filters posts by
|
||||||
|
case-insensitive match in title and body.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Split `lib/volumen/server.rb` into `api_routes.rb` and `admin_routes.rb` modules,
|
- Split `lib/volumen/server.rb` into `api_routes.rb` and `admin_routes.rb` modules,
|
||||||
|
|||||||
+19
-4
@@ -102,14 +102,29 @@ module Volumen
|
|||||||
|
|
||||||
def filtered_posts
|
def filtered_posts
|
||||||
posts = published_posts
|
posts = published_posts
|
||||||
if params["lang"]
|
posts = filter_by_lang(posts)
|
||||||
lang = params["lang"]
|
|
||||||
posts = posts.select { |post| post.lang == lang || post.all_langs? }
|
|
||||||
end
|
|
||||||
posts = posts.select { |post| post.tags.include?(params["tag"]) } if params["tag"]
|
posts = posts.select { |post| post.tags.include?(params["tag"]) } if params["tag"]
|
||||||
|
posts = search_posts(posts) if params["q"]
|
||||||
posts
|
posts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_by_lang(posts)
|
||||||
|
return posts unless params["lang"]
|
||||||
|
|
||||||
|
lang = params["lang"]
|
||||||
|
posts.select { |post| post.lang == lang || post.all_langs? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_posts(posts)
|
||||||
|
query = params["q"].to_s.strip.downcase
|
||||||
|
return posts if query.empty?
|
||||||
|
|
||||||
|
posts.select do |post|
|
||||||
|
post.title.to_s.downcase.include?(query) ||
|
||||||
|
post.body.downcase.include?(query)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def tag_counts
|
def tag_counts
|
||||||
counts = Hash.new(0)
|
counts = Hash.new(0)
|
||||||
published_posts.each { |post| post.tags.each { |tag| counts[tag] += 1 } }
|
published_posts.each { |post| post.tags.each { |tag| counts[tag] += 1 } }
|
||||||
|
|||||||
@@ -153,4 +153,29 @@ class ServerTest < Minitest::Test
|
|||||||
assert_includes data["items"].first["content_html"], "<strong>world</strong>"
|
assert_includes data["items"].first["content_html"], "<strong>world</strong>"
|
||||||
refute_includes data["items"].map { |i| i["title"] }, "Draft"
|
refute_includes data["items"].map { |i| i["title"] }, "Draft"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_search_by_title
|
||||||
|
get "/api/volumen/posts?q=Hello"
|
||||||
|
data = JSON.parse(last_response.body)
|
||||||
|
assert_equal 1, data["total"]
|
||||||
|
assert_equal "hello", data["posts"].first["slug"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_search_by_body
|
||||||
|
get "/api/volumen/posts?q=world"
|
||||||
|
data = JSON.parse(last_response.body)
|
||||||
|
assert_equal 1, data["total"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_search_no_match
|
||||||
|
get "/api/volumen/posts?q=nonexistent"
|
||||||
|
data = JSON.parse(last_response.body)
|
||||||
|
assert_equal 0, data["total"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_search_is_case_insensitive
|
||||||
|
get "/api/volumen/posts?q=HELLO"
|
||||||
|
data = JSON.parse(last_response.body)
|
||||||
|
assert_equal 1, data["total"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user