lanbos'blog

hexo遇到搜索问题小记

使用了next主题,next官方文档上提供了多种支持搜索的方法,之前用过Swiftype方案,但是服务不太稳定,想了使用本地搜索的方案

安装后问题显现

安装

1
npm install hexo-generator-searchdb --save

站点配置文件:

1
2
3
4
5
search:
path: search.xml
field: post
format: html
limit: 10000

主题配置文件:

1
2
3
  # Local search
local_search:
enable: true

配置完成重启后,顶部搜索出现但是点击没有反应。开始排查:

排查过程(可忽略不看)

hexo项目内搜索search.xml,找到localsearch.swig文件,找到ajax对应代码部分,小吐槽一下,看到源码发现写的如此简单粗暴。。。打开首页跟了下代码,发现ajax请求访问失败了。因为源码中没有写访问失败的调试报错,所以没有显现。加了代码之后发现问题是xml解析错误,有不符合utf8的字符。

解决方案

找到是因为生成的search.xml解析错误就很好排查了,进入xml格式检查网站,把search.xml粘进去一点一点找到报错的地方。最后发现是文章里有一处用了特殊字符,(前段时间vscode和搜狗配合使用会莫名其妙出一些特殊字符),删掉后hexo g重启,问题解决。

附上自己的localsearch.swig

源码简单粗暴,我本来想封装一下顺便改改弹窗样式,但犯懒了,就加了个请求错误报错的代码,帮助排查,随后有空加改吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  {% if config.search.path %}
<script type="text/javascript">
// Popup Window;
var isfetched = false;
// Search DB path;
var search_path = "{{ config.search.path }}";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "{{ config.root }}" + search_path;
// monitor main search box;

function proceedsearch() {
$("body").append('<div class="popoverlay">').css('overflow', 'hidden');
$('.popup').toggle();
}
// search function;
var searchFunc = function (path, search_id, content_id) {
$.ajax({
url: path,
dataType: "xml",
async: true,
success: function (xmlResponse) {
// get the contents from search data
isfetched = true;
$('.popup').detach().appendTo('.header-inner');
var datas = $("entry", xmlResponse).map(function () {
return {
title: $("title", this).text(),
content: $("content", this).text(),
url: $("url", this).text()
};
}).get();
var $input = document.getElementById(search_id);
var $resultContent = document.getElementById(content_id);
$input.addEventListener('input', function () {
var matchcounts = 0;
var str = '<ul class=\"search-result-list\">';
var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
$resultContent.innerHTML = "";
if (this.value.trim().length > 1) {
// perform local searching
datas.forEach(function (data) {
var isMatch = false;
var content_index = [];
var data_title = data.title.trim().toLowerCase();
var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
var data_url = decodeURIComponent(data.url);
var index_title = -1;
var index_content = -1;
var first_occur = -1;
// only match artiles with not empty titles and contents
if (data_title != '') {
keywords.forEach(function (keyword, i) {
index_title = data_title.indexOf(keyword);
index_content = data_content.indexOf(keyword);
if (index_title >= 0 || index_content >= 0) {
isMatch = true;
if (i == 0) {
first_occur = index_content;
}
}

});
}
// show search results
if (isMatch) {
matchcounts += 1;
str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title +
"</a>";
var content = data.content.trim().replace(/<[^>]+>/g, "");
if (first_occur >= 0) {
// cut out 100 characters
var start = first_occur - 20;
var end = first_occur + 80;
if (start < 0) {
start = 0;
}
if (start == 0) {
end = 50;
}
if (end > content.length) {
end = content.length;
}
var match_content = content.substring(start, end);
// highlight all keywords
keywords.forEach(function (keyword) {
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<b class=\"search-keyword\">" +
keyword + "</b>");
});

str += "<p class=\"search-result\">" + match_content + "...</p>"
}
str += "</li>";
}
})
};
str += "</ul>";
if (matchcounts == 0) {
str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
}
if (keywords == "") {
str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
}
$resultContent.innerHTML = str;
});
proceedsearch();
}
}).fail(function (data, errStr, err) {
console.log(errStr);
console.log(err);
})
}

// handle and trigger popup window;
$('.popup-trigger').click(function (e) {
e.stopPropagation();
if (isfetched == false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};
});

function renderData(xmlResponse) {

}

$('.popup-btn-close').click(function (e) {
$('.popup').hide();
$(".popoverlay").remove();
$('body').css('overflow', '');
});
$('.popup').click(function (e) {
e.stopPropagation();
});

</script>
{% endif %}