返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
StarBlog番外

Rewrite StarBlog's search functionality and pages, supporting weight settings and result highlighting

Rewriting search? Just counting weights for memory plus Regex? Too amateur. Title weights 100 times the body text? This kind of crude solution is surprisingly effective. The page has also been rewritten, but the Bootstrap style is quite primitive. Are you still expecting to use Lucene.NET for v2?

曦远 (DealiAxy)约 15 分钟
目录9 个章节

Preface

Recently, I was organizing some local notes.

For some entries where the dates were not quite correct, my blog had recorded the publication and update times, so I went to search for them.

That's when I discovered that StarBlog's search functionality was too simplistic.

Although the last update added a wave of features and also optimized the search function—previously it could only search titles, now it can search body content. See details: StarBlog v1.3.0 New Version: A Wave of Updates and Server Migration

However, there's an issue: there's no weighting. The weight of the title should be higher than that of the body content.

Ideally, these should be implemented using a full-text search engine like Elasticsearch, MeiliSearch, or similar. But these require additional services, which are too heavyweight.

At the very least, one should use something like Lucene.NET, which is the foundation of Elasticsearch. But it doesn't require an additional service, is purely local embedded, and supports features like weighting, highlighting, and word segmentation.

However, for the sake of quick implementation, I didn't want to use any of these. I decided to start with the simplest method for improvement.

At the same time, I also rewrote the search results page, as the previous one looked too amateurish.

Minimalist Implementation

My final solution is: manually calculating weights in memory + using Regex for result highlighting.

Very low cost and decent results

Implementation Effect

Let's see the effect

The frontend of this StarBlog uses Bootstrap, and all styling relies on CSS. Compared to the Tailwind CSS, Shadcn/ui, Magic UI, and similar modern tools I use now, it feels quite primitive. I've done my best to redesign this interface hhh😄

image-20250902212504494

Code

OK, now comes the code part that everyone is not interested in.

Model

First, define the search result model.

public class SearchPost {
    public Post Post { get; set; }
    public int TitleScore { get; set; }

    public int ContentScore { get; set; }

    // Title: +100 points for each match
    // Content: +1 point for a match
    public int Score => TitleScore * 100 + ContentScore;
    public string HighlightedTitle { get; set; }
    public string HighlightedSnippet { get; set; }
}

Search Logic

The search function logic is all contained in the src/StarBlog.Web/Controllers/SearchController.cs file.

Weight is calculated in memory. After retrieving data from the database, weight is computed using Linq; each keyword occurrence scores one point. The total score is calculated within SearchPost: each match in the title adds +100 points, and each match in the content adds +1 point.

var searchPosts = _postRepo
    .Where(a => a.IsPublish)
    .Where(a =>
           a.Title!.Contains(keyword) ||
           a.Content.Contains(keyword)
          )
    .Include(a => a.Category)
    .ToList()
    .Select(p => new SearchPost {
        Post = p,
        TitleScore = p.Title.ToLower().Split(keyword).Length - 1,
        ContentScore = p.Content?.ToLower().Split(keyword).Length - 1 ?? 0,
    })
    .OrderByDescending(x => x.Score)
    .ToList();

Search Result Highlighting

Use regular expressions to implement result highlighting.

var regex = new Regex(Regex.Escape(keyword), RegexOptions.IgnoreCase);
foreach (var item in searchPosts) {
    item.HighlightedTitle = regex.Replace(item.Post.Title, m => $"<mark>{m.Value}</mark>");
    item.HighlightedSnippet = GetHighlightedSnippet(item.Post.Content, keyword);
}

Generate Highlighted Snippet Summary

The approach is straightforward:

  • Locate the first matching position
  • Extract a segment of content of a certain length before and after (e.g., 50 characters on each side)
  • Use Regex replacement to add <mark> highlighting
  • Finally, attach ... as an ellipsis
public static string GetHighlightedSnippet(string content, string keyword, int snippetLength = 100) {
    if (string.IsNullOrEmpty(content) || string.IsNullOrEmpty(keyword))
        return string.Empty;

    var regex = new Regex(Regex.Escape(keyword), RegexOptions.IgnoreCase);
    var match = regex.Match(content);

    if (!match.Success) {
        // No match found, simply take the first snippetLength*2 characters as the summary
        return content.Length > snippetLength * 2
            ? content.Substring(0, snippetLength * 2) + "..."
            : content;
    }

    // Calculate the extraction range (snippetLength before and after the match position)
    int start = Math.Max(0, match.Index - snippetLength);
    int length = Math.Min(content.Length - start, match.Length + snippetLength * 2);

    string snippet = content.Substring(start, length);

    // Highlighting
    snippet = regex.Replace(snippet, m => $"<mark>{m.Value}</mark>");

    // Add ellipsis before and after (if not at the beginning or end of the full content)
    if (start > 0) snippet = "..." + snippet;
    if (start + length < content.Length) snippet += "...";

    return snippet;
}

Summary

After the refactoring, the experience has been elevated to a higher level.

However, patching up an old architecture ultimately isn't a sustainable long-term solution.

Once I have time, I should promptly begin developing the new v2 version 💪

PS: Moving forward, I might expand on this search functionality by adding support for multiple keyword searches, and it's entirely possible to further integrate it with Lucene.NET.

_EOF_

Rewrite StarBlog's search functionality and pages, supporting weight settings and result highlighting

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…

Rewrite StarBlog's search functionality and pages, supporting weight settings and result highlighting · StarBlog