北京好奇怪,昼夜温差很大,有点像月球。早起时窗上的冰霜很厚实,但到了白天阳光很足,风力很小,气温很好。大连现在确在降温,零下十几度,比较罕见。可能是我离开她一会,她却变冷了。呵呵~~
今日上午把lucene的高级部分给结束了。下午搞了一下Compass,Compass是对lucene的封闭。因为lucene使用起来有些麻烦。
一、lucene高级
1.分词器
一般的分词器流程:
1) 输入文本:输入查询的内容。
2) 关键词切分:使用分词器取出关键词。
3) 去除停用词:去除没有多大意义的词,比如a,an,the,的,呢,吧。
4) 形态还原:将词的某种形态还原为原形,比如英文复数形式恢复到单数形式。
5) 转为小写:将英文转为小写,不区分大小写。
Lucene中的分词器
1) 单字分词:对应的类ChineseAnalyzer,将输入的字符串中的每个字做为关键字。比如“中国人加油!”,会被切分为“中”、“国”、“人”、“加”、“油”。(标点符号属于停用词)
2) 二分法分词:对应的类CJKAnalyzer,将输入的字符串以二分法分词。比如“中国人加油!”,会被切分为“中国”、“国人”、“人加”、“加油”这四个关键字。(标点符号属于停用词)
3) 词库分词:对应的类MMAnalyzer,最为常用的分词器。比如“中国人加油!”,会被切分为“中国人”、“加油”。(标点符号属于停用词)
示例:
package cn.itcast.cc.anayzler;
import java.io.IOException; import java.io.StringReader; import jeasy.analysis.MMAnalyzer; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Token; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.cjk.CJKAnalyzer; import org.apache.lucene.analysis.cn.ChineseAnalyzer; import org.junit.Test;
public class AnalyzerTest {
Analyzer anaylzer1 = new ChineseAnalyzer();// 单字分词 Analyzer anaylzer2 = new CJKAnalyzer();// 二分分词法 Analyzer anaylzer3 = new MMAnalyzer();// 词库分词
@Test public void test() throws IOException { String str = "中国人加油!"; printAnalyzer(anaylzer1, str); printAnalyzer(anaylzer2, str); printAnalyzer(anaylzer3, str); }
private void printAnalyzer(Analyzer analyzer, String str) throws IOException { TokenStream tokenStream = analyzer.tokenStream("text", new StringReader( str)); System.out.println("*****分词器->" + analyzer.getClass().getName() + ",将'" + str + "'切分为:"); Token token = new Token(); while ((token = tokenStream.next(token)) != null) { System.out.println(token); } } } |
2.高亮
Lucene的高亮器(Highlighter类)可以实现lucene的关键字高亮:
package cn.itcast.cc.highlighter;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.*; import org.apache.lucene.document.Field.*; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter.MaxFieldLength; import org.apache.lucene.queryParser.*; import org.apache.lucene.search.*; import org.apache.lucene.search.highlight.*; import org.apache.lucene.search.highlight.Scorer; import org.junit.Test; import jeasy.analysis.MMAnalyzer;
public class HighLighterTesst { private String path = "./index/";// lucene的索引数据被保存在这个目录中 private Analyzer analyzer = new MMAnalyzer();// 词库分词器
@Test public void test() { try { // 创建索引数据 IndexWriter indexWriter = new IndexWriter(this.path, this.analyzer, MaxFieldLength.LIMITED); Document doc = new Document(); doc.add(new Field("content", "中国人民加油!", Store.YES, Index.ANALYZED)); indexWriter.addDocument(doc); indexWriter.close(); // 查询数据 IndexSearcher indexSearcher = new IndexSearcher(this.path); QueryParser queryParser = new MultiFieldQueryParser( new String[] { "content" }, this.analyzer); Query query = queryParser.parse("加油"); TopDocs topDocs = indexSearcher.search(query, null, 10); // 创建高亮 // SimpleHTMLFormatter的默认值为"<a></a>" Formatter formatter = new SimpleHTMLFormatter("<span>", "</span>"); Scorer scorer = new QueryScorer(query); Highlighter highlighter = new Highlighter(formatter, scorer); // 打印结果 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { // 取出查询结果 Document searchdoc = indexSearcher.doc(scoreDoc.doc); String searchstr = searchdoc.getField("content").stringValue(); // 添加高亮 String histr = highlighter.getBestFragment(this.analyzer, "content", searchstr); System.out.println(histr); } indexSearcher.close(); } catch (Exception e) { e.printStackTrace(); } } } |
3.排序(默认相关度排序)
a) 实验相关度排序,得分是在搜索时算出来的。
b) 影响区域
i. 整个文档
ii. 某个字段
1. 建立索引时,影响它的排名,创建QueryParser时指定“MultiFieldQueryParser(String[] fields, Analyzer analyzer, Map boosts)”中第三个参数为“new HashMap<String, Float>().put(String fieldname, float fraction)”提升对应字段内容的排名。一般网站内部搜索会将标题部分的内容提升,以至于标题内容的排名大于帖子内容或回复的排名。
2. 搜索时,标题与内容的含量排名,TopDoc有一个score属性,它是根据文档中关键字的数量,记录文档的得分。
c) 按指定的字段进行排序,就是在搜索时,指定的字段。
d) Sort排序器,由类Sort实现。
Sort sort = new Sort(); sort.setSort(SortField field); indexSearcher.search(query, null, 100, sort); |
4.过滤器
在查询时添加一个过滤器,过滤掉查询后不想要的内容,由Filter接口实现。
String lower = NumberTools.longToString(3); String upper = NumberTools.longToString(6); Filter filter = new RangeFilter("id", lower, upper, true, false); indexSearcher.search(query, filter, 100); |
在lucene中使用数值、日期数据时,必须使用NumberTools和DateTools。前后必须统一,比如使用longToString将Long转换为String,那么也必须使用stringToLong将String转换为Long。
5.搜索,Query的子类对应各种查询:
1) TermQuery,关键词查询:
Term term = new Term("context", "加油"); Query query = new TermQuery(term); indexSearcher.search(query, null, 100); |
2) RangeQuery,范围查询:
Term lower = new Term("content", NumberTools.longToString(3)); Term upper = new Term("content", NumberTools.longToString(10)); Query query = new RangeQuery(lower, upper, false/*包含边界*/); indexSearcher.search(query, null, 100); |
3) WildcardQuery,通配符查询:
Term term = new Term("context", "加?");//?匹配任意一个字符,*代表任意多个字符。 Query query = new TermQuery(term); indexSearcher.search(query, null, 100); indexSearcher.search(query, null, 100); |
4) PhraseQuery,短语查询:
PhraseQuery phraseQuery = new PhraseQuery(); phraseQuery.add(new Term("context", "中国人民")); phraseQuery.add(new Term("context", "加油")); phraseQuery.setSlop(5); // 指定关键词之间间隔的字符的最大数量 indexSearcher.search(query, null, 100); |
5) BooleanQuery,布尔查询:
Query query1 = new TermQuery(new Term("context", "中国人民")); Query query2 = new TermQuery(new Term("context", "加油")); BooleanQuery boolQuery = new BooleanQuery(); boolQuery.add(query1, Occur.MUST); boolQuery.add(query2, Occur.SHOULD);//Occur的常量值:MUST、SHOULD、MUST_NOT。 indexSearcher.search(query, null, 100); |
注意:经过分词器的所有字母,无论是添加索引还是查询,都会被转换为小写的。但上边这些查询类没有经过分词器,如果查询关键里有大写字母,将查询不到匹配的结果。所以在使用上边这些查询类时,查询文本中的字母全部为小写。我们之前使用的查询是通过QueryParser切分出查询关键字,所以使用QueryParser可以不注意大小写问题。
二、Compass
Compass是lucene的框架,它与hibernate极其相似(但不具有hibernate那些高级功能,比如缓存技术)。汤兄弟做的好,因为学习lucene之前的课程正是hibernate,他将hibernate与compass对比了一下,使我们学习compass更加容易了。OK,我总结一下。
1.hibernate与compass
|
hibernate |
compass |
配置方式 |
1.编程方式: Configuation cfg = new Configu...(); cfg.set...(); ... 2.配置文件: //默认配置,文件名为“hibernate.cfg.xml” Configuation cfg= new Configu...(); //指定文件名(文件名可变更) //cfg.configure("hibernate.cfg.xml"); |
1.编程方式: CompassConfiguation cfg = new CompassConf...(); cfg.set("key","value"); ... 2.配置文件 //默认配置,文件名为“compass.cfg.xml” cfg.configure(); //指定文件名(文件名可变更) cfg.configure("hibernate.cfg.xml"); |
配置文件 |
XML文件 注解 |
XML文件 注解 |
2.compass中的注解配置
package cn.itcast.cc.compass;
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;
@Searchable public class Article { private Long id; private String title; private String content; @SearchableId public Long getId() { return id;} @SearchableProperty(store = Store.YES, index = Index.ANALYZED, boost = 2.0F , excludeFromAll = ExcludeFromAll.YES/*这个属性的内容被查询忽略*/) public String getTitle() {return title;} @SearchableProperty(store = Store.YES, index = Index.ANALYZED) public String getContent() {return content;} public void setId(Long id) {this.id = id;} public void setTitle(String title) {this.title = title;} public void setContent(String content) {this.content = content;} } |
XML配置文件,可以查看“compass- 2.2.0 -M2/docs/reference/html_single/index.html”手册,内容较多。
3.compassAPI
CompassConfiguration cfg = new CompassConfiguration(); cfg.setConnection("./indexPath/");// 连接信息 cfg.addClass(Article.class);// 声明映射信息 Compass compassSessionFactory = cfg.buildCompass(); CompassTransaction tx = session.beginTransaction(); session.create(article); tx.commit(); session.close(); |
其他API:
session.find(String arg0); session.save(Object arg0); session.delete(Class arg0, Object arg1); |
这两天的课程相对比较容易些,虽然容易但仍然需要一定的练习。后天就要学习jbpm与OA项目了,终于开始第一个像样的项目了,呵呵!不过感觉并没想像中的那么复杂,因为有了jbpm。