参考文档:
还有网上各类solr suggest的文章,抱歉没有一一列举。
1.实现前台自动补全
1.1首先定义word实体Bean,用来直接处理字典与前台交互信息:
package bean;
/**
* 首先定义word实体Bean,用来直接处理字典与前台交互信息
* @author 毛毛二号
*/
public class Word {
//名称
private String name ;
public Word(){}
public Word(int number, String name){
this.name = name ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.2编写AutoCompleteServlet.这个Servle用来t测试一下能不能生成XML文档
package servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import bean.Word;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created by lhx on 14-12-9 上午9:31
*
* @project jspProject
* @package ${PACKAGE_NAME}
* @blog http://blog.youkuaiyun.com/u011439289
* @email 888xin@sina.com
* @Description
*/
public class AutoCompleteServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
StringBuffer sf = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sf.append("<message>");
List<Word> list = new ArrayList<Word>(10);
Word word = null ;
for (int i = 0; i < 10; i++) {
word = new Word(i, "abd" + i);
list.add(word);
}
Iterator<Word> it = list.iterator();
while (it.hasNext()){
Word word1 = it.next();
if (word1 == null){
continue;
}
String name = word1.getName();
sf.append("<word>"+name);
sf.append("</word>");
}
sf.append("</message>");
PrintWriter pw = null;
try {
response.setContentType("text/xml;charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
pw = response.getWriter();
pw.print(sf.toString());
pw.flush();
}catch (Exception e) {
e.printStackTrace();
}
finally {
if (pw != null)
pw.close();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
结果如下:
1.3 编写jsp,利用jQuery,实现前台的自动补全
1.3.1 下载jQuery插件,我使用的是jquery1.2.6.js。
1.3.2 编写auto2.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery 自动完成功能(优化版)</title>
<script type="text/javascript" src="js/jquery-2.1.1.js"></script>
</head>
<body>
<script type="text/javascript">
var highlightindex = -1;//表示当前高亮节点
var timeoutId;
$(document).ready(function() {
var wordInput = $("#word");//文本框
var wordInputOffset = wordInput.offset();//获得文本框位置
$("#auto").hide().css("border", "1px black solid").css("position", "absolute")
.css("top", wordInputOffset.top + wordInput.height() + 5 + "px")
.css("left", wordInputOffset.left + "px").width(wordInput.width() + 3 + "px");
wordInput.keyup(function(event) {
//处理文本框中的键盘事件
//如果输入字母,将文本框中最新信息发送给服务器
var myEvent = event || window.event;
var keyCode = myEvent.keyCode;//获得键值
if (keyCode == 27) {
var wordText = $("#word").val();
autoHide();
wordInput.text(wordText);
}
else {
if (keyCode >= 65 && keyCode <= 90 || keyCode == 8 || keyCode == 46) { //8对应退格键,46对应删除键
var wordText = $("#word").val();//获得文本框中的内容
var autoNode = $("#auto");
if (wordText != "") {
clearTimeout(timeoutId);//对上次未完成的延时操作进行取消
//延时操作,减少与服务器的交互次数,延时500ms,防止用户操作过快
timeoutId = setTimeout(function() {
$.post("AutoCompleteServlet", {word:wordText}, function(data) {//发送数据,第二项是属性名对应属性值
var jqueryObj = $(data);//将dom对象data转换成jQuery的对象
var wordNodes = jqueryObj.find("word");//找到所有word节点
autoNode.html("");
wordNodes.each(function(i) { //i是索引,用来给id赋值
var wordNode = $(this);//获取单词内容
var newDivNode = $("<div>").attr("id", i).css("backgroundColor", "white");
newDivNode.html(wordNode.text()).appendTo(autoNode);//新建div节点,加入单词内容
//增加鼠标进入事件,高亮节点
newDivNode.mouseover(function() {
//将原来高亮的节点取消高亮
if (highlightindex != -1) {
$("#auto").children("div").eq(highlightindex)
.css("backgroundColor", "white");
}
//记录新的高亮索引
highlightindex = $(this).attr("id");
$(this).css("backgroundColor", "#3366CC").css("cursor","pointer");
});
//增加鼠标移出事件,取消节点高亮
newDivNode.mouseout(function() {
if (keyCode == 13) { //判断是否按下回车键
//下拉框有高亮
if (highlightindex != -1) {
lightEventHide();
highlightindex = -1;
} else {
alert("文本框中的[" + $("#word").val() + "]被提交了");
autoHide();
$("#word").get(0).blur();//让文本框失去焦点
}
//取消鼠标移出节点的高亮
//$(this).css("backgroundColor", "white");
}
}
);
//增加鼠标点击事件,可以进行补全
newDivNode.click(function() {
//取出高亮节点的文本内容
var comText = $(this).text();
autoHide();
highlightindex = -1;
//文本框内容变为高亮节点内容
$("#word").val(comText);
});
});
//添加单词内容到弹出框
if (wordNodes.length > 0) {
autoNode.show();
} else {
autoNode.hide();
highlightindex = -1;//弹出框隐藏,高亮节点索引设成-1
}
}, "xml");
}, 300);
}
else
{
autoNode.hide();
highlightindex = -1;
}
} else if (keyCode == 38 || keyCode == 40) { //判断是否输入的是向上38向下40按键
if (keyCode == 38) {
var autoNodes = $("#auto").children("div").css("background-color", "white");
if (highlightindex != -1) {
autoNodes.eq(highlightindex).css("background-color", "white");
highlightindex--;
} else {
lightEvent();
highlightindex = autoNodes.length - 1;
}
if (highlightindex == -1) {
highlightindex = autoNodes.length - 1;//如果改变索引值后index变成-1,则将索引值指向最后一个元素
}
lightEvent();
autoNodes.eq(highlightindex).css("backgroundColor", "#3366CC");
}
if (keyCode == 40) {
var autoNodes = $("#auto").children("div");
if (highlightindex != -1) {
autoNodes.eq(highlightindex).css("background-color", "white");
}
highlightindex++;
if (highlightindex == autoNodes.length) {
highlightindex = 0;//如果改变索引值等于最大长度,则将索引值指向第一个元素
}
lightEvent();
autoNodes.eq(highlightindex).css("backgroundColor", "#3366CC");
}
} else if (keyCode == 13) { //判断是否按下回车键
//下拉框有高亮
if (highlightindex != -1) {
lightEventHide();
highlightindex = -1;
} else {
alert("文本框中的[" + $("#word").val() + "]被提交了");
$("#auto").hide();
$("#word").get(0).blur();//让文本框失去焦点
}
//下拉框没有高亮
}
}
}
)
;
$("input[type='button']").click(function() {
alert("文本框中的[" + $("#word").val() + "]被提交了");
});
});
function lightEventHide(){
var comText = $("#auto").hide().children("div").eq(highlightindex).text();
$("#word").val(comText);
}
function lightEvent(){
var comText = $("#auto").children("div").eq(highlightindex).text();
$("#word").val(comText);
}
function autoHide(){
$("#auto").hide();
}
</script>
<h3>
<center>仿google自动补全(jQuery优化版)</center>
</h3>
<br />
<table align="center">
<tr><td>
<input type="text" id="word" maxlength=2048 size=55 />
<br/>
<td></tr>
<tr><td align="center">
<input type="button" value="shiyang 搜索"/>
</td></tr>
</table>
<br />
<div id="auto"></div>
</body>
</html>
结果如下:
至此,根据各位大神文档的实现,能基本实现前台的自动补全。下一步和solr结合。
2. 后台solr实现自动补全
2.1. 配置suggest模块
修改core,所在的solrconfig.xml.添加suggest相关检查拼写插件,如下<searchComponent name="suggest" class="solr.SpellCheckComponent">
<str name="queryAnalyzerFieldType">string</str>
<lst name="spellchecker">
<str name="name">suggest</str>
<str name="classname">org.apache.solr.spelling.suggest.Suggester</str>
<str name="lookupImpl">org.apache.solr.spelling.suggest.tst.TSTLookup</str>
<str name="field">content</str>
<float name="threshold">0.0001</float>
<str name="comparatorClass">freq</str>
<str name="buildOnOptimize">true</str>
<str name="buildOnCommit">true</str>
</lst>
</searchComponent>
<requestHandler name="/suggest" class="org.apache.solr.handler.component.SearchHandler">
<lst name="defaults">
<str name="spellcheck">true</str>
<str name="spellcheck.dictionary">suggest</str>
<str name="spellcheck.count">10</str>
<str name="spellcheck.onlyMorePopular">true</str>
<str name="spellcheck.extendedResults">false</str>
<str name="spellcheck.collate">true</str>
</lst>
<arr name="components">
<str>suggest</str>
</arr>
</requestHandler>
其中, <str name="field">content</str> 指定了我是根据solr索引的content字段进行配置。
2.2 编写后台类方法,开启suggest功能,从而实现nutch爬取的数据传递给word工具类。
public List<Word> queryAll(String queryfilter) throws SolrServerException{
// 获取连接服务
CommonsHttpSolrServer server = SolrServer.getInstance().getServer();
// 搜索条件的设置
SolrQuery query = new SolrQuery();
// 查询,添加分词
query.set("q", queryfilter);
// 设置分页.设置起始位置与返回结果数
query.setStart(0);
query.setRows(10);
//设置搜索智能提示Suggest
query.set("qt", "/suggest");
query.set("spellcheck", "on");
query.set("spellcheck.build", "true");
QueryResponse qrsp = server.query(query);
List<Word> listWord = new ArrayList<Word>() ;
//智能提示
SpellCheckResponse spellCheckResponse = qrsp
.getSpellCheckResponse();
if (spellCheckResponse != null) {
List<Suggestion> suggestionList = spellCheckResponse
.getSuggestions();
for (Suggestion suggestion : suggestionList) {
System.out.println("Suggestions NumFound: "
+ suggestion.getNumFound());
System.out.println("Token: " + suggestion.getToken());
System.out.println("Suggested: ");
List<String> suggestedWordList = suggestion.getAlternatives();
for (String word : suggestedWordList) {
System.out.println(word + ", ");
Word wordlist = new Word() ;
wordlist.setName(word);
listWord.add(wordlist);
}
System.out.println("------next-----");
}
}
return listWord ;
}
2.3 修改AutoCompleteServlet,主要修改了之前写死的字典数据,把静态数据替换成了从solr后台获取的动态的数据listword集合
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.solr.client.solrj.SolrServerException;
import service.Search;
import tool.Page;
import bean.Word;
/**
* Servlet implementation class AutoCompleteServlet
*/
public class AutoCompleteServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public AutoCompleteServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doPost(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// 构造XML文档。
StringBuffer sf = new StringBuffer(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sf.append("<message>");
//从jQuery传递过来参数中获取值,并转码
String queryfilter = new String(request.getParameter("word").getBytes(
"ISO-8859-1"), "utf-8");
// String queryfilter = request.getParameter("word");
System.out.println("word" + queryfilter);
Search s = new Search();
List<Word> list = null;
try {
list = s.queryAll(queryfilter);
Iterator<Word> it = list.iterator();
while (it.hasNext()) {
Word word1 = it.next();
System.out.println(word1.getName());
if (word1 == null) {
continue;
}
String name = word1.getName();
sf.append("<word>" + name);
sf.append("</word>");
}
} catch (SolrServerException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
sf.append("</message>");
PrintWriter pw = null;
try {
response.setContentType("text/xml;charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
pw = response.getWriter();
pw.print(sf.toString());
pw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pw != null)
pw.close();
}
}
}
2.4.根据1.3.2模仿修改我的搜索主页面。主要添加两个id : word,auto。
2.4.1 修改jsp,我的前台如下:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ page import="java.io.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery1.2.6.js"></script>
<script type="text/javascript" src="js/my.js"></script>
</head>
<body>
<div style="position:absolute;top:50%; left:50%;margin:-100px 0 0 -300px;text-align:center;vertical-align:middle;">
<img src="images/3.jpg" style="width:300px;height:105px;"/>
<form id="search" name="search" action="search" method="get">
<input id="word" name="param" type="text" style=" width:500px; height:30px;">
<input style="color: blue;width:80px; height:30px;" type="submit" value="查询" >
</form>
</div>
<div id="auto"></div>
</body>
</html>
其中my.js,就是之前1.3.2中的js片段。
这个时候,出现英文能够自动补全,但是中文却不能。如下图
2.5 解决中文不能自动补全。
2.5.1.问题还是出在我solr后台建立索引的字段,在2.1中,我设置的content,是自动给英文分词,而中文没有分词,所以我需要设置分词,我采取的是IK,参考上面提到的文档,然后修改core中schema.xml 的content type类型。 <field name="content" type="text_ik" stored="true" indexed="true"/>。
2.5.2 修改my.js.仔细分析my.js.
wordInput.keyup(function(event) {
//处理文本框中的键盘事件
//如果输入字母,将文本框中最新信息发送给服务器
var myEvent = event || window.event;
var keyCode = myEvent.keyCode;//获得键值
if (keyCode == 27) {
var wordText = $("#word").val();
autoHide();
wordInput.text(wordText);
}
else {
if (keyCode >= 65 && keyCode <= 90|| keyCode == 8 || keyCode == 46) { //8对应退格键,46对应删除键
这一段,else分析了输入框针对键盘按键的反应。英文字母a--z,都可以直接进入else,进行处理。但是中文输入,一般需要在按键玩再按下空白键才能完成输入。因此修改else语句,添加判断条件,即可。具体如下:
var highlightindex = -1;//表示当前高亮节点
var timeoutId;
$(document).ready(function() {
var wordInput = $("#word");//文本框
var wordInputOffset = wordInput.offset();//获得文本框位置
$("#auto").hide().css("border", "1px black solid").css("position", "absolute")
.css("top", wordInputOffset.top + wordInput.height() + 5 + "px")
.css("left", wordInputOffset.left + "px").width(wordInput.width() + 3 + "px");
wordInput.keyup(function(event) {
//处理文本框中的键盘事件
//如果输入字母,将文本框中最新信息发送给服务器
var myEvent = event || window.event;
var keyCode = myEvent.keyCode;//获得键值
if (keyCode == 27) {
var wordText = $("#word").val();
autoHide();
wordInput.text(wordText);
}
else {
if (keyCode >= 65 && keyCode <= 90|| keyCode == 32 || keyCode == 8 || keyCode == 46) { //8对应退格键,46对应删除键 ,32表示空白键,方便汉字输入检查的
var wordText = $("#word").val();//获得文本框中的内容
var autoNode = $("#auto");
if (wordText != "") {
clearTimeout(timeoutId);//对上次未完成的延时操作进行取消
//延时操作,减少与服务器的交互次数,延时500ms,防止用户操作过快
timeoutId = setTimeout(function() {
$.post("AutoCompleteServlet", {word:wordText}, function(data) {//发送数据,第二项是属性名对应属性值
var jqueryObj = $(data);//将dom对象data转换成jQuery的对象
var wordNodes = jqueryObj.find("word");//找到所有word节点
autoNode.html("");
wordNodes.each(function(i) { //i是索引,用来给id赋值
var wordNode = $(this);//获取单词内容
var newDivNode = $("<div>").attr("id", i).css("backgroundColor", "white");
newDivNode.html(wordNode.text()).appendTo(autoNode);//新建div节点,加入单词内容
//增加鼠标进入事件,高亮节点
newDivNode.mouseover(function() {
//将原来高亮的节点取消高亮
if (highlightindex != -1) {
$("#auto").children("div").eq(highlightindex)
.css("backgroundColor", "white");
}
//记录新的高亮索引
highlightindex = $(this).attr("id");
$(this).css("backgroundColor", "#3366CC").css("cursor","pointer");
});
//增加鼠标移出事件,取消节点高亮
newDivNode.mouseout(function() {
if (keyCode == 13) { //判断是否按下回车键
//下拉框有高亮
if (highlightindex != -1) {
lightEventHide();
highlightindex = -1;
} else {
// alert("文本框中的[" + $("#word").val() + "]被提交了");
autoHide();
$("#word").get(0).blur();//让文本框失去焦点
}
//取消鼠标移出节点的高亮
//$(this).css("backgroundColor", "white");
}
}
);
//增加鼠标点击事件,可以进行补全
newDivNode.click(function() {
//取出高亮节点的文本内容
var comText = $(this).text();
autoHide();
highlightindex = -1;
//文本框内容变为高亮节点内容
$("#word").val(comText);
});
});
//添加单词内容到弹出框
if (wordNodes.length > 0) {
autoNode.show();
} else {
autoNode.hide();
highlightindex = -1;//弹出框隐藏,高亮节点索引设成-1
}
}, "xml");
}, 300);
}
else
{
autoNode.hide();
highlightindex = -1;
}
} else if (keyCode == 38 || keyCode == 40) { //判断是否输入的是向上38向下40按键
if (keyCode == 38) {
var autoNodes = $("#auto").children("div").css("background-color", "white");
if (highlightindex != -1) {
autoNodes.eq(highlightindex).css("background-color", "white");
highlightindex--;
} else {
lightEvent();
highlightindex = autoNodes.length - 1;
}
if (highlightindex == -1) {
highlightindex = autoNodes.length - 1;//如果改变索引值后index变成-1,则将索引值指向最后一个元素
}
lightEvent();
autoNodes.eq(highlightindex).css("backgroundColor", "#3366CC");
}
if (keyCode == 40) {
var autoNodes = $("#auto").children("div");
if (highlightindex != -1) {
autoNodes.eq(highlightindex).css("background-color", "white");
}
highlightindex++;
if (highlightindex == autoNodes.length) {
highlightindex = 0;//如果改变索引值等于最大长度,则将索引值指向第一个元素
}
lightEvent();
autoNodes.eq(highlightindex).css("backgroundColor", "#3366CC");
}
} else if (keyCode == 13) { //判断是否按下回车键
//下拉框有高亮
if (highlightindex != -1) {
lightEventHide();
highlightindex = -1;
} else {
// alert("文本框中的[" + $("#word").val() + "]被提交了");
$("#auto").hide();
$("#word").get(0).blur();//让文本框失去焦点
}
//下拉框没有高亮
}
}
}
)
;
/* $("input[type='button']").click(function() {
alert("文本框中的[" + $("#word").val() + "]被提交了");
});*/
});
function lightEventHide(){
var comText = $("#auto").hide().children("div").eq(highlightindex).text();
$("#word").val(comText);
}
function lightEvent(){
var comText = $("#auto").children("div").eq(highlightindex).text();
$("#word").val(comText);
}
function autoHide(){
$("#auto").hide();
}
最终效果如下: