Lucene、Compass学习以及与SSH的整合
Lucene、Compass学习以及与SSH的整合
一、准备
个人在学习中采用Struts2 + Hibernate3.2 + Spring2.5 + Compass2.2.0, 一下图片为本次学习中用到的jar包:
图中圈出的jar包为本次学习的主要部分,另外用绿色框圈出的jar包为分词器,主要用来做实验看分词效果的,选用一个即可。
二、什么是Compass
Compass是一个Java搜索框架。它封装了Lucene,增加了一些Lucene不支持的特性(例如实时更新索引),支持各种数据(Java对象、xml、json)到索引的映射,支持各种数据源(JDBC, Hibernate, iBatis)

图解:
- Compass - 一般在程序启动时建立并被整个程序共享,主要用于建立CompassSession并通过其管理索引数据。与Hibernate的SessionFactory类似
- CompassSession - 用于处理数据的session。与Hibernate的Session类似
- CompassTransaction - 手动进行事务管理,如果不使用,Compass会自动管理事务。与Hibenate的事物管理类似
- CompassTemplate - 将session和transaction透明化。类似Spring提供的HibernateTemplate
- 数据到索引的各种映射 - OSEM, XSEM, JSEM, RSEM。支持通过程序、XML、JSON进行配置。
- CompassGps - Gps的核心模块,管理GpsDevice,有两种实现:SingleCompassGps和DualCompassGps。
- CompassGpsDevice - 处理各种数据源到索引的操作:JDBC, Hibernate, iBatis等。不能独立使用而必须融合到CompassGps中。
三、与Spring、Hibernate整合
这里主要结合代码进行。
1.数据库脚本(Oracle)
- --创建表Article
- create table ARTICLE
- (
- ID NUMBER,--ID,主键
- TITLE VARCHAR2(100 ),--标题
- CONTENT CLOB,--文章内容
- PUBDATE DATE--发布日期
- )
2.配置Compass的OSEM 以及Hibernate映射
- import java.io.Serializable;
- import java.util.Date;
-
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.Id;
- import javax.persistence.Lob;
- import javax.persistence.Table;
- import javax.persistence.Temporal;
- import javax.persistence.TemporalType;
-
- import org.compass.annotations.Index;
- import org.compass.annotations.Searchable;
- import org.compass.annotations.SearchableId;
- import org.compass.annotations.SearchableProperty;
- import org.compass.annotations.Store;
- import org.hibernate.annotations.GenericGenerator;
-
-
- @Searchable (alias = "article" )
- @Entity
- @Table (name = "ARTICLE" , schema = "SCOTT" )
- public class Article implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- private Long id;
- private String title;
- private Date pubdate = new Date();
- private String content;
-
- @SearchableId (
- name = "id" ,
- store = Store.NO,
- index = Index.NOT_ANALYZED)
- @Id
- @GeneratedValue (generator = "paymentableGenerator" )
- @GenericGenerator (name = "paymentableGenerator" , strategy = "increment" )
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this .id = id;
- }
-
- @SearchableProperty (
- name = "title" ,
- store = Store.YES,
- index = Index.ANALYZED)
- @Column (name = "TITLE" )
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this .title = title;
- }
-
- @SearchableProperty (
- name = "pubdate" ,
- store = Store.NO,
- index = Index.UN_TOKENIZED)
- @Temporal (TemporalType.TIMESTAMP)
- @Column (name = "PUBDATE" )
- public Date getPubdate() {
- return pubdate;
- }
-
- public void setPubdate(Date pubdate) {
- this .pubdate = pubdate;
- }
-
- @SearchableProperty (
- name = "content" ,
- index = Index.TOKENIZED,
- store = Store.YES,
- converter = "htmlPropertyConverter" )
- @Lob
- @Column (name = "CONTENT" )
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this .content = content;
- }
-
- }
说明:
@Searchable(alias="article")表示这个是可以搜索实体,别名为article.
@SearchableId 这个是实体搜索的标识ID,和hibernate里的概念差不多,用来区分索引文件里的实体索引。
@SearchableProperty(index = Index.NOT_ANALYZED, store = Store.NO) 表示这个属性存入索引文件,不进行分词,不存储要索引中。
另外在getContent()方法上的@SearchableProperty中还加入了converter = "htmlPropertyConverter",主要是用来将文章中的HTML标签进行过滤获取纯文本,在建立到索引中。在后面会具体介绍这个转换器。
3.建立Compass搜索的类
- import java.util.ArrayList;
- import java.util.List;
-
- import javax.annotation.Resource;
-
- import org.compass.core.Compass;
- import org.compass.core.CompassHighlighter;
- import org.compass.core.CompassHits;
- import org.compass.core.CompassQuery;
- import org.compass.core.CompassQueryBuilder;
- import org.compass.core.CompassSession;
- import org.compass.core.CompassTemplate;
- import org.compass.core.CompassHighlighter.TextTokenizer;
- import org.compass.core.CompassQuery.SortPropertyType;
- import org.springframework.stereotype.Component;
-
- import com.compass.example.dao.SearchArticleDao;
- import com.compass.example.model.Article;
-
- @Component ( "SearchArticleDao" )
- public class SearchArticleDaoCompass implements SearchArticleDao {
-
- @Resource
- private CompassTemplate compassTemplate;
-
- @Override
- public List<Article> searchWithList( final String queryString) {
-
- Compass compass = compassTemplate.getCompass();
- CompassSession session = compass.openSession();
- CompassQueryBuilder builder = session.queryBuilder();
- CompassQuery compassQuery = builder.queryString(queryString).toQuery().addSort("article.id" ,SortPropertyType.STRING);
- CompassHits compassHits = compassQuery.hits();
-
-
- List<Article> articles = new ArrayList<Article>();
- for ( int i= 0 ; i<compassHits.length(); i++) {
- Article article = (Article) compassHits.data(i);
- CompassHighlighter highlighter = compassHits.highlighter(i);
- String title = highlighter.fragment("title" );
- if (title != null ) {
- article.setTitle(title);
- }
- String content = highlighter.setTextTokenizer(TextTokenizer.AUTO).fragment("content" );
- if (content != null ) {
- article.setContent(content);
- }
- articles.add(article);
- }
- return articles;
- }
-
- }
索引的查询主要是根据传过来的参数,关键字keyword,是搜索的关键字
String title = hits.highlighter(i).fragment("title");这段是检索titile这个属性有没有出现搜索的关键字,有就将它高亮 (其实就是在关键字前后加个html标记设置颜色,等下可以看到在配置文件里可以自由设置高亮的颜色).
String content = hits.highlighter(i).setTextTokenizer(
CompassHighlighter.TextTokenizer.AUTO)
.fragment("content");
这段代码和上面的title具有一样的一样的功能,另外还多了个很重要的功能,自动选择正文中最匹配关键字的内容中的一部分输出。因为很多时候一篇文章几千字,我们只想显示有关键字的那部分的摘要,这时候这个功能就很方便.
4.建立索引,将在服务器启动时或定时重建索引
- import org.compass.gps.CompassGps;
- import org.springframework.beans.factory.InitializingBean;
-
-
-
-
-
-
-
-
-
- public class CompassIndexBuilder implements InitializingBean {
-
-
- private boolean buildIndex = false ;
-
- private int lazyTime = 10 ;
-
- private CompassGps compassGps;
-
- private Thread indexThread = new Thread() {
-
- @Override
- public void run() {
- try {
- System.out.println("lazyTime: " + lazyTime);
- Thread.sleep(lazyTime * 1000 );
- System.out.println("begin compass index ..." );
- long beginTime = System.currentTimeMillis();
-
-
-
-
- compassGps.index();
-
- long costTime = System.currentTimeMillis() - beginTime;
- System.out.println("compss index finished." );
- System.out.println("costed " + costTime + " milliseconds" );
-
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- }
-
- };
-
-
-
-
-
-
- @Override
- public void afterPropertiesSet() throws Exception {
- if (buildIndex) {
- indexThread.setDaemon(true );
- indexThread.setName("Compass Indexer" );
- indexThread.start();
- }
- }
-
- public boolean isBuildIndex() {
- return buildIndex;
- }
-
- public void setBuildIndex( boolean buildIndex) {
- this .buildIndex = buildIndex;
- }
-
- public int getLazyTime() {
- return lazyTime;
- }
-
- public void setLazyTime( int lazyTime) {
- this .lazyTime = lazyTime;
- }
-
- public CompassGps getCompassGps() {
- return compassGps;
- }
-
- public void setCompassGps(CompassGps compassGps) {
- this .compassGps = compassGps;
- }
-
- }
5.转换器
- import org.apache.log4j.Logger;
- import org.compass.core.Property;
- import org.compass.core.converter.ConversionException;
- import org.compass.core.converter.basic.AbstractBasicConverter;
- import org.compass.core.mapping.ResourcePropertyMapping;
- import org.compass.core.marshall.MarshallingContext;
-
- import com.compass.example.utils.StringUtil;
-
- public class HtmlPropertyConverter extends AbstractBasicConverter<String> {
-
- private static Logger logger = Logger.getLogger(HtmlPropertyConverter. class );
-
- public HtmlPropertyConverter() {
- super ();
-
- logger.info("----------HtmlPropertyConverter Initializing ..." );
- }
-
-
-
-
- @Override
- protected String doFromString(String str,
- ResourcePropertyMapping resourcePropertyMapping,
- MarshallingContext context) throws ConversionException {
- logger.info("----------calling doFromString..." );
- return str;
- }
-
-
-
-
-
- @Override
- protected Property createProperty(String value,
- ResourcePropertyMapping resourcePropertyMapping,
- MarshallingContext context) {
- logger.info("----------calling createProperty..." );
-
- value = StringUtil.removeHTML(value);
-
- return super .createProperty(value, resourcePropertyMapping, context);
- }
-
-
- public class StringUtil {
-
-
-
-
-
-
-
-
- public static String removeHTML(String str, boolean addSpace) {
-
-
-
- if (str == null ) return "" ;
- StringBuffer ret = new StringBuffer(str.length());
- int start = 0 ;
- int beginTag = str.indexOf( "<" );
- int endTag = 0 ;
- if (beginTag == - 1 ) return str;
-
- while (beginTag >= start) {
- if (beginTag > 0 ) {
- ret.append(str.substring(start, beginTag));
-
-
- if (addSpace) ret.append( " " );
- }
- endTag = str.indexOf(">" , beginTag);
-
-
- if (endTag > - 1 ) {
- start = endTag + 1 ;
- beginTag = str.indexOf("<" , start);
- }
-
-
- else {
- ret.append(str.substring(beginTag));
- break ;
- }
- }
-
- if (endTag >- 1 && endTag + 1 < str.length()) {
- ret.append(str.substring(endTag + 1 ));
- }
-
-
-
- return ret.toString().trim();
- }
-
-
-
-
-
-
-
-
- public static String removeHTML(String str) {
- return removeHTML(str, true );
- }
- }
-
5.配置文件
sql server2005导出报表的有关问题
sql server2005导出报表的问题
问题是这样的,用微软sql server 2005的报表工具做的报表完成显示数据结果后,导出数据为execl数据文件格式.发现导出的execl格式的数据行与报表上面的报表拦上面的开始时间和结束时间不能在同一个数据列上面.如何做才能让报表导出后的标题列上面的开始时间或结束时间与数据列在同一个数据列上面.目前是不在同一个列上面,有的数据列占几个数据列.如何设计才能让标题列与数据列在同一个列上面.谢谢各位高手解答.
标题列: 开始时间: 2009-12-01 结束时间:2009-12-20
数据列: 小李 001 002 1236
要达到的效果是让标题列与数据列在同一个列里面.
------解决方案--------------------
画面不是自己布置的么?
没懂。。。
------解决方案--------------------
是自己布置的,但一个是标题列,一个是数据列.导出后用execl打开,下面的一列数据列占了好几列.正常情况下应该是数据列每列都只占一列吗.目前数据列有的一列占了好几列的空间是因为标题拦引起的.
------解决方案--------------------
没做过,不懂楼主的意思。
------解决方案--------------------
SQL code
不懂,帮顶,学习,蹭分.
------解决方案--------------------
问题是这样的,用微软sql server 2005的报表工具做的报表完成显示数据结果后,导出数据为execl数据文件格式.发现导出的execl格式的数据行与报表上面的报表拦上面的开始时间和结束时间不能在同一个数据列上面.如何做才能让报表导出后的标题列上面的开始时间或结束时间与数据列在同一个数据列上面.目前是不在同一个列上面,有的数据列占几个数据列.如何设计才能让标题列与数据列在同一个列上面.谢谢各位高手解答.
也正有此问题,不知道是不是做成报表的导出来格式都不标准了,晕,EXCEL格式最差
------解决方案--------------------
由于报表在导出的时候,根据栏位的长度或者说你控件Cell的长度来定义Excel中的栏位长度的,所以,只有调整好你报表页面上的控件长度/Cell长度来满足你的要求.如果Excel长度不够,则系统会自动合并Cell来满足报表的要求.
PS:SSRS 2005导出为Excel导致Cell合并这个是一直存在的问题,貌似还没有好的解决方式.不知道2008里面有没有改进.
------解决方案--------------------
不懂
------解决方案--------------------
我也遇到类似问题,一行数据占三行的空间,三行重叠,相当难看。好像是通过把内容粘贴到word里,转换后再粘贴到新建excel工作簿里。
------解决方案--------------------
ipxu b
------解决方案--------------------
没有明白什么意思
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:116537189 , 2号QQ群:246889341,在群里结识技术精英和交流技术^_^
本站联系邮箱:155120699@qq.com
OCX CToolBar提示信息显示不出来
我用VC作了一个OCX组件,里面有一个Dlg类,在Dlg类中有一个工具条CToolBarCtrl,我在Dlg类中加入了TTN_NEEDTEXT消息的相应函数以用于显示tooltip,可以进入响应函数,但是提示信息显示不出来,Dlg类放到我自己测试Demo(非OCX)下是好使得
------最佳解决方案--------------------
那把分给我吧,hoho~~
------其他解决方案--------------------
问题已经解决,在TTN_NEEDTEXT消息的相应函数里面加上UpdateTipText就解决此问题
sql server2005导出报表的问题
问题是这样的,用微软sql server 2005的报表工具做的报表完成显示数据结果后,导出数据为execl数据文件格式.发现导出的execl格式的数据行与报表上面的报表拦上面的开始时间和结束时间不能在同一个数据列上面.如何做才能让报表导出后的标题列上面的开始时间或结束时间与数据列在同一个数据列上面.目前是不在同一个列上面,有的数据列占几个数据列.如何设计才能让标题列与数据列在同一个列上面.谢谢各位高手解答.
标题列: 开始时间: 2009-12-01 结束时间:2009-12-20
数据列: 小李 001 002 1236
要达到的效果是让标题列与数据列在同一个列里面.
------解决方案--------------------
画面不是自己布置的么?
没懂。。。
------解决方案--------------------
是自己布置的,但一个是标题列,一个是数据列.导出后用execl打开,下面的一列数据列占了好几列.正常情况下应该是数据列每列都只占一列吗.目前数据列有的一列占了好几列的空间是因为标题拦引起的.
------解决方案--------------------
没做过,不懂楼主的意思。
------解决方案--------------------
SQL code
不懂,帮顶,学习,蹭分.
------解决方案--------------------
问题是这样的,用微软sql server 2005的报表工具做的报表完成显示数据结果后,导出数据为execl数据文件格式.发现导出的execl格式的数据行与报表上面的报表拦上面的开始时间和结束时间不能在同一个数据列上面.如何做才能让报表导出后的标题列上面的开始时间或结束时间与数据列在同一个数据列上面.目前是不在同一个列上面,有的数据列占几个数据列.如何设计才能让标题列与数据列在同一个列上面.谢谢各位高手解答.
也正有此问题,不知道是不是做成报表的导出来格式都不标准了,晕,EXCEL格式最差
------解决方案--------------------
由于报表在导出的时候,根据栏位的长度或者说你控件Cell的长度来定义Excel中的栏位长度的,所以,只有调整好你报表页面上的控件长度/Cell长度来满足你的要求.如果Excel长度不够,则系统会自动合并Cell来满足报表的要求.
PS:SSRS 2005导出为Excel导致Cell合并这个是一直存在的问题,貌似还没有好的解决方式.不知道2008里面有没有改进.
------解决方案--------------------
不懂
------解决方案--------------------
我也遇到类似问题,一行数据占三行的空间,三行重叠,相当难看。好像是通过把内容粘贴到word里,转换后再粘贴到新建excel工作簿里。
------解决方案--------------------
ipxu b
------解决方案--------------------
没有明白什么意思