Lucene API使用方法与和性能优化(2)

6 如何维护索引
 索引的维护操作都是由IndexReader类提供。
 
6.1 如何删除索引
 lucene提供了两种从索引中删除document的方法,一种是
 
void deleteDocument(int docNum)
 
这种方法是根据document在索引中的编号来删除,每个document加进索引后都会有个唯一编号,所以根据编号删除是一种精确删除,但是这个编号是索引的内部结构,一般我们不会知道某个文件的编号到底是几,所以用处不大。另一种是
 
void deleteDocuments(Term term)
 
这种方法实际上是首先根据参数term执行一个搜索操作,然后把搜索到的结果批量删除了。我们可以通过这个方法提供一个严格的查询条件,达到删除指定document的目的。
 下面给出一个例子:
 
Directory dir = FSDirectory.getDirectory(PATH, false);
 IndexReader reader = IndexReader.open(dir);
 Term term = new Term(field, key);
 reader.deleteDocuments(term);
 reader.close();
 
6.2 如何更新索引
 lucene并没有提供专门的索引更新方法,我们需要先将相应的document删除,然后再将新的document加入索引。例如:
 
Directory dir = FSDirectory.getDirectory(PATH, false);
 IndexReader reader = IndexReader.open(dir);
 Term term = new Term(“title”, “lucene introduction”);
 reader.deleteDocuments(term);
 reader.close();
 
IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true);
 Document doc = new Document();
 doc.add(new Field(“title”, ”lucene introduction”, Field.Store.YES, Field.Index.TOKENIZED));
 doc.add(new Field(“content”, ”lucene is funny”, Field.Store.YES, Field.Index.TOKENIZED));
 writer.addDocument(doc);
 writer.optimize();
 writer.close();
 7 如何搜索
 lucene的搜索相当强大,它提供了很多辅助查询类,每个类都继承自Query类,各自完成一种特殊的查询,你可以像搭积木一样将它们任意组合使用,完成一些复杂操作;另外lucene还提供了Sort类对结果进行排序,提供了Filter类对查询条件进行限制。你或许会不自觉地拿它跟SQL语句进行比较:“lucene能执行and、or、order by、where、like ‘%xx%’操作吗?”回答是:“当然没问题!”
 
7.1 各种各样的Query
 下面我们看看lucene到底允许我们进行哪些查询操作:
 
7.1.1 TermQuery
 首先介绍最基本的查询,如果你想执行一个这样的查询:“在content域中包含‘lucene’的document”,那么你可以用TermQuery:
 
Term t = new Term(“content”, ” lucene”;
 Query query = new TermQuery(t);
 
7.1.2 BooleanQuery
 如果你想这么查询:“在content域中包含java或perl的document”,那么你可以建立两个TermQuery并把它们用BooleanQuery连接起来:
 
TermQuery termQuery1 = new TermQuery(new Term(“content”, ”java”);
 TermQuery termQuery 2 = new TermQuery(new Term(“content”, ”perl”);
 BooleanQuery booleanQuery = new BooleanQuery();
 booleanQuery.add(termQuery 1, BooleanClause.Occur.SHOULD);
 booleanQuery.add(termQuery 2, BooleanClause.Occur.SHOULD);
 
7.1.3 WildcardQuery
 如果你想对某单词进行通配符查询,你可以用WildcardQuery,通配符包括’?’匹配一个任意字符和’*’匹配零个或多个任意字符,例如你搜索’use*’,你可能找到’useful’或者’useless’:
 
Query query = new WildcardQuery(new Term(“content”, ”use*”);
 
7.1.4 PhraseQuery
 你可能对中日关系比较感兴趣,想查找‘中’和‘日’挨得比较近(5个字的距离内)的文章,超过这个距离的不予考虑,你可以:
 
PhraseQuery query = new PhraseQuery();
 query.setSlop(5);
 query.add(new Term(“content ”, “中”));
 query.add(new Term(“content”, “日”));
 
那么它可能搜到“中日合作……”、“中方和日方……”,但是搜不到“中国某高层领导说日本欠扁”。
 
7.1.5 PrefixQuery
 如果你想搜以‘中’开头的词语,你可以用PrefixQuery:
 
PrefixQuery query = new PrefixQuery(new Term(“content ”, ”中”);
 
7.1.6 FuzzyQuery
 FuzzyQuery用来搜索相似的term,使用Levenshtein算法。假设你想搜索跟‘wuzza’相似的词语,你可以:
 
Query query = new FuzzyQuery(new Term(“content”, ”wuzza”);
 
你可能得到‘fuzzy’和‘wuzzy’。
 
7.1.7 RangeQuery
 另一个常用的Query是RangeQuery,你也许想搜索时间域从20060101到20060130之间的document,你可以用RangeQuery:
 
RangeQuery query = new RangeQuery(new Term(“time”, “20060101”), new Term(“time”, “20060130”), true);
 
最后的true表示用闭合区间。
 
7.2 QueryParser
 看了这么多Query,你可能会问:“不会让我自己组合各种Query吧,太麻烦了!”当然不会,lucene提供了一种类似于SQL语句的查询语句,我们姑且叫它lucene语句,通过它,你可以把各种查询一句话搞定,lucene会自动把它们查分成小块交给相应Query执行。下面我们对应每种Query演示一下:
 TermQuery可以用“field:key”方式,例如“content:lucene”。
 BooleanQuery中‘与’用‘+’,‘或’用‘ ’,例如“content:java contenterl”。
 WildcardQuery仍然用‘?’和‘*’,例如“content:use*”。
 PhraseQuery用‘~’,例如“content:”中日”~5”。
 PrefixQuery用‘*’,例如“中*”。
 FuzzyQuery用‘~’,例如“content: wuzza ~”。
 RangeQuery用‘[]’或‘{}’,前者表示闭区间,后者表示开区间,例如“time:[20060101 TO 20060130]”,注意TO区分大小写。
 你可以任意组合query string,完成复杂操作,例如“标题或正文包括lucene,并且时间在20060101到20060130之间的文章”可以表示为:“+ (title:lucene content:lucene) +time:[20060101 TO 20060130]”。代码如下:
 
Directory dir = FSDirectory.getDirectory(PATH, false);
 IndexSearcher is = new IndexSearcher(dir);
 QueryParser parser = new QueryParser(“content”, new StandardAnalyzer());
 Query query = parser.parse(“+(title:lucene content:lucene) +time:[20060101 TO 20060130]“;
 Hits hits = is.search(query);
 for (int i = 0; i < hits.length(); i++)
 {
 Document doc = hits.doc(i);
 System.out.println(doc.get(“title”);
 }
 is.close();
 
首先我们创建一个在指定文件目录上的IndexSearcher。
 然后创建一个使用StandardAnalyzer作为分析器的QueryParser,它默认搜索的域是content。
 接着我们用QueryParser来parse查询字串,生成一个Query。
 然后利用这个Query去查找结果,结果以Hits的形式返回。
 这个Hits对象包含一个列表,我们挨个把它的内容显示出来。
 
7.3 Filter
 filter的作用就是限制只查询索引的某个子集,它的作用有点像SQL语句里的where,但又有区别,它不是正规查询的一部分,只是对数据源进行预处理,然后交给查询语句。注意它执行的是预处理,而不是对查询结果进行过滤,所以使用filter的代价是很大的,它可能会使一次查询耗时提高一百倍。
 最常用的filter是RangeFilter和QueryFilter。RangeFilter是设定只搜索指定范围内的索引;QueryFilter是在上次查询的结果中搜索。
 Filter的使用非常简单,你只需创建一个filter实例,然后把它传给searcher。继续上面的例子,查询“时间在20060101到20060130之间的文章”除了将限制写在query string中,你还可以写在RangeFilter中:
 
Directory dir = FSDirectory.getDirectory(PATH, false);
 IndexSearcher is = new IndexSearcher(dir);
 QueryParser parser = new QueryParser(“content”, new StandardAnalyzer());
 Query query = parser.parse(“title:lucene content:lucene”;
 RangeFilter filter = new RangeFilter(“time”, ”20060101″, ”20060230″, true, true);
 Hits hits = is.search(query, filter);
 for (int i i < hits.length(); i++)
 {
 Document doc = hits.doc(i);
 System.out.println(doc.get(“title”);
 }
 is.close();
 
7.4 Sort
 有时你想要一个排好序的结果集,就像SQL语句的“order by”,lucene能做到:通过Sort。
 Sort sort Sort(“time”); //相当于SQL的“order by time”
 Sort sort = new Sort(“time”, true); // 相当于SQL的“order by time desc”
 下面是一个完整的例子:
 
Directory dir = FSDirectory.getDirectory(PATH, false);
 IndexSearcher is = new IndexSearcher(dir);
 QueryParser parser = new QueryParser(“content”, new StandardAnalyzer());
 Query query = parser.parse(“title:lucene content:lucene”;
 RangeFilter filter = new RangeFilter(“time”, ”20060101″, ”20060230″, true, true);
 Sort sort = new Sort(“time”);
 Hits hits = is.search(query, filter, sort);
 for (int i = 0; i < hits.length(); i++)
 {
 Document doc = hits.doc(i);
 System.out.println(doc.get(“title”);
 }
 is.close();

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/2f35712610129c816ee0802e7f550cc4.html