木其工作室代写程序 [原]Lucene 实例教程(四)之检索方法总结

本文主要是介绍木其工作室代写程序 [原]Lucene 实例教程(四)之检索方法总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

淘宝网上专业IT程序代写
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本人声明。否则将追究法律责任。
作者: 永恒の_☆ 地址: http://blog.csdn.net/chenghui0317/article/details/10824789

一、 前言

       前面简单介绍了如何使用Lucene删除索引和修改索引的操作,这样子避免了索引文件的累加会产生脏数据或者数据重复 ,地址:http://blog.csdn.net/chenghui0317/article/details/10366255

       在项目实战中,这还不能完全解决一些比较棘手的问题,比如:

       1、前面只提供了一种打开索引文件的方式 和 一种 实现全文检索的方式,检索方式的单一性不能根据检索条件权衡筛选最优检索方式;

       2、并且在检索的时候还有一点排序上的瑕疵,最优匹配的document对象并没有显示在最上面, 以及 无法实现对索引结果的操作。

       接下来介绍几中检索索引的方式,比如多字段搜索、多条件搜索、模糊搜索、前缀搜索、使用过滤器过滤查询等等,使用了这些方式之后,可以更好的理解Lucene的执行原理和检索方式的多样化。 其中的好处还需要慢慢去理解和体会。


二、使用Lucene实战


1、通过QueryParser绑定单个字段来检索索引记录

    这是一种最为简单的查询方式,首先封装好QueryParser这个查询转换器,然后根据关键字封装查询对象,这个做好之后交给IndexSearch就可以返回结果了。

具体代码如下:

package com.lucene.test;import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;import com.lucene.entity.Article;/*** Lucene 检索各种索引的实现方式总结* @author Administrator**/
public class LuceneSearchDemo {public static final String INDEX_DIR_PATH = "indexDir";/* 创建简单中文分析器 创建索引使用的分词器必须和查询时候使用的分词器一样,否则查询不到想要的结果 */private Analyzer analyzer = null;// 索引保存目录private File indexFile = null;//目录对象,因为操作索引文件都要用到它,所以定义为全局变量private Directory directory = null;//索引搜索对象private IndexSearcher indexSearcher;/*** 初始化方法* @throws IOException */public void init() throws IOException{analyzer = new IKAnalyzer(true);indexFile = new File(INDEX_DIR_PATH);directory = new SimpleFSDirectory(indexFile);indexSearcher = new IndexSearcher(directory);System.out.println("*****************初始化成功**********************");}/*** 根据传递的结果集 封装成集合后显示出来* @param scoreDocs* @throws IOException * @throws CorruptIndexException */public void showResult(ScoreDoc[] scoreDocs) throws CorruptIndexException, IOException{List<Article> articles = new ArrayList<Article>();for (int i = 0; i < scoreDocs.length; i++) {int doc = scoreDocs[i].doc;//索引idDocument document = indexSearcher.doc(doc);Article article = new Article();if (document.get("id") == null) {System.out.println("id为空");} else {article.setId(Integer.parseInt(document.get("id")));article.setTitle(document.get("title"));article.setContent(document.get("content"));articles.add(article);}			}if(articles.size()!=0){for (Article article : articles) {System.out.println(article);}}else{System.out.println("没有查到记录。");}}/*** 通过QueryParser绑定单个字段来检索索引记录* @param keyword* @throws ParseException * @throws IOException * @throws CorruptIndexException */public void searchByQueryParser(String keyword) throws ParseException, CorruptIndexException, IOException{System.out.println("*****************通过QueryParser来检索索引记录**********************");QueryParser queryParser = new QueryParser(Version.LUCENE_36, "title", analyzer);Query query = queryParser.parse(keyword);// public TopFieldDocs search(Query query, int n, Sort sort)// 参数分别表示 Query查询对象,返回的查询数目,排序对象TopDocs topDocs = indexSearcher.search(query, 10, new Sort());showResult(topDocs.scoreDocs);}public static void main(String[] args) {LuceneSearchDemo luceneInstance = new LuceneSearchDemo();try {luceneInstance.init();luceneInstance.searchByQueryParser("沪K");} catch (CorruptIndexException e) {e.printStackTrace();} catch (ParseException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
}
根据mian方法传递的 “沪K”关键字,查询结果如下图:

控制台输出显示都是满足检索条件的结果。

需要注意的是:

    <1> 添加索引时用的什么分词器,那么检索的时候也必须使用这个分词器,否则查询不到任何记录。因为插入索引的时候是按照分词器规则插入的,所以检索的时候会以这种规则为匹配方式;

    <2> 由于次查询方式是使用的标题为查询条件,所以内容中出现的任何字符不会作为匹配元素。如果要使用多字段作为查询条件那么就要使用MultiQueryParser绑定多个字段封装Query查询对象了。


2、通过MultiQueryParser绑定多个字段来检索索引记录

这种实现方式较QueryParser 的区别在于,构造方法中的第二个参数由原先的String变成了String[] ,这就表示可以使用多字段的数组了。

具体代码如下:

	/*** 通过MultiQueryParser绑定多个字段来检索索引记录* @param keyword* @throws ParseException * @throws IOException * @throws CorruptIndexException */public void searchByMultiFieldQueryParser(String keyword) throws ParseException, CorruptIndexException, IOException{System.out.println("*****************通过MultiQueryParser绑定多个字段来检索索引记录**********************");QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_36, new String[]{"title", "content"}, analyzer); Query query = queryParser.parse(keyword);Sort sort = new Sort(); //不传参数则按照最优匹配来排序//Sort sort = new Sort(new SortField("title", SortField.STRING, false));   //最后一个参数很关键,默认是降序的,如果要指定为升序改为true即可。TopDocs topDocs = indexSearcher.search(query, 10, sort);showResult(topDocs.scoreDocs);}
这样,title和content这两个字段共同作为条件查询检索索引了。运行该方法,使用 “沪K”关键字,具体效果如下:


这次使用的是多字段检索,所以两个字段都会去匹配,根据上面结果,最优匹配的第一条记录并没有第二条出现的多,可能是这样:

因为是多字段匹配,那么两个字段都要匹配到,上面传递的"沪K"关键字都要在title和content中出现才算最优匹配。所以出现了上面结果。

如果要使用排序可以使用Sort的有参数构造方法,

里面传递SortField的实例,参数个数无上限,需要说明的是它的构造方法

    public SortField(String field, int type, boolean reverse)

第一个是字段名,第二个是字段的类型,第三个是排序的升降序,true表示升序,false表示降序,如果不指定默认是false

现在使用title作为排序字段降序排列,具体效果如下:

上图显示的结果是根据title降序排列的。

需要说明的是:

<1>实际开发如果是全文检索就不建议使用排序,因为排序会导致最优匹配的结果错乱,但是如果是使用单个字段查询的话 倒是没有问题。


3、通过Term绑定字段检索索引记录

这种实现方式主要是通过Term组合查询,具体代码如下:
	/*** 通过term组合检索索引记录* @param keyword* @throws IOException*/public void searchByTerm(String keyword) throws IOException{System.out.println("*****************通过term组合检索索引记录**********************");//Term term = new Term("title", keyword);Term term = new Term("content", keyword);//MultiTermQueryQuery query = new TermQuery(term);TopDocs topDocs = indexSearcher.search(query, 10);showResult(topDocs.scoreDocs);}

实践证明:

这种查询方式有点怪,关键字只能是常用的词组,比如“上海”“中文” 等等,不是常用的词组则什么都不会显示,比如:“文和”“京津沪”等等。

另外 根据前面的经验单个Term绑定单个字段,那么MultiTermQuery就可以绑定多个字段查询了。

使用常用词组作为关键字的查询效果如下:



综合所有的因素考虑:既然用起来不方便所以要慎用。


4、通过wildcard使用通配符组合检索索引记录

这种实现方式主要是可以像sql语句一样使用通配符来查询想要的结果,具体代码如下:

	/*** 通过wildcard使用通配符组合检索索引记录* @param keyword* @throws IOException*/public void searchByWildcard(String keyword) throws IOException{System.out.println("*****************通过wildcard使用通配符组合检索索引记录**********************");//Term term = new Term("title", "*" + keyword + "*");//Term term = new Term("content", "*" + keyword + "*");Term term = new Term("content", "*" + keyword + "?");Query query = new WildcardQuery(term);TopDocs topDocs = indexSearcher.search(query, 10);showResult(topDocs.scoreDocs);}
需要说明的是:

    <1> *代表0个或多个字符,?代表0个或一个字符 ;
    <2> 这种查询方式根据通配符针对中文有效, 对英文和数字完全没有效果,任何英文和数字都不行;
    <3> 该检索方式对空格不敏感,就是说 如果最后一个字符为空格,然后匹配的时候空格不会作为匹配内容。

接下来使用“中文”作为关键字运行的效果如下所示:

其实这种实现方式非常非常的好,但是就是不支持英文和数字,就太不友好了。


5、通过prefix作为前缀组合检索索引记录

	/*** 通过prefix作为前缀组合检索索引记录* @param keyword* @throws IOException*/public void searchByPrefix(String keyword) throws IOException{System.out.println("*****************通过prefix作为前缀组合检索索引记录**********************");//Term term = new Term("title", keyword);Term term = new Term("content", keyword);Query query = new PrefixQuery(term);TopDocs topDocs = indexSearcher.search(query, 10);showResult(topDocs.scoreDocs);}	
实践证明:

    <1>如果使用的分词器是SimpleAnalyzer,那么会严格按照把关键字作为前缀去检索,但是如果使用的分词器是IKAanlayzer,那么会模糊匹配查询

    <2>同样该方式针对中文有效, 对英文和数字完全没有效果,任何英文和数字都不行;

接下来使用“中文”关键字作为检索条件,运行之后的效果如下:

通过前面使用了WildcardQuery通配符的配置之后,现在这种查询方式显得有点多余的感觉。


6、通过filter过滤条件组合检索索引记录

使用这种过滤器先封装查询对象,然后过滤满足条件的结果显示出来,并且这种过滤方式支持多条件,具体代码如下:

	/*** 通过filter过滤条件组合检索索引记录* @param keywords* @throws IOException*/public void searchByFilter(String[] keywords) throws IOException{System.out.println("*****************通过filter过滤条件组合检索索引记录**********************");List<Filter> filterList = new ArrayList<Filter>();Term term = new Term("content", "*" + keywords[0] + "*");Query query = new WildcardQuery(term);//添加过滤器QueryWrapperFilter filter = new QueryWrapperFilter(new WildcardQuery(new Term("content","*" + keywords[1] + "*")));filterList.add(filter);filter = new QueryWrapperFilter(new WildcardQuery(new Term("content","*" + keywords[2] + "*")));filterList.add(filter);for (Filter f : filterList) {query = new FilteredQuery(query, f);  //这里面不断的构造query,传递过滤器封装最终的Query查询对象}TopDocs topDocs = indexSearcher.search(query, 10);showResult(topDocs.scoreDocs);}
这里面首先是使用的一个模糊查询,然后后面添加了两个同样是模糊查询的过滤条件,通过不断创建FilteredQuery的实例来封装Query查询对象,并且只有条件满足的才会显示出来。

如果想看看Query里面到底存放的是什么可以直接输出Query,在这个例子中传递参数 new String[]{"分词","组合","左边"} ,输出的query分别是:

content:*分词*
filtered(content:*分词*)->QueryWrapperFilter(content:*组合*)
filtered(filtered(content:*分词*)->QueryWrapperFilter(content:*组合*))->QueryWrapperFilter(content:*左边*)

接下来运行该方法的效果如下所示:

由此可见,所有的过滤条件都满足了才会被匹配到结果集中显示。


7、通过boolean检索索引记录

这种实现方式主要是讲多个query查询对象进行筛选,这里包括三种方式:

    <1>取出他们共同的部分,即条件都满足的才会匹配;

    <2>取出他们不同的部分,即条件必须不满足的才会匹配;

    <3>取出他们所有部分,即满足任何一个条件即可的就可以匹配。

具体实现代码如下:

	/*** 通过boolean检索索引记录* @param keyword* @throws IOException*/public void searchByBoolean(String[] keywords) throws IOException{System.out.println("*****************通过boolean检索索引记录**********************");Query query1 = new WildcardQuery(new Term("content","*" + keywords[0] + "*"));Query query2 = new WildcardQuery(new Term("content","*" + keywords[1] + "*"));BooleanQuery query = new BooleanQuery();query.add(query1, BooleanClause.Occur.MUST);query.add(query2, BooleanClause.Occur.MUST);//query.add(query2, BooleanClause.Occur.MUST_NOT);//query.add(query2, BooleanClause.Occur.SHOULD);System.out.println(query);TopDocs topDocs = indexSearcher.search(query, 10);showResult(topDocs.scoreDocs);		}

接下来,传递参数 new String[]{"分词","一个"} 运行一下,具体效果如下所示:


其实,booleanQuery的效果和filterQuery效果差不多,都是将所有的Query返回的交集记录整合在一起记录,也就是条件都要满足才行。这里的前提是使用BooleanClause.Occur.MUST这个“必须满足的”参数;

如果是使用 BooleanClause.Occur.SHOULD 返回的是所有的Query返回的并集集合在一起的记录
而使用 BooleanClause.Occur.MUST_NOT表示 必须没有,这里的查询条件中必须不能出现在最终返回的结果中。


那么,Lucene提供的几种常用的检索方式就介绍完了,最终这些检索方式好不好用还取决于自己去尝试之后,慢慢体会才能体会和领悟。

作者:ch656409110 发表于2013-9-1 17:43:19 原文链接
阅读:1079 评论:0 查看评论

转载于:https://www.cnblogs.com/sourcecode2014/p/3295407.html

这篇关于木其工作室代写程序 [原]Lucene 实例教程(四)之检索方法总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/269791

相关文章

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

macOS无效Launchpad图标轻松删除的4 种实用方法

《macOS无效Launchpad图标轻松删除的4种实用方法》mac中不在appstore上下载的应用经常在删除后它的图标还残留在launchpad中,并且长按图标也不会出现删除符号,下面解决这个问... 在 MACOS 上,Launchpad(也就是「启动台」)是一个便捷的 App 启动工具。但有时候,应

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

mysql出现ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的解决方法

《mysql出现ERROR2003(HY000):Can‘tconnecttoMySQLserveron‘localhost‘(10061)的解决方法》本文主要介绍了mysql出现... 目录前言:第一步:第二步:第三步:总结:前言:当你想通过命令窗口想打开mysql时候发现提http://www.cpp

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE