3.8 高级检索方式(二)
上节已经介绍了五种高级检索方式,现在我们来学习另外五种。事实上,今天要介绍的五种高级检索方式有共通之处:都可以解决上节提到的最后一个用户需求:帮助小明快速定位游戏分类,过滤掉其他分类内容。
六、BooleanQuery
BooleanQuery是布尔查询,通过对其他查询(如上节讲到的TermQuery,PhraseQuery或其他BooleanQuery)的组合来实现逻辑运算。
BooleanQuery的逻辑运算符是通过BooleanQuery.Occur(文档)来确定的。
- BooleanClause.Occur.MUST:与运算
- BooleanClause.Occur.SHOULD:或运算
- BooleanClause.Occur.MUST_NOT:非运算
- BooleanClause.Occur.FILTER:相当于与运算,但是不参与评分。
Lucene6.2.1版本废弃了BooleanFilter,合并到了BooleanClause.OCCUR.FILTER中,那么Filter和Query有什么区别呢?
其实各种Query和各种Filter之间非常相似,可以互相转换,最大的区别是:Query有评分操作,返回的结果集有相关性评分;Filter的结果集无相关性评分,返回的结果是无排序的。
这四者组合,妙用无穷:
- MUST和MUST:取得多个查询子句的交集。
- MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
- SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。
- SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序,是在MUST搜出来的doc里面,根据SHOULD的query进行打分。
- SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。
- MUST_NOT和MUST_NOT:无意义,检索无结果。
- 上述的MUST换成FILTER,就变成了不带评分的过滤功能,
1 package testAdvancedQuery; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.PrintWriter; 7 import java.net.ServerSocket; 8 import java.net.Socket; 9 import java.nio.file.Paths; 10 11 import org.apache.lucene.document.Document; 12 import org.apache.lucene.index.DirectoryReader; 13 import org.apache.lucene.index.Term; 14 import org.apache.lucene.queryparser.classic.QueryParser; 15 import org.apache.lucene.search.BooleanClause; 16 import org.apache.lucene.search.BooleanQuery; 17 import org.apache.lucene.search.IndexSearcher; 18 import org.apache.lucene.search.Query; 19 import org.apache.lucene.search.ScoreDoc; 20 import org.apache.lucene.search.TermQuery; 21 import org.apache.lucene.search.TopDocs; 22 import org.apache.lucene.search.highlight.Fragmenter; 23 import org.apache.lucene.search.highlight.Highlighter; 24 import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; 25 import org.apache.lucene.search.highlight.QueryScorer; 26 import org.apache.lucene.search.highlight.SimpleHTMLFormatter; 27 import org.apache.lucene.search.highlight.SimpleSpanFragmenter; 28 import org.apache.lucene.store.Directory; 29 import org.apache.lucene.store.FSDirectory; 30 import org.apache.lucene.util.Version; 31 32 import IkAnalyzer.MyIkAnalyzer; 33 34 public class testBooleanQuery { 35 public static Version luceneVersion = Version.LATEST; 36 public static void indexSearch(String keywords){ 37 DirectoryReader reader = null; 38 try{ 39 Directory directory = FSDirectory.open(Paths.get("index3")); 40 reader = DirectoryReader.open(directory); 41 IndexSearcher searcher = new IndexSearcher(reader); 42 QueryParser parserkey1 = new QueryParser("key1",new MyIkAnalyzer());//content表示搜索的域或者说字段 43 Query querykey1 = parserkey1.parse(keywords); 44 String ss=querykey1.toString(); 45 System.out.println(ss); 46 QueryParser parserkey2 = new QueryParser("key2",new MyIkAnalyzer());//content表示搜索的域或者说字段 47 Query querykey2 = parserkey2.parse(keywords);//被搜索的内容 48 System.out.println(keywords); 49 BooleanQuery query=null; 50 String cate1="知识"; 51 String cate2="百科"; 52 Query querycate1=new TermQuery(new Term("category1",cate1)); 53 Query querycate2=new TermQuery(new Term("category2",cate2)); 54 query=new BooleanQuery.Builder().add(querycate1,BooleanClause.Occur.FILTER).add(querycate2,BooleanClause.Occur.FILTER).add(querykey1,BooleanClause.Occur.SHOULD).add(querykey2,BooleanClause.Occur.SHOULD).build(); 55 TopDocs tds = searcher.search(query, 20); 56 ScoreDoc[] sds = tds.scoreDocs; 57 int cou=0; 58 for(ScoreDoc sd:sds){ 59 cou++; 60 Document d = searcher.doc(sd.doc); 61 String output=cou+". "+d.get("skey1")+"\n"+d.get("skey2"); 62 System.out.println(output); 63 } 64 }catch(Exception e){ 65 e.printStackTrace(); 66 }finally{ 67 //9、关闭reader 68 try { 69 reader.close(); 70 } catch (IOException e) { 71 e.printStackTrace(); 72 } 73 } 74 } 75 public static void main(String[] args) throws IOException 76 { 77 String keywords="为什么眼睛会流泪?"; 78 indexSearch(keywords); 79 } 80 }
BooleanQuery
对于小明的需求,我们就可以利用BooleanQuery来设计:查询一是域为“分类”,搜索词为“游戏”的TermQuery;查询二为检索搜索关键词的某一Query;用BooleanQuery组合两个查询,查询一的运算符为过滤,查询二为MUST,这样,就能查找所有分类为“游戏”的内容了。
七、MultiFieldQuery
MultiFieldQuery是多域查询。比如用户有这样的需求:一个文档中含有“标题”,“正文”等字段,搜索一个关键词,不管它在标题中出现还是在正文中出现都算符合条件。这时,我们就用到了多域查询。
1 package testAdvancedQuery; 2 import java.nio.file.Paths; 3 import java.io.*; 4 5 import org.apache.lucene.analysis.Analyzer; 6 import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; 7 import org.apache.lucene.analysis.standard.StandardAnalyzer; 8 import org.apache.lucene.document.Document; 9 import org.apache.lucene.index.DirectoryReader; 10 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 11 import org.apache.lucene.search.BooleanClause; 12 import org.apache.lucene.search.IndexSearcher; 13 import org.apache.lucene.search.Query; 14 import org.apache.lucene.search.ScoreDoc; 15 import org.apache.lucene.search.TopDocs; 16 import org.apache.lucene.store.Directory; 17 import org.apache.lucene.store.FSDirectory; 18 import org.apache.lucene.util.Version; 19 import IkAnalyzer.MyIkAnalyzer; 20 public class testMultiFieldQuery { 21 public static Version luceneVersion = Version.LATEST; 22 public static void indexSearch(){ 23 DirectoryReader reader = null; 24 try{ 25 Directory directory = FSDirectory.open(Paths.get("index3"));//在硬盘上生成Directory 26 reader = DirectoryReader.open(directory); 27 IndexSearcher searcher = new IndexSearcher(reader); 28 Analyzer analyzer1=new StandardAnalyzer(); 29 Analyzer analyzer2=new SmartChineseAnalyzer(); 30 Analyzer analyzer3=new MyIkAnalyzer(); 31 // //方法一:利用BooleanQuery,在两个TermQuery之间做逻辑运算 32 // Term t1=new Term("key1","张飞"); 33 // Term t2=new Term("key2","刘备"); 34 // TermQuery q1=new TermQuery(t1); 35 // TermQuery q2=new TermQuery(t2); 36 // BooleanQuery query=new BooleanQuery.Builder().add(q1,BooleanClause.Occur.FILTER).add(q2,BooleanClause.Occur.MUST).build(); 37 // 38 //方法二:MultiFieldQueryParser类,实现多字段搜索,实际上只是一个封装,用起来简单,内部还是用BooleanQuery实现 39 String fields[]={"key1","key2"}; 40 String kws[]={"张飞","刘备"}; 41 //MUST:and;SHOULD:OR;MUST_NOT:NOT;FILTER:相当于MUST,但是不参与打分。 42 BooleanClause.Occur[] flags=new BooleanClause.Occur[]{BooleanClause.Occur.FILTER,BooleanClause.Occur.MUST}; 43 Query query=MultiFieldQueryParser.parse(kws,fields,flags,analyzer3); 44 String ss=query.toString(); 45 System.out.println(ss); 46 TopDocs tds = searcher.search(query, 20); 47 ScoreDoc[] sds = tds.scoreDocs; 48 int cou=0; 49 for(ScoreDoc sd:sds){ 50 cou++; 51 Document d = searcher.doc(sd.doc); 52 String output=cou+". "+d.get("skey1")+"\n"+d.get("skey2"); 53 System.out.println(output); 54 } 55 }catch(Exception e){ 56 e.printStackTrace(); 57 }finally{ 58 try { 59 reader.close(); 60 } catch (IOException e) { 61 e.printStackTrace(); 62 } 63 } 64 } 65 public static void main(String[] args) throws IOException 66 { 67 indexSearch(); //搜索的内容可以修改 68 } 69 }
MultiFieldQuery
MultiFieldQuery有两种实现方法:
方法一是利用BooleanQuery在多个TermQuery之间做逻辑运算
1 //方法一:利用BooleanQuery,在两个TermQuery之间做逻辑运算 2 Term t1=new Term("key1","张飞"); 3 Term t2=new Term("key2","刘备"); 4 TermQuery q1=new TermQuery(t1); 5 TermQuery q2=new TermQuery(t2); 6 BooleanQuery query=new BooleanQuery.Builder().add(q1,BooleanClause.Occur.FILTER).add(q2,BooleanClause.Occur.MUST).build();
方法一
方法二是MultiFieldQueryParser类,实现多域搜索,实际上只是一个封装,用起来简单,内部还是用BooleanQuery实现。
1 //方法二:MultiFieldQueryParser类,实现多字段搜索,实际上只是一个封装,用起来简单,内部还是用BooleanQuery实现 2 String fields[]={"key1","key2"}; 3 String kws[]={"张飞","刘备"}; 4 //MUST:and;SHOULD:OR;MUST_NOT:NOT;FILTER:相当于MUST,但是不参与打分。 5 BooleanClause.Occur[] flags=new BooleanClause.Occur[]{BooleanClause.Occur.FILTER,BooleanClause.Occur.MUST}; 6 Query query=MultiFieldQueryParser.parse(kws,fields,flags,analyzer3);
方法二
1 String ss=query.toString(); 2 System.out.println(ss);
打印查询对象
可以利用上述代码把查询对象打印出来,便于直观感受lucene对查询的解析。
可以看出,MultiFieldQuery本质上也是BooleanQuery的应用,具体内容可以参考官方文档。
八、FieldQuery
FieldQuery是域搜索,用户可以通过输入符合语法规则的查询语句指定一次查询是在哪些域上进行。例如,如果索引的文档包含两个域,Title 和Content,用户可以使用查询 “Title: Lucene AND Content: Java” 来返回所有在 Title域上包含 Lucene 并且在 Content 域上包含 Java 的文档。
1 package testAdvancedQuery; 2 import java.io.IOException; 3 import java.nio.file.Paths; 4 5 import org.apache.lucene.document.Document; 6 import org.apache.lucene.index.DirectoryReader; 7 import org.apache.lucene.queryparser.classic.QueryParser; 8 import org.apache.lucene.search.IndexSearcher; 9 import org.apache.lucene.search.ScoreDoc; 10 import org.apache.lucene.search.TopDocs; 11 import org.apache.lucene.store.Directory; 12 import org.apache.lucene.store.FSDirectory; 13 import org.apache.lucene.util.Version; 14 15 import IkAnalyzer.MyIkAnalyzer; 16 public class testFieldQuery { 17 public static Version luceneVersion = Version.LATEST; 18 public static void indexSearch(String keywords){ 19 DirectoryReader reader = null; 20 try{ 21 Directory directory = FSDirectory.open(Paths.get("index3")); 22 reader= DirectoryReader.open(directory); 23 IndexSearcher searcher = new IndexSearcher(reader); 24 QueryParser parser = new QueryParser("key1",new MyIkAnalyzer());//content表示搜索的域或者说字段 25 org.apache.lucene.search.Query query = parser.parse(keywords);//被搜索的内容 26 String ss=query.toString(); 27 System.out.println(ss); 28 TopDocs tds = searcher.search(query, 20); 29 ScoreDoc[] sds = tds.scoreDocs; 30 int cou=0; 31 for(ScoreDoc sd:sds){ 32 cou++; 33 Document d = searcher.doc(sd.doc); 34 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1")+"\n"+d.get("skey2"); 35 System.out.println(output); 36 } 37 }catch(Exception e){ 38 e.printStackTrace(); 39 }finally{ 40 try { 41 reader.close(); 42 } catch (IOException e) { 43 e.printStackTrace(); 44 } 45 } 46 } 47 public static void main(String[] args) throws IOException 48 { 49 String keywords="key1:猪八戒 AND key2:孙悟空 "; 50 indexSearch(keywords); 51 52 } 53 }
FieldQuery
1 String ss=query.toString(); 2 System.out.println(ss);
打印查询对象
打印查询对象后发现,lucene对用户查询语句的处理与对BooleanQuery的处理结果是一样的。
九、MultiSearcher
MultiSearcher是多索引搜索。可以这样理解:
为了减少单个索引目录的大小,时常将索引放在许多目录中,这些索引的结构都是一致的。比如有一个城市的网站搜索引擎,随着时间的增长,我们可能会将索引的目录按照年份分成2003、2004、2005等。旧的索引目录被搜索的几率小,所以将其单独分出去,这样,可以减小新的索引目录,加快搜索速度。但是有些时候,必须实现多个索引的同时搜索,因为我们需要存放在这些索引中的信息。要实现多索引搜索,只需要对每个索引目录都用IndexSearcher搜索一遍,最后将搜索结果合并起来。
实际上,lucene6.2.1已经废弃了MultiSearcher这个类,改用MultiReader来实现。
1 package testAdvancedQuery; 2 import java.nio.file.Paths; 3 import java.io.*; 4 5 import org.apache.lucene.analysis.Analyzer; 6 import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; 7 import org.apache.lucene.analysis.standard.StandardAnalyzer; 8 import org.apache.lucene.document.Document; 9 import org.apache.lucene.index.DirectoryReader; 10 import org.apache.lucene.index.MultiReader; 11 import org.apache.lucene.index.Term; 12 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 13 import org.apache.lucene.queryparser.classic.QueryParser; 14 import org.apache.lucene.search.BooleanClause; 15 import org.apache.lucene.search.BooleanClause.Occur; 16 import org.apache.lucene.search.BooleanQuery; 17 import org.apache.lucene.search.Explanation; 18 import org.apache.lucene.search.IndexSearcher; 19 import org.apache.lucene.search.Query; 20 import org.apache.lucene.search.ScoreDoc; 21 import org.apache.lucene.search.TermQuery; 22 import org.apache.lucene.search.TopDocs; 23 import org.apache.lucene.store.Directory; 24 import org.apache.lucene.store.FSDirectory; 25 import org.apache.lucene.util.Version; 26 import IkAnalyzer.MyIkAnalyzer; 27 28 public class testMultiSearcher { 29 public static Version luceneVersion = Version.LATEST; 30 public static void indexSearch(String keywords){ 31 String res = ""; 32 DirectoryReader reader1 = null; 33 DirectoryReader reader2 = null; 34 MultiReader mr=null; 35 try{ 36 Directory directory1 = FSDirectory.open(Paths.get("index1")); 37 Directory directory2 = FSDirectory.open(Paths.get("index2")); 38 reader1 = DirectoryReader.open(directory1); 39 reader2 = DirectoryReader.open(directory2); 40 mr=new MultiReader(reader1,reader2); 41 IndexSearcher searcher1 = new IndexSearcher(mr); 42 IndexSearcher searcher2=new IndexSearcher(reader1); 43 IndexSearcher searcher3=new IndexSearcher(reader2); 44 QueryParser parser = new QueryParser("key2",new MyIkAnalyzer());//content表示搜索的域或者说字段 45 Query query = parser.parse(keywords); 46 String ss=query.toString(); 47 System.out.println(ss); 48 TopDocs tds = searcher1.search(query, 20); 49 ScoreDoc[] sds = tds.scoreDocs; 50 int cou=0; 51 System.out.println("MultiSearcher的结果:"); 52 for(ScoreDoc sd:sds){ 53 cou++; 54 Document d = searcher1.doc(sd.doc); 55 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1"); 56 System.out.println(output); 57 } 58 //************************************************* 59 System.out.println("只搜索百科的结果:"); 60 tds = searcher2.search(query, 10); 61 sds = tds.scoreDocs; 62 cou=0; 63 for(ScoreDoc sd:sds){ 64 cou++; 65 Document d = searcher2.doc(sd.doc); 66 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1"); 67 System.out.println(output); 68 } 69 //******************************************** 70 System.out.println("只搜索课本的结果:"); 71 tds = searcher3.search(query, 10); 72 sds = tds.scoreDocs; 73 cou=0; 74 for(ScoreDoc sd:sds){ 75 cou++; 76 Document d = searcher3.doc(sd.doc); 77 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1"); 78 System.out.println(output); 79 } 80 }catch(Exception e){ 81 e.printStackTrace(); 82 }finally{ 83 try { 84 mr.close(); 85 } catch (IOException e) { 86 e.printStackTrace(); 87 } 88 } 89 } 90 public static void main(String[] args) throws IOException 91 { 92 String keyword="眼睛"; 93 indexSearch(keyword); 94 } 95 }
MultiSearcher
这样,我们可以为“游戏”,“课本”等独立地建立索引,用户想搜索游戏时,我们可以指定索引目录为游戏,用户想综合所有类别搜索时,我们可以用MultiSearcher来搜索所有类目~
我们可能担心,在索引的过程中,分散地存储到多个索引目录中,是否在搜索时能够得到全局的相关度计算得分?其实Lucene的这个方法支持全局得分的计算,也就是说,虽然索引分布在多个索引目录中,在搜索的时候还会将全部的索引数据聚合在一起进行查询匹配和得分计算。
利用多域搜索还可以实现多线程搜索,这个有待研究~
十、QueryParser
QueryParse类不是一种Query,但可以通过设置QueryParser的参数,实现多字段搜索,也能实现BooleanQuery的一部分效果。
使用QueryParser解析多个关键词,比如用户搜索“love China”,打印查询对象后发现,两个关键词是或的关系。
使用下面的方法后,QueryParser就可以对两个关键词取交了:
1 //方式一: 2 parser.setDefaultOperator(QueryParser.Operator.AND); 3 //方式二: 4 parser.createBooleanQuery("key1", keywords, Occur.MUST);
QueryParser
方法一:
1 parser.setDefaultOperator(QueryParser.Operator.AND);
方法一
参数QueryParser.Operator.AND表示与,QueryParser.Operator.OR表示或。
方法二:
1 parser.createBooleanQuery("key1", keywords, Occur.MUST);
方法二
参数一表示域,参数二表示搜索关键词,参数三只有两种,Occur.MUST和Occur.SHOULD,而且都表示逻辑与。
以上就是这节的五种高级检索,我们发现,这五种方法都可以解决分类搜索或过滤问题,其中BooleanQuery是最基本的用法。
综合一二两节,我们已经学习了十种高级检索方式,其实lucene内部还有很多方法,以后有机会我们再来一起探索。
接下来,我们会一起揭开“lucene近实时搜索”的神秘面纱~~
转载于:https://www.cnblogs.com/itcsl/p/6843309.html
3.8 高级检索方式(二)相关推荐
- Case Study: 利用JS设计高级检索功能通过PHP获取MySQL数据
一.目标 该笔记的目的是引导读者借助WampServer平台和MySQL数据库,利用HTML/CSS/JS/PHP设计一个含有高级检索功能的数据库网页.该功能效果如图1所示.用户在文本框中输入相应内容 ...
- 怎样在计算机上进行高级搜索,怎么用中国知网的高级检索才能准确找到自己想要的文献?...
原标题:怎么用中国知网的高级检索才能准确找到自己想要的文献? 知网的检索方式很多,一框式检索是小伙伴们常用的方式.但需要匹配多个检索词时,小伙伴肯定会用到CNKI高级检索啦.高级检索可以帮助小伙伴们匹 ...
- mysql高级篇(二)mysql索引优化分析
mysql高级篇笔记 mysql高级篇(一)mysql的安装配置.架构介绍及SQL语句的复习. mysql高级篇(二)mysql索引优化分析. mysql高级篇(三)查询截取分析(慢查询日志).主从复 ...
- JAVAWEB开发之Hibernate详解(三)——Hibernate的检索方式、抓取策略以及利用二级缓存进行优化、解决数据库事务并发问题
Hibernate的检索方式 Hibernate提供了以下几种检索对象的方式: 导航对象图检索方式:根据已经加载的对象导航到其他对象. OID检索方式:按照对象的OID来检索对象. HQL检索方式: ...
- (一)硕博生常用的中文文献检索方式推荐
写在这里的初衷,一是备忘,二是希望得到高人指点,三是希望能遇到志同道合的朋友. 常用的中文文献检索方式 1.中国知网 2.百度学术 3.国家科技图书文献中心数据库 4.中国科学院文献服务系统 5.万方 ...
- 高级SQL优化(二) ——《12年资深DBA教你Oracle开发与优化——性能优化部分》
目录: Oracle数据完整性和锁机制 索引及优化之表分析 表分析.约束及表间关系 Oracle体系结构1 Oracle体系结构2 海量数据库及分区1 海量数据库及分区2 海量数据库及分区 ...
- Elasticsearch实现类Google高级检索
一.高级检索的功能点 通过高级搜索配置搜索项,能更准确的过滤掉不相干信息,获取最想要的检索信息. 以Google搜索为例(截取核心片段): 二.高级检索拆分 1.包含以下全部的关键词: 需要分词处 ...
- JavaScript 高级编程(二)
JavaScript 高级编程(二) BOM 一套操作浏览器的API. 常见对象 window: 代表整个浏览器窗口 注意: window是BOM中的一个对象, 并且是一个顶级的对象(全局) Navi ...
- JS高级的学习(二)
JS高级的学习(二) set对象 Set 是一个对象 存放数据 数据永远不会重复 Set 当成是一个数组 遍历 使用 数组方法 find findIndex Map 数组转成 Set对象 const ...
最新文章
- mysql哪些优化手段_mysql explain 及常见优化手段
- [HOW TO]-VirtualBox的虚拟机通过宿主机代理上网
- Python 之 向上取整、向下取整以及四舍五入函数
- springboot整合aliyun的物流订单查询
- GDT中的轮廓度标注
- 医学数据窗宽窗位调节
- mybatis数组越界异常 Error preparing statement
- CentOS 中安装nginx
- oc 协议 回调 静态成员_深入iOS系统底层之静态库
- (收藏)刘德华获奖全记录“经典”
- reflection removal
- n阶差分方程重根计算公式的一般证明
- 做给家人吃的菜:趣店预制菜要让生活更加美好
- InfiniBand的版本演进、基础观念、传量传速
- K314.3卸载不了问题处理
- 硬盘和计算机的接口类型有哪两种,常见的不同类型接口的固态硬盘有哪些
- 构建档案馆发展新趋势:智慧档案馆三维可视化方案
- Kdevelop如何调整程序的字体大小和颜色
- 关于gitlab拉代码和传代码
- 9月中旬川藏骑行准备工作