用compass实现站内全文搜索引擎

Compass是一个强大的,事务的,高性能的对象/搜索引擎映射(OSEM:object/search engine mapping)与一个Java持久层框架.Compass包括: 
* 搜索引擎抽象层(使用Lucene搜索引荐),
* OSEM (Object/Search Engine Mapping) 支持,
* 事务管理,
* 类似于Google的简单关键字查询语言,
* 可扩展与模块化的框架,
* 简单的API.

如果你需要做站内搜索引擎,而且项目里用到了hibernate,那用compass是你的最佳选择。

本文用到compass2.14和IK中文分词包,另外将会使用注解来实现

废话不说,先给出关键的实现代码

假如现在有个需求,需要根据关键字搜索出文章,

Java代码
  1. @Searchable (alias= "article" )   
  2. public   class  Article {  
  3.   
  4.     private  Long ID;  // 标识ID   
  5.     private  String content;  // 正文   
  6.     private  String title;  // 文章标题   
  7.     private  Date createTime;  // 创建时间   
  8.   
  9.     @SearchableId   
  10.     public  Long getID() {  
  11.         return  ID;  
  12.     }  
  13.   
  14.     public   void  setID(Long id) {  
  15.         ID = id;  
  16.     }  
  17.   
  18.     @SearchableProperty (index = Index.TOKENIZED, store = Store.YES)  
  19.     public  String getContent() {  
  20.         return  content;  
  21.     }  
  22.   
  23.     public   void  setContent(String content) {  
  24.         this .content = content;  
  25.     }  
  26.   
  27.   
  28.   
  29.     @SearchableProperty (index = Index.TOKENIZED, store = Store.YES)  
  30.     public  String getTitle() {  
  31.         return  title;  
  32.     }  
  33.   
  34.     public   void  setTitle(String title) {  
  35.         this .title = title;  
  36.     }  
  37.   
  38.   
  39.     @SearchableProperty (index = Index.TOKENIZED, store = Store.YES)  
  40.     public  Date getCreateTime() {  
  41.         return  createTime;  
  42.     }  
  43.   
  44.     public   void  setCreateTime(Date createTime) {  
  45.         this .createTime = createTime;  
  46.     }  
  47.   
  48.   
  49.   
  50.       
  51.   
  52.   
  53. }  
@Searchable(alias="article") 
public class Article {

	private Long ID; // 标识ID
	private String content; // 正文
	private String title; // 文章标题
	private Date createTime; // 创建时间

	@SearchableId
	public Long getID() {
		return ID;
	}

	public void setID(Long id) {
		ID = id;
	}

	@SearchableProperty(index = Index.TOKENIZED, store = Store.YES)
	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}



	@SearchableProperty(index = Index.TOKENIZED, store = Store.YES)
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}


	@SearchableProperty(index = Index.TOKENIZED, store = Store.YES)
	public Date getCreateTime() {
		return createTime;
	}

	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}



	


}




简单解释一下:
@Searchable(alias="article")表示这个是可以搜索实体,别人为article.
@SearchableId  这个是实体搜索的标识ID,和hibernate里的概念差不多,用来区分索引文件里的实体索引。
@SearchableProperty(index = Index.TOKENIZED, store = Store.YES) 表示这个属性存入索引文件,而且是在分词后在存入.

 

 

接下来是要建立搜索的服务类

Java代码
  1. import  java.util.ArrayList;  
  2. import  java.util.HashMap;  
  3. import  java.util.List;  
  4. import  java.util.Map;  
  5. import  org.compass.core.Compass;  
  6. import  org.compass.core.CompassCallback;  
  7. import  org.compass.core.CompassException;  
  8. import  org.compass.core.CompassHighlighter;  
  9. import  org.compass.core.CompassHits;  
  10. import  org.compass.core.CompassQuery;  
  11. import  org.compass.core.CompassSession;  
  12. import  org.compass.core.CompassTemplate;  
  13. import  org.compass.core.CompassTransaction;  
  14. import  cn.rgcenter.entity.Article;  
  15.   
  16. public   class  SearchServiceBean {  
  17.   
  18.     private  Compass compass;  
  19.     /** 索引查询 * */   
  20.     public  Map find( final  String keywords,  final  String type,  final   int  start,  
  21.             final   int  end) {  
  22.         CompassTemplate ct = new  CompassTemplate(compass);  
  23.         return  ct.execute( new  CompassCallback<Map>() {  
  24.   
  25.             public  Map doInCompass(CompassSession session)  
  26.                     throws  CompassException {  
  27.                 List result = new  ArrayList();  
  28.                 int  totalSize =  0 ;  
  29.                 Map container = new  HashMap();  
  30.                 CompassQuery query = session.queryBuilder().queryString(  
  31.                         keywords).toQuery();  
  32.                 CompassHits hits = query.setAliases(type).hits();  
  33.                 totalSize = hits.length();  
  34.                 container.put("size" , totalSize);  
  35.                 int  max =  0 ;  
  36.                 if  (end < hits.length()) {  
  37.                     max = end;  
  38.                 } else  {  
  39.                     max = hits.length();  
  40.                 }  
  41.   
  42.        if (type.equals( "article" )){  
  43.                     for  ( int  i = start; i < max; i++) {  
  44.                         Article article = (Article) hits.data(i);  
  45.                         String title = hits.highlighter(i).fragment("title" );  
  46.                         if  (title !=  null ) {  
  47.                             article.setTitle(title);  
  48.                         }  
  49.                         String content = hits.highlighter(i).setTextTokenizer(  
  50.                                 CompassHighlighter.TextTokenizer.AUTO)  
  51.                                 .fragment("content" );  
  52.                         if  (content !=  null ) {  
  53.   
  54.                             article.setContent(content);  
  55.                         }  
  56.                         result.add(article);  
  57.                     }  
  58.                 }  
  59.                 container.put("result" , result);  
  60.                 return  container;  
  61.             }  
  62.         });  
  63.     }  
  64.   
  65.     public  Compass getCompass() {  
  66.         return  compass;  
  67.     }  
  68.   
  69.     public   void  setCompass(Compass compass) {  
  70.         this .compass = compass;  
  71.     }  
  72.   
  73. }  
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.compass.core.Compass;
import org.compass.core.CompassCallback;
import org.compass.core.CompassException;
import org.compass.core.CompassHighlighter;
import org.compass.core.CompassHits;
import org.compass.core.CompassQuery;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.CompassTransaction;
import cn.rgcenter.entity.Article;

public class SearchServiceBean {

	private Compass compass;
	/** 索引查询 * */
	public Map find(final String keywords, final String type, final int start,
			final int end) {
		CompassTemplate ct = new CompassTemplate(compass);
		return ct.execute(new CompassCallback<Map>() {

			public Map doInCompass(CompassSession session)
					throws CompassException {
				List result = new ArrayList();
				int totalSize = 0;
				Map container = new HashMap();
				CompassQuery query = session.queryBuilder().queryString(
						keywords).toQuery();
				CompassHits hits = query.setAliases(type).hits();
				totalSize = hits.length();
				container.put("size", totalSize);
				int max = 0;
				if (end < hits.length()) {
					max = end;
				} else {
					max = hits.length();
				}

       if(type.equals("article")){
					for (int i = start; i < max; i++) {
						Article article = (Article) hits.data(i);
						String title = hits.highlighter(i).fragment("title");
						if (title != null) {
							article.setTitle(title);
						}
						String content = hits.highlighter(i).setTextTokenizer(
								CompassHighlighter.TextTokenizer.AUTO)
								.fragment("content");
						if (content != null) {

							article.setContent(content);
						}
						result.add(article);
					}
				}
				container.put("result", result);
				return container;
			}
		});
	}

	public Compass getCompass() {
		return compass;
	}

	public void setCompass(Compass compass) {
		this.compass = compass;
	}

}




索引的查询主要是根据传过来的参数,关键字keywords,是搜索的关键字,类型type,先判断是不是要搜索文章,因为一般来说,页面的搜索引擎不单单只搜索文章一个实体.
至于int 和end是为了分页取出部分结果的.
String title = hits.highlighter(i).fragment("title");这段是检索titile这个属性有没有出现搜索的关键字,有就将它高亮 (其实就是在关键字前后加个<font></font>的html标记设置颜色,等下可以看到在配置文件里可以自由设置高亮的颜 色).
String content = hits.highlighter(i).setTextTokenizer(
CompassHighlighter.TextTokenizer.AUTO)
.fragment("content");

这段代码和上面的title具有一样的一样的功能,另外还多了个很重要的功能,自动选择正文中最匹配关键字的内容中的一部分输出。因为很多时候一篇文章几千字,我们只想显示有关键字的那部分的摘要,这时候这个功能就很方便.


这之后还要写一个建立索引的服务类,让服务器启动的时候或者定时重建索引.

Java代码
  1. import  org.compass.gps.CompassGps;  
  2. import  org.springframework.beans.factory.InitializingBean;  
  3.   
  4. public   class  CompassIndexBuilder  implements  InitializingBean {    
  5.       
  6.     // 是否需要建立索引,可被设置为false使本Builder失效.      
  7.     private   boolean  buildIndex =  false ;     
  8.     
  9.     // 索引操作线程延时启动的时间,单位为秒      
  10.     private   int  lazyTime =  10 ;     
  11.     
  12.     // Compass封装      
  13.     private  CompassGps compassGps;     
  14.     
  15.     // 索引线程      
  16.     private  Thread indexThread =  new  Thread() {     
  17.     
  18.         @Override     
  19.         public   void  run() {     
  20.             try  {     
  21.                 Thread.sleep(lazyTime * 1000 );     
  22.                 System.out.println("begin compass index..." );     
  23.                 long  beginTime = System.currentTimeMillis();     
  24.                 // 重建索引.      
  25.                 // 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引,      
  26.                 // 索引完成后再进行覆盖.      
  27.                 compassGps.index();     
  28.                 long  costTime = System.currentTimeMillis() - beginTime;     
  29.                 System.out.println("compss index finished." );     
  30.                 System.out.println("costed "  + costTime +  " milliseconds" );     
  31.             } catch  (InterruptedException e) {     
  32.                 e.printStackTrace();     
  33.             }     
  34.         }     
  35.     };     
  36.     
  37.     /**    
  38.      * 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程.  
  39.      */     
  40.     public   void  afterPropertiesSet()  throws  Exception {     
  41.         if  (buildIndex) {     
  42.             indexThread.setDaemon(true );     
  43.             indexThread.setName("Compass Indexer" );     
  44.             indexThread.start();     
  45.         }     
  46.     }     
  47.     
  48.     public   void  setBuildIndex( boolean  buildIndex) {     
  49.         this .buildIndex = buildIndex;     
  50.     }     
  51.     
  52.     public   void  setLazyTime( int  lazyTime) {     
  53.         this .lazyTime = lazyTime;     
  54.     }     
  55.     
  56.     public   void  setCompassGps(CompassGps compassGps) {     
  57.         this .compassGps = compassGps;     
  58.     }     
  59. }   
import org.compass.gps.CompassGps;
import org.springframework.beans.factory.InitializingBean;

public class CompassIndexBuilder implements InitializingBean {  
	
    // 是否需要建立索引,可被设置为false使本Builder失效.   
    private boolean buildIndex = false;   
  
    // 索引操作线程延时启动的时间,单位为秒   
    private int lazyTime = 10;   
  
    // Compass封装   
    private CompassGps compassGps;   
  
    // 索引线程   
    private Thread indexThread = new Thread() {   
  
        @Override  
        public void run() {   
            try {   
                Thread.sleep(lazyTime * 1000);   
                System.out.println("begin compass index...");   
                long beginTime = System.currentTimeMillis();   
                // 重建索引.   
                // 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引,   
                // 索引完成后再进行覆盖.   
                compassGps.index();   
                long costTime = System.currentTimeMillis() - beginTime;   
                System.out.println("compss index finished.");   
                System.out.println("costed " + costTime + " milliseconds");   
            } catch (InterruptedException e) {   
                e.printStackTrace();   
            }   
        }   
    };   
  
    /**  
     * 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程.
     */  
    public void afterPropertiesSet() throws Exception {   
        if (buildIndex) {   
            indexThread.setDaemon(true);   
            indexThread.setName("Compass Indexer");   
            indexThread.start();   
        }   
    }   
  
    public void setBuildIndex(boolean buildIndex) {   
        this.buildIndex = buildIndex;   
    }   
  
    public void setLazyTime(int lazyTime) {   
        this.lazyTime = lazyTime;   
    }   
  
    public void setCompassGps(CompassGps compassGps) {   
        this.compassGps = compassGps;   
    }   
} 



实现了spring的InitializingBean接口,让服务器启动,bean初始化的时候去建立索引


剩下的就是配置文件了

Java代码
  1. <?xml version= "1.0"  encoding= "UTF-8" ?>  
  2.   
  3. <beans xmlns="http://www.springframework.org/schema/beans"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  5.     xmlns:aop="http://www.springframework.org/schema/aop"   
  6.     xmlns:tx="http://www.springframework.org/schema/tx"   
  7.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  8.             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd   
  9.             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">   
  10.   
  11.     <bean id="annotationConfiguration"   
  12.         class = "org.compass.annotations.config.CompassAnnotationsConfiguration" >  
  13.     </bean>  
  14.   
  15.     <!-- compass Bean  -->  
  16.     <bean id="compass"   class = "org.compass.spring.LocalCompassBean" >  
  17.         <property name="compassConfiguration"   
  18.             ref="annotationConfiguration"  />  
  19.         <!-- 数据索引存储位置 -->  
  20.         <property name="connection" >  
  21.             <value>/compass/indexes</value>  
  22.         </property>  
  23.         <property name="transactionManager"  ref= "transactionManager"  />  
  24.         <property name="compassSettings" >  
  25.             <props>  
  26.                 <prop key="compass.transaction.factory" >  
  27.                     org.compass.spring.transaction.SpringSyncTransactionFactory  
  28.                 </prop>  
  29.               
  30.                 <prop  
  31.                     key="compass.engine.highlighter.default.formatter.simple.pre" >  
  32.                     <![CDATA[<span style='background-color:yellow;color:red;' >]]>  
  33.                 </prop>  
  34.                 <prop  
  35.                     key="compass.engine.highlighter.default.formatter.simple.post" >  
  36.                     <![CDATA[</span>]]>  
  37.                 </prop>  
  38.     <!--定义分词器-->            
  39. <prop  
  40.                     key="compass.engine.analyzer.default.type" >  
  41.                     org.mira.lucene.analysis.IK_CAnalyzer  
  42.                 </prop>  
  43.             </props>  
  44.         </property>  
  45.         <property name="classMappings" >  
  46.             <list>  
  47.               
  48.                 <value>cn.rgcenter.entity.Article</value>  
  49.             </list>  
  50.         </property>  
  51.     </bean>  
  52.   
  53.     <!--hibernate驱动-->  
  54.     <bean id="hibernateGpsDevice"   
  55.         class = "org.compass.spring.device.hibernate.dep.SpringHibernate3GpsDevice" >  
  56.         <property name="name" >  
  57.             <value>hibernateDevice</value>  
  58.         </property>  
  59.         <property name="sessionFactory"  ref= "sessionFactory"  />  
  60.         <property name="mirrorDataChanges" >  
  61.             <value>true </value>  
  62.         </property>  
  63.     </bean>  
  64.   
  65.     <!-- 数据库中的数据变化后同步更新索引 -->  
  66.     <bean id="hibernateGps"   
  67.         class = "org.compass.gps.impl.SingleCompassGps"  init-method= "start"   
  68.         destroy-method="stop" >  
  69.         <property name="compass" >  
  70.             <ref bean="compass"  />  
  71.         </property>  
  72.         <property name="gpsDevices" >  
  73.             <list>  
  74.                 <bean  
  75.                     class = "org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper" >  
  76.                     <property name="gpsDevice"  ref= "hibernateGpsDevice"  />  
  77.                 </bean>  
  78.             </list>  
  79.         </property>  
  80.     </bean>  
  81.   
  82.     <!-- compass模版 -->  
  83.     <bean id="compassTemplate"   
  84.         class = "org.compass.core.CompassTemplate" >  
  85.         <property name="compass"  ref= "compass"  />  
  86.     </bean>  
  87.   
  88.     <!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 -->  
  89.     <bean id="compassIndexBuilder"   
  90.         class = "cn.rgcenter.compass.service.CompassIndexBuilder"   
  91.         lazy-init="false" >  
  92.         <property name="compassGps"  ref= "hibernateGps"  />  
  93.         <property name="buildIndex"  value= "true"  />  
  94.         <property name="lazyTime"  value= "5"  />  
  95.     </bean>  
  96.   
  97.     <!-- 搜索引擎服务类 -->  
  98.     <bean id="searchService"   
  99.         class = "cn.rgcenter.compass.service.SearchServiceBean" >  
  100.         <property name="compass" >  
  101.             <ref bean="compass"  />  
  102.         </property>  
  103.     </bean>  
  104.   
  105.     <!-- 搜索引擎Action -->  
  106.     <bean id="searchAction"   class = "cn.rgcenter.action.SearchAction" >  
  107.         <property name="searchService" >  
  108.             <ref bean="searchService"  />  
  109.         </property>  
  110.     </bean>  
  111.   
  112. </beans>  
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

	<bean id="annotationConfiguration"
		class="org.compass.annotations.config.CompassAnnotationsConfiguration">
	</bean>

	<!-- compass Bean  -->
	<bean id="compass" class="org.compass.spring.LocalCompassBean">
		<property name="compassConfiguration"
			ref="annotationConfiguration" />
	    <!-- 数据索引存储位置 -->
	    <property name="connection">
            <value>/compass/indexes</value>
        </property>
		<property name="transactionManager" ref="transactionManager" />
		<property name="compassSettings">
			<props>
				<prop key="compass.transaction.factory">
					org.compass.spring.transaction.SpringSyncTransactionFactory
				</prop>
			
				<prop
					key="compass.engine.highlighter.default.formatter.simple.pre">
					<![CDATA[<span style='background-color:yellow;color:red;'>]]>
				</prop>
				<prop
					key="compass.engine.highlighter.default.formatter.simple.post">
					<![CDATA[</span>]]>
				</prop>
	<!--定义分词器-->			
<prop
					key="compass.engine.analyzer.default.type">
					org.mira.lucene.analysis.IK_CAnalyzer
				</prop>
			</props>
		</property>
		<property name="classMappings">
			<list>
			
				<value>cn.rgcenter.entity.Article</value>
			</list>
		</property>
	</bean>

	<!--hibernate驱动-->
	<bean id="hibernateGpsDevice"
		class="org.compass.spring.device.hibernate.dep.SpringHibernate3GpsDevice">
		<property name="name">
			<value>hibernateDevice</value>
		</property>
		<property name="sessionFactory" ref="sessionFactory" />
		<property name="mirrorDataChanges">
			<value>true</value>
		</property>
	</bean>

	<!-- 数据库中的数据变化后同步更新索引 -->
	<bean id="hibernateGps"
		class="org.compass.gps.impl.SingleCompassGps" init-method="start"
		destroy-method="stop">
		<property name="compass">
			<ref bean="compass" />
		</property>
		<property name="gpsDevices">
			<list>
				<bean
					class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper">
					<property name="gpsDevice" ref="hibernateGpsDevice" />
				</bean>
			</list>
		</property>
	</bean>

	<!-- compass模版 -->
	<bean id="compassTemplate"
		class="org.compass.core.CompassTemplate">
		<property name="compass" ref="compass" />
	</bean>

	<!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 -->
	<bean id="compassIndexBuilder"
		class="cn.rgcenter.compass.service.CompassIndexBuilder"
		lazy-init="false">
		<property name="compassGps" ref="hibernateGps" />
		<property name="buildIndex" value="true" />
		<property name="lazyTime" value="5" />
	</bean>

    <!-- 搜索引擎服务类 -->
	<bean id="searchService"
		class="cn.rgcenter.compass.service.SearchServiceBean">
		<property name="compass">
			<ref bean="compass" />
		</property>
	</bean>

	<!-- 搜索引擎Action -->
	<bean id="searchAction" class="cn.rgcenter.action.SearchAction">
		<property name="searchService">
			<ref bean="searchService" />
		</property>
	</bean>

</beans>




至于action就不列出代码了,很简单了,只需要传搜索方法的那几个参数过去就可以了.

最后看一下搜索结果示例图:



  • 大小: 41.1 KB
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值