整合compass构建搜索

最近参照springside
http://wiki.springside.org.cn/display/springside/Compass
重新开发了一下网站搜索功能.
引用
DataMirror会把数据库的增删改变化实时映射到索引文件中。

    如果你采用Hibernate等ORM方案,Compass就会与Hibernate的event机制结合,或者使用AOP的方式,自动在数据库增删改时变更索引;如果你只是采用JDBC,也可以在XML文件配置Table Mapping或ResultSet Mapping,指定version列,Compasss定时对version列变化了的数据进行索引更新。

    而且,Compass还支持事务,在查询数据库遍历结果集的过程中如果出现异常,会在Index Segments 文件一级进行事务控制。

    如果没有Compass,我们一般会在每天深夜重建一次索引。相比Compass的做法,
    一来反应迟缓,平均延时半天;
    二来效率没有Compass高。如果采用完全重建索引,效率就不用说了。如果进行增量索引,就要增加一个字段,在数据更新时进行特殊的处理,删除时也不能直接删除数据,要等lucene删完索引数据才能删除,这样Lucene对应用就非常不透明了。
    三来不支持事务,如果建立索引过程中出现异常,索引文件的状态是不可控的


---------------------------------------------------------------------
第一步,宣告待搜索的POJO!
一系列的annotation...我喜欢使用annotation
@Searchable
@SearchableId
@SearchableProperty
@SearchableComponent(refAlias="")

@Searchable(alias="article")
public class Article  extends AbstractAutoID implements java.io.Serializable {


	 @SearchableComponent(refAlias = "column")
     private ColumnModel column;
     private String title;
     @SearchableProperty
     private String content;
     private Integer hot;
     private String author;
     private Date loadtime;
     private String srcfrom;
     private Set<ArticleComment> comment;
   getter/setter...
     


这一步有个注意点:关联的类,在写注解的时候,要写在属性上面,不要写在get方法上面..
不然搜索到的关联类为null.

  而hibernate的关联注解要写get方法上,不然会报错...这是个区别...
事实不是像很多资料上写的,属性与方法上随便写...

第二步:Compass:核心定义类,定义要搜索的POJO 和 索引存储的路径。
CompassGPS: 定义使用了Hibernate3GPS,定义了init-method 和destory-method,会自动随ApplicaitonContext的启动,开始监控Hibernate的变化。
这个关键是配置文件了:
直接参考springside的就可以使用:
<bean id="compass" class="org.compass.spring.LocalCompassBean">
	<!-- anontaition式设置 -->
	<property name="classMappings">
		<list>
			<value>org.springside.bookstore.model.Book</value>
			<value>org.springside.bookstore.model.Category</value>
		</list>
	</property>

	<property name="compassConfiguration">
		<bean class="org.compass.annotations.config.CompassAnnotationsConfiguration"/>
	</property>

	<property name="compassSettings">
		<props>
			<prop key="compass.engine.connection">
				file://${user.home}/springside/compass
			</prop>
			<prop key="compass.transaction.factory">
				org.compass.spring.transaction.SpringSyncTransactionFactory
			</prop>
		</props>
	</property>

	<property name="transactionManager" ref="transactionManager"/>
</bean>

<!-- Compass中建立索引与mirror database change的部件 -->
<bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps"
		  init-method="start" destroy-method="stop">
	<property name="compass" ref="compass"/>
	<property name="gpsDevices">
		<list>
			<bean class="org.compass.spring.device.hibernate.SpringHibernate3GpsDevice">
					<property name="name" value="hibernateDevice"/>
					<property name="sessionFactory" ref="sessionFactory"/>
			</bean>
		</list>
	</property>
</bean>


第三步:就是写搜索类...controller ,service..

   重建索引的action..参考springside就可以了..

我使用的还是struts1.x..
public class RebuildSearchIndexAction extends Action {
	private static final Logger log = Logger.getLogger(RebuildSearchIndexAction.class);

	

	// 索引操作线程延时启动的时间,单位为秒
	private int lazyTime = 10;

	// Compass封装
	private CompassGps compassGps;
	public void setCompassGps(CompassGps compassGps) {
		this.compassGps = compassGps;
	}
	// 索引线程
	private Thread indexThread = new Thread() {

		@Override
		public void run() {
			try {
				Thread.sleep(lazyTime * 1000);
				if(log.isInfoEnabled())
					log.info("begin compass index...");
				long beginTime = System.currentTimeMillis();
				// 重建索引.
				// 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引,
				// 索引完成后再进行覆盖.
				compassGps.index();
				long costTime = System.currentTimeMillis() - beginTime;
				if(log.isInfoEnabled())
					log.info("compss index finished.");
				if(log.isInfoEnabled())
					log.info("costed " + costTime + " milliseconds");
			} catch (InterruptedException e) {
				// simply proceed
			}
		}
	};


	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response) {
		indexThread.setDaemon(true);
		indexThread.setName("Compass Indexer");
		indexThread.start();
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = null;
		try {
			out = response.getWriter();
		}catch (Exception e){
			
		}
		out.print("compass indexer finish!!");
		return null;
	   
	}
}


一条关键语句:
compassGps.index();

不再像过去,先删除旧的,再建新的.


搜索的controller
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response) {
		// TODO Auto-generated method stub
		String query = StringUtil.encode(request.getParameter("query"),"utf-8");
		Integer pageNo = IntegerUtil.parseInt(request.getParameter("pageNo"));
		String type=request.getParameter("type");
		// form get request,encode charactes
		if (type==null){
			type = "article";
		}
		AdvancedSearchCommand searchCommand = new AdvancedSearchCommand();
		searchCommand.setQuery(query);
		searchCommand.setHighlightFields(new String[] {"title"});
		searchCommand.setPage(pageNo);
		
		searchCommand.setAlias(type);

		Page page = new Page(15);
		SearchResults searchResults = new SearchResults(page);
		searchResults.setQuery(query);
		searchResults.setAlias(type);
		searchResults.setCompassSearchResults(compassSearchService.search(searchCommand,page));
		
		request.setAttribute("searchResults",searchResults);
		/**
		 * 根据type类型,跳转到相应的页面.
		 * type = article | resource
		 */
		return mapping.findForward(type);
	}


对于service,直接使用springside里的就可以.
但不太喜欢那个分页的效果,所以我重写了下..加了一个Page类..我现在分页都是使用taglib-pager组件..使用方便.

还改了一个地方:
  
	/**
	 * 构建Lucene搜索器.
	 */
	protected CompassQuery buildQuery(CompassSearchCommand searchCommand,
			CompassSession session) {
		AdvancedSearchCommand ac = (AdvancedSearchCommand) searchCommand;
		CompassBooleanQueryBuilder queryBuilder = session.queryBuilder().bool();
		if (ac.getAlias() != null) {
			
			queryBuilder.addMust(session.queryBuilder().alias(ac.getAlias()));
		}
		queryBuilder.addMust(session.queryBuilder().queryString(
				ac.getQuery().trim()).toQuery());

这儿搜索加了个条件..搜索的alias..因为网站有文章外还有别的搜索源.....

效果可以到 http://www.java1995.cn上看看..只是现在还有一个乱码问题..
我使用的form method=get..自己也处理了字符编码..
  但空间提供商,把server.xml中设置了URIEncode="GB2312"...我相当无语..
这个问题还在交涉....提供商的技术水平让人相当无语...

你可能感兴趣的:(spring,AOP,thread,Hibernate,Lucene)