diff --git a/pgml-dashboard/src/api/cms.rs b/pgml-dashboard/src/api/cms.rs index ee1060d02..56e46757a 100644 --- a/pgml-dashboard/src/api/cms.rs +++ b/pgml-dashboard/src/api/cms.rs @@ -16,7 +16,7 @@ use crate::{ guards::Cluster, responses::{Response, ResponseOk, Template}, templates::docs::*, - utils::config, + utils::{config, markdown::SearchResult}, }; use serde::{Deserialize, Serialize}; use std::fmt; @@ -561,6 +561,40 @@ impl Collection { #[get("/search?", rank = 20)] async fn search(query: &str, site_search: &State) -> ResponseOk { let results = site_search.search(query, None).await.expect("Error performing search"); + + let results: Vec = results + .into_iter() + .map(|document| { + let snippet = if let Some(description) = document.description { + description + } else { + let author = document.author.unwrap_or_else(|| String::from("xzxzxz")); + // The heuristics used here are ok, not the best it will be better when we can just use the description field + document + .contents + .lines() + .find(|l| !l.is_empty() && !l.contains(&document.title) && !l.contains(&author) && l.len() > 30) + .unwrap_or("") + .split(' ') + .take(20) + .collect::>() + .join(" ") + + " ..." + }; + let path = document + .path + .to_str() + .unwrap_or_default() + .replace(".md", "") + .replace(&config::static_dir().display().to_string(), ""); + SearchResult { + title: document.title, + path, + snippet, + } + }) + .collect(); + ResponseOk( Template(Search { query: query.to_string(), diff --git a/pgml-dashboard/src/utils/markdown.rs b/pgml-dashboard/src/utils/markdown.rs index 2d4deea6a..5a674e836 100644 --- a/pgml-dashboard/src/utils/markdown.rs +++ b/pgml-dashboard/src/utils/markdown.rs @@ -1,6 +1,7 @@ use crate::api::cms::{DocType, Document}; use crate::{templates::docs::TocLink, utils::config}; use anyhow::Context; +use comrak::{format_html_with_plugins, parse_document, ComrakPlugins}; use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; @@ -21,6 +22,9 @@ use std::fmt; use std::sync::Mutex; use url::Url; +// Excluded paths in the pgml-cms directory +const EXCLUDED_DOCUMENT_PATHS: [&str; 1] = ["blog/README.md"]; + pub struct MarkdownHeadings { header_map: Arc>>, } @@ -1291,7 +1295,7 @@ impl SiteSearch { .collect() } - pub async fn search(&self, query: &str, doc_type: Option) -> anyhow::Result> { + pub async fn search(&self, query: &str, doc_type: Option) -> anyhow::Result> { let mut search = serde_json::json!({ "query": { // "full_text_search": { @@ -1323,10 +1327,8 @@ impl SiteSearch { "limit": 10 }); if let Some(doc_type) = doc_type { - search["query"]["filter"] = serde_json::json!({ - "doc_type": { - "$eq": doc_type - } + search["query"]["filter"]["doc_type"] = serde_json::json!({ + "$eq": doc_type }); } let results = self.collection.search_local(search.into(), &self.pipeline).await?; @@ -1334,18 +1336,10 @@ impl SiteSearch { results["results"] .as_array() .context("Error getting results from search")? - .into_iter() + .iter() .map(|r| { - let SearchResultWithoutSnippet { title, contents, path } = - serde_json::from_value(r["document"].clone())?; - let path = path - .replace(".md", "") - .replace(&config::static_dir().display().to_string(), ""); - Ok(SearchResult { - title, - path, - snippet: contents.split(' ').take(20).collect::>().join(" ") + " ...", - }) + let document: Document = serde_json::from_value(r["document"].clone())?; + Ok(document) }) .collect() } @@ -1358,6 +1352,24 @@ impl SiteSearch { .map(|path| async move { Document::from_path(&path).await }), ) .await?; + // Filter out documents who only have 1 line (this is usually just an empty document with the title as the first line) + // and documents that are in our excluded paths list + let documents: Vec = documents + .into_iter() + .filter(|f| { + !EXCLUDED_DOCUMENT_PATHS + .iter() + .any(|p| f.path == config::cms_dir().join(p)) + && !f + .contents + .lines() + .skip(1) + .collect::>() + .join("") + .trim() + .is_empty() + }) + .collect(); let documents: Vec = documents .into_iter() .map(|d| { diff --git a/pgml-dashboard/static/js/search.js b/pgml-dashboard/static/js/search.js index b08237435..02bd989b9 100644 --- a/pgml-dashboard/static/js/search.js +++ b/pgml-dashboard/static/js/search.js @@ -15,11 +15,16 @@ export default class extends Controller { this.target.addEventListener('shown.bs.modal', this.focusSearchInput) this.target.addEventListener('hidden.bs.modal', this.updateSearch) this.searchInput.addEventListener('input', (e) => this.search(e)) + + this.timer; } search(e) { + clearTimeout(this.timer); const query = e.currentTarget.value - this.searchFrame.src = `/search?query=${query}` + this.timer = setTimeout(() => { + this.searchFrame.src = `/search?query=${query}` + }, 250); } focusSearchInput = (e) => {