參考文檔:
還有網上各類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.csdn.net/u011439289
* @email [email protected]
* @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();
}
最終效果如下: