本文共 9064 字,大约阅读时间需要 30 分钟。
package com.dongsheng.lucene.dslucene.lucene;import org.apache.lucene.analysis.Analyzer;import org.apache.lucene.analysis.core.SimpleAnalyzer;import org.apache.lucene.document.Document;import org.apache.lucene.index.DirectoryReader;import org.apache.lucene.index.IndexReader;import org.apache.lucene.queryparser.classic.ParseException;import org.apache.lucene.queryparser.classic.QueryParser;import org.apache.lucene.search.IndexSearcher;import org.apache.lucene.search.Query;import org.apache.lucene.search.ScoreDoc;import org.apache.lucene.search.TopDocs;import org.apache.lucene.store.Directory;import org.apache.lucene.store.FSDirectory;import java.io.IOException;import java.nio.file.Paths;/*** *@author dongsheng *@date 2020/3/19 16:59 *@version 1.0.0 *@Description 搜索代码示例 */public class SearchFlow { public static void main(String[] args) throws IOException, ParseException { // 使用的分词器 Analyzer analyzer = new SimpleAnalyzer(); // 索引存储目录 Directory directory = FSDirectory.open(Paths.get("e:/test/indextest")); // 索引读取器 IndexReader indexReader = DirectoryReader.open(directory); // 索引搜索器 IndexSearcher indexSearcher = new IndexSearcher(indexReader); // 要搜索的字段 String filedName = "name"; // 查询生成器(解析输入生成Query查询对象) QueryParser parser = new QueryParser(filedName, analyzer); // 通过parse解析输入(分词),生成query对象 Query query = null; try { query = parser.parse("Thinkpad"); } catch (ParseException e) { e.printStackTrace(); } // 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分))) TopDocs topDocs = indexSearcher.search(query, 10); //前10条 //获得总命中数 System.out.println(topDocs.totalHits); // 遍历topN结果的scoreDocs,取出文档id对应的文档信息 for (ScoreDoc sdoc : topDocs.scoreDocs) { // 根据文档id取存储的文档 Document hitDoc = indexSearcher.doc(sdoc.doc); // 取文档的字段 System.out.println(hitDoc.get(filedName)); } // 使用完毕,关闭、释放资源 indexReader.close(); directory.close(); }}
Open一个读取器,读取的是该时刻点的索引视图
分为两类
注: indexRead是线程安全的。
应用通过调用它的search(Query,int)重载方法在一个IndexReader上实现搜索。出于性能的考虑,请使用一个IndexSearcher实例,除非索引发生变化。如索引更新了则通过DirectoryReader.openIfChanged(DirectoryReader) 取得新的读取器,再创建新的搜索器。
注: IndexSearcher 是线程安全的。
/** The total number of hits for the query. */public long totalHits; 宗命中数/** The top hits for the query. */public ScoreDoc[] scoreDocs; Top-N结果集/** Stores the maximum score value encountered, needed for normalizing. */private float maxScore;取得最大分数
/** The score of this document for the query. */ public float score; 得分值 /** A hit document's number. * @see IndexSearcher#doc(int) */ public int doc; 文档id
//词项查询,最基本、最常用的查询。用来查询指定字段包含指定词项的文档TermQuery tq = new TermQuery(new Term("fieldName", "term"));
搜索的条件往往是多个的,如要查询名称包含“电脑” 或 “thinkpad”的商品,就需要两个词项查询做或合并。布尔查询就是用来组合多个子查询的。每个子查询称为布尔字句 BooleanClause,布尔字句自身也可以是组合的。
布尔查询默认的最大字句数为1024,在将通配符查询这样的查询rewriter为布尔查询时,往往会产生很多的字句,可能抛出TooManyClauses 异常。可通过BooleanQuery.setMaxClauseCount(int)设置最大字句数。
// 布尔查询Query query1 = new TermQuery(new Term(filedName, "thinkpad"));Query query2 = new TermQuery(new Term("simpleIntro", "英特尔"));BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();booleanQueryBuilder.add(query1, Occur.SHOULD);booleanQueryBuilder.add(query2, Occur.MUST);BooleanQuery booleanQuery = booleanQueryBuilder.build();// 可像下一行这样写// BooleanQuery booleanQuery = new BooleanQuery.Builder()// .add(query1, Occur.SHOULD).add(query2, Occur.MUST).build();
组合关系支持如下四种:
表示0个或多个字符,?表示1个字符,\是转义符。通配符查询可能会比较慢,不可以通配符开头。
// WildcardQuery 通配符查询WildcardQuery wildcardQuery = new WildcardQuery( new Term("name", "think*"));// WildcardQuery 通配符查询WildcardQuery wildcardQuery2 = new WildcardQuery( new Term("name", "厉害了???"));
最常用的查询,匹配特点序列的多个词项。PhraserQuery使用一个位置移动因子(slop)来决定任意两个词项的位置可最大移动多少个位置来进行匹配,默认为0。有两种方式来构建对象:
1.直接构造方法
2.用里面的Builder构建
PhraseQuery phraseQuery1 = new PhraseQuery("name", "thinkpad", "carbon");PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad", "carbon");PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");PhraseQuery phraseQuery4 = new PhraseQuery.Builder() .add(new Term("name", "笔记本电脑"), 4) .add(new Term("name", "联想"), 5).build();// 这两句等同PhraseQuery phraseQuery5 = new PhraseQuery.Builder() .add(new Term("name", "笔记本电脑"), 0) .add(new Term("name", "联想"), 1).build();
slop 移动因子说明
1、如果想用 “thinkpad carbon” 来匹配 name。因中间有 x1,则需要将thinkpad 向右移动1个位置。
2、如果想用 “carbon thinkpad” 来匹配 name。因中间有 x1,则需要将carbon 向右移动3个位置。
// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";// PhraseQuery 短语查询PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad","carbon");// slop示例PhraseQuery phraseQuery2Slop = new PhraseQuery(3, "name", "carbon", "thinkpad");PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");// slop示例PhraseQuery phraseQuery3Slop = new PhraseQuery(2, "name", "联想","笔记本电脑");
查询包含以xxx为前缀的词项的文档,是通配符查询,如 app,实际是 app*
// PrefixQuery 前缀查询PrefixQuery prefixQuery = new PrefixQuery(new Term("name", "think"));
短语查询的一种更通用的用法,支持同位置多个词的OR匹配。通过里面的Builder来构建MultiPhraseQuery
// 4 MultiPhraseQuery 多重短语查询Term[] terms = new Term[2];terms[0] = new Term("name", "笔记本");terms[1] = new Term("name", "笔记本电脑");Term t = new Term("name", "联想");MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery.Builder() .add(terms).add(t).build();// 对比 PhraseQuery在同位置加入多个词 ,同位置的多个词都需匹配,所以查不出。PhraseQuery pquery = new PhraseQuery.Builder().add(terms[0], 0) .add(terms[1], 0).add(t, 1).build();
简单地与索引词项进行相近匹配,允许最大2个不同字符。常用于拼写错误的容错:如把 “thinkpad” 拼成 “thinkppd”或 “thinkd”,使用FuzzyQuery 仍可搜索到正确的结果。
// FuzzyQuery 模糊查询FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("name", "thind"));FuzzyQuery fuzzyQuery2 = new FuzzyQuery(new Term("name", "thinkd"), 2);FuzzyQuery fuzzyQuery3 = new FuzzyQuery(new Term("name", "thinkpaddd"));FuzzyQuery fuzzyQuery4 = new FuzzyQuery(new Term("name", "thinkdaddd"));
词项符合某正则表达式
// RegexpQuery 正则表达式查询RegexpQuery regexpQuery = new RegexpQuery(new Term("name", "厉害.{4}"));
用于查询包含某个范围内的词项的文档,如以字母开头a到c的词项。词项在反向索引中是排序的,只需指定的开始词项、结束词项,就可以查询该范围的词项。
如果是做数值的范围查询则用 PointRangeQuery 。
// TermRangeQuery 词项范围查询TermRangeQuery termRangeQuery = TermRangeQuery.newStringRange("name", "carbon", "张三", false, true);
前提:查询的数值字段必须索引。通过 IntPoint, LongPoint, FloatPoint, or DoublePoint 中的方法构建对应的查询。
// 精确值查询Query exactQuery = IntPoint.newExactQuery("price", 1999900);// 数值范围查询Query pointRangeQuery = IntPoint.newRangeQuery("price", 499900,1000000);// 集合查询Query setQuery = IntPoint.newSetQuery("price", 1999900, 1000000,2000000);
用于更复杂的短语查询,可以指定词间位置的最大间隔跨度。通过组合一系列的SpanQuery 实例来进行查询,可以指定是否按顺序匹配、slop、gap。
// SpanNearQuery 临近查询SpanTermQuery tq1 = new SpanTermQuery(new Term("name", "thinkpad"));SpanTermQuery tq2 = new SpanTermQuery(new Term("name", "carbon"));SpanNearQuery spanNearQuery = new SpanNearQuery( new SpanQuery[] { tq1, tq2 }, 1, true);// SpanNearQuery 临近查询 gap slop 使用SpanNearQuery.Builder spanNearQueryBuilder = SpanNearQuery .newOrderedNearQuery("name");spanNearQueryBuilder.addClause(tq1).addGap(0).setSlop(1) .addClause(tq2);SpanNearQuery spanNearQuery5 = spanNearQueryBuilder.build();
两类查询解析器
QueryParser
MultiFieldQueryParser
//传统解析器-单默认字段 QueryParserQueryParser parser = new QueryParser("defaultFiled", analyzer);//parser.setPhraseSlop(2);Query query = parser.parse("query String");// 传统查询解析器-多默认字段String[] multiDefaultFields = { "name", "type", "simpleIntro" };MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser( multiDefaultFields, analyzer);// 设置默认的组合操作,默认是 ORmultiFieldQueryParser.setDefaultOperator(Operator.OR);Query query4 = multiFieldQueryParser.parse("笔记本电脑 AND price:1999900");
StandardQueryParser
StandardQueryParser queryParserHelper = new StandardQueryParser(analyzer);// 设置默认字段// queryParserHelper.setMultiFields(CharSequence[] fields);// queryParserHelper.setPhraseSlop(8);// Query query = queryParserHelper.parse("a AND b", "defaultField");Query query5 = queryParserHelper.parse( "(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:1999900","name");
使用查询解析器需要考虑的三点:
转载地址:http://oujdi.baihongyu.com/