在本篇文章中,你会学习到如何利用 Lucene 实现高级搜索功能以及如何利用 Lucene 来创建 Web 搜索应用程序。通过这些学习,你就可以利用 Lucene 来创建自己的搜索应用程序。 架构概览 通常一个 Web 搜索引擎的架构分为前端和后端两部分,就像图一中所示。在前端流程中,用户在搜索引擎提供的界面中输入要搜索的关键词,这里提到的用户界面一般是一个带有输入框的 Web 页面,然后应用程序将搜索的关键词解析成搜索引擎可以理解的形式,并在索引文件上进行搜索操作。在排序后,搜索引擎返回搜索结果给用户。在后端流程中,网络爬虫或者机器人从因特网上获取 Web 页面,然后索引子系统解析这些 Web 页面并存入索引文件中。如果你想利用 Lucene 来创建一个 Web 搜索应用程序,那么它的架构也和上面所描述的类似,就如图一中所示。 利用 Lucene 实现高级搜索 Lucene 支持多种形式的高级搜索,我们在这一部分中会进行探讨,然后我会使用 Lucene 的 API 来演示如何实现这些高级搜索功能。 布尔操作符 大多数的搜索引擎都会提供布尔操作符让用户可以组合查询,典型的布尔操作符有 AND, OR, NOT。Lucene 支持 5 种布尔操作符,分别是 AND, OR, NOT, 加(+), 减(-)。接下来我会讲述每个操作符的用法。 接下来我们看一下如何利用 Lucene 提供的 API 来实现布尔查询。清单1?显示了如果利用布尔操作符进行查询的过程。 域搜索(Field Search) Lucene 支持域搜索,你可以指定一次查询是在哪些域(Field)上进行。例如,如果索引的文档包含两个域, 通配符搜索(Wildcard Search) Lucene 支持两种通配符:问号(?)和星号(*)。你可以使用问号(?)来进行单字符的通配符查询,或者利用星号(*)进行多字符的通配符查询。例如,如果你想搜索 tiny 或者 tony,你就可以使用查询语句 “t?ny”;如果你想查询 Teach, Teacher 和 Teaching,你就可以使用查询语句 “Teach*”。清单3?显示了通配符查询的过程。 模糊查询 Lucene 提供的模糊查询基于编辑距离算法(Edit distance algorithm)。你可以在搜索词的尾部加上字符 ~ 来进行模糊查询。例如,查询语句 “think~” 返回所有包含和 think 类似的关键词的文档。清单 4?显示了如果利用 Lucene 的 API 进行模糊查询的代码。 范围搜索(Range Search) 范围搜索匹配某个域上的值在一定范围的文档。例如,查询 “age:[18 TO 35]” 返回所有 age 域上的值在 18 到 35 之间的文档。清单5显示了利用 Lucene 的 API 进行返回搜索的过程。 回页首 在 Web 应用程序中集成 Lucene 接下来我们开发一个 Web 应用程序利用 Lucene 来检索存放在文件服务器上的 HTML 文档。在开始之前,需要准备如下环境: 这个例子使用 Eclipse 进行 Web 应用程序的开发,最终这个 Web 应用程序跑在 Tomcat 5.0 上面。在准备好开发所必需的环境之后,我们接下来进行 Web 应用程序的开发。 1、创建一个动态 Web 项目 2. 设计 Web 项目的架构 在我们的设计中,把该系统分成如下四个子系统: 图4?显示了我们设计的详细信息,我们将用户接口子系统放到 webContent 目录下面。你会看到一个名为 search.jsp 的页面在这个文件夹里面。请求管理子系统在包? 3. 子系统的实现 在分析了系统的架构设计之后,我们接下来看系统实现的详细信息。 这个JSP的第二部分负责显示搜索结果给用户,如图6所示: 在清单6中, 在清单7中,注意到在这个类里面有三个私有属性。第一个是? 在类? 这个类包含两个私有属性,分别是? 现在我们来看一下放在包? 5.在 Tomcat 5.0 上运行应用程序 现在我们可以在 Tomcat 5.0 上运行开发好的应用程序。 现在我们已经成功的完成了示例项目的开发,并成功的用Lucene实现了搜索和索引功能。你可以下载这个项目的源代码(下载)。 回页首 总结 Lucene 提供了灵活的接口使我们更加方便的设计我们的 Web 搜索应用程序。如果你想在你的应用程序中加入搜索功能,那么 Lucene 是一个很好的选择。在设计你的下一个带有搜索功能的应用程序的时候可以考虑使用 Lucene 来提供搜索功能。
Figure 1. Web 搜索引擎架构
?
清单1:使用布尔操作符
//Test boolean operator
public void testOperator(String indexDirectory) throws Exception{
Directory dir = FSDirectory.getDirectory(indexDirectory,false);
IndexSearcher indexSearcher = new IndexSearcher(dir);
String[] searchWords = {"Java AND Lucene", "Java NOT Lucene", "Java OR Lucene",
"+Java +Lucene", "+Java -Lucene"};
Analyzer language = new StandardAnalyzer();
Query query;
for(int i = 0; i < searchWords.length; i++){
query = QueryParser.parse(searchWords[i], "title", language);
Hits results = indexSearcher.search(query);
System.out.println(results.length() + "search results for query " + searchWords[i]);
}
}
Title
?和?Content
,你就可以使用查询 “Title: Lucene AND Content: Java” 来返回所有在 Title 域上包含 Lucene 并且在 Content 域上包含 Java 的文档。清单 2显示了如何利用 Lucene 的 API 来实现域搜索。
清单2:实现域搜索
//Test field search
public void testFieldSearch(String indexDirectory) throws Exception{
Directory dir = FSDirectory.getDirectory(indexDirectory,false);
IndexSearcher indexSearcher = new IndexSearcher(dir);
String searchWords = "title:Lucene AND content:Java";
Analyzer language = new StandardAnalyzer();
Query query = QueryParser.parse(searchWords, "title", language);
Hits results = indexSearcher.search(query);
System.out.println(results.length() + "search results for query " + searchWords);
}
清单3:进行通配符查询
//Test wildcard search
public void testWildcardSearch(String indexDirectory)throws Exception{
Directory dir = FSDirectory.getDirectory(indexDirectory,false);
IndexSearcher indexSearcher = new IndexSearcher(dir);
String[] searchWords = {"tex*", "tex?", "?ex*"};
Query query;
for(int i = 0; i < searchWords.length; i++){
query = new WildcardQuery(new Term("title",searchWords[i]));
Hits results = indexSearcher.search(query);
System.out.println(results.length() + "search results for query " + searchWords[i]);
}
}
清单4:实现模糊查询
//Test fuzzy search
public void testFuzzySearch(String indexDirectory)throws Exception{
Directory dir = FSDirectory.getDirectory(indexDirectory,false);
IndexSearcher indexSearcher = new IndexSearcher(dir);
String[] searchWords = {"text", "funny"};
Query query;
for(int i = 0; i < searchWords.length; i++){
query = new FuzzyQuery(new Term("title",searchWords[i]));
Hits results = indexSearcher.search(query);
System.out.println(results.length() + "search results for query " + searchWords[i]);
}
}
清单5:测试范围搜索
//Test range search
public void testRangeSearch(String indexDirectory)throws Exception{
Directory dir = FSDirectory.getDirectory(indexDirectory,false);
IndexSearcher indexSearcher = new IndexSearcher(dir);
Term begin = new Term("birthDay","20000101");
Term end = new Term("birthDay","20060606");
Query query = new RangeQuery(begin,end,true);
Hits results = indexSearcher.search(query);
System.out.println(results.length() + "search results is returned");
}
图二:创建动态Web项目
?
图三:动态 Web 项目的结构
?
sample.dw.paper.lucene.servlet
?下面,类?SearchController
?负责功能的实现。搜索子系统放在包sample.dw.paper.lucene.search
?当中,它包含了两个类,SearchManager
?和?SearchResultBean
,第一个类用来实现搜索功能,第二个类用来描述搜索结果的结构。索引子系统放在包?sample.dw.paper.lucene.index
?当中。类?IndexManager
?负责为 HTML 文件创建索引。该子系统利用包?sample.dw.paper.lucene.util
?里面的类?HTMLDocParser
?提供的方法?getTitle
?和?getContent
?来对 HTML 页面进行解析。
图四:项目的架构设计
?
图5:向Web服务器提交搜索请求
?
图6:显示搜索结果
?
SearchController
?的 servlet 用来实现该子系统。清单6给出了这个类的源代码。
清单6:请求管理器的实现
package sample.dw.paper.lucene.servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sample.dw.paper.lucene.search.SearchManager;
/**
* This servlet is used to deal with the search request
* and return the search results to the client
*/
public class SearchController extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
String searchWord = request.getParameter("searchWord");
SearchManager searchManager = new SearchManager(searchWord);
List searchResult = null;
searchResult = searchManager.search();
RequestDispatcher dispatcher = request.getRequestDispatcher("search.jsp");
request.setAttribute("searchResult",searchResult);
dispatcher.forward(request, response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
doPost(request, response);
}
}
doPost
?方法从客户端获取搜索词并创建类?SearchManager
?的一个实例,其中类?SearchManager
?在搜索子系统中进行了定义。然后,SearchManager
?的方法 search 会被调用。最后搜索结果被返回到客户端。
SearchManager
?和?SearchResultBean
。第一个类用来实现搜索功能,第二个类是个JavaBean,用来描述搜索结果的结构。清单7给出了类?SearchManager
?的源代码。
清单7:搜索功能的实现
package sample.dw.paper.lucene.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import sample.dw.paper.lucene.index.IndexManager;
/**
* This class is used to search the
* Lucene index and return search results
*/
public class SearchManager {
private String searchWord;
private IndexManager indexManager;
private Analyzer analyzer;
public SearchManager(String searchWord){
this.searchWord = searchWord;
this.indexManager = new IndexManager();
this.analyzer = new StandardAnalyzer();
}
/**
* do search
*/
public List search(){
List searchResult = new ArrayList();
if(false == indexManager.ifIndexExist()){
try {
if(false == indexManager.createIndex()){
return searchResult;
}
} catch (IOException e) {
e.printStackTrace();
return searchResult;
}
}
IndexSearcher indexSearcher = null;
try{
indexSearcher = new IndexSearcher(indexManager.getIndexDir());
}catch(IOException ioe){
ioe.printStackTrace();
}
QueryParser queryParser = new QueryParser("content",analyzer);
Query query = null;
try {
query = queryParser.parse(searchWord);
} catch (ParseException e) {
e.printStackTrace();
}
if(null != query >> null != indexSearcher){
try {
Hits hits = indexSearcher.search(query);
for(int i = 0; i < hits.length(); i ++){
SearchResultBean resultBean = new SearchResultBean();
resultBean.setHtmlPath(hits.doc(i).get("path"));
resultBean.setHtmlTitle(hits.doc(i).get("title"));
searchResult.add(resultBean);
}
} catch (IOException e) {
e.printStackTrace();
}
}
return searchResult;
}
}
searchWord
,代表了来自客户端的搜索词。第二个是?indexManager
,代表了在索引子系统中定义的类?IndexManager
?的一个实例。第三个是?analyzer
,代表了用来解析搜索词的解析器。现在我们把注意力放在方法?search
?上面。这个方法首先检查索引文件是否已经存在,如果已经存在,那么就在已经存在的索引上进行检索,如果不存在,那么首先调用类?IndexManager
?提供的方法来创建索引,然后在新创建的索引上进行检索。搜索结果返回后,这个方法从搜索结果中提取出需要的属性并为每个搜索结果生成类?SearchResultBean
?的一个实例。最后这些?SearchResultBean
?的实例被放到一个列表里面并返回给请求管理器。SearchResultBean
?中,含有两个属性,分别是?htmlPath
?和?htmlTitle
,以及这个两个属性的 get 和 set 方法。这也意味着我们的搜索结果包含两个属性:htmlPath
?和?htmlTitle
,其中?htmlPath
?代表了 HTML 文件的路径,htmlTitle
?代表了 HTML 文件的标题。
IndexManager
?用来实现这个子系统。清单8?给出了这个类的源代码。
清单8:索引子系统的实现
package sample.dw.paper.lucene.index;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import sample.dw.paper.lucene.util.HTMLDocParser;
/**
* This class is used to create an index for HTML files
*
*/
public class IndexManager {
//the directory that stores HTML files
private final String dataDir = "c:\\dataDir";
//the directory that is used to store a Lucene index
private final String indexDir = "c:\\indexDir";
/**
* create index
*/
public boolean createIndex() throws IOException{
if(true == ifIndexExist()){
return true;
}
File dir = new File(dataDir);
if(!dir.exists()){
return false;
}
File[] htmls = dir.listFiles();
Directory fsDirectory = FSDirectory.getDirectory(indexDir, true);
Analyzer analyzer = new StandardAnalyzer();
IndexWriter indexWriter = new IndexWriter(fsDirectory, analyzer, true);
for(int i = 0; i < htmls.length; i++){
String htmlPath = htmls[i].getAbsolutePath();
if(htmlPath.endsWith(".html") || htmlPath.endsWith(".htm")){
addDocument(htmlPath, indexWriter);
}
}
indexWriter.optimize();
indexWriter.close();
return true;
}
/**
* Add one document to the Lucene index
*/
public void addDocument(String htmlPath, IndexWriter indexWriter){
HTMLDocParser htmlParser = new HTMLDocParser(htmlPath);
String path = htmlParser.getPath();
String title = htmlParser.getTitle();
Reader content = htmlParser.getContent();
Document document = new Document();
document.add(new Field("path",path,Field.Store.YES,Field.Index.NO));
document.add(new Field("title",title,Field.Store.YES,Field.Index.TOKENIZED));
document.add(new Field("content",content));
try {
indexWriter.addDocument(document);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* judge if the index exists already
*/
public boolean ifIndexExist(){
File directory = new File(indexDir);
if(0 < directory.listFiles().length){
return true;
}else{
return false;
}
}
public String getDataDir(){
return this.dataDir;
}
public String getIndexDir(){
return this.indexDir;
}
}
dataDir
?和?indexDir
。dataDir
?代表存放等待进行索引的 HTML 页面的路径,indexDir
?代表了存放 Lucene 索引文件的路径。类?IndexManager
?提供了三个方法,分别是?createIndex
,?addDocument
?和?ifIndexExist
。如果索引不存在的话,你可以使用方法?createIndex
?去创建一个新的索引,用方法?addDocument
?去向一个索引上添加文档。在我们的场景中,一个文档就是一个 HTML 页面。方法?addDocument
?会调用由类?HTMLDocParser
?提供的方法对 HTML 文档进行解析。你可以使用最后一个方法?ifIndexExist
?来判断 Lucene 的索引是否已经存在。sample.dw.paper.lucene.util
?里面的类?HTMLDocParser
。这个类用来从 HTML 文件中提取出文本信息。这个类包含三个方法,分别是?getContent
,getTitle
?和?getPath
。第一个方法返回去除了 HTML 标记的文本内容,第二个方法返回 HTML 文件的标题,最后一个方法返回 HTML 文件的路径。清单9?给出了这个类的源代码。
清单9:HTML 解析器
package sample.dw.paper.lucene.util;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import org.apache.lucene.demo.html.HTMLParser;
public class HTMLDocParser {
private String htmlPath;
private HTMLParser htmlParser;
public HTMLDocParser(String htmlPath){
this.htmlPath = htmlPath;
initHtmlParser();
}
private void initHtmlParser(){
InputStream inputStream = null;
try {
inputStream = new FileInputStream(htmlPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if(null != inputStream){
try {
htmlParser = new HTMLParser(new InputStreamReader(inputStream, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
public String getTitle(){
if(null != htmlParser){
try {
return htmlParser.getTitle();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "";
}
public Reader getContent(){
if(null != htmlParser){
try {
return htmlParser.getReader();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public String getPath(){
return this.htmlPath;
}
}
图7:配置 Tomcat 5.0
?
图8:选择 Tomcat 5.0
?
图9:完成Tomcat 5.0的配置
?
图10:用户界面
?
图11:搜索结果
?
图12:详细信息
?
详细解决方案
用 Lucene 加紧 Web 搜索应用程序的开发(转)
热度:469 发布时间:2012-08-16 12:02:16.0
相关解决方案
- lucene:怎么实现数据实时同步
- lucene 报错,该怎么解决
- lucene 里面如何样查询一个term是几个指定的值的内容? 就是说比如是要查询 ID 是 1,2,3,4,5 这5个数字都可以,这样的查询如何写
- lucene 中的TermRangeQuery,该如何解决
- Lucene 3.4 索引速度为什么没有2.3快,该如何处理
- Lucene 搜索出正文后,想依照google的结果,截取一段正文,显示搜索列表下解决方法
- lucene 能不能满足这样的需求?该怎么解决
- lucene.net并发修改索引有关问题(多用户同时操作索引)
- Lucene.net 的使用方法解决办法
- Lucene.Net 搜索时候 怎么拆词,分词啊已经建立好索引了
- Lucene 增量目录
- Lucene.net建立目录时出错:访问被拒绝
- windows 2003服务器-事件查看器-应用程序-复核失败-SA
- Lucene 有关问题。
- lucene.net的搜索有关问题,多谢指点
- Lucene.net 多个词查询,该怎么解决
- Lucene.net如何用
- 应用程序“DEFAULT WEB SITE”中的服务器异常有关问题,好烦的
- lucene.net分词搜索有关问题求解
- lucene .net ,怎么使用,如何结合数据库的一个表显示数据,能给个数据库表和lucenne的简单例子么
- lucene.net 分词搜索有关问题
- lucene.net 为什么要分词?解决方法
- Lucene.net全文检索,解决办法
- lucene 做什么用的?解决方案
- @@-引用的程序集“Lucene.Net”没有强名称-这个如何解决
- Lucene.Net 初级有关问题求教
- 根据关键字 使用 lucene 搜索本站下的文件, 和是使用GOOGLE提供的本站搜索工具,效果是一样的吧。都是可以本站下的文件。解决办法
- c#应用程序,一个简单的有关问题
- 会导致 asp.net 应用程序 重启 的所有 可能原因,该如何处理
- Lucene.net搜完索的怪有关问题