天天看點

實作Hugo中的搜尋功能安裝需要的元件生成索引檔案編寫搜尋邏輯最終效果

基于Grunt實作Hugo中的搜尋功能,先生成索引檔案,再根據索引檔案檢索關鍵詞。

安裝需要的元件

安裝Grunt

npm install -g grunt-cli
           

安裝需要的包工具

npm install --save-dev toml string html-entities marked grunt
           

生成索引檔案

  1. 建立Gruntfile檔案,并修改其中相關參數。
  • CONTENT_PATH_PREFIX:要搜尋的内容路徑
  • FILE_SUFFIX:目标檔案的檔案字尾名
  • FILE_SPLIT:文章頭部分隔符
  • INDEX_JSON_FILE_PATH:最終生成的索引檔案的路徑

Gruntfile.js内容如下:

var toml = require("toml");
var S = require("string");
var Entities = require('html-entities').AllHtmlEntities;
var Html = new Entities();
var marked = require("marked");

var CONTENT_PATH_PREFIX = "./content/post";
var FILE_SUFFIX = ".md";
var FILE_SPLIT = "+++";
var INDEX_JSON_FILE_PATH = "./static/lunr.json";
var documentsStore = {};

module.exports = function(grunt) {

    grunt.registerTask("index", function() {
		
		 var indexPages = function() {
			//周遊目标路徑下所有檔案
            grunt.file.recurse(CONTENT_PATH_PREFIX, function(abspath, rootdir, subdir, filename) {
                grunt.verbose.writeln("Parse file:",abspath);
                processFile(abspath, filename);
            });

            return documentsStore;
        };
		
		//處理不同類型的檔案
		var processFile = function(abspath, filename) {
            if (S(filename).endsWith(FILE_SUFFIX)){
                processMDFile(abspath, filename);
            }
        };
        //處理markdown檔案
		var processMDFile = function(abspath, filename) {
			var body = grunt.file.read(abspath);

			if(!body){
				return null;
			}
			body = body.split(FILE_SPLIT);
			var frontMatter;
			try {
				frontMatter = toml.parse(body[1].trim());
			} catch (e) {
				console.log(e.message);
				return null;
			}
			
			// 生成對應檔案連接配接,用于搜尋結果跳轉
			var href = S(abspath).chompLeft("content").chompRight(".md").s;
			if (filename === "index.md") {
				href = S(abspath).chompLeft(CONTENT_PATH_PREFIX).chompRight(filename).s;
			}

			// 移除非内容的元素
			var content = body[2].trim();
			content = marked(content);
			content = content.replace(/\<script.*\/script\>/g,"");
			content = Html.decode(content);
			content = content.replace(/(<([^>]+)>)/ig, '');
			content = content.replace(/[\n,\r ]+/g, ' ');
			
			// 可以根據目标檔案的格式定義不同的結構體
			var doc = {
				url: href.slice(1),
                title: frontMatter.title,
                date: frontMatter.date,
                tags: frontMatter.tags,
				body: content
			};

			documentsStore[href] = doc;
		};
		
		// 生成的索引檔案
		grunt.file.write(INDEX_JSON_FILE_PATH, JSON.stringify(indexPages()));
        grunt.log.ok("Index built");
    });
};
           
  1. 執行
grunt index
           

編寫搜尋邏輯

搜尋邏輯如下:

var $results, INDEX_DATA={};
    // 加載索引檔案
    function initLunr() {
        // First retrieve the index file
        $.getJSON('/lunr.json')
            .done(function(data) {
                INDEX_DATA = data;
            })
            .fail(function(jqxhr, textStatus, error) {
                var err = textStatus + ", " + error;
                console.error("Error getting Hugo index flie:", err);
            });
    }
    // 開始搜素
    function initUI() {
        $results = $(".content");
        $('#search').on('click',function(){
            $('.content').html("");
            var query = $("#search-input").val();

            var results = search(query,500);
            // 無搜尋結果
            if(results === undefined|| results.length===0){
                $("#content-post .noresult").remove();
                $results.append("<h1 class='noresult'>很抱歉,我們沒有找到與“"+query+"“相關的部落格</h1>");
            }
            else{
                renderResults(results);
            }
        });
    }
    
    function escapeReg(keyword) {
        //escape regexp prevserve word
        return String(keyword).replace(/([\*\.\?\+\$\^\[\]\(\)\{\}\|\/\\])/g, '\\$1');
    }
    
    // 搜尋比對結果
    function search(keyword,MAX_DESCRIPTION_SIZE) {
        if (keyword == null || keyword.trim() === '') return;
    
        var results = [],
            index = -1;
        for (var page in INDEX_DATA) {
            if ((index = INDEX_DATA[page].body.toLowerCase().indexOf(keyword.toLowerCase())) !== -1) {
                results.push({
                    url: INDEX_DATA[page].url,
                    title: INDEX_DATA[page].title,
                    date: INDEX_DATA[page].date,
                    tags: INDEX_DATA[page].tags,
                    body: INDEX_DATA[page].body.substr(Math.max(0, index - 50), MAX_DESCRIPTION_SIZE).replace(new RegExp('(' + escapeReg(keyword) + ')', 'gi'), '<span style="background:#ff0;">$1</span>')
                });
            }
        }
        return results;
    }
    
    // 将搜尋結果添加到界面上
    function renderResults(results) {
        if (!results.length) {
            return;
        }
        $(".content .noresult").remove();
        // 隻顯示10條
        results.slice(0, 10).forEach(function(result) {
            var $result = $('<article class="post">');
            $result.append('<h1><a href="/'+ result.url.toLowerCase() +'" target="_blank" rel="external nofollow"  title="'+ result.title +'">'+ result.title +'</a></h1>');

            var $footer = $('<footer class="post-info">Posted on');

            var $span = $('<span class="post-meta">');
            $span.append('<time datetime="'+ result.date +'">'+ result.date +'</time>· Tagged in');
            for (index=0; index < result.tags.length; index++){
                $span.append('<a href="/tags/'+ result.tags[index].toLowerCase() +'" target="_blank" rel="external nofollow" >'+result.tags[index]+'</a>')
            }
            $span.append('</span>');

            $footer.append($span);
            $footer.append('</footer>');
            
            $result.append($footer);
            $result.append('<div>'+result.body +'</div>');
            $result.append('<a href="/' + result.url.toLowerCase() +'" target="_blank" rel="external nofollow"  title="'+ result.title +'">Read more »</a>');
            $result.append('</article>');
            $results.append($result);
        });
    }

    $(document).ready(function() {
        initLunr();
        initUI();
    });
           

最終效果

實作Hugo中的搜尋功能安裝需要的元件生成索引檔案編寫搜尋邏輯最終效果