lucene 搜索详解(4)

搜索流程

 

搜索代码示例

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();
    }
}

核心API图

核心API详解

1.IndexReader  索引读取器

       Open一个读取器,读取的是该时刻点的索引视图

  • DirectoryReader.open(IndexWriter indexWriter)     
  • DirectoryReader.open(Directory)
  • DirectoryReader.openIfChanged(DirectoryReader  

  分为两类

    

  • 叶子读取器:支持获取stored fields, doc values, terms(词项), and postings (词项对应的文档)
  • 复合读取器,多个读取器的复合。只可直接用它获取stored fields 。在内部通CompositeReader.getSequentialSubReaders 得到里面的叶子读取器来获取其他数据。
  • DirectoryReader 是 复合读取器

      注:  indexRead是线程安全的。

2.IndexSearcher 索引搜索器

应用通过调用它的search(Query,int)重载方法在一个IndexReader上实现搜索。出于性能的考虑,请使用一个IndexSearcher实例,除非索引发生变化。如索引更新了则通过DirectoryReader.openIfChanged(DirectoryReader)  取得新的读取器,再创建新的搜索器。

  注:  IndexSearcher 是线程安全的。

TopDocs

/** 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;取得最大分数

ScoreDoc

  /** The score of this document for the query. */
  public float score; 得分值

  /** A hit document's number.
   * @see IndexSearcher#doc(int) */
  public int doc; 文档id

3.Query查询的实例类

  • TermQuery   词项查询

     

//词项查询,最基本、最常用的查询。用来查询指定字段包含指定词项的文档
TermQuery tq = new TermQuery(new Term("fieldName", "term"));

  • BooleanQuery  布尔查询

搜索的条件往往是多个的,如要查询名称包含“电脑” 或 “thinkpad”的商品,就需要两个词项查询做或合并。布尔查询就是用来组合多个子查询的。每个子查询称为布尔字句 BooleanClause,布尔字句自身也可以是组合的。

  • Occur.SHOULD       
  • Occur.MUST          
  • Occur.MUST_NOT        且非
  • Occur.FILTER    MUST,但该字句不参与评分

布尔查询默认的最大字句数为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();

组合关系支持如下四种:

  • WildcardQuery   通配符查询

表示0个或多个字符,?表示1个字符,\是转义符。通配符查询可能会比较慢,不可以通配符开头。

// WildcardQuery 通配符查询
WildcardQuery wildcardQuery = new WildcardQuery(
	new Term("name", "think*"));

// WildcardQuery 通配符查询
WildcardQuery wildcardQuery2 = new WildcardQuery(
	new Term("name", "厉害了???"));
  • PhraseQuery  短语查询

最常用的查询,匹配特点序列的多个词项。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", "联想","笔记本电脑");
  • PrefixQuery   前缀查询

查询包含以xxx为前缀的词项的文档,是通配符查询,如 app,实际是 app*

// PrefixQuery 前缀查询
PrefixQuery prefixQuery = new PrefixQuery(new Term("name", "think"));
  • MultiPhraseQuery  多重短语查询

短语查询的一种更通用的用法,支持同位置多个词的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();
  • FuzzyQuery  模糊查询

   简单地与索引词项进行相近匹配,允许最大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  正则表示查询

词项符合某正则表达式

  • TermRangeQuery  词项范围查询
// RegexpQuery 正则表达式查询
RegexpQuery regexpQuery = new RegexpQuery(new Term("name", "厉害.{4}"));

用于查询包含某个范围内的词项的文档,如以字母开头ac的词项。词项在反向索引中是排序的,只需指定的开始词项、结束词项,就可以查询该范围的词项。

如果是做数值的范围查询则用 PointRangeQuery

// TermRangeQuery 词项范围查询
TermRangeQuery termRangeQuery = TermRangeQuery.newStringRange("name",
	"carbon", "张三", false, true);
  • PointRangeQuery  单个或多维点查询
  • ConstantScoreQuery 
  • DisjunctionMaxQuery 
  • MatchAllDocsQuery  匹配所有
  • 数值查询

前提:查询的数值字段必须索引。通过 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);
  • SpanNearQuery 临近查询(跨度查询)

用于更复杂的短语查询,可以指定词间位置的最大间隔跨度。通过组合一系列的SpanQuery 实例来进行查询,可以指定是否按顺序匹配、slopgap

// 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详解

 

两类查询解析器

1. 传统的解析器

  QueryParser

  MultiFieldQueryParser

//传统解析器-单默认字段   QueryParser
QueryParser 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);
// 设置默认的组合操作,默认是 OR
multiFieldQueryParser.setDefaultOperator(Operator.OR);
Query query4 = multiFieldQueryParser.parse("笔记本电脑 AND price:1999900");
2. 基于新的 flexible 框架的解析器

  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");

使用查询解析器需要考虑的三点:

  1. 查询字符串应是由人输入的,而不应是你编程产生。如果你为了用查询解析器,而在你的应用中编程产生查询字符串,不  可取,更应该直接使用基本查询API;
  2. 未分词的字段,应直接使用基本查询API加入到查询中,而不应使用查询解析器;
  3. 对于普通文本字段,使用查询解析器,而其他值字段:如 时间、数值,则应使用基本查询API;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值