From: Adam Dullage Date: Mon, 5 Sep 2022 12:43:12 +0000 (+0100) Subject: Show tag matches in search results + hide search highlights X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=37091b74368e2a612b2a25ac3803ceb312d2f990;p=flatnotes.git Show tag matches in search results + hide search highlights --- diff --git a/flatnotes/flatnotes.py b/flatnotes/flatnotes.py index 609f415..70fc489 100644 --- a/flatnotes/flatnotes.py +++ b/flatnotes/flatnotes.py @@ -12,6 +12,7 @@ from whoosh.analysis import CharsetFilter, KeywordAnalyzer, StemmingAnalyzer from whoosh.fields import ID, STORED, TEXT, SchemaClass from whoosh.index import Index from whoosh.qparser import MultifieldParser +from whoosh.query import Every from whoosh.searching import Hit from whoosh.support.charset import accent_map @@ -67,6 +68,7 @@ class Note: @title.setter def title(self, new_title): + new_title = new_title.strip() if not self._is_valid_title(new_title): raise InvalidTitleError new_filepath = os.path.join( @@ -101,10 +103,26 @@ class Note: class SearchResult(Note): def __init__(self, flatnotes: "Flatnotes", hit: Hit) -> None: super().__init__(flatnotes, strip_ext(hit["filename"])) - self._title_highlights = hit.highlights("title", text=self.title) - self._content_highlights = hit.highlights( - "content", - text=self.content, + + self._matched_fields = self._get_matched_fields(hit.matched_terms()) + + self._title_highlights = ( + hit.highlights("title", text=self.title) + if "title" in self._matched_fields + else None + ) + self._content_highlights = ( + hit.highlights( + "content", + text=self.content, + ) + if "content" in self._matched_fields + else None + ) + self._tag_matches = ( + [field[1] for field in hit.matched_terms() if field[0] == "tags"] + if "tags" in self._matched_fields + else None ) @property @@ -115,6 +133,16 @@ class SearchResult(Note): def content_highlights(self): return self._content_highlights + @property + def tag_matches(self): + return self._tag_matches + + @staticmethod + def _get_matched_fields(matched_terms): + """Return a set of matched fields from a set of ('field', 'term') " + "tuples generated by whoosh.searching.Hit.matched_terms().""" + return set([matched_term[0] for matched_term in matched_terms]) + class Flatnotes(object): TAG_SECTION_RE = re.compile(r"(?:\s+#\w+)+$") @@ -250,8 +278,11 @@ class Flatnotes(object): """Search the index for the given term.""" self.update_index_debounced() with self.index.searcher() as searcher: - query = MultifieldParser( - ["title", "content", "tags"], self.index.schema - ).parse(term) - results = searcher.search(query, limit=None) + if term == "*": + query = Every() + else: + query = MultifieldParser( + ["title", "content", "tags"], self.index.schema + ).parse(term) + results = searcher.search(query, limit=None, terms=True) return tuple(SearchResult(self, hit) for hit in results) diff --git a/flatnotes/models.py b/flatnotes/models.py index ccb2b2d..9798a52 100644 --- a/flatnotes/models.py +++ b/flatnotes/models.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Dict, List, Optional from helpers import CamelCaseBaseModel @@ -34,6 +34,7 @@ class SearchResultModel(CamelCaseBaseModel): last_modified: int title_highlights: Optional[str] content_highlights: Optional[str] + tag_matches: Optional[List[str]] @classmethod def dump(self, search_result: SearchResult) -> Dict: @@ -42,4 +43,5 @@ class SearchResultModel(CamelCaseBaseModel): "lastModified": search_result.last_modified, "titleHighlights": search_result.title_highlights, "contentHighlights": search_result.content_highlights, + "tagMatches": search_result.tag_matches, } diff --git a/flatnotes/src/classes.js b/flatnotes/src/classes.js index 14ce77c..ecff15a 100644 --- a/flatnotes/src/classes.js +++ b/flatnotes/src/classes.js @@ -21,10 +21,11 @@ class Note { } class SearchResult extends Note { - constructor(title, lastModified, titleHighlights, contentHighlights) { + constructor(title, lastModified, titleHighlights, contentHighlights, tagMatches) { super(title, lastModified); this.titleHighlights = titleHighlights; this.contentHighlights = contentHighlights; + this.tagMatches = tagMatches; } get titleHighlightsOrTitle() { diff --git a/flatnotes/src/components/SearchResults.vue b/flatnotes/src/components/SearchResults.vue index 3c1d13c..016ae8c 100644 --- a/flatnotes/src/components/SearchResults.vue +++ b/flatnotes/src/components/SearchResults.vue @@ -14,17 +14,34 @@
+
@@ -38,6 +55,10 @@ margin: 0; } +.result-title { + color: $text; +} + .result-contents { color: $muted-text; } @@ -50,9 +71,20 @@ font-weight: bold; color: $logo-key-colour; } + +.tag { + color: white; + font-size: 14px; + background-color: $logo-key-colour; + padding: 2px 6px; + border-radius: 4px; +} diff --git a/flatnotes/src/constants.js b/flatnotes/src/constants.js index 4733166..858eafc 100644 --- a/flatnotes/src/constants.js +++ b/flatnotes/src/constants.js @@ -9,7 +9,11 @@ export const basePaths = { }; // Params -export const params = { searchTerm: "term", redirect: "redirect" }; +export const params = { + searchTerm: "term", + redirect: "redirect", + showHighlights: "showHighlights", +}; // Other export const alphabet = [ diff --git a/flatnotes/src/helpers.js b/flatnotes/src/helpers.js index 8e9bb7d..cc0763a 100644 --- a/flatnotes/src/helpers.js +++ b/flatnotes/src/helpers.js @@ -2,3 +2,11 @@ export function getSearchParam(paramName) { let urlSearchParams = new URLSearchParams(window.location.search); return urlSearchParams.get(paramName); } + +export function setSearchParam(paramName, value) { + let url = new URL(window.location.href); + let urlSearchParams = new URLSearchParams(url.search); + urlSearchParams.set(paramName, value); + url.search = urlSearchParams.toString(); + window.history.replaceState({}, "", url.toString()); +}