1)文件全文搜索
首先导入三个核心包(hibernate-search.jar、hibernate-commons-annotations.jar和lucene-core.jar)和一个高亮的包(lucene-highlighter-2.0.0.jar)到web app里面;
创建索引数据库类:
package com.golden.info.test;
import java.io.File;
import net.paoding.analysis.analyzer.PaodingAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexWriter;
/**
* @author liuxh
*初始化检索库
*/
public class CreateDataBase{
public CreateDataBase(){
}
public int createDataBase(File file){
int returnValue=0;
if(!file.isDirectory()){
file.mkdirs();
}
try{
IndexWriter indexWriter= new IndexWriter(file,new StandardAnalyzer(),true);
indexWriter.close();
returnValue=1;
}catch(Exception ex){
ex.printStackTrace();
}
return returnValue;
}
/**
*传入检索库路径,初始化库
* @paramfile
* @return
*/
public int createDataBase(String file){
return this.createDataBase(new File(file));
}
public static void main(String[]args){
CreateDataBase temp= new CreateDataBase();
if(temp.createDataBase("e:\\lucene\\holendb")==1){ //创建数据库存放的路径
System.out.println("数据库初始化成功");
}
}
}
然后添加记录:
package com.golden.info.test;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
/**
* @author liuxh
*记录加载
*/
public class InsertRecords{
public InsertRecords(){
}
public int insertRecords(String dbpath,File file){
int returnValue=0;
try{
IndexWriter indexWriter = new IndexWriter(dbpath,new StandardAnalyzer(),false);
this.addFiles(indexWriter,file);
returnValue=1;
}catch(Exception ex){
ex.printStackTrace();
}
return returnValue;
}
/**
*传入需加载的文件名
* @paramfile
* @return
*/
public int insertRecords(String dbpath,String file){
return this.insertRecords(dbpath,new File(file));
}
public void addFiles(IndexWriter indexWriter,File file){
Document doc= new Document();
try{
doc.add(new Field("filename",file.getCanonicalPath(),Field.Store.YES,Field.Index.UN_TOKENIZED)); //注
doc.add(new Field("content",this.chgFileToString(file),Field.Store.YES,Field.Index.TOKENIZED));//注
indexWriter.addDocument(doc);
indexWriter.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
*从文本文件中读取内容
* @paramfile
* @return
*/
public String chgFileToString(File file){
String returnValue= null;
StringBuffer sb= new StringBuffer();
char[]c= new char[4096];
try{
//读取的文件必须是UTF-8格式的,不然读出来的中文为乱码
Reader reader= new FileReader(file);
int n=0;
while(true){
n=reader.read(c);
if(n>0){
sb.append(c,0,n);
}else{
break;
}
}
reader.close();
}catch(Exception ex){
ex.printStackTrace();
}
returnValue=sb.toString();
return returnValue;
}
public static void main(String[] args){
InsertRecords temp= new InsertRecords();
String dbpath="e:\\lucene\\holendb";
//holen1.txt中包含关键字"holen"和"java"
if(temp.insertRecords(dbpath,"e:\\lucene\\holen1.txt")==1){
System.out.println("添加文件成功");
}
//holen2.txt中包含关键字"holen"和"chen"
if(temp.insertRecords(dbpath,"e:\\lucene\\holen2.txt")==1){
System.out.println("添加文件成功");
}
}
}
注:lucene 2.0中去掉了Field.Text和Field.KeyWord,不过网上的sample code还在用,所以会造成许多刚接触lucene的人遇到问题,我就是其中一个。
新的写法需要直接new一个Field对象:
Reader txtReader = new FileReader(f);
doc.add(new Field(”path”,f.getCanonicalPath(),Field.Store.YES,Field.Index.UN_TOKENIZED));
doc.add(new Field(”contents”,txtReader));
不过Field.Index.UN_TOKENIZED也即将在lucene3中消失了,到时候又要改了。
查询方法并高亮关键字:
package com.golden.info.test;
import java.io.StringReader;
import java.util.ArrayList;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
/**
* @author liuxh
*检索查询
*/
public class QueryRecords{
public QueryRecords(){
}
/**
*检索查询,将结果集返回
* @param searchkey
* @param dbpath
* @param searchfield
* @return
*/
public ArrayList queryRecords(String searchkey,String dbpath,String searchfield){
ArrayList list= null;
try{
Searcher searcher= new IndexSearcher(dbpath);
// 对于单个字段进行查询
// QueryParser parse=new QueryParser(searchfield,new StandardAnalyzer());
// Query query =parse.parse(searchkey);
// 对多个字段进行查询
String[] productFields = {"filename", "content"};
QueryParser parse = new MultiFieldQueryParser(productFields, new StandardAnalyzer());
parse.setAllowLeadingWildcard(true);
Query query=parse.parse(searchkey);
Hits hits=searcher.search(query);
if(hits!= null){
list= new ArrayList();
int temp_hitslength=hits.length();
Document doc= null;
Analyzer analyzer = new StandardAnalyzer();
//高亮显示设置
Highlighter highlighter = null;
SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<read>","</read>");
highlighter = new Highlighter(simpleHTMLFormatter,new QueryScorer(query));
highlighter.setTextFragmenter(new SimpleFragmenter(100));//这个100是指定关键字字符串的context的长度,你可以自己设定,因为不可能返回整篇正文内容
for(int i=0;i<temp_hitslength;i++){
doc=hits.doc(i);
TokenStream tokenStream2 =analyzer.tokenStream("filename", new StringReader(doc.get("filename")));
list.add(highlighter.getBestFragment(tokenStream2,doc.get("filename")));
TokenStream tokenStream =analyzer.tokenStream("content", new StringReader(doc.get("content")));
list.add(highlighter.getBestFragment(tokenStream,doc.get("content")));
}
}
}catch(Exception ex){
ex.printStackTrace();
}
return list;
}
public static void main(String[]args){
QueryRecords temp= new QueryRecords();
ArrayList list= null;
//搜索在那个字段中的内容,其中第一个参数为查询关键字,第二个参数为库文件的路劲,第三个参数为字段名字
list=temp.queryRecords("holen 我游 lucene","e:\\lucene\\holendb","content");
for(int i=0;i<list.size();i++){
System.out.println((String)list.get(i));
}
}
}
2)数据库全文搜索
首先,我们需要将相关配置添加到persistence.xml中,如下:
<!-- use a file system based index -->
<property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider" />
<!-- directory where the indexes will be stored -->
<property name="hibernate.search.default.indexBase" value="[/path/to/your/location/directory]" />
<property name="hibernate.ejb.event.post-insert" value="org.hibernate.search.event.FullTextIndexEventListener" />
<property name="hibernate.ejb.event.post-update" value="org.hibernate.search.event.FullTextIndexEventListener" />
<property name="hibernate.ejb.event.post-delete" value="org.hibernate.search.event.FullTextIndexEventListener" />
这样我们就设置好了类路径、索引路径、索引操作监听器路径。下面,我们需要添加hibernate-search.jar、hibernate-commons-annotations.jar和lucene-core.jar到你的ear里面。
好了,现在,我们需要告诉Hibernate Search哪个对象被索引,并且我们会对哪些属性感兴趣。
@Entity
@Name("product")
@Indexed
public class Product implements Serializable {
static final long serialVersionUID = 1l;
@Id @GeneratedValue @DocumentId
private Long id;
@NotNull
@Field(index = Index.TOKENIZED)
private String name;
@NotNull
@Field(index = Index.TOKENIZED)
private String description;
// getters and setters
}
@Indexed注解用来告诉Hibernate Search该持久类是拥有索引的。@DocumentId注解用来标明这个属性是这个对象的ID,并且未被编入索引。此外,我们还有两个属性,分别是 name和description。这两个属性都用@Field标注,这样Hibernate Search就被允许分析处理这两个属性。其他可选的属性有Index.NO(不要被分析)、Index.UN_TOKENIZED(不要被分析器预先处理)、Index.NO_NORM(不需要存储的普通属性)。
现在,我们已经拥有了Lucene的所有索引,我们还需要一个搜索的方法。所以我们需要建立一个SearchManager类。
@Name("SearchManager")
public class SearchManager {
@In
private FullTextEntityManager entityManager;
private String searchPattern;
// getters and setters for searchPattern
public List getResults() {
Map boostFields = new HashMap(2);
// increase the importance of the name field
// over the other product fields
boostFields.put("name", 4f);
String[] productFields = {"name", "description"};
QueryParser parser = new MultiFieldQueryParser(productFields, new StandardAnalyzer(), boostFields); parser.setAllowLeadingWildcard(true);
Query luceneQuery;
try {
luceneQuery = parser.parse(searchPattern);
} catch (ParseException pe) {
log.error("found a problem in search", pe);
return null;
}
// extract the products
List products =entityManager.createFullTextQuery(luceneQuery, Product.class).
setMaxResults(20).getResultList();
return products;
}
}
好了,现在我们可以创建search.xhtml文件,用来显示搜索结果了。下面是该文件的一个片段。
<rich:dataGrid value="#{SearchManager.results}" var="garage">
[ loop over the values ]
</rich:dataGrid>
然后,在pages.xml中添加一个到search.xhtml的入口。
<page view-id="/search.xhtml">
<param name="searchPattern" value="#{SearchManager.searchPattern}"/>
</page>
还剩下一个步骤。我们需要添加一个搜索框到菜单里面。如果你使用了Seam的默认布局,你就会看见/view/layout文件夹下面有一个menu.xhtml文件。如果没有找到,只需要将下面这一段添加到你需要的地方:
<h:form id="search_form">
<h:inputText id="searchPattern" required="true" value="#{SearchManager.searchPattern}" />
<h:commandButton action="/search.xhtml" value="search"></h:commandButton>
</h:form>
现在,你可以开始搜索你想要的对象了。是不是非常简单?但是,如果对象的一个属性是用来表示这个对象是否能显示在页面上,那应该怎么办?好的,在这种情况下,你需要添加一个过滤器。过滤器都继承自org.apache.lucene.search.Filter类。
想象一下,我们在产品类上有一个属性是用来标明产品的状态的,这个属性有三种值"L"表示有效的产品,"D"表示被删除的产品,"P"表示待发布的产品。显然,在用户搜索的时候,我们只想显示出可见的产品。所以我们需要添加一个过滤器:
1. public class LiveProductFilter extends Filter {
2. private static final long serialVersionUID = 1l;
3. public BitSet bits(IndexReader reader) throws IOException {
4. BitSet bitSet = new BitSet( reader.maxDoc() );
5. TermDocs termDocs = reader.termDocs( new Term("status", "L") );
6. while ( termDocs.next() ) {
7. bitSet.set( termDocs.doc() );
8. }
9. return bitSet;
10. }
11. }
为了让Hibernate Search能够找到我们在产品类上添加的过滤器,我们需要添加下面这个注解:
@FullTextFilterDefs ( { @FullTextFilterDef(name="liveProduct", impl = LiveProductFilter.class, cache=false) })好了,就写到这里。
参考文件:http://huxiuliang.iteye.com/blog/583136
注:数据库全文搜索暂时没有配置成功,加载监听器的时候报找不到类文件
首先导入三个核心包(hibernate-search.jar、hibernate-commons-annotations.jar和lucene-core.jar)和一个高亮的包(lucene-highlighter-2.0.0.jar)到web app里面;
创建索引数据库类:
package com.golden.info.test;
import java.io.File;
import net.paoding.analysis.analyzer.PaodingAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexWriter;
/**
* @author liuxh
*初始化检索库
*/
public class CreateDataBase{
public CreateDataBase(){
}
public int createDataBase(File file){
int returnValue=0;
if(!file.isDirectory()){
file.mkdirs();
}
try{
IndexWriter indexWriter= new IndexWriter(file,new StandardAnalyzer(),true);
indexWriter.close();
returnValue=1;
}catch(Exception ex){
ex.printStackTrace();
}
return returnValue;
}
/**
*传入检索库路径,初始化库
* @paramfile
* @return
*/
public int createDataBase(String file){
return this.createDataBase(new File(file));
}
public static void main(String[]args){
CreateDataBase temp= new CreateDataBase();
if(temp.createDataBase("e:\\lucene\\holendb")==1){ //创建数据库存放的路径
System.out.println("数据库初始化成功");
}
}
}
然后添加记录:
package com.golden.info.test;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
/**
* @author liuxh
*记录加载
*/
public class InsertRecords{
public InsertRecords(){
}
public int insertRecords(String dbpath,File file){
int returnValue=0;
try{
IndexWriter indexWriter = new IndexWriter(dbpath,new StandardAnalyzer(),false);
this.addFiles(indexWriter,file);
returnValue=1;
}catch(Exception ex){
ex.printStackTrace();
}
return returnValue;
}
/**
*传入需加载的文件名
* @paramfile
* @return
*/
public int insertRecords(String dbpath,String file){
return this.insertRecords(dbpath,new File(file));
}
public void addFiles(IndexWriter indexWriter,File file){
Document doc= new Document();
try{
doc.add(new Field("filename",file.getCanonicalPath(),Field.Store.YES,Field.Index.UN_TOKENIZED)); //注
doc.add(new Field("content",this.chgFileToString(file),Field.Store.YES,Field.Index.TOKENIZED));//注
indexWriter.addDocument(doc);
indexWriter.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
*从文本文件中读取内容
* @paramfile
* @return
*/
public String chgFileToString(File file){
String returnValue= null;
StringBuffer sb= new StringBuffer();
char[]c= new char[4096];
try{
//读取的文件必须是UTF-8格式的,不然读出来的中文为乱码
Reader reader= new FileReader(file);
int n=0;
while(true){
n=reader.read(c);
if(n>0){
sb.append(c,0,n);
}else{
break;
}
}
reader.close();
}catch(Exception ex){
ex.printStackTrace();
}
returnValue=sb.toString();
return returnValue;
}
public static void main(String[] args){
InsertRecords temp= new InsertRecords();
String dbpath="e:\\lucene\\holendb";
//holen1.txt中包含关键字"holen"和"java"
if(temp.insertRecords(dbpath,"e:\\lucene\\holen1.txt")==1){
System.out.println("添加文件成功");
}
//holen2.txt中包含关键字"holen"和"chen"
if(temp.insertRecords(dbpath,"e:\\lucene\\holen2.txt")==1){
System.out.println("添加文件成功");
}
}
}
注:lucene 2.0中去掉了Field.Text和Field.KeyWord,不过网上的sample code还在用,所以会造成许多刚接触lucene的人遇到问题,我就是其中一个。
新的写法需要直接new一个Field对象:
Reader txtReader = new FileReader(f);
doc.add(new Field(”path”,f.getCanonicalPath(),Field.Store.YES,Field.Index.UN_TOKENIZED));
doc.add(new Field(”contents”,txtReader));
不过Field.Index.UN_TOKENIZED也即将在lucene3中消失了,到时候又要改了。
查询方法并高亮关键字:
package com.golden.info.test;
import java.io.StringReader;
import java.util.ArrayList;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
/**
* @author liuxh
*检索查询
*/
public class QueryRecords{
public QueryRecords(){
}
/**
*检索查询,将结果集返回
* @param searchkey
* @param dbpath
* @param searchfield
* @return
*/
public ArrayList queryRecords(String searchkey,String dbpath,String searchfield){
ArrayList list= null;
try{
Searcher searcher= new IndexSearcher(dbpath);
// 对于单个字段进行查询
// QueryParser parse=new QueryParser(searchfield,new StandardAnalyzer());
// Query query =parse.parse(searchkey);
// 对多个字段进行查询
String[] productFields = {"filename", "content"};
QueryParser parse = new MultiFieldQueryParser(productFields, new StandardAnalyzer());
parse.setAllowLeadingWildcard(true);
Query query=parse.parse(searchkey);
Hits hits=searcher.search(query);
if(hits!= null){
list= new ArrayList();
int temp_hitslength=hits.length();
Document doc= null;
Analyzer analyzer = new StandardAnalyzer();
//高亮显示设置
Highlighter highlighter = null;
SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<read>","</read>");
highlighter = new Highlighter(simpleHTMLFormatter,new QueryScorer(query));
highlighter.setTextFragmenter(new SimpleFragmenter(100));//这个100是指定关键字字符串的context的长度,你可以自己设定,因为不可能返回整篇正文内容
for(int i=0;i<temp_hitslength;i++){
doc=hits.doc(i);
TokenStream tokenStream2 =analyzer.tokenStream("filename", new StringReader(doc.get("filename")));
list.add(highlighter.getBestFragment(tokenStream2,doc.get("filename")));
TokenStream tokenStream =analyzer.tokenStream("content", new StringReader(doc.get("content")));
list.add(highlighter.getBestFragment(tokenStream,doc.get("content")));
}
}
}catch(Exception ex){
ex.printStackTrace();
}
return list;
}
public static void main(String[]args){
QueryRecords temp= new QueryRecords();
ArrayList list= null;
//搜索在那个字段中的内容,其中第一个参数为查询关键字,第二个参数为库文件的路劲,第三个参数为字段名字
list=temp.queryRecords("holen 我游 lucene","e:\\lucene\\holendb","content");
for(int i=0;i<list.size();i++){
System.out.println((String)list.get(i));
}
}
}
2)数据库全文搜索
首先,我们需要将相关配置添加到persistence.xml中,如下:
<!-- use a file system based index -->
<property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider" />
<!-- directory where the indexes will be stored -->
<property name="hibernate.search.default.indexBase" value="[/path/to/your/location/directory]" />
<property name="hibernate.ejb.event.post-insert" value="org.hibernate.search.event.FullTextIndexEventListener" />
<property name="hibernate.ejb.event.post-update" value="org.hibernate.search.event.FullTextIndexEventListener" />
<property name="hibernate.ejb.event.post-delete" value="org.hibernate.search.event.FullTextIndexEventListener" />
这样我们就设置好了类路径、索引路径、索引操作监听器路径。下面,我们需要添加hibernate-search.jar、hibernate-commons-annotations.jar和lucene-core.jar到你的ear里面。
好了,现在,我们需要告诉Hibernate Search哪个对象被索引,并且我们会对哪些属性感兴趣。
@Entity
@Name("product")
@Indexed
public class Product implements Serializable {
static final long serialVersionUID = 1l;
@Id @GeneratedValue @DocumentId
private Long id;
@NotNull
@Field(index = Index.TOKENIZED)
private String name;
@NotNull
@Field(index = Index.TOKENIZED)
private String description;
// getters and setters
}
@Indexed注解用来告诉Hibernate Search该持久类是拥有索引的。@DocumentId注解用来标明这个属性是这个对象的ID,并且未被编入索引。此外,我们还有两个属性,分别是 name和description。这两个属性都用@Field标注,这样Hibernate Search就被允许分析处理这两个属性。其他可选的属性有Index.NO(不要被分析)、Index.UN_TOKENIZED(不要被分析器预先处理)、Index.NO_NORM(不需要存储的普通属性)。
现在,我们已经拥有了Lucene的所有索引,我们还需要一个搜索的方法。所以我们需要建立一个SearchManager类。
@Name("SearchManager")
public class SearchManager {
@In
private FullTextEntityManager entityManager;
private String searchPattern;
// getters and setters for searchPattern
public List getResults() {
Map boostFields = new HashMap(2);
// increase the importance of the name field
// over the other product fields
boostFields.put("name", 4f);
String[] productFields = {"name", "description"};
QueryParser parser = new MultiFieldQueryParser(productFields, new StandardAnalyzer(), boostFields); parser.setAllowLeadingWildcard(true);
Query luceneQuery;
try {
luceneQuery = parser.parse(searchPattern);
} catch (ParseException pe) {
log.error("found a problem in search", pe);
return null;
}
// extract the products
List products =entityManager.createFullTextQuery(luceneQuery, Product.class).
setMaxResults(20).getResultList();
return products;
}
}
好了,现在我们可以创建search.xhtml文件,用来显示搜索结果了。下面是该文件的一个片段。
<rich:dataGrid value="#{SearchManager.results}" var="garage">
[ loop over the values ]
</rich:dataGrid>
然后,在pages.xml中添加一个到search.xhtml的入口。
<page view-id="/search.xhtml">
<param name="searchPattern" value="#{SearchManager.searchPattern}"/>
</page>
还剩下一个步骤。我们需要添加一个搜索框到菜单里面。如果你使用了Seam的默认布局,你就会看见/view/layout文件夹下面有一个menu.xhtml文件。如果没有找到,只需要将下面这一段添加到你需要的地方:
<h:form id="search_form">
<h:inputText id="searchPattern" required="true" value="#{SearchManager.searchPattern}" />
<h:commandButton action="/search.xhtml" value="search"></h:commandButton>
</h:form>
现在,你可以开始搜索你想要的对象了。是不是非常简单?但是,如果对象的一个属性是用来表示这个对象是否能显示在页面上,那应该怎么办?好的,在这种情况下,你需要添加一个过滤器。过滤器都继承自org.apache.lucene.search.Filter类。
想象一下,我们在产品类上有一个属性是用来标明产品的状态的,这个属性有三种值"L"表示有效的产品,"D"表示被删除的产品,"P"表示待发布的产品。显然,在用户搜索的时候,我们只想显示出可见的产品。所以我们需要添加一个过滤器:
1. public class LiveProductFilter extends Filter {
2. private static final long serialVersionUID = 1l;
3. public BitSet bits(IndexReader reader) throws IOException {
4. BitSet bitSet = new BitSet( reader.maxDoc() );
5. TermDocs termDocs = reader.termDocs( new Term("status", "L") );
6. while ( termDocs.next() ) {
7. bitSet.set( termDocs.doc() );
8. }
9. return bitSet;
10. }
11. }
为了让Hibernate Search能够找到我们在产品类上添加的过滤器,我们需要添加下面这个注解:
@FullTextFilterDefs ( { @FullTextFilterDef(name="liveProduct", impl = LiveProductFilter.class, cache=false) })好了,就写到这里。
参考文件:http://huxiuliang.iteye.com/blog/583136
注:数据库全文搜索暂时没有配置成功,加载监听器的时候报找不到类文件