Skip to content

Search refinements #1337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion pgml-dashboard/src/api/cms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -561,6 +561,40 @@ impl Collection {
#[get("/search?<query>", rank = 20)]
async fn search(query: &str, site_search: &State<crate::utils::markdown::SiteSearch>) -> ResponseOk {
let results = site_search.search(query, None).await.expect("Error performing search");

let results: Vec<SearchResult> = 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::<Vec<&str>>()
.join(" ")
+ "&nbsp;..."
};
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(),
Expand Down
44 changes: 28 additions & 16 deletions pgml-dashboard/src/utils/markdown.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Mutex<HashMap<String, usize>>>,
}
Expand Down Expand Up @@ -1291,7 +1295,7 @@ impl SiteSearch {
.collect()
}

pub async fn search(&self, query: &str, doc_type: Option<DocType>) -> anyhow::Result<Vec<SearchResult>> {
pub async fn search(&self, query: &str, doc_type: Option<DocType>) -> anyhow::Result<Vec<Document>> {
let mut search = serde_json::json!({
"query": {
// "full_text_search": {
Expand Down Expand Up @@ -1323,29 +1327,19 @@ 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?;

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::<Vec<&str>>().join(" ") + "&nbsp;...",
})
let document: Document = serde_json::from_value(r["document"].clone())?;
Ok(document)
})
.collect()
}
Expand All @@ -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<Document> = documents
.into_iter()
.filter(|f| {
!EXCLUDED_DOCUMENT_PATHS
.iter()
.any(|p| f.path == config::cms_dir().join(p))
&& !f
.contents
.lines()
.skip(1)
.collect::<Vec<&str>>()
.join("")
.trim()
.is_empty()
})
.collect();
let documents: Vec<pgml::types::Json> = documents
.into_iter()
.map(|d| {
Expand Down
7 changes: 6 additions & 1 deletion pgml-dashboard/static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
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