- 浏览: 124442 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
102111:
这个问题怎么解决的?我也遇到了同样的问题。SB boss要在同 ...
最近这个项目 用到Tomcat7,郁闷!!! -
暗蓝幽谷:
不是什么高阶之类,标题党
高阶Java-Java注解 Java annotation 使用详解【转载的】 -
暗蓝幽谷:
《Think in Java 第四版》里面多东西,而且是最基础 ...
高阶Java-Java注解 Java annotation 使用详解【转载的】 -
bcw104:
32位机最大数据集文件2G
MongoDb can't map file memory - mongo requires 64 bit build for larger datasets -
javaOpen:
taoge2121 写道怎么使用啊?
跟使用 ExtJS 分页 ...
ExtJS 分页扩展
【转】http://luyucs.blog.163.com/blog/static/2576653200982102149598/
第 12 章 全文检索
12.1. Lucene 基础指南
Lucene是apache下的一个采用java实现的全文搜索引擎,功能非常强大,而
它的API又比较简单,主要就做两件事:建立索引和进行搜索。
12.1.1. 建立索引的术语
Document:一个需要进行索引的单元,相当于数据库的一行纪录,任何想
要被索引的数据,都必须转化为Document对象存放。
Field:Document中的一个字段,相当于数据库中的Column ,Field是lucene
中含义比较多的一个术语,详情见后。
IndexWriter:负责将Document写入索引文件。通常情况下,IndexWriter的构
造函数包括了以下3个参数:索引存放的路径,分析器和是否重新创建索引
。特别注意的一点,当IndexWriter执行完addDocument方法后,一定要记得
调用自身的close方法来关闭它。只有在调用了close方法后,索引器才会将
存放在内在中的所有内容写入磁盘并关闭输出流。
Analyzer:分析器,主要用于文本分词。常用的有StandardAnalyzer分析器,
StopAnalyzer分析器,WhitespaceAnalyzer分析器等。
Directory:索引存放的位置。lucene提供了两种索引存放的位置,一种是磁
盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了
FSDirectory和RAMDirectory两个类。
段:Segment,是Lucene索引文件的最基本的一个单位。Lucene说到底就是不
断加入新的Segment,然后按一定的规则算法合并不同的Segment以合成新
的Segment。
lucene建立索引的过程就是将待索引的对象转化为Lucene的Document对象,
使用IndexWriter将其写入lucene自定义格式的索引文件中。
待索引的对象可以来自文件、数据库等任意途径,用户自行编码遍历目录
读取文件或者查询数据库表取得ResultSet,Lucene的API只负责和字符串打
交道。
12.1.1.1. Field的含义
Field的构造方法如下:
Field(String name, byte[] value, Field.Store store)
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
Field(String name, String value, Field.Store store, Field.Index index)
Field(String name, String value, Field.Store store, Field.Index index,
Field.TermVector termVector)
在Field当中有三个内部类:Field.Index,Field.Store,Field.TermVector。其中
Field.Index有五个属性,分别是:
Field.Index.ANALYZED:分词索引
Field.Index.NOT_ANALYZED:分词进行索引,如作者名,日期等,Rod
Johnson本身为一单词,不再需要分词。
Field.Index.NO:不进行索引,存放不能被搜索的内容如文档的一些附加属
性如文档类型, URL等。
Field.Index.NOT_ANALYZED_NO_NORMS:不使用分词索引,不使用存
储规则。
Field.Index.ANALYZED_NO_NORMS:使用分词索引,不使用存储规则。
Field.Store有三个属性
Field.Store.YES:索引文件本来只存储索引数据, 此设计将原文内容直接也
存储在索引文件中,如文档的标题。
Field.Store.NO:原文不存储在索引文件中,搜索结果命中后,再根据其他
附加属性如文件的Path,数据库的主键等,重新连接打开原文,适合原文
内容较大的情况。
Field.Store.COMPRESS:压缩存储。
termVector是Lucene 1.4.3新增的它提供一种向量机制来进行模糊查询,不太
常用。
上面所说的Field属性与lucene1.4.3版本的有比较大的不同,在旧版的1.4.3里
lucene是通过Field.Keyword(...),FieldUnIndexed(...),FieldUnstored(...)和
Field.Text(...)来设置不同字段的类型以达到不同的用途,而当前版本由
Field.Index和Field.Store两个字段的不同组合来达到上述效果。
还有一点说明,其中的两个构造函数其默认的值为Field.Store.NO和
Field.Index.TOKENIZED。
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
12.1.1.2. 限制Field的长度
IndexWriter类提供了一个setMaxFieldLength的方法来对Field的长度进行限制
,看一下源代码就知道其默认值为10000;我们可以在使用时重新设置此参
数。如果使用默认值,那么Lucene就仅仅对文档的前面的10000个term进行
索引,超过这一个数的文档就不会被建立索引。
12.1.1.3. 索引的合并、删除、优化
IndexWriter中的addIndexes方法将索引进行合并,当在不同的地方创建了索
引后,如果需要将索引合并,这时候使用addIndexes方法就显得很有意义。
可以通过IndexReader类从索引中进行文档的删除。IndexReader是很特别的
一个类,看源代码就知道它主要是通过自身的静态方法来完成构造的。示
例:
IndexReader reader = IndexReader.open("d:/lucene/index");
reader.deleteDocument(X); //这里的X是一个int的常数;不推荐这一种删除方
法
reader.deleteDocument(new Term("name", "oa")); //这是另一种删除索引的方
法,按字段来删除,推荐使用这一种做法
reader.close();
优化索引:可以使用IndexWriter类的optimize方法来进行优先,它会将多个
Segment进行合并,组成一个新的Segment,可以加快建立索引后搜索的速度
。另外需要注意的一点,optimize方法会降低建立索引的速度,而且要求的
磁盘空间会增加。
12.1.2. 进行搜索时的术语
IndexSearcher:是lucene中最基本的检索工具,所有的检索都会用到
IndexSearcher工具。初始化IndexSearcher需要设置索引存放的路径,让查询
器能定位索引而进行搜索。
Query:查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等
等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类
。
QueryParser::是一个解析用户输入的工具,可以通过扫描用户输入的字符
串,生成Query对象。
Hits:在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才
算是完成搜索的目的。在lucene中,搜索的结果的集合是用Hits类的实例来
表示的。Hits对象中主要方法有:
length(): 返回搜索结果的总数,下面简单的用法中有用到Hit的这一个方法
doc(int n): 返回第n个文档
iterator(): 返回一个迭代器
这里再提一下Hits,这也是Lucene比较精彩的地方,熟悉hibernate的朋友都
知道hibernate有一个延迟加载的属性,同样,Lucene也有。Hits对象也是采
用延迟加载的方式返回结果的,当要访问某个文档时,Hits对象就在内部
对Lucene的索引又进行一次检索,最后才将结果返回到页面显示。
12.1.3. 一个简单的实例
首先把lucene的包放在classpath路径中去,写下面一个简单的类:
public class FSDirectoryTest {
//建立索引的路径
public static final String path = "d:/index2";
public static void main(String[] args) throws Exception {
Document doc1 = new Document();
doc1.add(new Field("name", "www family168 com", Field.Store.YES,
Field.Index.TOKENIZED));
Document doc2 = new Document();
doc2.add(new Field("name", "family168 blog bbs", Field.Store.YES,
Field.Index.TOKENIZED));
IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(
path, true), new StandardAnalyzer(), true);
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.close();
IndexSearcher searcher = new IndexSearcher(path);
Hits hits = null;
Query query = null;
QueryParser qp = new QueryParser("name", new StandardAnalyzer());
query = qp.parse("family168");
hits = searcher.search(query);
System.out.println("查找\"family168\" 共" + hits.length() + "个结果");
query = qp.parse("bbs");
hits = searcher.search(query);
System.out.println("查找\"bbs\" 共" + hits.length() + "个结果");
}
}
执行的结果:
查找"family168" 共2个结果
查找"bbs" 共1个结果
12.1.4. 复杂一点的实例
在windows系统下的的C盘,建一个名叫s的文件夹,在该文件夹里面随便建
三个txt文件,随便起名啦,就叫"1.txt","2.txt"和"3.txt"。
其中1.txt的内容如下:
family168
文档,资料
2009
而"2.txt"和"3.txt"的内容也可以随便写几写,这里懒写,就复制一个和1.txt
文件的内容一样吧
下载lucene包,放在classpath路径中,然后建立索引:
public class LuceneExample {
public static void main(String[] args) throws Exception {
File fileDir = new File("D:/lucene"); // 指明要索引文件夹的位置,这里
是C盘的S文件夹下
File indexDir = new File("D:/lucene/index"); // 这里放索引文件的位置
File[] textFiles = fileDir.listFiles();
Analyzer luceneAnalyzer = new StandardAnalyzer();
IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
indexFile(luceneAnalyzer,indexWriter, textFiles);
indexWriter.optimize(); // optimize()方法是对索引进行优化
indexWriter.close();
}
public static void indexFile(Analyzer luceneAnalyzer,IndexWriter
indexWriter,File[] textFiles) throws Exception {
// 增加document到索引去
for (int i = 0; i < textFiles.length; i++) {
if (textFiles[i].isFile() && textFiles[i].getName().endsWith(".txt")) {
String temp = FileReaderAll(textFiles[i].getCanonicalPath(),"GBK");
Document document = new Document();
Field FieldBody = new Field("body", temp,
Field.Store.YES,Field.Index.TOKENIZED);
document.add(FieldBody);
indexWriter.addDocument(document);
}
}
}
public static String FileReaderAll(String FileName, String charset)throws
IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(FileName), charset));
String line = "";
String temp = "";
while ((line = reader.readLine()) != null) {
temp += line;
}
reader.close();
return temp;
}
}
执行查询:
public class TestQuery {
public static void main(String[] args)
throws IOException, ParseException {
Hits hits = null;
String queryString = "family168";
Query query = null;
IndexSearcher searcher = new IndexSearcher("D:/lucene/index");
Analyzer analyzer = new StandardAnalyzer();
try {
QueryParser qp = new QueryParser("body", analyzer);
query = qp.parse(queryString);
} catch (ParseException e) {
e.printStackTrace();
}
if (searcher != null) {
hits = searcher.search(query);
if (hits.length() > 0) {
System.out.println("找到:" + hits.length() + " 个结果!");
}
}
}
}
执行结果:
找到:3 个结果!
12.1.5. Hibernate与lucene的结合使用:
参考这一篇文章,里面讲得很详细
http://wiki.redsaga.com/confluence/display/HART/Hibernate+Lucene+Integrati
on
12.2. Compass
12.2.1. 概述
compass官方网站: http://www.compass-project.org/
Compass是一流的JAVA搜索框架,可以快速修饰你的应用,使其具备
Searchable的能力。
在没有Compass,单纯用Lucene的环境里,一般做法是定期遍历数据库,编
码将里面的内容转为索引。
而Compass通过与Hibernate和Spring的结合,简单的将Domain Object转换为
Lucene引擎的映射,而且索引库能够随Domain Object同步更新(Data Mirror)
。
使用compass快速建立全文搜索的流程如下:
用简单的compass annotation把User对象映射到Lucene。
提供Index service和Search service。
编写查询结果的显示页面,将查询返回的变量显示出来。
12.2.2. Object/Search Engine Mapping的Annotations配置
使用JDK5的annotation 来进行OSEM(Object/Search Engine Mapping)比用xml
文件按简单许多,下面就是简单的搜索类,可见@SearchableID,
@SearchableProperty与@SearchableComponent 三个标记,分别代表主键、可
搜索的属性与关联的,另一个可搜索的对象,另外Compass要求POJO要有
默认构造函数,要实现equals()和hashcode():
@Entity
@Searchable
public class Child {
private Long id;
private String name;
private Parent parent;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@SearchableId
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column
@SearchableProperty(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID")
@SearchableComponent(refAlias = "parent")
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
注意可被搜索的对象必须遵守下面的规则
所有的持久化类必须有默认构造函数(没参数的构造函数).
OSEM要求映射类JavaBean必须有一个或更多的id主建. id属性可以是对象类
型,例如java.lang.String or java.util.Date.
并且类注解使用@SearchableClassConverter或者使用定义Converter(通常继承
Compass AbstractBasicConverter).
使用JavaBean规范, getter/setter.
12.2.3. Data Change Mirror功能
DataMirror会把数据库的增删改变化实时映射到索引文件中。
如果你采用Hibernate等ORM方案,Compass就会与Hibernate的event机制结
合,或者使用AOP的方式,自动在数据库增删改时变更索引;如果你只是
采用JDBC,也可以在XML文件配置Table Mapping或ResultSet Mapping,指
定version列,Compasss定时对version列变化了的数据进行索引更新。
而且Compass还支持事务,在查询数据库遍历结果集的过程中如果出现异
常,会在Index Segments文件一级进行事务控制。
如果没有Compass,我们一般会在每天深夜重建一次索引。相比Compass的
做法,
一来反应迟缓,平均延时半天。
二来效率没有Compass高。如果采用完全重建索引,效率就不用说了。如
果进行增量索引,就要增加一个字段,在数据更新时进行特殊的处理,删
除时也不能直接删除数据,要等lucene删完索引数据才能删除,这样Lucene
对应用就非常不透明了。
三来不支持事务,如果建立索引过程中出现异常,索引文件的状态是不可
控的。
12.2.4. 与spring,hibernate集成配置
下面是compass在spring中的简明配置:
<bean id="compass" class="org.compass.spring.LocalCompassBean">
<!-- anontaition式设置 -->
<property name="classMappings">
<list>
<value>com.family168.domain.Child</value>
<value>com.family168.domain.Parent</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}/oa/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.dep.SpringHibernate3GpsDevice">
<property name="name" value="hibernateDevice"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</list>
</property>
</bean>
上面要留意的配置有:
annotationConfiguration:使用annotation配置,指定要转换的POJO如User。
compass.engine.connection:索引文件在服务器上的存储路径。
hibernateGpsDevice:与hibernate的绑定,用Hibernate 3事件系统,支持Real
Time Data Mirroring .经Hiberante的数据改变会自动被反射到索引里面.
上面的Gps定义只是自启动Mirror Data Change,但索引初始建立就需要靠
程序员自己写代码调用CompassGps.index()来完成。
理论上,因为可以Mirror Data Change,所以索引只需要建立一次即可。如
果索引已存在,Compass会对它gracefule override,所谓graceful,就是
Compass会现在临时目录存放新的索引,新索引建立完毕后,再一次过覆
盖旧索引,重建索引的漫长过程中,旧索引可以正常工作。
为了方便演示,定义一个CompassIndexBuilder,实现了Spring的
InitializingBean接口,自动在Spring启动时,启动一条线程进行索引重建。
12.2.5. 封装搜索
自定义CompassSearchService和AdvancedSearchCommand,以此实现高级搜索
。
<bean id="compassSearchService" class="com.family168.CompassSearchService">
<property name="compass" ref="compass"/>
<property name="pageSize" value="15"/>
</bean>
12.2.6. View JSP
简单搜索页面:只需要一个queryString参数:
<INPUT type="text" size="20" name="queryString">
结果页面将返回几个变量,包括:
searchResults(搜索结果)包括hits(结果)和searchtime(耗时)
pages(分页信息)包括page_from,page_to等
command(原来的查询请求)
参见下面简单的代码:
<c:if test="${not empty searchResults}">
耗时: ${searchResults.searchTime}ms
<c:forEach var="hit" items="${searchResults.hits}">
<c:choose>
<c:when test="${hit.alias == 'child'}">
<div class="left_content">
<p>
<a href="#" class= "title">${hit.data.name}</a>
<br/> parent:${hit.data.parent.name}
<br/>
</p>
</div>
</c:when>
</c:choose>
</c:forEach>
</c:if>
12.2.7. Compass Highlighter
定义Highlighter字段,只需要在AdvancedSearchCommand上将需要
highlighting的Fields加入即可。
searchCommand.setHighlightFields(new String[] {"name"});
定义高亮的样式
另外,如果对高亮显示的效果不满意的话,可以在applicationContext-
compass.xml 文件里配置:
<prop key="compass.engine.highlighter.default.formatter.simple.pre">
<![CDATA[<font color="red"><b>]]>
</prop>
<prop key="compass.engine.highlighter.default.formatter.simple.post">
<![CDATA[</b></font>]]>
</prop>
12.3. Hibernate Search
在applicationContext.xml中添加两个参数:
<prop
key="hibernate.search.default.directory_provider">org.hibernate.search.store.FSDir
ectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">d:/lucene/indexes</prop>
想使用动态索引的话,还需要配置event listener:
<property name="eventListeners">
<map>
<entry key="post-update">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-insert">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-delete">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-collection-recreate">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-collection-remove">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-collection-update">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
</map>
</property>
因为使用的时日尚短,还不知道如何在系统启动的时候统一初始化索引,
在hibernate search官方文档上找到的方法是先查询,后逐条索引的方式:
public void index() {
FullTextSession fullTextSession = Search.getFullTextSession(getSession());
//Transaction tx = fullTextSession.beginTransaction();
List<User> users = getSession().createQuery("from User").list();
for (User user : users) {
fullTextSession.index(user);
}
//tx.commit();
}
建立索引之后,就可以进行查询:
public void search() throws ParseException {
FullTextSession fullTextSession = Search
.getFullTextSession(getSession());
QueryParser parser = new QueryParser("user_name",
new StopAnalyzer());
parser.setPhraseSlop(0);
org.apache.lucene.search.Query luceneQuery = parser.parse(
"username");
org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery
(luceneQuery,
User.class);
List<User> list = hibQuery.list();
System.out.println(list.size());
}
第 12 章 全文检索
12.1. Lucene 基础指南
Lucene是apache下的一个采用java实现的全文搜索引擎,功能非常强大,而
它的API又比较简单,主要就做两件事:建立索引和进行搜索。
12.1.1. 建立索引的术语
Document:一个需要进行索引的单元,相当于数据库的一行纪录,任何想
要被索引的数据,都必须转化为Document对象存放。
Field:Document中的一个字段,相当于数据库中的Column ,Field是lucene
中含义比较多的一个术语,详情见后。
IndexWriter:负责将Document写入索引文件。通常情况下,IndexWriter的构
造函数包括了以下3个参数:索引存放的路径,分析器和是否重新创建索引
。特别注意的一点,当IndexWriter执行完addDocument方法后,一定要记得
调用自身的close方法来关闭它。只有在调用了close方法后,索引器才会将
存放在内在中的所有内容写入磁盘并关闭输出流。
Analyzer:分析器,主要用于文本分词。常用的有StandardAnalyzer分析器,
StopAnalyzer分析器,WhitespaceAnalyzer分析器等。
Directory:索引存放的位置。lucene提供了两种索引存放的位置,一种是磁
盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了
FSDirectory和RAMDirectory两个类。
段:Segment,是Lucene索引文件的最基本的一个单位。Lucene说到底就是不
断加入新的Segment,然后按一定的规则算法合并不同的Segment以合成新
的Segment。
lucene建立索引的过程就是将待索引的对象转化为Lucene的Document对象,
使用IndexWriter将其写入lucene自定义格式的索引文件中。
待索引的对象可以来自文件、数据库等任意途径,用户自行编码遍历目录
读取文件或者查询数据库表取得ResultSet,Lucene的API只负责和字符串打
交道。
12.1.1.1. Field的含义
Field的构造方法如下:
Field(String name, byte[] value, Field.Store store)
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
Field(String name, String value, Field.Store store, Field.Index index)
Field(String name, String value, Field.Store store, Field.Index index,
Field.TermVector termVector)
在Field当中有三个内部类:Field.Index,Field.Store,Field.TermVector。其中
Field.Index有五个属性,分别是:
Field.Index.ANALYZED:分词索引
Field.Index.NOT_ANALYZED:分词进行索引,如作者名,日期等,Rod
Johnson本身为一单词,不再需要分词。
Field.Index.NO:不进行索引,存放不能被搜索的内容如文档的一些附加属
性如文档类型, URL等。
Field.Index.NOT_ANALYZED_NO_NORMS:不使用分词索引,不使用存
储规则。
Field.Index.ANALYZED_NO_NORMS:使用分词索引,不使用存储规则。
Field.Store有三个属性
Field.Store.YES:索引文件本来只存储索引数据, 此设计将原文内容直接也
存储在索引文件中,如文档的标题。
Field.Store.NO:原文不存储在索引文件中,搜索结果命中后,再根据其他
附加属性如文件的Path,数据库的主键等,重新连接打开原文,适合原文
内容较大的情况。
Field.Store.COMPRESS:压缩存储。
termVector是Lucene 1.4.3新增的它提供一种向量机制来进行模糊查询,不太
常用。
上面所说的Field属性与lucene1.4.3版本的有比较大的不同,在旧版的1.4.3里
lucene是通过Field.Keyword(...),FieldUnIndexed(...),FieldUnstored(...)和
Field.Text(...)来设置不同字段的类型以达到不同的用途,而当前版本由
Field.Index和Field.Store两个字段的不同组合来达到上述效果。
还有一点说明,其中的两个构造函数其默认的值为Field.Store.NO和
Field.Index.TOKENIZED。
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
12.1.1.2. 限制Field的长度
IndexWriter类提供了一个setMaxFieldLength的方法来对Field的长度进行限制
,看一下源代码就知道其默认值为10000;我们可以在使用时重新设置此参
数。如果使用默认值,那么Lucene就仅仅对文档的前面的10000个term进行
索引,超过这一个数的文档就不会被建立索引。
12.1.1.3. 索引的合并、删除、优化
IndexWriter中的addIndexes方法将索引进行合并,当在不同的地方创建了索
引后,如果需要将索引合并,这时候使用addIndexes方法就显得很有意义。
可以通过IndexReader类从索引中进行文档的删除。IndexReader是很特别的
一个类,看源代码就知道它主要是通过自身的静态方法来完成构造的。示
例:
IndexReader reader = IndexReader.open("d:/lucene/index");
reader.deleteDocument(X); //这里的X是一个int的常数;不推荐这一种删除方
法
reader.deleteDocument(new Term("name", "oa")); //这是另一种删除索引的方
法,按字段来删除,推荐使用这一种做法
reader.close();
优化索引:可以使用IndexWriter类的optimize方法来进行优先,它会将多个
Segment进行合并,组成一个新的Segment,可以加快建立索引后搜索的速度
。另外需要注意的一点,optimize方法会降低建立索引的速度,而且要求的
磁盘空间会增加。
12.1.2. 进行搜索时的术语
IndexSearcher:是lucene中最基本的检索工具,所有的检索都会用到
IndexSearcher工具。初始化IndexSearcher需要设置索引存放的路径,让查询
器能定位索引而进行搜索。
Query:查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等
等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类
。
QueryParser::是一个解析用户输入的工具,可以通过扫描用户输入的字符
串,生成Query对象。
Hits:在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才
算是完成搜索的目的。在lucene中,搜索的结果的集合是用Hits类的实例来
表示的。Hits对象中主要方法有:
length(): 返回搜索结果的总数,下面简单的用法中有用到Hit的这一个方法
doc(int n): 返回第n个文档
iterator(): 返回一个迭代器
这里再提一下Hits,这也是Lucene比较精彩的地方,熟悉hibernate的朋友都
知道hibernate有一个延迟加载的属性,同样,Lucene也有。Hits对象也是采
用延迟加载的方式返回结果的,当要访问某个文档时,Hits对象就在内部
对Lucene的索引又进行一次检索,最后才将结果返回到页面显示。
12.1.3. 一个简单的实例
首先把lucene的包放在classpath路径中去,写下面一个简单的类:
public class FSDirectoryTest {
//建立索引的路径
public static final String path = "d:/index2";
public static void main(String[] args) throws Exception {
Document doc1 = new Document();
doc1.add(new Field("name", "www family168 com", Field.Store.YES,
Field.Index.TOKENIZED));
Document doc2 = new Document();
doc2.add(new Field("name", "family168 blog bbs", Field.Store.YES,
Field.Index.TOKENIZED));
IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(
path, true), new StandardAnalyzer(), true);
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.close();
IndexSearcher searcher = new IndexSearcher(path);
Hits hits = null;
Query query = null;
QueryParser qp = new QueryParser("name", new StandardAnalyzer());
query = qp.parse("family168");
hits = searcher.search(query);
System.out.println("查找\"family168\" 共" + hits.length() + "个结果");
query = qp.parse("bbs");
hits = searcher.search(query);
System.out.println("查找\"bbs\" 共" + hits.length() + "个结果");
}
}
执行的结果:
查找"family168" 共2个结果
查找"bbs" 共1个结果
12.1.4. 复杂一点的实例
在windows系统下的的C盘,建一个名叫s的文件夹,在该文件夹里面随便建
三个txt文件,随便起名啦,就叫"1.txt","2.txt"和"3.txt"。
其中1.txt的内容如下:
family168
文档,资料
2009
而"2.txt"和"3.txt"的内容也可以随便写几写,这里懒写,就复制一个和1.txt
文件的内容一样吧
下载lucene包,放在classpath路径中,然后建立索引:
public class LuceneExample {
public static void main(String[] args) throws Exception {
File fileDir = new File("D:/lucene"); // 指明要索引文件夹的位置,这里
是C盘的S文件夹下
File indexDir = new File("D:/lucene/index"); // 这里放索引文件的位置
File[] textFiles = fileDir.listFiles();
Analyzer luceneAnalyzer = new StandardAnalyzer();
IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
indexFile(luceneAnalyzer,indexWriter, textFiles);
indexWriter.optimize(); // optimize()方法是对索引进行优化
indexWriter.close();
}
public static void indexFile(Analyzer luceneAnalyzer,IndexWriter
indexWriter,File[] textFiles) throws Exception {
// 增加document到索引去
for (int i = 0; i < textFiles.length; i++) {
if (textFiles[i].isFile() && textFiles[i].getName().endsWith(".txt")) {
String temp = FileReaderAll(textFiles[i].getCanonicalPath(),"GBK");
Document document = new Document();
Field FieldBody = new Field("body", temp,
Field.Store.YES,Field.Index.TOKENIZED);
document.add(FieldBody);
indexWriter.addDocument(document);
}
}
}
public static String FileReaderAll(String FileName, String charset)throws
IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(FileName), charset));
String line = "";
String temp = "";
while ((line = reader.readLine()) != null) {
temp += line;
}
reader.close();
return temp;
}
}
执行查询:
public class TestQuery {
public static void main(String[] args)
throws IOException, ParseException {
Hits hits = null;
String queryString = "family168";
Query query = null;
IndexSearcher searcher = new IndexSearcher("D:/lucene/index");
Analyzer analyzer = new StandardAnalyzer();
try {
QueryParser qp = new QueryParser("body", analyzer);
query = qp.parse(queryString);
} catch (ParseException e) {
e.printStackTrace();
}
if (searcher != null) {
hits = searcher.search(query);
if (hits.length() > 0) {
System.out.println("找到:" + hits.length() + " 个结果!");
}
}
}
}
执行结果:
找到:3 个结果!
12.1.5. Hibernate与lucene的结合使用:
参考这一篇文章,里面讲得很详细
http://wiki.redsaga.com/confluence/display/HART/Hibernate+Lucene+Integrati
on
12.2. Compass
12.2.1. 概述
compass官方网站: http://www.compass-project.org/
Compass是一流的JAVA搜索框架,可以快速修饰你的应用,使其具备
Searchable的能力。
在没有Compass,单纯用Lucene的环境里,一般做法是定期遍历数据库,编
码将里面的内容转为索引。
而Compass通过与Hibernate和Spring的结合,简单的将Domain Object转换为
Lucene引擎的映射,而且索引库能够随Domain Object同步更新(Data Mirror)
。
使用compass快速建立全文搜索的流程如下:
用简单的compass annotation把User对象映射到Lucene。
提供Index service和Search service。
编写查询结果的显示页面,将查询返回的变量显示出来。
12.2.2. Object/Search Engine Mapping的Annotations配置
使用JDK5的annotation 来进行OSEM(Object/Search Engine Mapping)比用xml
文件按简单许多,下面就是简单的搜索类,可见@SearchableID,
@SearchableProperty与@SearchableComponent 三个标记,分别代表主键、可
搜索的属性与关联的,另一个可搜索的对象,另外Compass要求POJO要有
默认构造函数,要实现equals()和hashcode():
@Entity
@Searchable
public class Child {
private Long id;
private String name;
private Parent parent;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@SearchableId
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column
@SearchableProperty(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID")
@SearchableComponent(refAlias = "parent")
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
注意可被搜索的对象必须遵守下面的规则
所有的持久化类必须有默认构造函数(没参数的构造函数).
OSEM要求映射类JavaBean必须有一个或更多的id主建. id属性可以是对象类
型,例如java.lang.String or java.util.Date.
并且类注解使用@SearchableClassConverter或者使用定义Converter(通常继承
Compass AbstractBasicConverter).
使用JavaBean规范, getter/setter.
12.2.3. Data Change Mirror功能
DataMirror会把数据库的增删改变化实时映射到索引文件中。
如果你采用Hibernate等ORM方案,Compass就会与Hibernate的event机制结
合,或者使用AOP的方式,自动在数据库增删改时变更索引;如果你只是
采用JDBC,也可以在XML文件配置Table Mapping或ResultSet Mapping,指
定version列,Compasss定时对version列变化了的数据进行索引更新。
而且Compass还支持事务,在查询数据库遍历结果集的过程中如果出现异
常,会在Index Segments文件一级进行事务控制。
如果没有Compass,我们一般会在每天深夜重建一次索引。相比Compass的
做法,
一来反应迟缓,平均延时半天。
二来效率没有Compass高。如果采用完全重建索引,效率就不用说了。如
果进行增量索引,就要增加一个字段,在数据更新时进行特殊的处理,删
除时也不能直接删除数据,要等lucene删完索引数据才能删除,这样Lucene
对应用就非常不透明了。
三来不支持事务,如果建立索引过程中出现异常,索引文件的状态是不可
控的。
12.2.4. 与spring,hibernate集成配置
下面是compass在spring中的简明配置:
<bean id="compass" class="org.compass.spring.LocalCompassBean">
<!-- anontaition式设置 -->
<property name="classMappings">
<list>
<value>com.family168.domain.Child</value>
<value>com.family168.domain.Parent</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}/oa/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.dep.SpringHibernate3GpsDevice">
<property name="name" value="hibernateDevice"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</list>
</property>
</bean>
上面要留意的配置有:
annotationConfiguration:使用annotation配置,指定要转换的POJO如User。
compass.engine.connection:索引文件在服务器上的存储路径。
hibernateGpsDevice:与hibernate的绑定,用Hibernate 3事件系统,支持Real
Time Data Mirroring .经Hiberante的数据改变会自动被反射到索引里面.
上面的Gps定义只是自启动Mirror Data Change,但索引初始建立就需要靠
程序员自己写代码调用CompassGps.index()来完成。
理论上,因为可以Mirror Data Change,所以索引只需要建立一次即可。如
果索引已存在,Compass会对它gracefule override,所谓graceful,就是
Compass会现在临时目录存放新的索引,新索引建立完毕后,再一次过覆
盖旧索引,重建索引的漫长过程中,旧索引可以正常工作。
为了方便演示,定义一个CompassIndexBuilder,实现了Spring的
InitializingBean接口,自动在Spring启动时,启动一条线程进行索引重建。
12.2.5. 封装搜索
自定义CompassSearchService和AdvancedSearchCommand,以此实现高级搜索
。
<bean id="compassSearchService" class="com.family168.CompassSearchService">
<property name="compass" ref="compass"/>
<property name="pageSize" value="15"/>
</bean>
12.2.6. View JSP
简单搜索页面:只需要一个queryString参数:
<INPUT type="text" size="20" name="queryString">
结果页面将返回几个变量,包括:
searchResults(搜索结果)包括hits(结果)和searchtime(耗时)
pages(分页信息)包括page_from,page_to等
command(原来的查询请求)
参见下面简单的代码:
<c:if test="${not empty searchResults}">
耗时: ${searchResults.searchTime}ms
<c:forEach var="hit" items="${searchResults.hits}">
<c:choose>
<c:when test="${hit.alias == 'child'}">
<div class="left_content">
<p>
<a href="#" class= "title">${hit.data.name}</a>
<br/> parent:${hit.data.parent.name}
<br/>
</p>
</div>
</c:when>
</c:choose>
</c:forEach>
</c:if>
12.2.7. Compass Highlighter
定义Highlighter字段,只需要在AdvancedSearchCommand上将需要
highlighting的Fields加入即可。
searchCommand.setHighlightFields(new String[] {"name"});
定义高亮的样式
另外,如果对高亮显示的效果不满意的话,可以在applicationContext-
compass.xml 文件里配置:
<prop key="compass.engine.highlighter.default.formatter.simple.pre">
<![CDATA[<font color="red"><b>]]>
</prop>
<prop key="compass.engine.highlighter.default.formatter.simple.post">
<![CDATA[</b></font>]]>
</prop>
12.3. Hibernate Search
在applicationContext.xml中添加两个参数:
<prop
key="hibernate.search.default.directory_provider">org.hibernate.search.store.FSDir
ectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">d:/lucene/indexes</prop>
想使用动态索引的话,还需要配置event listener:
<property name="eventListeners">
<map>
<entry key="post-update">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-insert">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-delete">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-collection-recreate">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-collection-remove">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
<entry key="post-collection-update">
<bean class="org.hibernate.search.event.FullTextIndexEventListener"/>
</entry>
</map>
</property>
因为使用的时日尚短,还不知道如何在系统启动的时候统一初始化索引,
在hibernate search官方文档上找到的方法是先查询,后逐条索引的方式:
public void index() {
FullTextSession fullTextSession = Search.getFullTextSession(getSession());
//Transaction tx = fullTextSession.beginTransaction();
List<User> users = getSession().createQuery("from User").list();
for (User user : users) {
fullTextSession.index(user);
}
//tx.commit();
}
建立索引之后,就可以进行查询:
public void search() throws ParseException {
FullTextSession fullTextSession = Search
.getFullTextSession(getSession());
QueryParser parser = new QueryParser("user_name",
new StopAnalyzer());
parser.setPhraseSlop(0);
org.apache.lucene.search.Query luceneQuery = parser.parse(
"username");
org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery
(luceneQuery,
User.class);
List<User> list = hibQuery.list();
System.out.println(list.size());
}
发表评论
-
JGroups 简介、适用场合、配置、程序例子Demo等完全使用指南
2010-05-14 09:24 2520JGroups 简介、适用场合、配置、程序例子Demo等完全使 ... -
Lombok 消除Java的冗长
2010-05-06 12:12 1336【转至】http://www.blogjava.net/men ... -
Swing Jtable 学习
2010-04-28 20:53 1564一.创建表格控件的各种方式:1) 调用无参构造函数. ... -
高阶Java-Java注解 Java annotation 使用详解【转载的】
2010-04-16 22:22 8572来越的开源框架都提供了“元数据”支持了。为了不outdate ... -
log4j配置祥解
2010-04-09 11:03 759[转] http://www.blogjava.net/kit ... -
Java Concurrency(java 并发)
2010-04-04 15:31 1340【转】http://www.blogjava.net/vinc ... -
Jbpm4常见概念
2010-03-25 09:55 803[color=green][size=medium] [ ... -
在eclipse项目中使用maven管理依赖
2010-01-10 21:20 3279概览: 如何导入maven项目 ... -
JRebal(原名JavaRebel)破解小记
2009-10-22 13:13 3010原文地址:http://www.blogjava.net/us ... -
perf4j 全局性能
2009-10-21 16:47 958http://perf4j.codehaus.org/devg ... -
java泛型
2009-09-27 13:08 905在已发布的Java1.4中在核心代码库中增加了许多新的API( ... -
JPA
2009-02-09 10:49 19621.JPA概述 JPA(Java Pe ... -
EJB3.0开发指南之多对多和一对一
2009-02-09 10:47 1987学生和老师就是多对多的关系。一个学生有多个老师,一个老师教多个 ... -
java笔试题
2009-02-01 10:29 39241。 java.lang.String类是fina ... -
多表联结查询怎样得到重复f01Id数据的最后一条记录
2007-11-23 11:30 1724select a.f01Id,a.costCenter,a. ... -
java中的rmi技术
2007-10-05 15:56 1603java远程方法调用
相关推荐
它提供了一套高级文本搜索程序库,让开发者能够在 .NET 应用程序中轻松实现全文检索功能。在本案例中,我们将在 .Net MVC4 框架上使用 Lucene.Net 来构建一个全文检索系统。 首先,我们需要理解全文检索的基本概念...
标题中的“excel全文检索工具”指的是一个专门针对Excel文件进行全文搜索的软件或应用程序。在IT领域,全文检索是搜索引擎技术的重要组成部分,它允许用户在大量文本数据中快速找到包含特定词汇或短语的记录。这里...
Lucene是Apache软件基金会的一个开源项目,它是一个高性能、可扩展的信息检索库,为Java开发人员提供了全文检索和分析的核心工具。这个库使得开发者能够轻松地在应用程序中集成高级的搜索功能,支持英文和其他语言的...
这些库能够解析PDF文档的内容,将其转换为可搜索的文本,以便纳入全文检索系统。PDF文档通常包含丰富的格式和图像,解析它们并提取文本是一项复杂任务,但通过这样的库可以简化这一过程。 在实际应用中,你需要根据...
Lucene是Apache软件基金会的一个开源项目,它是Java开发的全文检索库,提供了高效的索引和查询能力。Lucene的核心功能包括文本分析、索引构建、查询解析以及结果排序等,为开发者提供了强大的全文搜索解决方案。 二...
全文检索是一种在海量数据中快速查找特定信息的技术,尤其在处理大量文档时,其效率尤为重要。在本场景中,描述提到“检索40万文档只需要1秒”,这展示了全文检索的强大性能,它能够以极快的速度帮助用户从海量文档...
Apache Lucene 是一个开源的全文检索库,它提供了一种高效、可扩展的方式来构建全文搜索引擎。在Java开发中,Lucene被广泛用于实现文件的全文检索功能,包括对doc、docx、pdf、txt等常见格式文档的文本内容检索。在...
横瓜全文检索引擎ASP版是一款基于ASP(Active Server Pages)技术构建的全文搜索引擎,它为开发者提供了一种方便快捷的方式,使得用户可以在自己的网站或应用上实现高效、精确的文本搜索功能。全文检索是一种能够在...
Java 全文检索库 Lucene 正是为此目的而设计的,它是一个高性能、可扩展的开源搜索引擎库。 **Lucene 简介** Lucene 是 Apache 软件基金会的一个顶级项目,由 Doug Cutting 创建,起初是为了解决他个人的文档检索...
为了满足开发人员的需求,本项目旨在基于SVN构建一个全文搜索功能,该功能将允许用户通过Web界面进行文本内容的检索,并展示相关的代码文件路径。具体来说,目标包括: 1. **内容检索功能**:支持对文本文件中的...
全文检索允许用户使用关键字进行高效的搜索,而不必精确匹配字段中的完整词汇,尤其适用于处理大量非结构化和半结构化数据。 在SQL Server 2005中,全文检索技术的核心特点包括: 1. **断字符(Word Breaker)**:...
Sphinx是一个高性能、开源的全文检索引擎,专为配合SQL数据库而设计,如MySQL和PostgreSQL,用于实现高效且专业的全文搜索功能。它的核心优势在于能够提供比数据库原生搜索更强大的搜索性能,并且易于集成到各种使用...
《Lucene全文检索:简单索引与搜索实例详解》 Lucene是Apache软件基金会的开源项目,是一款强大的全文检索库,被广泛应用于Java开发中,为开发者提供了构建高性能搜索引擎的能力。在本文中,我们将深入探讨如何基于...
在C#中,实现全文检索可以借助于各种库,如Lucene.NET,它是一个强大的、开源的全文搜索引擎库。当然,也可以自定义实现,这样更便于理解和控制整个过程。从“建立索引”这个文件名来看,我们将讨论如何创建倒排索引...
全文检索是MySQL中的一个重要功能,它允许用户通过关键字搜索数据库中的文本内容,而不是仅仅依赖于精确匹配的列值。本篇文章将深入探讨MySQL的全文检索机制,以期帮助你构建高效的全文搜索引擎。 全文检索在信息...
"全文检索jar包含ik分词jar"这一描述揭示了一个基于Java的全文检索解决方案,其中包含了Lucene搜索引擎库和IK Analyzer中文分词器。这样的组合使得开发者能够轻松地处理中文文本,构建出高效、精准的全文检索系统。...
- **全文检索例子.doc**:这个文档可能包含一个或多个示例,演示如何在实际项目中创建索引、执行查询和处理搜索结果。 - **基于Java的全文索引引擎--Lucene.ppt**:这份PPT可能详细解释了如何在Java应用中集成Lucene...