提交 b6918d8c 编写于 作者: EvanOne(文一)'s avatar EvanOne(文一)

refactor: Optimize local search

chore: Change i18n
上级 852f2494
......@@ -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
......
......@@ -73,7 +73,7 @@ algolia_search:
# 本地搜索
local_search:
input_placeholder: 开始搜索
input_placeholder: 搜索文章
hits_empty: 没有找到任何搜索结果
# 打赏
......
......@@ -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 += '<b>' + text.slice(hit.index, hitWordEnd) + '</b>';
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;
}
}
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册