欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

用compass快速给你的网站添加搜索功能<二> 博客分类: Compass Lucene log4jJ#BeanSpringfreemarker 

程序员文章站 2024-03-08 14:11:46
...
在上一篇文章中主要讲了,配置要索引的表和compass与spring整合时的配置。接下来我把余下的两部分写出来。
       第三步:配置手动创建索引的功能。
       这个其实只需在第一次生成索引的时候用,当系统正常运行时,compass中的hibernateGps会自动检测数据的变动,同时同步索引文件的。
      首先在applicationContext.xml中配置bean.
       <!-- 手工生成索引 -->
 <bean id="buildIndexController"
  class="org.compass.spring.web.mvc.CompassIndexController">
  <property name="compassGps" ref="hibernateGps" />
  <property name="indexView" value="/ftl/create.ftl" />
  <property name="indexResultsView" value="/ftl/create.ftl" />
 </bean>
 同时我们也要配置与之相应的请求映射。
 <bean id="urlHandlerMapping"
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="mappings">
   <props>
       <prop key="/buildindex.htm">buildIndexController</prop>
      </props>
  </property>
 </bean>
  最后就是自动生成索引的页面了,这里我用的是freemarker.用什么都一样,你可以用jsp,html.
 create.ftl(在WebRoot下的ftl文件夹下)
 <html>
 <head>
  <title>builderIndex</title>
 </head>
 <body>
  <h1>
   手工重建索引
  </h1>
  <p>
   <form name="keyword" action="/buildindex.htm" method="post">
   <INPUT type="hidden" name="doIndex" value="true">
   <input type="submit" value="手工重建索引" />
  </form>
  <#if indexResults?exists>
    本次索引耗时${indexResults.indexTime}毫秒!
    </#if>
    <p>
 </body>
</html>
 当我们点击手工重建索引按钮时,会在我们指定的索引目录下<prop key="compass.engine.connection">E:/video</prop>也就先在E:/video下生成gpsindex/video-index当索引生成完以后,会返回生成索引所用的毫秒数。这时就会把gpsindex文件夹下的video-index覆盖到E:/video/index/下的video-index文件夹。这时gpsindex/video-index也消失了。这就是简单的生成索引的过程。接下来我们要说最后一步了。索引我们已建好了,怎么去搜索我们想要的数据了。
   第四步:建立搜索功能。
     首先写一个controller.即searchController.java
public class SearchController extends AbstractCompassCommandController {
 private String searchView;
 private String searchResultsView;
 private String searchResultsName = "searchResults";
 private Integer pageSize;
 private Integer sectionSize;
 private CompassSearchHelper searchHelper;
 public CompassSearchHelper getSearchHelper() {
  return searchHelper;
 }
 public SearchController() {
  setCommandClass(CompassSearchCommand.class);
 }
 public void afterPropertiesSet() throws Exception {
  super.afterPropertiesSet();
  if (searchView == null) {
   throw new IllegalArgumentException(
     "Must set the searchView property");
  }
  if (searchResultsView == null) {
   throw new IllegalArgumentException(
     "Must set the serachResultsView property");
  }
  if (searchHelper == null) {
   searchHelper = new CompassSearchHelper(getCompass(), getPageSize());
  }
 }
 protected ModelAndView handle(HttpServletRequest request,
   HttpServletResponse response, Object command, BindException errors)
   throws Exception {
  Log log4j = LogFactory.getLog("video");
  log4j.info("SearchController say: hello");
  final CompassSearchCommand searchCommand = (CompassSearchCommand) command;
  String query = searchCommand.getQuery();
  searchCommand.setQuery(query);
  if (!StringUtils.hasText(searchCommand.getQuery())) {
   return new ModelAndView(getSearchView(), getCommandName(),
     searchCommand);
  }
  CompassSearchResults searchResults = searchHelper.search(searchCommand);
  log4j.info("searchResults.getHits().length = "
    + searchResults.getHits().length);
  
  CompassPage page = new CompassPage(searchResults.getHits(),
    pageSize.intValue(),
    searchCommand.getPage().intValue() + 1,
    searchResults.getTotalHits(),
    sectionSize.intValue(),
    searchCommand.getQuery(),
    searchResults.getSearchTime());
  return new ModelAndView(getSearchResultsView(), "data", page);
 }
 /**
  * Returns the view that holds the screen which the user will initiate the
  * search operation.
  */
 public String getSearchView() {
  return searchView;
 }
 /**
  * Sets the view that holds the screen which the user will initiate the
  * search operation.
  */
 public void setSearchView(String searchView) {
  this.searchView = searchView;
 }
 /**
  * Returns the name of the results that the
  * {@link org.compass.core.support.search.CompassSearchResults} will be
  * saved under. Defaults to "searchResults".
  */
 public String getSearchResultsName() {
  return searchResultsName;
 }
 /**
  * Sets the name of the results that the
  * {@link org.compass.core.support.search.CompassSearchResults} will be
  * saved under. Defaults to "searchResults".
  */
 public void setSearchResultsName(String searchResultsName) {
  this.searchResultsName = searchResultsName;
 }
 /**
  * Returns the view which will show the results of the search operation.
  */
 public String getSearchResultsView() {
  return searchResultsView;
 }
 /**
  * Sets the view which will show the results of the search operation.
  */
 public void setSearchResultsView(String resultsView) {
  this.searchResultsView = resultsView;
 }
 /**
  * Sets the page size for the pagination of the results. If not set, not
  * pagination will be used.
  */
 public Integer getPageSize() {
  return pageSize;
 }
 /**
  * Returns the page size for the pagination of the results. If not set, not
  * pagination will be used.
  * 
  * @param pageSize
  *            The page size when using paginated results
  */
 public void setPageSize(Integer pageSize) {
  this.pageSize = pageSize;
 }
 /**
  * <p>
  * The search helper is used to execute teh actual search. By default (if
  * not set) the search controller will create a new search helper. If
  * provided, the search controller will use it to perform the search.
  * 
  * <p>
  * Mainly used to extend the search helper and execute additional operation
  * within specific calbacks the search helper exposes.
  * 
  * @param searchHelper
  *            A specific search helper to use
  */
 public void setSearchHelper(CompassSearchHelper searchHelper) {
  this.searchHelper = searchHelper;
 }
 public Integer getSectionSize() {
  return sectionSize;
 }
 public void setSectionSize(Integer sectionSize) {
  this.sectionSize = sectionSize;
 }
}
 还需要一个CompassPage.java主要来实现分页的。
 public class CompassPage implements Page {
 private CompassHit[] elements;
 private int pageSize;
 private int pageNumber;
 private int totalElements = 0;
 private int sectionSize;
 private String query;
 private long searchTime;
 public long getSearchTime() {
  return searchTime;
 }
 public void setSearchTime(long searchTime) {
  this.searchTime = searchTime;
 }
 /**
  * 
  * @param pageNumber
  *            当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
  *            如果你不知道最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
  *            这两种情况将重新更改当前页的页码为最后一页编码。
  * @param pageSize
  *            每一页显示的条目数
  * @param sectionSize
  *            每一节显示的页数.
  */
 public CompassPage(CompassHit[] elements, int pageSize, int pageNumber, int totalElements, int sectionSize, String query, long searchTime) {
  super();
  this.elements = elements;
  this.pageSize = pageSize;
  this.pageNumber = pageNumber;
  this.totalElements = totalElements;
  this.sectionSize = sectionSize;
  this.query = query;
  this.searchTime = searchTime;
  
  if (Integer.MAX_VALUE == this.pageNumber
    || this.pageNumber > getLastPageNumber()) // last page
  {
   this.pageNumber = getLastPageNumber();
  } 
 }
 public String getQuery() {
  return query;
 }
 public void setQuery(String query) {
  this.query = query;
 }
 public CompassHit[] getElements() {
  return elements;
 }
 public void setElements(CompassHit[] elements) {
  this.elements = elements;
 }
 public int getPageNumber() {
  return pageNumber;
 }
 public void setPageNumber(int pageNumber) {
  this.pageNumber = pageNumber;
 }
 public int getSectionSize() {
  return sectionSize;
 }
 public void setSectionSize(int sectionSize) {
  this.sectionSize = sectionSize;
 }
 public int getTotalElements() {
  return totalElements;
 }
 public void setTotalElements(int totalElements) {
  this.totalElements = totalElements;
 }
 public void setPageSize(int pageSize) {
  this.pageSize = pageSize;
 }
 public int getLastPageNumber() {
  return totalElements % this.pageSize == 0 ? totalElements
    / this.pageSize : totalElements / this.pageSize + 1;
 }
 public int getNextPageNumber() {
  return getThisPageNumber() + 1;
 }
 public int getNextSectionFirstPageNumber() {
  return (pageNumber / sectionSize + 1) * sectionSize + 1;
 }
 public int getPageSize() {
  return pageSize;
 }
 public int getPreviousPageNumber() {
  return (getThisPageNumber() == 1) ? 1 : getThisPageNumber() - 1;
 }
 public int getPreviousSectionFirstPageNumber() {
  return (pageNumber / sectionSize - 1) * sectionSize + 1;
 }
 public Object getThisPageElements() {
  return elements;
 }
 public int getThisPageFirstElementNumber() {
  return (getThisPageNumber() - 1) * getPageSize() + 1;
 }
 public int getThisPageLastElementNumber() {
  int fullPage = getThisPageFirstElementNumber() + getPageSize() - 1;
  return getTotalNumberOfElements() < fullPage ? getTotalNumberOfElements()
    : fullPage;
 }
 public int getThisPageNumber() {
  return pageNumber;
 }
 public int getThisSectionFirstPageNumber() {
  return (getThisPageNumber() % sectionSize == 0) ? getThisPageNumber()
    / sectionSize * sectionSize : getThisPageNumber() / sectionSize
    * sectionSize + 1;
 }
 public int getThisSectionLastPageNumber() {
  int fullSection = (getThisPageNumber() / sectionSize + 1) * sectionSize;
  return getLastPageNumber() < fullSection ? getLastPageNumber()
    : fullSection;
 }
 public List getThisSectionPagesBar() {
  // 翻页 页码条
  List pagesBar = new ArrayList();
  if (hasPreviousSection()) { // 不是第一捆
   if (hasNextSection()) {// 不是最后一捆
    for (int j = getPreviousSectionFirstPageNumber() + sectionSize; j <= getNextSectionFirstPageNumber() - 1; j++) {
     pagesBar.add(new Integer(j));
    }
   } else { // 是最后一捆
    for (int j = getPreviousSectionFirstPageNumber() + sectionSize; j <= (getLastPageNumber()); j++) {
     pagesBar.add(new Integer(j));
    }
   }
  } else { // 是第一捆
   // log4j.info("是第一捆 getLastPageNumber()=" + getLastPageNumber());
   for (int j = 1; j <= (getLastPageNumber() < sectionSize ? getLastPageNumber()
     : sectionSize); j++) {
    // log4j.info("j=" + j);
    pagesBar.add(new Integer(j));
   }
  }
  return pagesBar;
 }
 public int getTotalNumberOfElements() {
  return totalElements;
 }
 public boolean hasNextPage() {
  return getLastPageNumber() > getThisPageNumber();
 }
 public boolean hasNextSection() {
  return getLastPageNumber() > (pageNumber / sectionSize + 1)
    * sectionSize;
 }
 public boolean hasPreviousPage() {
  return getThisPageNumber() > 1;
 }
 public boolean hasPreviousSection() {
  return pageNumber / sectionSize * sectionSize > 1;
 }
 public boolean isFirstPage() {
  return getThisPageNumber() == 1;
 }
 public boolean isLastPage() {
  return getThisPageNumber() >= getLastPageNumber();
 }
}
在定义一个分页接口Page.java。
/**
 * 分页信息接口
 */
public interface Page
{
    /**
     * 是否是首页(第一页),第一页页码为1
     *
     * @return 首页标识
     */
    public boolean isFirstPage();
    /**
     * 是否是最后一页
     *
     * @return 末页标识
     */
    public boolean isLastPage();
    /**
     * 是否有下一页
     *
     * @return 下一页标识
     */
    public boolean hasNextPage();
    /**
     * 是否有上一页
     *
     * @return 上一页标识
     */
    public boolean hasPreviousPage();
    /**
      是否有下一个页面单元
    */
    public boolean hasNextSection();
    /**是否有前一个页面单元*/
    public boolean hasPreviousSection();
    /**
     * 获取最后一页页码,也就是总页数
     *
     * @return 最后一页页码
     */
    public int getLastPageNumber();
    /**
     * 当前页包含的数据,不同的情况可能返回的数据类型不一样,如List,RowSet等,请参考具体的实现
     *
     * @return 当前页数据源
     */
    public Object getThisPageElements();
    /**
     * 总的数据条目数量,0表示没有数据
     *
     * @return 总数量
     */
    public int getTotalNumberOfElements();
    /**
     * 获取当前页的首条数据的行编码
     *
     * @return 当前页的首条数据的行编码
     */
    public int getThisPageFirstElementNumber();
    /**
     * 获取当前页的末条数据的行编码
     *
     * @return 当前页的末条数据的行编码
     */
    public int getThisPageLastElementNumber();
    /**
     * 获取下一页编码
     *
     * @return 下一页编码
     */
    public int getNextPageNumber();
    /**
     * 获取上一页编码
     *
     * @return 上一页编码
     */
    public int getPreviousPageNumber();
    /* 获取下一个页面单元的第一页 */
    public int getNextSectionFirstPageNumber();
    /* 获取前一个页面单元的第一页 */
    public int getPreviousSectionFirstPageNumber();
    /**
     * 每一页显示的条目数
     *
     * @return 每一页显示的条目数
     */
    public int getPageSize();
    /**
     * 当前页的页码
     *
     * @return 当前页的页码
     */
    public int getThisPageNumber();

    /*
     当前片断第一页页码
    */
    public int getThisSectionFirstPageNumber();
    /*
     当前片断第一页页码
    */
    public int getThisSectionLastPageNumber();
   /*
    *当前片断 页码表 
    */
    public List getThisSectionPagesBar();

}

最后要配置一下bean和请求映射了。
<bean id="urlHandlerMapping"
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="mappings">
   <props>
     <prop key="/search.htm">searchController</prop> 
   </props>
  </property>
 </bean>
 
<!-- 搜索的控制器 -->
 <bean id="searchController"
  class="com.jack.video.SearchController" lazy-init="true">
  <property name="compass" ref="compass" />
  <property name="searchView" value="/ftl/search.ftl" />
  <property name="searchResultsView"
   value="/ftl/search.ftl" />
  <property name="pageSize" value="10" />
  <property name="sectionSize" value="10" />
  <property name="searchHelper">
   <ref local="advanceCompassSearchHelper" />
  </property>
 </bean>
 最后为了能给搜索到的结果高亮显示。还需要配置。
<!-- 高亮字段显示 -->
 <bean id="advanceCompassSearchHelper"
  class="com.jack.video.util.AdvanceCompassSearchHelper">
  <property name="highlightFields">
   <list>
    <value>CName</value>
   </list>
  </property>
  <property name="pageSize">
   <value>10</value>
  </property>
  <constructor-arg ref="compass" />
 </bean>
 
AdvanceCompassSearchHelper .java文件
 
 public class AdvanceCompassSearchHelper extends CompassSearchHelper {
 private String[] highlightFields;
 public String[] getHighlightFields() {
  return highlightFields;
 }
 public void setHighlightFields(String[] highlightFields) {
  this.highlightFields = highlightFields;
 }
 /**
  * @param compass
  */
 public AdvanceCompassSearchHelper(Compass compass) {
  super(compass);
 }
 /*
  * (non-Javadoc)
  * 
  * @see org.compass.core.support.search.CompassSearchHelper#doProcessBeforeDetach(org.compass.core.support.search.CompassSearchCommand,
  *      org.compass.core.CompassSession, org.compass.core.CompassHits, int,
  *      int)
  */
 @Override
 protected void doProcessBeforeDetach(CompassSearchCommand searchCommand,
   CompassSession session, CompassHits hits, int from, int size) {
  if (from < 0) {
   from = 0;
   size = hits.getLength();
  }
  if (highlightFields == null) {
   return;
  }
  // highlight fields
  for (int i = from; i < size; i++) {
   for (String highlightField : highlightFields) {
    hits.highlighter(i).fragment(highlightField);
   }
  }
 }
}



这是搜索页主要的代码。
<form method="get" action="/search.htm">
	<input type="text" size="50" name="query" value="${query?if_exists}" />
      <input type="hidden" name="page" value="1"/>      <input type="submit" value="搜索" />
</form>
以下是结果页的主要代码。


<#macro s_search_result data="" config=""> <script type="text/javascript">  var videoList = {   channelId: ${channelId}  }; </script> <div class="box">     <div class="ir_box">         <div class="title">    <@s_option_button />                <ul class="tag_menu">                 <li><a class="current" href="javascript:;"><span>搜索结果</span></a></li>                </ul>            </div>            <div class="content">             <@s_module_config data=config />                <div class="show_mode">                </div>                <div class="show_mode">                 <ul class="nonce_mode">                     <li>共有 ${data.totalElements} 项符合 ${data.query} 的查询结果</li>                        <li>第 ${data.pageNumber}/${data.lastPageNumber} 页</li>                  <li>用时 ${data.searchTime} 毫秒</li>                    </ul>                 <ul class="choose_mode">                  <li></li>                  <li></li>                 </ul>                </div>              <#if data.elements?exists && data.elements?has_content >               <#list data.elements as d>                 <div class="veol_cloud">                                       <div class="vec_content">                                                    <div class="vec_info">                              <p>                                    <strong>名字:</strong><@s_se v=d.data.description />                                 </p>                                 <p>                                     <strong>导演:</strong>                                     <span class="null_director"><@s_se v=d.data.director /></span>                                 </p>                                                                <p>                                  <strong>类型:</strong>                                  <span class="null_genre"><@s_se v=d.data.genre /></span>                                 </p>                                                                    </div>                                                                                   </div>                     </div>                 </div>            </#list>    <ul class="paging">     <li>      <span>共有 <b>${data.totalElements}</b> 项&nbsp;第 <b>${data.pageNumber}</b> / <b>${data.lastPageNumber}</b> 页</span>     </li>     <li>      <a href="/search.htm?query=${data.query}&page=1" title="第一页">&larr;</a>     </li>     <#if (data.getThisSectionFirstPageNumber()>1)>      <li>       <a href="/search.htm?query=${data.query}&page=${data.getThisSectionFirstPageNumber()}" title="快速向前翻10页" />&lt;&lt;</a>      </li>     </#if>     <#if (data.getPreviousPageNumber()>1)>       <li>       <a href="/search.htm?query=${data.query}&page=${data.getPreviousPageNumber()}" title="向前翻1页" />&lt;</a>      </li>     </#if>     <#if data.getThisSectionPagesBar()?exists>      <#list data.getThisSectionPagesBar() as d>       <li>        <a href="/search.htm?query=${data.query}&page=${d}">${d}</a>       </li>      </#list>     </#if>     <#if (data.getNextPageNumber()<=data.lastPageNumber)>      <li>       <a href="/search.htm?query=${data.query}&page=${data.getNextPageNumber()}" title="向后翻1页" />&gt;</a>      </li>     </#if>     <#if (data.getNextSectionFirstPageNumber()<=data.lastPageNumber)>      <li>       <a href="/search.htm?query=${data.query}&page=${data.getNextSectionFirstPageNumber()}" title="快速向后翻10页" />&gt;&gt;</a>      </li>     </#if>     <li>      <a href="/search.htm?query=${data.query}&page=${data.lastPageNumber}" title="最后一页">&rarr;</a>     </li>    </ul>              <#else>                  暂无相关影片              </#if>            </div>        </div>    </div></#macro>


现在把快速给你的网站搭建搜索功能已讲完。写的过程中可能有不对的地方,希望你在调试的时候该过来。基本的实现原理及过程已讲了,至于如何用,就看你自己来。我主要快速的把要点讲了一下。里面还有很多细节。有时间的话再和大家讨论。