??xml version="1.0" encoding="utf-8" standalone="yes"?>国产91精品一区二区麻豆网站,亚洲国产成人久久,日本亚洲欧美天堂免费http://www.aygfsteel.com/wyl232/zh-cnFri, 20 Jun 2025 00:35:50 GMTFri, 20 Jun 2025 00:35:50 GMT60?Lucene 加?Web 搜烦应用E序的开?/title><link>http://www.aygfsteel.com/wyl232/archive/2008/09/19/229890.html</link><dc:creator>wyl</dc:creator><author>wyl</author><pubDate>Fri, 19 Sep 2008 03:19:00 GMT</pubDate><guid>http://www.aygfsteel.com/wyl232/archive/2008/09/19/229890.html</guid><wfw:comment>http://www.aygfsteel.com/wyl232/comments/229890.html</wfw:comment><comments>http://www.aygfsteel.com/wyl232/archive/2008/09/19/229890.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/wyl232/comments/commentRss/229890.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/wyl232/services/trackbacks/229890.html</trackback:ping><description><![CDATA[<h1>?Lucene 加?Web 搜烦应用E序的开?/h1> <p>U别Q?中</p> <p><a cmimpressionsent="1">?L</a> (<a href="mailto:zhoudengpeng@yahoo.com.cn?subject=?Lucene 加?Web 搜烦应用E序的开?amp;cc=htc@us.ibm.com" cmimpressionsent="1">zhoudengpeng@yahoo.com.cn</a>), 软g工程? 上v交通大?br /> </p> <p>2006 q?9 ?06 ?/p> <blockquote>Lucene 是基?Java 的全文信息检索包Q它目前?Apache Jakarta 家族下面的一个开源项目。在q篇文章中,我们首先来看如何利用 Lucene 实现高搜烦功能Q然后学习如何利?Lucene 来创Z个健壮的 Web 搜烦应用E序?/blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES--> <p>在本文章中Q你会学习到如何利用 Lucene 实现高搜烦功能以及如何利用 Lucene 来创?Web 搜烦应用E序。通过q些学习Q你可以利?Lucene 来创q搜烦应用E序?/p> <p><a name="N1005A"><span id="wmqeeuq" class="atitle">架构概览</span></a></p> <p>通常一?Web 搜烦引擎的架构分为前端和后端两部分,像<a cmimpressionsent="1">图一</a>中所C。在前端程中,用户在搜索引擎提供的界面中输入要搜烦的关键词Q这里提到的用户界面一般是一个带有输入框?Web 面Q然后应用程序将搜烦的关键词解析成搜索引擎可以理解的形式Qƈ在烦引文件上q行搜烦操作。在排序后,搜烦引擎q回搜烦l果l用戗在后端程中,|络爬虫或者机器h从因特网上获?Web 面Q然后烦引子pȝ解析q些 Web 面q存入烦引文件中。如果你惛_?Lucene 来创Z?Web 搜烦应用E序Q那么它的架构也和上面所描述的类|如<a cmimpressionsent="1">图一</a>中所C?/p> <br /> <a name="figure1"><strong>Figure 1. Web 搜烦引擎架构</strong></a><br /> <img height="380" alt="Web搜烦引擎架构" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure1.gif" width="449" /> <br /> <p><a name="N1007B"><span id="wmqeeuq" class="atitle">利用 Lucene 实现高搜烦</span></a></p> <p>Lucene 支持多种形式的高U搜索,我们在这一部分中会q行探讨Q然后我会?Lucene ?API 来演C如何实现这些高U搜索功能?/p> <p><a name="N10084"><span id="wmqeeuq" class="smalltitle">布尔操作W?/span></a></p> <p>大多数的搜烦引擎都会提供布尔操作W让用户可以l合查询Q典型的布尔操作W有 AND, OR, NOT。Lucene 支持 5 U布操作符Q分别是 AND, OR, NOT, ?+), ?-)。接下来我会讲述每个操作W的用法?</p> <ul> <li><strong>OR</strong>: 如果你要搜烦含有字符 A 或?B 的文档,那么需要?OR 操作W。需要记住的是,如果你只是简单的用空格将两个关键词分割开Q其实在搜烦的时候搜索引擎会自动在两个关键词之间加上 OR 操作W。例如,“Java OR Lucene” ?“Java Lucene” 都是搜烦含有 Java 或者含?Lucene 的文档? <li><strong>AND</strong>: 如果你需要搜索包含一个以上关键词的文档,那么需要?AND 操作W。例如,“Java AND Lucene” q回所有既包含 Java 又包?Lucene 的文档? <li><strong>NOT</strong>: Not 操作W得包含紧跟在 NOT 后面的关键词的文档不会被q回。例如,如果你想搜烦所有含?Java 但不含有 Lucene 的文档,你可以用查询语?“Java NOT Lucene”。但是你不能只对一个搜索词使用q个操作W,比如Q查询语?“NOT Java” 不会q回Ml果? <li><strong>加号Q?Q?/strong>: q个操作W的作用?AND 差不多,但它只对紧跟着它的一个搜索词起作用。例如,如果你想搜烦一定包?JavaQ但不一定包?Lucene 的文档,可以用查询语?#8220;+Java Lucene”? <li><strong>减号Q?Q?/strong>: q个操作W的功能?NOT 一P查询语句 “Java -Lucene” q回所有包?Java 但不包含 Lucene 的文档?</li> </ul> <p>接下来我们看一下如何利?Lucene 提供?API 来实现布查询?a cmimpressionsent="1">清单1</a> 昄了如果利用布操作符q行查询的过E?/p> <br /> <a name="Listing1"><strong>清单1Q用布操作符</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode"> //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]); } } </pre> </td> </tr> </tbody> </table> <br /> <p><a name="N100BA"><span id="wmqeeuq" class="smalltitle">域搜?Field Search)</span></a></p> <p>Lucene 支持域搜索,你可以指定一ơ查询是在哪些域(Field)上进行。例如,如果索引的文档包含两个域Q?code>Title</code> ?<code>Content</code>Q你可以用查?“Title: Lucene AND Content: Java” 来返回所有在 Title 域上包含 Lucene q且?Content 域上包含 Java 的文档?a cmimpressionsent="1">清单 2</a> 昄了如何利?Lucene ?API 来实现域搜烦?</p> <br /> <a name="Listing2"><strong>清单2Q实现域搜烦</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">//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); } </pre> </td> </tr> </tbody> </table> <br /> <p><a name="N100D9"><span id="wmqeeuq" class="smalltitle">通配W搜?Wildcard Search)</span></a></p> <p>Lucene 支持两种通配W:问号Q?Q和星号Q?Q。你可以使用问号Q?Q来q行单字W的通配W查询,或者利用星P*Q进行多字符的通配W查询。例如,如果你想搜烦 tiny 或?tonyQ你可以用查询语?“t?ny”Q如果你x?Teach, Teacher ?TeachingQ你可以用查询语?“Teach*”?a cmimpressionsent="1">清单3</a> 昄了通配W查询的q程?</p> <br /> <a name="Listing3"><strong>清单3Q进行通配W查?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">//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]); } } </pre> </td> </tr> </tbody> </table> <br /> <p><a name="N100F0"><span id="wmqeeuq" class="smalltitle">模糊查询</span></a></p> <p>Lucene 提供的模p查询基于编辑距ȝ?Edit distance algorithm)。你可以在搜索词的尾部加上字W?~ 来进行模p查询。例如,查询语句 “think~” q回所有包含和 think cM的关键词的文档?a cmimpressionsent="1">清单 4</a> 昄了如果利?Lucene ?API q行模糊查询的代码?</p> <br /> <a name="Listing4"><strong>清单4Q实现模p查?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">//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]); } } </pre> </td> </tr> </tbody> </table> <br /> <p><a name="N10107"><span id="wmqeeuq" class="smalltitle">范围搜烦(Range Search)</span></a></p> <p>范围搜烦匚w某个域上的值在一定范围的文档。例如,查询 “age:[18 TO 35]” q回所?age 域上的值在 18 ?35 之间的文档?a cmimpressionsent="1">清单5</a>昄了利?Lucene ?API q行q回搜烦的过E?</p> <br /> <a name="Listing5"><strong>清单5Q测试范围搜?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">//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"); } </pre> </td> </tr> </tbody> </table> <br /> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /> </td> <td valign="top" align="right"><a class="fbox" cmimpressionsent="1"><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="N1011E"><span id="wmqeeuq" class="atitle">?Web 应用E序中集?Lucene</span></a></p> <p>接下来我们开发一?Web 应用E序利用 Lucene 来检索存攑֜文g服务器上?HTML 文档。在开始之前,需要准备如下环境:</p> <ol> <li>Eclipse 集成开发环? <li>Tomcat 5.0 <li>Lucene Library <li>JDK 1.5 </li> </ol> <p>q个例子使用 Eclipse q行 Web 应用E序的开发,最l这?Web 应用E序跑在 Tomcat 5.0 上面。在准备好开发所必需的环境之后,我们接下来进?Web 应用E序的开发?</p> <p><a name="N10139"><span id="wmqeeuq" class="smalltitle">1、创Z个动?Web 目</span></a></p> <ol> <li>?Eclipse 里面Q选择 <strong>File > New > Project</strong>Q然后再弹出的窗口中选择<strong>动?Web 目</strong>Q如<a cmimpressionsent="1">图二</a>所C?</li> </ol> <br /> <a name="figure2"><strong>图二Q创建动态Web目</strong></a><br /> <img height="473" alt="创徏动态Web目" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure2.jpg" width="496" /> <br /> <ol start="2"> <li>在创建好动?Web 目之后Q你会看到创建好的项目的l构Q如<a cmimpressionsent="1">图三</a>所C,目的名UCؓ sample.dw.paper.lucene?</li> </ol> <br /> <a name="figure3"><strong>图三Q动?Web 目的结?/strong></a><br /> <img height="338" alt="动?Web 目的结? src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure3.jpg" width="329" /> <br /> <p><a name="N10178"><span id="wmqeeuq" class="smalltitle">2. 设计 Web 目的架?/span></a></p> <p>在我们的设计中,把该pȝ分成如下四个子系l:</p> <ol> <li><strong>用户接口</strong>: q个子系l提供用L面用户可以?Web 应用E序服务器提交搜索请求,然后搜烦l果通过用户接口来显C出来。我们用一个名?search.jsp 的页面来实现该子pȝ? <li><strong>h理?/strong>: q个子系l管理从客户端发送过来的搜烦hq把搜烦h分发到搜索子pȝ中。最后搜索结果从搜烦子系l返回ƈ最l发送到用户接口子系l。我们用一?Servlet 来实现这个子pȝ? <li><strong>搜烦子系l?/strong>: q个子系l负责在索引文g上进行搜索ƈ把搜索结构传递给h理器。我们?Lucene 提供?API 来实现该子系l? <li><strong>索引子系l?/strong>: q个子系l用来ؓ HTML 面来创建烦引。我们?Lucene ?API 以及 Lucene 提供的一?HTML 解析器来创徏该子pȝ?</li> </ol> <p><a cmimpressionsent="1">?</a> 昄了我们设计的详细信息Q我们将用户接口子系l放?webContent 目录下面。你会看C个名?search.jsp 的页面在q个文g多w面。请求管理子pȝ在包 <code>sample.dw.paper.lucene.servlet</code> 下面Q类 <code>SearchController</code> 负责功能的实现。搜索子pȝ攑֜?<code>sample.dw.paper.lucene.search</code> 当中Q它包含了两个类Q?code>SearchManager</code> ?<code>SearchResultBean</code>Q第一个类用来实现搜烦功能Q第二个cȝ来描q搜索结果的l构。烦引子pȝ攑֜?<code>sample.dw.paper.lucene.index</code> 当中。类 <code>IndexManager</code> 负责?HTML 文g创徏索引。该子系l利用包 <code>sample.dw.paper.lucene.util</code> 里面的类 <code>HTMLDocParser</code> 提供的方?<code>getTitle</code> ?<code>getContent</code> 来对 HTML 面q行解析?</p> <br /> <a name="figure4"><strong>囑֛Q项目的架构设计</strong></a><br /> <img height="324" alt="目的架构设? src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure4.jpg" width="384" /> <br /> <p><a name="N101DA"><span id="wmqeeuq" class="smalltitle">3. 子系l的实现</span></a></p> <p>在分析了pȝ的架构设计之后,我们接下来看pȝ实现的详l信息?</p> <ol> <li><strong>用户接口</strong>: q个子系l有一个名?search.jsp ?JSP 文g来实玎ͼq个 JSP 面包含两个部分。第一部分提供了一个用h口去?Web 应用E序服务器提交搜索请求,?a cmimpressionsent="1">?</a>所C。注意到q里的搜索请求发送到了一个名?SearchController ?Servlet 上面。Servlet 的名字和具体实现的类的对应关pd web.xml 里面指定?</li> </ol> <br /> <a name="figure5"><strong>?Q向Web服务器提交搜索请?/strong></a><br /> <img height="207" alt="向Web服务器提交搜索请? src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure5.jpg" width="532" /> <br /> <p>q个JSP的第二部分负责显C搜索结果给用户Q如<a cmimpressionsent="1">?</a>所C: </p> <br /> <a name="figure6"><strong>?Q显C搜索结?/strong></a><br /> <img height="360" alt="昄搜烦l果" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure6.jpg" width="572" /> <br /> <ol start="2"> <li><strong>h理?/strong>: 一个名?<code>SearchController</code> ?servlet 用来实现该子pȝ?a cmimpressionsent="1">清单Q?/a>l出了这个类的源代码?</li> </ol> <br /> <a name="Listing6"><strong>清单Q:h理器的实现</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">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); } } </pre> </td> </tr> </tbody> </table> <br /> <p>?a cmimpressionsent="1">清单6</a>中,<code>doPost</code> Ҏ从客L获取搜烦词ƈ创徏c?<code>SearchManager</code> 的一个实例,其中c?<code>SearchManager</code> 在搜索子pȝ中进行了定义。然后,<code>SearchManager</code> 的方?search 会被调用。最后搜索结果被q回到客L?</p> <ol start="3"> <li><strong>搜烦子系l?/strong>: 在这个子pȝ中,我们定义了两个类Q?code>SearchManager</code> ?<code>SearchResultBean</code>。第一个类用来实现搜烦功能Q第二个cL个JavaBeanQ用来描q搜索结果的l构?a cmimpressionsent="1">清单7</a>l出了类 <code>SearchManager</code> 的源代码?</li> </ol> <br /> <a name="Listing7"><strong>清单7Q搜索功能的实现</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">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; } } </pre> </td> </tr> </tbody> </table> <br /> <p>?a cmimpressionsent="1">清单7</a>中,注意到在q个c里面有三个U有属性。第一个是 <code>searchWord</code>Q代表了来自客户端的搜烦词。第二个?<code>indexManager</code>Q代表了在烦引子pȝ中定义的c?<code>IndexManager</code> 的一个实例。第三个?<code>analyzer</code>Q代表了用来解析搜烦词的解析器。现在我们把注意力放在方?<code>search</code> 上面。这个方法首先检查烦引文件是否已l存在,如果已经存在Q那么就在已l存在的索引上进行检索,如果不存在,那么首先调用c?<code>IndexManager</code> 提供的方法来创徏索引Q然后在新创建的索引上进行检索。搜索结果返回后Q这个方法从搜烦l果中提取出需要的属性ƈ为每个搜索结果生成类 <code>SearchResultBean</code> 的一个实例。最后这?<code>SearchResultBean</code> 的实例被攑ֈ一个列表里面ƈq回l请求管理器?/p> <p>在类 <code>SearchResultBean</code> 中,含有两个属性,分别?<code>htmlPath</code> ?<code>htmlTitle</code>Q以及这个两个属性的 get ?set Ҏ。这也意味着我们的搜索结果包含两个属性:<code>htmlPath</code> ?<code>htmlTitle</code>Q其?<code>htmlPath</code> 代表?HTML 文g的\径,<code>htmlTitle</code> 代表?HTML 文g的标题?</p> <ol start="4"> <li><strong>索引子系l?/strong>: c?<code>IndexManager</code> 用来实现q个子系l?a cmimpressionsent="1">清单8</a> l出了这个类的源代码?</li> </ol> <br /> <a name="Listing8"><strong>清单8Q烦引子pȝ的实?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">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; } } </pre> </td> </tr> </tbody> </table> <br /> <p>q个cd含两个私有属性,分别?<code>dataDir</code> ?<code>indexDir</code>?code>dataDir</code> 代表存放{待q行索引?HTML 面的\径,<code>indexDir</code> 代表了存?Lucene 索引文g的\径。类 <code>IndexManager</code> 提供了三个方法,分别?<code>createIndex</code>, <code>addDocument</code> ?<code>ifIndexExist</code>。如果烦引不存在的话Q你可以使用Ҏ <code>createIndex</code> dZ个新的烦引,用方?<code>addDocument</code> d一个烦引上d文档。在我们的场景中Q一个文档就是一?HTML 面。方?<code>addDocument</code> 会调用由c?<code>HTMLDocParser</code> 提供的方法对 HTML 文档q行解析。你可以使用最后一个方?<code>ifIndexExist</code> 来判?Lucene 的烦引是否已l存在?</p> <p>现在我们来看一下放在包 <code>sample.dw.paper.lucene.util</code> 里面的类 <code>HTMLDocParser</code>。这个类用来?HTML 文g中提取出文本信息。这个类包含三个ҎQ分别是 <code>getContent</code>Q?code>getTitle</code> ?<code>getPath</code>。第一个方法返回去除了 HTML 标记的文本内容,W二个方法返?HTML 文g的标题,最后一个方法返?HTML 文g的\径?a cmimpressionsent="1">清单9</a> l出了这个类的源代码?</p> <br /> <a name="Listing9"><strong>清单9QHTML 解析?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">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; } } </pre> </td> </tr> </tbody> </table> <br /> <p><a name="N10326"><span id="wmqeeuq" class="smalltitle">5Q在 Tomcat 5.0 上运行应用程?/span></a></p> <p>现在我们可以?Tomcat 5.0 上运行开发好的应用程序?</p> <ol> <li>右键单击 <strong>search.jsp</strong>Q然后选择 <strong>Run as > Run on Server</strong>Q如<a cmimpressionsent="1">?</a>所C?</li> </ol> <br /> <a name="figure7"><strong>?Q配|?Tomcat 5.0</strong></a><br /> <img height="434" alt="配置 Tomcat 5.0" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure7.jpg" width="478" /> <br /> <ol start="2"> <li>在弹出的H口中,选择 <strong>Tomcat v5.0 Server</strong> 作ؓ目标 Web 应用E序服务器,然后点击 <strong>Next</strong>Q如<a cmimpressionsent="1">?</a> 所C: </li> </ol> <br /> <a name="figure8"><strong>?Q选择 Tomcat 5.0</strong></a><br /> <img height="433" alt="选择 Tomcat 5.0" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure8.jpg" width="467" /> <br /> <ol start="3"> <li>现在需要指定用来运?Web 应用E序?Apache Tomcat 5.0 以及 JRE 的\径。这里你所选择?JRE 的版本必d你用来编?Java 文g?JRE 的版本一致。配|好之后Q点?<strong>Finish</strong>。如 <a cmimpressionsent="1">?</a> 所C?</li> </ol> <br /> <a name="figure9"><strong>?Q完成Tomcat 5.0的配|?/strong></a><br /> <img height="436" alt="完成Tomcat 5.0的配|? src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure9.jpg" width="468" /> <br /> <ol start="4"> <li>配置好之后,Tomcat 会自动运行,q且会对 search.jsp q行~译q显C给用户。如 <a cmimpressionsent="1">?0</a> 所C?</li> </ol> <br /> <a name="figure10"><strong>?0Q用L?/strong></a><br /> <img height="403" alt="用户界面" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure10.jpg" width="521" /> <br /> <ol start="5"> <li>在输入框中输入关键词 “information” 然后单击 <strong>Search</strong> 按钮。然后这个页面上会显C出搜烦l果来,?<a cmimpressionsent="1">?1</a> 所C?</li> </ol> <br /> <a name="figure11"><strong>?1Q搜索结?/strong></a><br /> <img height="416" alt="搜烦l果" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure11.jpg" width="526" /> <br /> <ol start="6"> <li>单击搜烦l果的第一个链接,面上就会显C出所链接到的面的内宏V如 <a cmimpressionsent="1">?2</a> 所C? </li> </ol> <br /> <a name="figure12"><strong>?2Q详l信?/strong></a><br /> <img height="419" alt="详细信息" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure12.jpg" width="525" /> <br /> <p>现在我们已经成功的完成了CZ目的开发,q成功的用Lucene实现了搜索和索引功能。你可以下蝲q个目的源代码Q?a cmimpressionsent="1">下蝲</a>Q?</p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /> <table cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /> </td> <td valign="top" align="right"><a class="fbox" cmimpressionsent="1"><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="N103E3"><span id="wmqeeuq" class="atitle">ȝ</span></a></p> <p>Lucene 提供了灵zȝ接口使我们更加方便的设计我们?Web 搜烦应用E序。如果你惛_你的应用E序中加入搜索功能,那么 Lucene 是一个很好的选择。在设计你的下一个带有搜索功能的应用E序的时候可以考虑使用 Lucene 来提供搜索功能?/p> <img src ="http://www.aygfsteel.com/wyl232/aggbug/229890.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/wyl232/" target="_blank">wyl</a> 2008-09-19 11:19 <a href="http://www.aygfsteel.com/wyl232/archive/2008/09/19/229890.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战 LuceneQ第 1 部分: 初识 Lucenehttp://www.aygfsteel.com/wyl232/archive/2008/09/19/229889.htmlwylwylFri, 19 Sep 2008 03:16:00 GMThttp://www.aygfsteel.com/wyl232/archive/2008/09/19/229889.htmlhttp://www.aygfsteel.com/wyl232/comments/229889.htmlhttp://www.aygfsteel.com/wyl232/archive/2008/09/19/229889.html#Feedback0http://www.aygfsteel.com/wyl232/comments/commentRss/229889.htmlhttp://www.aygfsteel.com/wyl232/services/trackbacks/229889.htmlU别Q?初

?周登 (zhoudengpeng@yahoo.com.cn), 软g工程?br />

2006 q?4 ?20 ?/p>

本文首先介绍了Lucene的一些基本概念,然后开发了一个应用程序演CZ利用Lucene建立索引q在该烦引上q行搜烦的过E?/blockquote>

Lucene ?/span>

Lucene 是一个基?Java 的全文信息检索工具包Q它不是一个完整的搜烦应用E序Q而是Z的应用程序提供烦引和搜烦功能。Lucene 目前?Apache Jakarta 家族中的一个开源项目。也是目前最为流行的Z Java 开源全文检索工具包?/p>

目前已经有很多应用程序的搜烦功能是基?Lucene 的,比如 Eclipse 的帮助系l的搜烦功能。Lucene 能够为文本类型的数据建立索引Q所以你只要能把你要索引的数据格式{化的文本的,Lucene p对你的文档进行烦引和搜烦。比如你要对一?HTML 文档QPDF 文档q行索引的话你就首先需要把 HTML 文档?PDF 文档转化成文本格式的Q然后将转化后的内容交给 Lucene q行索引Q然后把创徏好的索引文g保存到磁盘或者内存中Q最后根据用戯入的查询条g在烦引文件上q行查询。不指定要烦引的文档的格式也?Lucene 能够几乎适用于所有的搜烦应用E序?/p>

?1 表示了搜索应用程序和 Lucene 之间的关p,也反映了利用 Lucene 构徏搜烦应用E序的流E:


?. 搜烦应用E序?Lucene 之间的关p?/strong>
?. 搜烦应用E序?Lucene 之间的关p? src=




回页?/strong>


索引和搜?/span>

索引是现代搜索引擎的核心Q徏立烦引的q程是把源数据处理成非常方便查询的索引文g的过E。ؓ什么烦引这么重要呢Q试想你现在要在大量的文档中搜烦含有某个关键词的文档Q那么如果不建立索引的话你就需要把q些文档序的读入内存,然后查这个文章中是不是含有要查找的关键词Q这L话就会耗费非常多的旉Q想x索引擎可是在毫秒U的旉内查扑և要搜索的l果的。这是׃建立了烦引的原因Q你可以把烦引想象成q样一U数据结构,他能够你快速的随机讉K存储在烦引中的关键词Q进而找到该关键词所兌的文档。Lucene 采用的是一U称为反向烦引(inverted indexQ的机制。反向烦引就是说我们l护了一个词/短语表,对于q个表中的每个词/短语Q都有一个链表描qC有哪些文档包含了q个?短语。这样在用户输入查询条g的时候,p非常快的得到搜烦l果。我们将在本pd文章的第二部分详l介l?Lucene 的烦引机Ӟ׃ Lucene 提供了简单易用的 APIQ所以即使读者刚开始对全文本进行烦引的机制q不太了解,也可以非常容易的使用 Lucene 对你的文档实现烦引?/p>

Ҏ档徏立好索引后,可以在q些索引上面q行搜烦了。搜索引擎首先会Ҏ索的关键词进行解析,然后再在建立好的索引上面q行查找Q最l返回和用户输入的关键词相关联的文档?/p>



回页?/strong>


Lucene 软g包分?/span>

Lucene 软g包的发布形式是一?JAR 文gQ下面我们分析一下这?JAR 文g里面的主要的 JAVA 包,使读者对之有个初步的了解?/p>

Package: org.apache.lucene.document

q个包提供了一些ؓ装要烦引的文档所需要的c,比如 Document, Field。这P每一个文档最l被装成了一?Document 对象?/p>

Package: org.apache.lucene.analysis

q个包主要功能是Ҏ档进行分词,因ؓ文档在徏立烦引之前必要q行分词Q所以这个包的作用可以看成是为徏立烦引做准备工作?/p>

Package: org.apache.lucene.index

q个包提供了一些类来协助创建烦引以及对创徏好的索引q行更新。这里面有两个基的类QIndexWriter ?IndexReaderQ其?IndexWriter 是用来创建烦引ƈd文档到烦引中的,IndexReader 是用来删除烦引中的文档的?/p>

Package: org.apache.lucene.search

q个包提供了对在建立好的索引上进行搜索所需要的cR比?IndexSearcher ?Hits, IndexSearcher 定义了在指定的烦引上q行搜烦的方法,Hits 用来保存搜烦得到的结果?/p>



回页?/strong>


一个简单的搜烦应用E序

假设我们的电脑的目录中含有很多文本文档,我们需要查扑֓些文档含有某个关键词。ؓ了实现这U功能,我们首先利用 Lucene 对这个目录中的文档徏立烦引,然后在徏立好的烦引中搜烦我们所要查扄文档。通过q个例子读者会对如何利?Lucene 构徏自己的搜索应用程序有个比较清楚的认识?/p>



回页?/strong>


建立索引

ZҎ档进行烦引,Lucene 提供了五个基的类Q他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介l一下这五个cȝ用途:

Document

Document 是用来描q文档的Q这里的文档可以指一?HTML 面Q一电子邮Ӟ或者是一个文本文件。一?Document 对象由多?Field 对象l成的。可以把一?Document 对象惌成数据库中的一个记录,而每?Field 对象是记录的一个字Dc?/p>

Field

Field 对象是用来描qC个文档的某个属性的Q比如一电子邮件的标题和内容可以用两个 Field 对象分别描述?/p>

Analyzer

在一个文档被索引之前Q首先需要对文档内容q行分词处理Q这部分工作是?Analyzer 来做的。Analyzer cL一个抽象类Q它有多个实现。针对不同的语言和应用需要选择适合?Analyzer。Analyzer 把分词后的内容交l?IndexWriter 来徏立烦引?/p>

IndexWriter

IndexWriter ?Lucene 用来创徏索引的一个核心的c,他的作用是把一个个?Document 对象加到索引中来?/p>

Directory

q个cM表了 Lucene 的烦引的存储的位|,q是一个抽象类Q它目前有两个实玎ͼW一个是 FSDirectoryQ它表示一个存储在文gpȝ中的索引的位|。第二个?RAMDirectoryQ它表示一个存储在内存当中的烦引的位置?/p>

熟悉了徏立烦引所需要的q些cdQ我们就开始对某个目录下面的文本文件徏立烦引了Q清?l出了对某个目录下的文本文g建立索引的源代码?/p>
清单 1. Ҏ本文件徏立烦?/strong>
package TestLucene;
            import java.io.File;
            import java.io.FileReader;
            import java.io.Reader;
            import java.util.Date;
            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;
            /**
            * This class demonstrate the process of creating index with Lucene
            * for text files
            */
            public class TxtFileIndexer {
            public static void main(String[] args) throws Exception{
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("D:\\luceneIndex");
            //dataDir is the directory that hosts the text files that to be indexed
            File   dataDir  = new File("D:\\luceneData");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            File[] dataFiles  = dataDir.listFiles();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            long startTime = new Date().getTime();
            for(int i = 0; i < dataFiles.length; i++){
            if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){
            System.out.println("Indexing file " + dataFiles[i].getCanonicalPath());
                    		Document document = new Document();
            Reader txtReader = new FileReader(dataFiles[i]);
            document.add(Field.Text("path",dataFiles[i].getCanonicalPath()));
            document.add(Field.Text("contents",txtReader));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("It takes " + (endTime - startTime)
            + " milliseconds to create index for the files in directory "
            + dataDir.getPath());
            }
            }
            

在清?中,我们注意到类 IndexWriter 的构造函数需要三个参敎ͼW一个参数指定了所创徏的烦引要存放的位|,他可以是一?File 对象Q也可以是一?FSDirectory 对象或?RAMDirectory 对象。第二个参数指定?Analyzer cȝ一个实玎ͼ也就是指定这个烦引是用哪个分词器Ҏ挡内容进行分词。第三个参数是一个布型的变量,如果?true 的话׃表创Z个新的烦引,?false 的话׃表在原来索引的基上进行操作。接着E序遍历了目录下面的所有文本文档,qؓ每一个文本文档创Z一?Document 对象。然后把文本文档的两个属性:路径和内容加入到了两?Field 对象中,接着在把q两?Field 对象加入?Document 对象中,最后把q个文档?IndexWriter cȝ add Ҏ加入到烦引中厅R这h们便完成了烦引的创徏。接下来我们q入在徏立好的烦引上q行搜烦的部分?/p>



回页?/strong>


搜烦文档

利用Luceneq行搜烦像建立索引一样也是非常方便的。在上面一部分中,我们已经Z个目录下的文本文档徏立好了烦引,现在我们p在这个烦引上q行搜烦以找到包含某个关键词或短语的文档。Lucene提供了几个基的类来完成这个过E,它们分别是呢IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍q几个类的功能?/p>

Query

q是一个抽象类Q他有多个实玎ͼ比如TermQuery, BooleanQuery, PrefixQuery. q个cȝ目的是把用户输入的查询字W串装成Lucene能够识别的Query?/p>

Term

Term是搜索的基本单位Q一个Term对象有两个Stringcd的域l成。生成一个Term对象可以有如下一条语句来完成QTerm term = new Term(“fieldName”,”queryWord”); 其中W一个参C表了要在文档的哪一个Field上进行查找,W二个参C表了要查询的关键词?/p>

TermQuery

TermQuery是抽象类Query的一个子c,它同时也是Lucene支持的最为基本的一个查询类。生成一个TermQuery对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的构造函数只接受一个参敎ͼ那就是一个Term对象?/p>

IndexSearcher

IndexSearcher是用来在建立好的索引上进行搜索的。它只能以只ȝ方式打开一个烦引,所以可以有多个IndexSearcher的实例在一个烦引上q行操作?/p>

Hits

Hits是用来保存搜索的l果的?/p>

介绍完这些搜索所必须的类之后Q我们就开始在之前所建立的烦引上q行搜烦了,清单2l出了完成搜索功能所需要的代码?/p>
清单2 Q在建立好的索引上进行搜?/strong>
package TestLucene;
            import java.io.File;
            import org.apache.lucene.document.Document;
            import org.apache.lucene.index.Term;
            import org.apache.lucene.search.Hits;
            import org.apache.lucene.search.IndexSearcher;
            import org.apache.lucene.search.TermQuery;
            import org.apache.lucene.store.FSDirectory;
            /**
            * This class is used to demonstrate the
            * process of searching on an existing
            * Lucene index
            *
            */
            public class TxtFileSearcher {
            public static void main(String[] args) throws Exception{
            String queryStr = "lucene";
            //This is the directory that hosts the Lucene index
            File indexDir = new File("D:\\luceneIndex");
            FSDirectory directory = FSDirectory.getDirectory(indexDir,false);
            IndexSearcher searcher = new IndexSearcher(directory);
            if(!indexDir.exists()){
            System.out.println("The Lucene index is not exist");
            return;
            }
            Term term = new Term("contents",queryStr.toLowerCase());
            TermQuery luceneQuery = new TermQuery(term);
            Hits hits = searcher.search(luceneQuery);
            for(int i = 0; i < hits.length(); i++){
            Document document = hits.doc(i);
            System.out.println("File: " + document.get("path"));
            }
            }
            }
            

在清?中,cIndexSearcher的构造函数接受一个类型ؓDirectory的对象,Directory是一个抽象类Q它目前有两个子c:FSDirctory和RAMDirectory. 我们的程序中传入了一个FSDirctory对象作ؓ其参敎ͼ代表了一个存储在盘上的索引的位|。构造函数执行完成后Q代表了q个IndexSearcher以只ȝ方式打开了一个烦引。然后我们程序构造了一个Term对象Q通过q个Term对象Q我们指定了要在文档的内容中搜烦包含关键?#8221;lucene”的文档。接着利用q个Term对象构造出TermQuery对象q把q个TermQuery对象传入到IndexSearcher的searchҎ中进行查询,q回的结果保存在Hits对象中。最后我们用了一个@环语句把搜烦到的文档的\径都打印了出来。好了,我们的搜索应用程序已l开发完毕,怎么P利用Lucene开发搜索应用程序是不是很简单?/p>



回页?/strong>


ȝ

本文首先介绍?Lucene 的一些基本概念,然后开发了一个应用程序演CZ利用 Lucene 建立索引q在该烦引上q行搜烦的过E。希望本文能够ؓ学习 Lucene 的读者提供帮助?/p>

关于作?/span>

 

周登朋,软g工程师,上v交通大学研I生Q对 Java 技术以及信息检索技术很感兴。您可以通过 zhoudengpeng@yahoo.com.cn 与他联系?/p>



wyl 2008-09-19 11:16 发表评论
]]> վ֩ģ壺 | | üɽ| ³ƶ| | ³ɽ| ԫ| ƽ| | | | | ֶ| Ѯ| ī񹤿| | ԰| | ֶ| | | ƽ| | | | ͡| ƽ½| | | | | | | ƽ| | | ͩ| | | | Ͼ|