http://crossoverjie.top/2016/07/06/SSM2/
实测,复制粘贴就可以使用,可以根据自己需要修改
1.加入依赖
<properties>
<lucene.version>6.0.1</lucene.version>
</properties>
<!--加入lucene-->
<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-core -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-queryparser -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>${lucene.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-common -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency>
<!--lucene中文分词-->
<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-smartcn -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>${lucene.version}</version>
</dependency>
<!--lucene高亮-->
<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-highlighter -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>${lucene.version}</version>
</dependency>
2.
package com.crossoverJie.lucene;
import com.crossoverJie.pojo.User;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import java.io.StringReader;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import com.crossoverJie.util.*;
/**
* 博客索引类
* @author Administrator
*
*/
public class LuceneIndex {
private Directory dir=null;
/**
* 获取IndexWriter实例
* @return
* @throws Exception
*/
private IndexWriter getWriter()throws Exception{
/**
* 生成的索引我放在了C盘,可以根据自己的需要放在具体位置
*/
dir= FSDirectory.open(Paths.get("C://lucene"));
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
IndexWriterConfig iwc=new IndexWriterConfig(analyzer);
IndexWriter writer=new IndexWriter(dir, iwc);
return writer;
}
/**
* 添加博客索引
* @param user
*/
public void addIndex(User user)throws Exception{
IndexWriter writer=getWriter();
Document doc=new Document();
doc.add(new StringField("id",String.valueOf(user.getUserId()), Field.Store.YES));
/**
* yes是会将数据存进索引,如果查询结果中需要将记录显示出来就要存进去,如果查询结果
* 只是显示标题之类的就可以不用存,而且内容过长不建议存进去
* 使用TextField类是可以用于查询的。
*/
doc.add(new TextField("username", user.getUsername(), Field.Store.YES));
doc.add(new TextField("description",user.getDescription(), Field.Store.YES));
writer.addDocument(doc);
writer.close();
}
/**
* 更新博客索引
* @param user
* @throws Exception
*/
public void updateIndex(User user)throws Exception{
IndexWriter writer=getWriter();
Document doc=new Document();
doc.add(new StringField("id",String.valueOf(user.getUserId()), Field.Store.YES));
doc.add(new TextField("username", user.getUsername(), Field.Store.YES));
doc.add(new TextField("description",user.getDescription(), Field.Store.YES));
writer.updateDocument(new Term("id", String.valueOf(user.getUserId())), doc);
writer.close();
}
/**
* 删除指定博客的索引
* @param userId
* @throws Exception
*/
public void deleteIndex(String userId)throws Exception{
IndexWriter writer=getWriter();
writer.deleteDocuments(new Term("id", userId));
writer.forceMergeDeletes(); // 强制删除
writer.commit();
writer.close();
}
/**
* 查询用户
* @param q 查询关键字
* @return
* @throws Exception
*/
public List<User> searchBlog(String q)throws Exception{
/**
* 注意的是查询索引的位置得是存放索引的位置,不然会找不到。
*/
dir= FSDirectory.open(Paths.get("C://lucene"));
IndexReader reader = DirectoryReader.open(dir);
IndexSearcher is=new IndexSearcher(reader);
BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
/**
* username和description就是我们需要进行查找的两个字段
* 同时在存放索引的时候要使用TextField类进行存放。
*/
QueryParser parser=new QueryParser("username",analyzer);
Query query=parser.parse(q);
QueryParser parser2=new QueryParser("description",analyzer);
Query query2=parser2.parse(q);
booleanQuery.add(query, BooleanClause.Occur.SHOULD);
booleanQuery.add(query2, BooleanClause.Occur.SHOULD);
TopDocs hits=is.search(booleanQuery.build(), 100);
QueryScorer scorer=new QueryScorer(query);
Fragmenter fragmenter = new SimpleSpanFragmenter(scorer);
/**
* 这里可以根据自己的需要来自定义查找关键字高亮时的样式。
*/
SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");
Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
highlighter.setTextFragmenter(fragmenter);
List<User> userList=new LinkedList<User>();
for(ScoreDoc scoreDoc:hits.scoreDocs){
Document doc=is.doc(scoreDoc.doc);
User user=new User();
user.setUserId(Integer.parseInt(doc.get(("id"))));
user.setDescription(doc.get(("description")));
String username=doc.get("username");
String description=doc.get("description");
if(username!=null){
TokenStream tokenStream = analyzer.tokenStream("username", new StringReader(username));
String husername=highlighter.getBestFragment(tokenStream, username);
if(StringUtil.isEmpty(husername)){
user.setUsername(username);
}else{
user.setUsername(husername);
}
}
if(description!=null){
TokenStream tokenStream = analyzer.tokenStream("description", new StringReader(description));
String hContent=highlighter.getBestFragment(tokenStream, description);
if(StringUtil.isEmpty(hContent)){
if(description.length()<=200){
user.setDescription(description);
}else{
user.setDescription(description.substring(0, 200));
}
}else{
user.setDescription(hContent);
}
}
userList.add(user);
}
return userList;
}
}
3.
@RequestMapping("/q")
public String search(@RequestParam(value = "q", required = false,defaultValue = "") String q,
@RequestParam(value = "page", required = false, defaultValue = "1") String page,
Model model,
HttpServletRequest request) throws Exception {
LuceneIndex luceneIndex = new LuceneIndex() ;
List<User> userList = luceneIndex.searchBlog(q);
/**
* 关于查询之后的分页我采用的是每次分页发起的请求都是将所有的数据查询出来,
* 具体是第几页再截取对应页数的数据,典型的拿空间换时间的做法,如果各位有什么
* 高招欢迎受教。
*/
Integer toIndex = userList.size() >= Integer.parseInt(page) * 5 ? Integer.parseInt(page) * 5 : userList.size();
List<User> newList = userList.subList((Integer.parseInt(page) - 1) * 5, toIndex);
model.addAttribute("userList",newList) ;
String s = this.genUpAndDownPageCode(Integer.parseInt(page), userList.size(), q, 5, request.getServletContext().
getContextPath());
model.addAttribute("pageHtml",s) ;
model.addAttribute("q",q) ;
model.addAttribute("resultTotal",userList.size()) ;
model.addAttribute("pageTitle","搜索关键字'" + q + "'结果页面") ;
return "queryResult";
}
4.
其中有用到一个genUpAndDownPageCode()方法来生成分页的Html代码,如下:
/**
* 查询之后的分页
* @param page
* @param totalNum
* @param q
* @param pageSize
* @param projectContext
* @return
*/
private String genUpAndDownPageCode(int page,Integer totalNum,String q,Integer pageSize,String projectContext){
long totalPage=totalNum%pageSize==0?totalNum/pageSize:totalNum/pageSize+1;
StringBuffer pageCode=new StringBuffer();
if(totalPage==0){
return "";
}else{
pageCode.append("<nav>");
pageCode.append("<ul class='pager' >");
if(page>1){
pageCode.append("<li><a href='"+projectContext+"/q?page="+(page-1)+"&q="+q+"'>上一页</a></li>");
}else{
pageCode.append("<li class='disabled'><a href='#'>上一页</a></li>");
}
if(page<totalPage){
pageCode.append("<li><a href='"+projectContext+"/q?page="+(page+1)+"&q="+q+"'>下一页</a></li>");
}else{
pageCode.append("<li class='disabled'><a href='#'>下一页</a></li>");
}
pageCode.append("</ul>");
pageCode.append("</nav>");
}
return pageCode.toString();
}
5.
<c:choose>
<c:when test="${userList.size()==0 }">
<div align="center" style="padding-top: 20px"><font color="red">${q}</font>未查询到结果,请换个关键字试试!</div>
</c:when>
<c:otherwise>
<div align="center" style="padding-top: 20px">
查询<font color="red">${q}</font>关键字,约${resultTotal}条记录!
</div>
<c:forEach var="u" items="${userList }" varStatus="status">
<div class="panel-heading ">
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<b>
<a href="<%=path %>/user/showUser/${u.userId}">${u.username}</a>
</b>
<br/>
${u.description}
</div>
</div>
</div>
<div class="col-md-4 col-md-offset-2">
<p class="text-muted text-right">
${u.password}
</p>
</div>
</div>
</div>
<div class="panel-footer">
<p class="text-right">
<span class="label label-default">
<span class="glyphicon glyphicon-comment" aria-hidden="true"></span>
${u.password}
</span>
</p>
</div>
</c:forEach>
</c:otherwise>
</c:choose>