Lucene 6.x 高级查询

编程教程 > Java (59) 2025-04-30 14:52:13

需求

用户的需求是多种多样的。比如:

  • 用户可能对中日关系比较感兴趣,想查找‘中’和‘日’挨得比较近(5个字的距离内)的文章,超过这个距离的不予考虑。比如:“中日双方能否和解?”“中美日三方”等是你想要的结果,而“中国某高层领导说日本欠扁”不是你想要的结果。这时该如何设计检索方式?
  • 用户可能是一个警察,需要调查以前的一个旧案,想搜索1999年1月1日到1999年7月7日的新闻,别的时间段的新闻都不是你想要的。这时该如何设计检索方式?
  • 用户可能是一个小朋友,上课学到了一个成语“亡羊补牢”,回来后写作业不记得到底是亡什么补牢,为了帮小朋友写作业,我们该如何设计检索?
  • 小明是一个调皮的小男孩,趁妈妈不在想偷偷玩游戏,这是该如何快速帮他找到分类“游戏”,过滤掉他不想看的“电子课本”?

现在,来介绍几种lucene的高级检索方式,来帮助我们满足各种各样的用户需求~~

一、PhraseQuery

用户在搜索引擎中进行搜索时,常常查找的并非是一个简单的单词,很有可能是几个不同的关键字。这些关键字之间要么是紧密相联,成为一个精确的短语,要么是可能在这几个关键字之间还插有其他无关的关键字。此时,用户希望将它们找出来。不过很显然,从评分的角度看,这些关键字之间拥有与查找内容无关短语所在的文档的分值一般会较低一些。
PhraseQuery正是Lucene所提供的满足上述需求的一种Query对象。它可以让用户往其内部添加关键字,在添加完毕后,用户还可以通过设置slop参数来设定一个称之为“坡度”的变量来确定关键字之间是否允许、允许多少个无关词汇的存在。

import java.io.IOException;
import java.nio.file.Paths;
  
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
 
 public class testPhraseQuery {
     public static Version luceneVersion = Version.LATEST;
     public static void indexSearch(){
         DirectoryReader reader = null;
         try{        
              Directory directory = FSDirectory.open(Paths.get("index3"));
              reader= DirectoryReader.open(directory);
              IndexSearcher searcher =  new IndexSearcher(reader);
              Term t1=new Term("key2","孙悟空");
              Term t2=new Term("key2","猪八戒");
              //slop,term...;slop represents the maximum distance between the given terms.reference:
              //http://lucene.apache.org/core/6_2_1/core/org/apache/lucene/search/PhraseQuery.html
              PhraseQuery query=new PhraseQuery(5,"key2",t1.bytes(),t2.bytes());
              String ss=query.toString();
              System.out.println(ss);
              TopDocs tds = searcher.search(query, 20);
              ScoreDoc[] sds = tds.scoreDocs;
              int cou=0;
              for(ScoreDoc sd:sds){
                  cou++;
                  Document d = searcher.doc(sd.doc);
                  String output=cou+". "+d.get("category2")+"\n"+d.get("skey1")+"\n"+d.get("skey2");
                  System.out.println(output);
              }
         }catch(Exception e){
             e.printStackTrace();
         }finally{
             try {
                 reader.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
     public static void main(String[] args) throws IOException
     {
         indexSearch();
     }
 }

PhraseQuery的构造方法有四种:文档。这里介绍演示代码的构造方法:

PhraseQuery(int slop, String field, BytesRef... terms)

slop是int型,通过设置slop“坡度”来确定关键字之间是否允许、允许多少个无关词汇的存在。

filed是String,是要搜索的域。

terms是ByteRef,是用户要搜索的关键字。

因此,第一个需求可以使用PhraseQuery来满足。

二、RangeQuery

RangeQuery是对字符串进行范围查询的,索引中的所有项都以字典顺序排列。它允许用户在某个范围内搜索,该范围的起始项和最终项都可以指定包含或不包含。

import java.io.IOException;
import java.nio.file.Paths;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
 
public class testRangeQuery {
 public static Version luceneVersion = Version.LATEST;
 public static void indexSearch(){
	 DirectoryReader reader = null;
	 try{        
		  Directory directory = FSDirectory.open(Paths.get("indexrangequery"));
		  reader= DirectoryReader.open(directory);
		  IndexSearcher searcher =  new IndexSearcher(reader);
		  //*************测试一*******************
//             Term begin = new Term("birthday","19980101");
//             Term end = new Term("birthday","20040606");
//             Query query = new TermRangeQuery("birthday",begin.bytes(),end.bytes(),false,false);
		  //*************测试二*******************
		  Term begin = new Term("lex","ab");
		  Term end = new Term("lex","cd");
		  Query query = new TermRangeQuery("lex",begin.bytes(),end.bytes(),false,false);
		  String ss=query.toString();
		  System.out.println(ss);
		  TopDocs tds = searcher.search(query, 20);
		  ScoreDoc[] sds = tds.scoreDocs;
		  System.out.println(sds.length);
		  int cou=0;
		  for(ScoreDoc sd:sds)
		  {
			  cou++;
			  Document d = searcher.doc(sd.doc);
			  String output=cou+". "+d.get("sname")+" "+d.get("sbirthday")+" "+d.get("sid")+" "+d.get("slex");
			  System.out.println(output);
		  }
	 }catch(Exception e){
		 e.printStackTrace();
	 }finally{
		 //9、关闭reader
		 try {
			 reader.close();
		 } catch (IOException e) {
			 e.printStackTrace();
		 }
	 }
 }
 public static void main(String[] args) throws IOException
 {
	 indexSearch();
 }
}

构造方法如下:

TermRangeQuery(String field, BytesRef lowerTerm, BytesRef upperTerm, boolean includeLower, boolean includeUpper)

field指明搜索的域;lowerTerm个upperTerm分别的起始项和最终项,最后两个boolean指定是开区间还是闭区间。

这样,对于用户第二个需求,就轻松解决了~~

三、 FuzzyQuery

FuzzyQuery是模糊匹配,基于编辑距离(Edit Distance)的Damerau-Levenshtein算法,编辑距离就是两个字符串有一个转变成另一个所需要的最小的操作步骤。

import java.io.IOException;
import java.nio.file.Paths;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
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 org.apache.lucene.util.Version;
 
public class testFuzzyQuery {
 public static Version luceneVersion = Version.LATEST;
 public static void indexSearch(String keywords){
	 DirectoryReader reader = null;
	 try{        
		  Directory directory = FSDirectory.open(Paths.get("index3"));
		  reader= DirectoryReader.open(directory);
		  IndexSearcher searcher =  new IndexSearcher(reader);
		  FuzzyQuery query=new FuzzyQuery(new Term("key1",keywords));
		  String ss=query.toString();
		  System.out.println(ss);
		  TopDocs tds = searcher.search(query, 20);
		  ScoreDoc[] sds = tds.scoreDocs;
		  int cou=0;
		  for(ScoreDoc sd:sds){
			  cou++;
			  Document d = searcher.doc(sd.doc);
			  String output=cou+". "+d.get("category2")+"\n"+d.get("skey1");
			  System.out.println(output);
		  }
	 }catch(Exception e){
		 e.printStackTrace();
	 }finally{
		 try {
			 reader.close();
		 } catch (IOException e) {
			 e.printStackTrace();
		 }
	 }
 }
 public static void main(String[] args) throws IOException
 {
	 String keywords[]={"流星","眼睛","小学生"};
	 for(int i=0;i<keywords.length;i++)
	 {
		 indexSearch(keywords[i]);
	 }
	 
 }
}

该函数有四个构造方法,参加FuzzyQuery文档。关于FuzzyQuery的构造方法,这篇博客讲得很好:Lucene5学习之FuzzyQuery使用

四、WildCardQuery

WildCardQuery是通配符查询,通配符“?”代表1个字符,而“*”则代表0至多个字符。使用方法很简单:

import java.io.IOException;
import java.nio.file.Paths;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
 
public class testWildCardQuery {
 public static Version luceneVersion = Version.LATEST;
 public static void indexSearch(String keywords){
	 DirectoryReader reader = null;
	 try{        
		  Directory directory = FSDirectory.open(Paths.get("index3"));
		  reader= DirectoryReader.open(directory);
		  IndexSearcher searcher =  new IndexSearcher(reader);
		  WildcardQuery query=new WildcardQuery(new Term("key1",keywords));
		  String ss=query.toString();
		  System.out.println(ss);
		  TopDocs tds = searcher.search(query, 20);
		  ScoreDoc[] sds = tds.scoreDocs;
		  int cou=0;
		  for(ScoreDoc sd:sds){
			  cou++;
			  Document d = searcher.doc(sd.doc);
			  String output=cou+". "+d.get("category2")+"\n"+d.get("skey1");
			  System.out.println(output);
		  } 
	 }catch(Exception e){
		 e.printStackTrace();
	 }finally{
		 try {
			 reader.close();
		 } catch (IOException e) {
			 e.printStackTrace();
		 }
	 }
 }
 public static void main(String[] args) throws IOException
 {
	 String keywords[]={"流?雨","星*","小学*"};
	 for(int i=0;i<keywords.length;i++)
	 {
		 indexSearch(keywords[i]);
	 }    
 }
}

具体用法参考文档:WildCardQuery文档

WildCardQuery和FuzzyQuery由于需要对字段关键字进行字符串匹配,所以,在搜索的性能上面会受到一些影响。

五、PrefixQuery

PrefixQuery用于匹配其索引开始以指定的字符串的文档。用法很简单:

import java.io.IOException;
import java.nio.file.Paths;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
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 org.apache.lucene.util.Version;
 
public class testPrefixQuery {
 public static Version luceneVersion = Version.LATEST;
 public static void indexSearch(){
	 DirectoryReader reader = null;
	 try{        
		  Directory directory = FSDirectory.open(Paths.get("index3"));
		  reader= DirectoryReader.open(directory);
		  IndexSearcher searcher =  new IndexSearcher(reader);
		  PrefixQuery query=new PrefixQuery(new Term("key1","中"));
		  String ss=query.toString();
		  System.out.println(ss);
		  TopDocs tds = searcher.search(query, 20);
		  ScoreDoc[] sds = tds.scoreDocs;
		  System.out.println(sds.length);
		  int cou=0;
		  for(ScoreDoc sd:sds){
			  cou++;
			  Document d = searcher.doc(sd.doc);
			  String output=cou+". "+d.get("category2")+"\n"+d.get("skey1")+"\n"+d.get("skey2");
			  System.out.println(output);
		  }     
	 }catch(Exception e){
		 e.printStackTrace();
	 }finally{
		 try {
			 reader.close();
		 } catch (IOException e) {
			 e.printStackTrace();
		 }
	 }
 }
 public static void main(String[] args) throws IOException
 {
	 indexSearch();
 }
}

详细说明参考官方文档:PrefixQuery文档

上面的FuzzyQuery,WildCardQuery和PrefixQuery都是不精确查询,可以解决用户的第三个需求~~

登录后查阅

此处内容需登录后查阅(刷新)

登录/注册

 


评论
User Image
提示:请评论与当前内容相关的回复,广告、推广或无关内容将被删除。

相关文章
需求用户的需求是多种多样的。比如:用户可能对中日关系比较感兴趣,想查找‘中’和‘日’挨得比较近(5个字的距离内)的文章,超过这个距离的不予考虑。比如:“中日双方
Lucene Field简介在 Apache Lucene 中,Field 类是文档中存储数据的基础。不同类型的 Field 用于存储不同类型的数据(如文本、数
Apache Lucene一款 Apache托管的全文索引组件,纯Java实现。Lucene的作用用户—&gtl;服务器—&gtl;Lucene API—&gtl;索引库—&gtl;数据库/文
Apache lucene 与Java jdk版本对应关系表参考:Apache Lucene版本JDK版本备注
项目环境maven 项目&lt;properties&gtl; &lt;maven.compiler.source&gtl;17&lt;/maven.compiler.source&gtl; &...
背景介绍 Lucene是一款高性能、可扩展的信息检索工具库,是用于全文检索和搜寻的Java开放源码程序库,最初是由Doug Cutting所撰写,2000年发行
StandardAnalyzer自带的标准分词器源码示例 public static void main(String[] args) throws IOExc
mybatis Interceptor拦截器实现自定义扩展查询兼容mybatis plus @Intercepts({ @Signature(type = Executor.c...
案例问题描述有这么一个SQL,外查询 where 子句的 bizCustomerIncoming_id 字段,和子查询 where 字句的 cid 字段都有高效
接上一篇:mybatis Interceptor拦截器实现自定义扩展查询兼容mybatis plus-xqlee (blog.xqlee.com)这里进行自定义分页查询扩展,基于mybatis ...
Java编程使用原生JDBC查询数据库数据,并将返回结果赋值给JSON/JSONArray对象,用于返回数据。源码参考:import java.sql.Resu
常见优化技术我们先从工业实践角度总结,几条常见 MySQL 查询优化策略。索引优化为常用的查询条件(WHERE、JOIN、GROUP BY、ORDER BY)添
MySQL慢查询优化_MySQL慢查询排查_MySQL慢查询设置配置
简单介绍在graylog web界面如何查询想要的日志 查询单个字符串 "your-info" 查询包含A和B的记录
有些时候为了方便,会把多个值存在一个字段里面,然后用逗号分隔