diff --git a/languages/en.yml b/languages/en.yml index 0f1fc40465a385d66add8ec7c51d12af0b3cc646..3d066d5652f04c4f41cd0b1c87018e1a3c3f2851 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -73,7 +73,7 @@ algolia_search: # Local search local_search: - input_placeholder: Start to search + input_placeholder: Search for Posts hits_empty: We didn't find any results for the search # Reward diff --git a/languages/zh-CN.yml b/languages/zh-CN.yml index 2018eb7d06473115fa5643cbe18ca6e5cbef330a..6eefe766c81224385b96b2a747995df907466df5 100644 --- a/languages/zh-CN.yml +++ b/languages/zh-CN.yml @@ -73,7 +73,7 @@ algolia_search: # 本地搜索 local_search: - input_placeholder: 开始搜索 + input_placeholder: 搜索文章 hits_empty: 没有找到任何搜索结果 # 打赏 diff --git a/layout/_third-party/search/localsearch.pug b/layout/_third-party/search/localsearch.pug index 0adb30317364badc0304a0e49107f73d90d5c3f6..33f65e22e15a46d91899d0deea82310363f8b219 100644 --- a/layout/_third-party/search/localsearch.pug +++ b/layout/_third-party/search/localsearch.pug @@ -55,26 +55,27 @@ if theme.local_search.enable return { title: $('title', this).text(), content: $('content', this).text(), - url: $('url', this).text(), - categories: $('category', this).text().trim().split(/[\s]+/), - tags: $('tag', this).text().trim().split(/[\s]+/) + url: $('url', this).text() }; }).get() : res; var $input = $('.localsearch-input-wrapper input'); var $result = $('.localsearch-result'); - // 搜索对象(标题,标签,分类,内容)的权值,影响显示顺序 - var WEIGHT = { title: 100, tag: 10, category: 10, content: 1 }; + // 搜索对象(标题、内容)的权重,影响显示顺序 + var WEIGHT = { title: 100, content: 1 }; var searchPost = function () { var searchText = $input.val().toLowerCase().trim(); // 根据空白字符分隔关键字 var keywords = searchText.split(/[\s]+/); - // 将关键字进行排列组合,使得搜索到结果的可能性更大 - var groupKeywords = sortGroup(keywords); // 搜索结果 var matchPosts = []; + + // 有多个关键字时,将原文字整个保存下来 + if (keywords.length > 1) { + keywords.push(searchText); + } // 防止未输入字符时搜索 if (searchText.length > 0) { @@ -82,34 +83,32 @@ if theme.local_search.enable var isMatch = false; // 没有标题的文章使用预设的 i18n 变量代替 - var title = (data.title && data.title.trim()) || '!{_p("post.untitled")}';; + var title = (data.title && data.title.trim()) || '!{_p("post.untitled")}'; var titleLower = title && title.toLowerCase(); // 删除 HTML 标签 和 所有空白字符 var content = data.content && data.content.replace(/<[^>]+>|\s+/, ''); var contentLower = content && content.toLowerCase(); // 删除重复的 / var postURL = data.url && decodeURI(data.url).replace(/\/{2,}/, '/'); - var tags = data.tags; - var categories = data.categories; // 标题中匹配到的关键词 var titleHitSlice = []; // 内容中匹配到的关键词 var contentHitSlice = []; - groupKeywords.forEach(function (keyword) { + keywords.forEach(function (keyword) { /** - * 获取匹配文字的索引 + * 获取匹配的关键词的索引 * @param {String} keyword 要匹配的关键字 * @param {String} text 原文字 * @param {Boolean} caseSensitive 是否区分大小写 - * @param {Number} weight 权重 + * @param {Number} weight 匹配对象的权重。权重大的优先显示 * @return {Array} */ function getIndexByword (word, text, caseSensitive, weight) { if (!word || !text) return []; - var startIndex = 0; // 开始匹配的索引 + var startIndex = 0; // 每次匹配的开始索引 var index = -1; // 匹配到的索引值 var result = []; // 匹配结果 @@ -119,15 +118,27 @@ if theme.local_search.enable } while((index = text.indexOf(word, startIndex)) !== -1) { - result.push({ index: index, word: word, weight: weight }); + var hasMatch = false; + + // 索引位置相同的关键词,保留长度较长的 + titleHitSlice.forEach(function (hit) { + if (hit.index === index && + hit.word.length < word.length) { + hit.word = word; + hasMatch = true; + } + }); startIndex = index + word.length; + !hasMatch && result.push({ index: index, word: word, weight: weight }); } return result; } - - titleHitSlice = titleHitSlice.concat(getIndexByword(keyword, titleLower, false, WEIGHT.title)); - contentHitSlice = contentHitSlice.concat(getIndexByword(keyword, contentLower, false, WEIGHT.content)); + + titleHitSlice = titleHitSlice.concat( + getIndexByword(keyword, titleLower, false, WEIGHT.title)); + contentHitSlice = contentHitSlice.concat( + getIndexByword(keyword, contentLower, false, WEIGHT.content)); }); var hitTitle = titleHitSlice.length; @@ -138,20 +149,29 @@ if theme.local_search.enable } if (isMatch) { + ;[titleHitSlice, contentHitSlice].forEach(function (hit) { + // 按照匹配文字的索引的递增顺序排序 + hit.sort(function (left, right) { + return left.index - right.index; + }); + }); + /** - * 给匹配的文本添加标记标签 + * 给文本中匹配到的关键词添加标记,从而进行高亮显示 * @param {String} text 原文本 * @param {Array} hitSlice 匹配项的索引信息 + * @param {Number} start 开始索引 + * @param {Number} end 结束索引 * @return {String} */ - function highlightKeyword (text, hitSlice) { + function highlightKeyword (text, hitSlice, start, end) { if (!text || !hitSlice || !hitSlice.length) return; - var startIndex = 0; var result = ''; + var startIndex = start; + var endIndex = end; hitSlice.forEach(function (hit) { - // 匹配到较长的字符串后,防止再匹配较短的字符串 if (hit.index < startIndex) return; var hitWordEnd = hit.index + hit.word.length; @@ -160,16 +180,18 @@ if theme.local_search.enable result += '' + text.slice(hit.index, hitWordEnd) + ''; startIndex = hitWordEnd; }); - result += text.slice(startIndex); + result += text.slice(startIndex, endIndex); return result; } var postData = {}; - // 计算文章总的搜索权重 + // 文章总的搜索权重 var postWeight = titleHitSlice.length * 100 + contentHitSlice.length; - var postTitle = highlightKeyword(title, titleHitSlice) || title; - var postContent = highlightKeyword(content, contentHitSlice) || content; + // 标记匹配关键词后的标题 + var postTitle = highlightKeyword(title, titleHitSlice, 0, title.length) || title; + // 标记匹配关键词后的内容 + var postContent; // 截取匹配的第一个字符,前后共 200 个字符来显示 if (contentHitSlice.length > 0) { @@ -177,9 +199,9 @@ if theme.local_search.enable var start = firstIndex > 20 ? firstIndex - 20 : 0; var end = firstIndex + 180; - postContent = postContent.slice(start, end); + postContent = highlightKeyword(content, contentHitSlice, start, end); } else { // 未匹配到内容,直接截取前 200 个字符来显示 - postContent = postContent.slice(0, 200); + postContent = content.slice(0, 200); } postData.title = postTitle; @@ -215,59 +237,6 @@ if theme.local_search.enable searchPost(); } }); - - /** - * 将关键字进行排列组合 - * @param {Array} keywords 关键词数组 - * @return {Array} - */ - function sortGroup (keywords) { - if (!Array.isArray(keywords)) return; - if (keywords.length <= 1) return keywords; - - // 对数组中的元素进行组合 - function groupSplit (arr, size) { - var r = []; // result - - function group(t, a, n) { // tempArr, arr, num - if (n === 0) { - r[r.length] = t; - return; - } - for (var i = 0, l = a.length - n; i <= l; i++) { - var b = t.slice(); - b.push(a[i]); - group(b, a.slice(i + 1), n - 1); - } - } - group([], arr, size); - - return r; - } - - // 组合结果 - var result = []; - - for (var i = keywords.length; i > 0; i--) { - var groupKeyword = groupSplit(keywords, i); - - if (groupKeyword.length !== 0) { - groupKeyword.forEach(function (item) { - // 将排列组合后的项,连接成新的关键词 - if (item.length > 1) { - result.push(item.join('')); - result.push(item.join(' ')); - result.push(item.slice(0).reverse().join('')); - result.push(item.slice(0).reverse().join(' ')); - } else { - result.push(item.join('')); - } - }); - } - } - - return result; - } } }); }