Skip to content

Commit 5905b57

Browse files
authored
Search refinements (#1337)
1 parent 1d9d3be commit 5905b57

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy