1. 索引
注意:在建立索引以后,内容的所有大写字母全部被转化为小写;索引更新实际是先删除原来的后创建新的
1) 存储域选项:
Field.Store.YES 或者 NO
设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原
设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,内容无法还原
2) 索引域选项:
Field.Index
Index.ANALYZED : 进行分词和索引,适用于标题、内容等
Index.NOT_ANALYZED : 进行索引,但是不进行分词,如果身份证,姓名,ID等,适用于精确搜索
Index.ANALYZED_NOT_NORMS : 进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和权值等信息
Index.NOT_ANALYZED_NOT_NORMS : 即不进行分词也不存储norms信息
Index.NO : 不进行索引
3) 给数字和日期索引
给数字添加索引:NumericField -> new NumericField("attachs", Field.Store.YES, true).setIntValue(attachs[i])
给日期添加索引:NumericField -> new NumericField("date", Field.Store.YES, true).setLongValue(dates[i].getTime())
4) IndexReader设计
IndexReader:打开和关闭都很耗时,通常设置为单例,在类初始化时候创建,使用之后都不要关闭
IndexSearcher:通过IndexReader创建,每次使用完成后需要关闭
注意:
IndexReader是单例的,当IndexWriter修改了索引之后,已经打开的IndexReader是不会更新修改的索引,只有重新打开IndexReader才会更新
解决办法:创建一个获取reader的方法,方法中代码如下
IndexReader newReader = IndexReader.openIfChanged(oldReader);//若发生了改变,那么就返回一个新的reader,否则返回null
if(newReader != null) { reader.close();oldReader = newReader;}//关闭原来的reader,重新赋值
IndexReader也可以执行删除操作,好处是删除了索引后,reader能够马上就知道,避免使用openIfChanged方法
5) IndexWriter设计
同IndexReader一样设置为单例,每次使用完成后不关闭,调用writer.commit()提交本次操作的改变
6) Directory
FSDirectory:索引存储到硬盘中
RAMDirectory:索引存储到内存中,可以根据硬盘中的索引创建
FSDirectory.open会根据当前的运行环境打开一个最合理的基于File的Directory
RAMDirectory会从内存中打开directory,好处是速度快,缺点是无法持久化
2. 搜索
1) 精确匹配查询 (TermQuery)
IndexSearcher searcher = getSearcher();
Query query = new TermQuery(new Term(field, value));
TopDocs topDocs = searcher.search(query, num);
System.out.println("totalHits:" + topDocs.totalHits);//显示共有多少匹配项,这个和 查询num 没有关系
2) 范围查询(TermRangeQuery)
field:域
lowerTerm:开始范围
upperTerm:结束范围
includeLower:包含开区间
includeUpper:包含闭区间
new TermRangeQuery(String field, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper)
注意:这种查询方式无法查询数字类型的域
3) 数字范围查询(NumericRangeQuery)
field:域
min:开始范围
max:结束范围
minInclusive:包含开区间
maxInclusive:包含闭区间
NumericRangeQuery.newIntRange(String field, Integer min, Integer max, boolean minInclusive, boolean maxInclusive)
4) 前缀查询(PrefixQuery)
new PrefixQuery(new Term(field, value))
5) 通配符查询(WildcardQuery)
- : 任意多个字符
? : 任意一个字符
new WildcardQuery(new Term(field, value))
5) 组合查询(BooleanQuery)
BooleanQuery query = new BooleanQuery(); //可以连接多个子查询
query.add(new WildcardQuery(new Term("name", "j*")), BooleanClause.Occur.MUST);
query.add(new TermQuery(new Term("content", "like")), BooleanClause.Occur.MUST);
Occur.MUST 必须出现
Occur.SHOULD 可以出现
Occur.MUST_NOT 不能出现
6) 短语查询(PhraseQuery)
PhraseQuery query = new PhraseQuery();
query.setSlop(1); //设置中间跳过的短语数
query.add(new Term("content", "i")); //设置前一个短语
query.add(new Term("content", "football")); //设置后一个短语
7) 模糊查询(FuzzyQuery)
查询相似度最接近的
8) QueryParser:使用较多
a. 创建时设置版本,默认查询的域,分词器
QueryParser queryParser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
b. 设置要查询的字符串,默认字符串被分词,每个词查询的结果被组合到了一起,可以通过一下方式设置查询结果的组合方式
queryParser.setDefaultOperator(QueryParser.Operator.AND);
Query query = queryParser.parse("like basketball");
c. 查询的时候指定查询域:
Query query = queryParser.parse("name:mike");
d. 使用通配符:
Query query = queryParser.parse("name:j*");
开启查询字符串首字母通配符匹配,默认是关闭的 效率低
queryParser.setAllowLeadingWildcard(true);
e. queryParser.parse("- name:mike + football"); //注意空格
- : 不包含
+ : 包含
f. query =queryParser.parse("id:[1 TO 3]"); 匹配一个区域,TO必须大写,开区间匹配 1,2,3
query = queryParser.parse("id:{1 TO 3}");
匹配一个区域,TO必须大写,闭区间匹配 2
注意:TO这种方式不能匹配数字,只适用于字符串
g. AND,OR使用
query = queryParser.parse("(mike OR john) AND address:xinde"); 名字有mike或者john,地址是xinde
h. 多个词整体匹配
query = queryParser.parse("\"i like football\"");
等同于
query = queryParser.parse("i AND like AND football");
i. query = queryParser.parse(""i football"~1"); 匹配 i 和 football 之间有一个单词
j. query = queryParser.parse("name:make~"); 模糊查询
9) Lucene分页查询
不支持类似数据库的分页方式;(再查询)
- 每次都会查询出所有的数据,然后在取出每一页的数据,查询的速度已经很快,不用担心性能
- 使用searchAfter方法(推荐)
private ScoreDoc getLastScoreDoc(int page, int size, Query query, IndexSearcher searcher) throws IOException { //获取出上一页的最后一个元素ScoreDoc
if (page == 1) return null;
int num = (page - 1) * size; //从0开始 每次只取需要的数据
TopDocs topDocs = searcher.search(query, num);
return topDocs.scoreDocs[num - 1];
}
public void searchPageByAfter(String query, int page, int size) throws ParseException, IOException {
IndexSearcher searcher = this.getSearcher();
QueryParser parser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
Query q = parser.parse(query);
ScoreDoc lastScoreDoc = getLastScoreDoc(page, size, q, searcher);//获取上一页的最后一个元素
TopDocs topDocs = searcher.searchAfter(lastScoreDoc, q, size); //通过上一页的最后一个元素搜索下一页
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document doc = searcher.doc(scoreDoc.doc);
System.out.println(scoreDoc.doc + "->" + doc.get("path"));
}
}
3. 分词
1) 3.5版本推荐的分词器:SimpleAnalyzer,StandardAnalyzer,StopAnalyzer,WhitespaceAnalyzer
2) 分词调用顺序:采用的是责任链模式,首先调用一系列的TokenFilter -> Tokenizer把输入的内容分词一个词组 ->回到TokenFilter,处理解析出来的词组
4. 排序
1) Sort
Sort.INDEXORDER :以文档编号排序 doc的id eg: searchUtils.search("java", Sort.INDEXORDER);
Sort.RELEVANCE :使用默认的评分排序 eg: searchUtils.search("java", Sort.RELEVANCE);
2) SortField 指定域排序
searchUtils.search("java", new Sort(new SortField("size",SortField.INT)); //文件大小排序
searchUtils.search("java", new Sort(new SortField("date",SortField.LONG)); //文件创建日期排序
searchUtils.search("java", new Sort(new SortField("filename",SortField.STRING)); //文件名排序
searchUtils.search("java", new Sort(new SortField("filename",SortField.STRING,true)); //文件名反转排序
searchUtils.search("java", new Sort(new SortField("size",SortField.INT),SortField.FIELD_SCORE); //先通过文件大小排序,后通过文件评分排序
5. 过滤Filter
1) TermRangeFilter 范围过滤
Filter filter = new TermRangeFilter("filename", "java.hhh", "java.kkk", true, true);
searchUtils.search("java", filter);
2) NumericRangeFilter 数字范围过滤
Filter filter =NumericRangeFilter.newIntRange("size", 500, 900, true, true);
searchUtils.search("java", filter);
3) QueryWrapperFilter 可以通过一个Query过滤
Filter filter =new QueryWrapperFilter(new WildcardQuery(new Term("filename","*.txt")));
searchUtils.search("java", filter);
6. 自定义评分
- 步骤:
1.创建一个类继承于CustomScoreQuery
2.覆盖getCustomScoreProvider
3.创建CustomScoreProvider类
4.覆盖customScore