Skip to content

Commit 79ebbfd

Browse files
committed
Search refinements (#1337)
1 parent dad08f5 commit 79ebbfd

File tree

3 files changed

+69
-18
lines changed

3 files changed

+69
-18
lines changed

pgml-dashboard/src/api/cms.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
guards::Cluster,
1717
responses::{Response, ResponseOk, Template},
1818
templates::docs::*,
19-
utils::config,
19+
utils::{config, markdown::SearchResult},
2020
};
2121
use serde::{Deserialize, Serialize};
2222
use std::fmt;
@@ -626,6 +626,40 @@ impl Collection {
626626
#[get("/search?<query>", rank = 20)]
627627
async fn search(query: &str, site_search: &State<crate::utils::markdown::SiteSearch>) -> ResponseOk {
628628
let results = site_search.search(query, None).await.expect("Error performing search");
629+
630+
let results: Vec<SearchResult> = results
631+
.into_iter()
632+
.map(|document| {
633+
let snippet = if let Some(description) = document.description {
634+
description
635+
} else {
636+
let author = document.author.unwrap_or_else(|| String::from("xzxzxz"));
637+
// The heuristics used here are ok, not the best it will be better when we can just use the description field
638+
document
639+
.contents
640+
.lines()
641+
.find(|l| !l.is_empty() && !l.contains(&document.title) && !l.contains(&author) && l.len() > 30)
642+
.unwrap_or("")
643+
.split(' ')
644+
.take(20)
645+
.collect::<Vec<&str>>()
646+
.join(" ")
647+
+ "&nbsp;..."
648+
};
649+
let path = document
650+
.path
651+
.to_str()
652+
.unwrap_or_default()
653+
.replace(".md", "")
654+
.replace(&config::static_dir().display().to_string(), "");
655+
SearchResult {
656+
title: document.title,
657+
path,
658+
snippet,
659+
}
660+
})
661+
.collect();
662+
629663
ResponseOk(
630664
Template(Search {
631665
query: query.to_string(),

pgml-dashboard/src/utils/markdown.rs

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::api::cms::{DocType, Document};
22
use crate::{templates::docs::TocLink, utils::config};
33
use anyhow::Context;
4+
use comrak::{format_html_with_plugins, parse_document, ComrakPlugins};
45
use std::cell::RefCell;
56
use std::collections::HashMap;
67
use std::path::PathBuf;
@@ -21,6 +22,9 @@ use std::fmt;
2122
use std::sync::Mutex;
2223
use url::Url;
2324

25+
// Excluded paths in the pgml-cms directory
26+
const EXCLUDED_DOCUMENT_PATHS: [&str; 1] = ["blog/README.md"];
27+
2428
pub struct MarkdownHeadings {
2529
header_map: Arc<Mutex<HashMap<String, usize>>>,
2630
}
@@ -1291,7 +1295,7 @@ impl SiteSearch {
12911295
.collect()
12921296
}
12931297

1294-
pub async fn search(&self, query: &str, doc_type: Option<DocType>) -> anyhow::Result<Vec<SearchResult>> {
1298+
pub async fn search(&self, query: &str, doc_type: Option<DocType>) -> anyhow::Result<Vec<Document>> {
12951299
let mut search = serde_json::json!({
12961300
"query": {
12971301
// "full_text_search": {
@@ -1323,29 +1327,19 @@ impl SiteSearch {
13231327
"limit": 10
13241328
});
13251329
if let Some(doc_type) = doc_type {
1326-
search["query"]["filter"] = serde_json::json!({
1327-
"doc_type": {
1328-
"$eq": doc_type
1329-
}
1330+
search["query"]["filter"]["doc_type"] = serde_json::json!({
1331+
"$eq": doc_type
13301332
});
13311333
}
13321334
let results = self.collection.search_local(search.into(), &self.pipeline).await?;
13331335

13341336
results["results"]
13351337
.as_array()
13361338
.context("Error getting results from search")?
1337-
.into_iter()
1339+
.iter()
13381340
.map(|r| {
1339-
let SearchResultWithoutSnippet { title, contents, path } =
1340-
serde_json::from_value(r["document"].clone())?;
1341-
let path = path
1342-
.replace(".md", "")
1343-
.replace(&config::static_dir().display().to_string(), "");
1344-
Ok(SearchResult {
1345-
title,
1346-
path,
1347-
snippet: contents.split(' ').take(20).collect::<Vec<&str>>().join(" ") + "&nbsp;...",
1348-
})
1341+
let document: Document = serde_json::from_value(r["document"].clone())?;
1342+
Ok(document)
13491343
})
13501344
.collect()
13511345
}
@@ -1358,6 +1352,24 @@ impl SiteSearch {
13581352
.map(|path| async move { Document::from_path(&path).await }),
13591353
)
13601354
.await?;
1355+
// Filter out documents who only have 1 line (this is usually just an empty document with the title as the first line)
1356+
// and documents that are in our excluded paths list
1357+
let documents: Vec<Document> = documents
1358+
.into_iter()
1359+
.filter(|f| {
1360+
!EXCLUDED_DOCUMENT_PATHS
1361+
.iter()
1362+
.any(|p| f.path == config::cms_dir().join(p))
1363+
&& !f
1364+
.contents
1365+
.lines()
1366+
.skip(1)
1367+
.collect::<Vec<&str>>()
1368+
.join("")
1369+
.trim()
1370+
.is_empty()
1371+
})
1372+
.collect();
13611373
let documents: Vec<pgml::types::Json> = documents
13621374
.into_iter()
13631375
.map(|d| {

pgml-dashboard/static/js/search.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@ export default class extends Controller {
1515
this.target.addEventListener('shown.bs.modal', this.focusSearchInput)
1616
this.target.addEventListener('hidden.bs.modal', this.updateSearch)
1717
this.searchInput.addEventListener('input', (e) => this.search(e))
18+
19+
this.timer;
1820
}
1921

2022
search(e) {
23+
clearTimeout(this.timer);
2124
const query = e.currentTarget.value
22-
this.searchFrame.src = `/search?query=${query}`
25+
this.timer = setTimeout(() => {
26+
this.searchFrame.src = `/search?query=${query}`
27+
}, 250);
2328
}
2429

2530
focusSearchInput = (e) => {

0 commit comments

Comments
 (0)