博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Lucene7.1.0版本的索引创建与查询以及维护
阅读量:4110 次
发布时间:2019-05-25

本文共 13353 字,大约阅读时间需要 44 分钟。

今日在学习Lucene是遇到了一些因版本更新出现的问题,通过学习别人的博客将问题解决,现分享一下。

lucene的创建索引,查询索引,搜索排序,通过权值查询,以及适配新版本的luke查询器,IK中文分词,高亮显示等最基本的使用!

maven:

org.apache.lucene
lucene-core
7.1.0
org.apache.lucene
lucene-analyzers-common
7.1.0
org.apache.lucene
lucene-queryparser
7.1.0
org.apache.lucene
lucene-highlighter
7.1.0
org.apache.lucene
lucene-analyzers-smartcn
7.1.0
commons-io
commons-io
2.4

创建索引:

IndexWriter writer=null;// 1 指定索引库存放路径// 硬盘路径try {    //好像是从5.x版本中打开目录就必须用Path类了,方法如下,不能像4.x版本之前的直接写路径    File indexrepository_file = new File("D:\\others\\lucene\\index");    Path path = indexrepository_file.toPath();    Directory directory=null;    directory = FSDirectory.open(path);//使用path    // 索引建立在内存中    //Directory directory01=new RAMDirectory();    // 2 创建writer    //指定一个分词器    Analyzer analyzer=new IKAnalyzer();//使用IK,需要用特定的高版本适配版    IndexWriterConfig config=new IndexWriterConfig(analyzer);    writer=new IndexWriter(directory,config);    // 3 创建document对象,并添加Filed域属性    Document doc=null;    File f=new File("D:\\others\\lucene\\doc");//文档目录    for(File file : f.listFiles()){//遍历每个文档        doc=new Document();        /**        * 新版本中使用了Int/Long/DoublePoint来表示数值型字段,但是默认不存储,不排序,也不支持加权        * 创建索引加权值在6.6版本后就已经废除了,并给了搜索时设置的新query,这个后面查询时再说        * 如果存储需要用StoredField写相同的字段,排序还要再使用NumericDocValuesField写相同的排序,        * 如下的fileSize,添加long值索引,存储并添加排序支持        /        //文件名        doc.add(new TextField("fileName", file.getName(), Store.YES););        //大小,数字类型使用point添加到索引中,同时如果需要存储,由于没有Stroe,所以需要再创建一个StoredField进行存储        // 即 IntPoint,DoublePoint等        doc.add(new LongPoint("fileSize", file.length()));        //大小        doc.add(new StoredField("fileSize", file.length()));        //同时添加排序支持        doc.add(new NumericDocValuesField("fileSize",file.length()));        //路径        doc.add(new StoredField("filePath",file.getPath()));        //内容        doc.add(new TextField("fileContent",FileUtils.readFileToString(file),Store.NO));        //doc.add(new TextField("fileContent",new FileReader(file)));        // 4 使用indexWriter将doc对象写入索引库,此过程创建索引,并将索引和文档对象写入索引库        writer.addDocument(doc);    }    writer.close();    } catch (IOException e) {    e.printStackTrace();}

7.1.0版本中数值型的都使用Point了,所以Long被废弃,

使用luke查询索引:

luke是一个查询索引的工具,使用时必须注意:版本要与lucene的版本完全一致,否则可能打不开索引信息

索引查询

这里先举个简单例子,使用内容关键字查询,通用使用Path类打开索引目录,其他区别不是很大:

try {    //1 创建Directory对象,索引存放位置    File indexrepository_file = new File("D:\\others\\lucene\\index");    Path path = indexrepository_file.toPath();    Directory directory = FSDirectory.open(path);    //2 创建IndexReader    IndexReader indexReader = DirectoryReader.open(directory);    //3 创建IndexSearch对象    IndexSearcher indexSearch=new IndexSearcher(indexReader);    //4 创建TermQuery对象,指定查询的域和关键字    Query query=new TermQuery(new Term("fileContent","姚振"));    //5 查询    TopDocs topDocs = indexSearch.search(query, 5);//前5个    //6 遍历结果    ScoreDoc[] scoreDocs = topDocs.scoreDocs;//文档id数组    for (ScoreDoc scoreDoc : scoreDocs) {        //根据id获取文档        Document doc = indexSearch.doc(scoreDoc.doc);        //获取结果,没有存储的是null,比如内容        System.out.println("文档名: "+doc.get("fileName"));        System.out.println("文档路径: "+doc.get("filePath"));        System.out.println("文档大小: "+doc.get("fileSize"));        System.out.println("文档内容: "+doc.get("fileContent"));        System.out.println("-------------------");    }    //7 关闭reader    indexReader.close();} catch (IOException e) {    e.printStackTrace();}

多种查询

查询主要有两种,一种是Query的子类查询,一种是语法解析查询,这里先说子类查询:

可以先写一个获取search的公共方法,以及遍历打印搜索结果的方法,之后就可以对各种查询进行方便测试了:

//获取IndexWriterpublic IndexWriter getIndexWriter() throws Exception{    File indexrepository_file = new File("D:\\others\\lucene\\index");    Path path = indexrepository_file.toPath();    Directory directory=null;    directory = FSDirectory.open(path);    Analyzer  analyzer=new IKAnalyzer();//使用IK    IndexWriterConfig config=new IndexWriterConfig(analyzer);    return new IndexWriter(directory,config);}
//获取IndexSearch    public IndexSearcher getIndexSearcher() throws Exception{        return new IndexSearcher(getIndexReader());    }
//执行查询并打印结果public void printResult(IndexSearcher indexSearcher,Query query,Integer num) throws Exception{     //使用排序     Sort sort  =new Sort();     SortField f  =new SortField("fileSize",Type.LONG,true); // 按照fileSize字段排序,true表示降序     sort.setSort(f);     // 多个条件排序     // Sort sort = new Sort();     // SortField f1 = new SortField("createdate", SortField.DOC, true);     // SortField f2 = new SortField("bookname", SortFiedl.INT, false);     // sort.setSort(new SortField[] { f1, f2 });     //高亮显示start      //算分      QueryScorer scorer=new QueryScorer(query);      //显示得分高的片段      Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);      //设置标签内部关键字的颜色      //第一个参数:标签的前半部分;第二个参数:标签的后半部分。      SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("","");             //第一个参数是对查到的结果进行实例化;第二个是片段得分(显示得分高的片段,即摘要)      Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);      //设置片段      highlighter.setTextFragmenter(fragmenter);      //高亮显示end    TopDocs topDocs = indexSearcher.search(query, num,sort);    ScoreDoc[] scoreDocs = topDocs.scoreDocs;//文档id数组    for (ScoreDoc scoreDoc : scoreDocs) {        //根据id获取文档        Document doc = indexSearcher.doc(scoreDoc.doc);        String name = doc.get("fileName");        if(name!=null){            //把全部得分高的摘要给显示出来             //第一个参数是对哪个参数进行设置;第二个是以流的方式读入                      TokenStream tokenStream=new IKAnalyzer().tokenStream("fileName", new StringReader(name));            //获取最高的片段             System.out.println("高亮文档名: "+highlighter.getBestFragment(tokenStream, name));                      }        //获取结果,没有存储的是null,比如内容        System.out.println("文档名: "+doc.get("fileName"));        System.out.println("文档路径: "+doc.get("filePath"));        System.out.println("文档大小: "+doc.get("fileSize"));        System.out.println("文档内容: "+doc.get("fileContent"));        System.out.println("-------------------");    }       }

Query子查询

//查询@Testpublic void testSelect01() throws Exception{    IndexSearcher indexSearcher = getIndexSearcher();    //数字范围查询, 两边都是闭区间   43< index  <103    //新版本中数值都使用Point进行查询,原理的Numic被废弃    //Query query=LongPoint.newRangeQuery("fileSize", 43L, 103L);    //数值精确匹配,只会查找参数里的数值索引   index  in   param    List
list=new ArrayList
(); list.add(43L); list.add(103L); Query query2=LongPoint.newSetQuery("fileSize", 43L,100L);//不定参数 Query query1=LongPoint.newSetQuery("fileSize", list); //集合参数 printResult(indexSearcher,query1,10); //关闭reader indexSearcher.getIndexReader().close();}/** * TermRangeQuery是用于字符串范围查询的,既然涉及到范围必然需要字符串比较大小, * 字符串比较大小其实比较的是ASC码值,即ASC码范围查询。 * 一般对于英文来说,进行ASC码范围查询还有那么一点意义, * 中文汉字进行ASC码值比较没什么太大意义,所以这个TermRangeQuery了解一下就行 */@Testpublic void testSelect02() throws Exception{ String lowerTermString = "侯征";//范围的下端的文字,后面boolean为真,对应值为闭区间 String upperTermString = "词语";//范围的上限内的文本,后面boolean为真,对应值为闭区间 IndexSearcher indexSearcher = getIndexSearcher(); //lucene 使用 BytesRef 在索引中表示utf-8编码的字符,此类含有偏移量_长度以及byte数组,可使用utf8toString API转换字符串 Query query=new TermRangeQuery("fileName",new BytesRef(lowerTermString),new BytesRef(upperTermString),true,true); printResult(indexSearcher,query,10); //关闭reader indexSearcher.getIndexReader().close();}/** * BooleanQuery也是实际开发过程中经常使用的一种Query。 * 它其实是一个组合的Query,在使用时可以把各种Query对象添加进去并标明它们之间的逻辑关系。 * 所有的Query都可以通过booleanQUery组合起来 * BooleanQuery本身来讲是一个布尔子句的容器,它提供了专门的API方法往其中添加子句, * 并标明它们之间的关系 */@Testpublic void testSelect03() throws Exception{ IndexSearcher indexSearcher = getIndexSearcher(); //组合条件 Query query1=new TermQuery(new Term("fileName","歌曲")); Query query2=new TermQuery(new Term("fileContent","美国")); //相当于一个包装类,将 Query 设置 Boost 值 ,然后包装起来。 //再通过复合查询语句,可以突出 Query 的优先级 BoostQuery query=new BoostQuery(query2, 2f); //创建BooleanQuery.Builder BooleanQuery.Builder builder=new BooleanQuery.Builder(); //添加逻辑 /** * 1.MUST和MUST:取得两个查询子句的交集。 and 2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。 3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。 4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。 5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。 6.MUST_NOT和MUST_NOT:无意义,检索无结果。 */ builder.add(query1, Occur.SHOULD);// 文件名不包含词语,但是内容必须包含姚振 builder.add(query, Occur.SHOULD); //build query BooleanQuery booleanQuery=builder.build(); printResult(indexSearcher,booleanQuery,10); //关闭reader indexSearcher.getIndexReader().close();}@Testpublic void testSelect() throws Exception{ IndexSearcher indexSearcher = getIndexSearcher(); //查询所有 Query queryAll=new MatchAllDocsQuery(); printResult(indexSearcher,queryAll,10); //关闭reader indexSearcher.getIndexReader().close();}@Testpublic void test05() throws Exception{ IndexSearcher indexSearcher = getIndexSearcher(); //查询文件名以新开头的索引 前缀匹配查询 Query query=new PrefixQuery(new Term("fileName","新")); System.out.println(query); printResult(indexSearcher,query,10); //关闭reader indexSearcher.getIndexReader().close();}/** * PhraseQuery,是指通过短语来检索,比如我想查“姚振 牛逼”这个短语, * 那么如果待匹配的document的指定项里包含了"姚振 牛逼"这个短语, * 这个document就算匹配成功。可如果待匹配的句子里包含的是“姚振 真他妈 牛逼”, * 那么就无法匹配成功了,如果也想让这个匹配,就需要设定slop, * 先给出slop的概念:slop是指两个项的位置之间允许的最大间隔距离 */@Testpublic void test06() throws Exception{ IndexSearcher indexSearcher = getIndexSearcher(); Builder build = new PhraseQuery.Builder(); build.add(new Term("fileContent","白富美")); build.add(new Term("fileContent","老阴逼")); //设置slop,即最大相隔多远,即多少个文字的距离, build.setSlop(6);//表示如果这两个词语相隔6个字以下的位置就匹配 PhraseQuery phraseQuery = build.build(); printResult(indexSearcher,phraseQuery,10); //关闭reader indexSearcher.getIndexReader().close();}@Testpublic void test07() throws Exception{ IndexSearcher indexSearcher = getIndexSearcher(); //FuzzyQuery是一种模糊查询,它可以简单地识别两个相近的词语 Query query=new FuzzyQuery(new Term("fileContent","牛逼")); printResult(indexSearcher,query,10); //关闭reader indexSearcher.getIndexReader().close();}@Testpublic void test09() throws Exception{ IndexSearcher indexSearcher = getIndexSearcher(); //Lucene也提供了通配符的查询,这就是WildcardQuery。 // 通配符“?”代表1个字符,而“*”则代表0至多个字符。 Query query=new WildcardQuery(new Term("fileName","?词语")); //名字以词语结尾 Query query1=new WildcardQuery(new Term("fileName","新*")); //名字以新开头 printResult(indexSearcher,query1,10); //关闭reader indexSearcher.getIndexReader().close();}

QueryParser条件解析查询

@Testpublic void test08() throws Exception{    /**     * 解析查询表达式     * QueryParser实际上就是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象     */    IndexSearcher indexSearcher = getIndexSearcher();    Query query=new TermQuery(new Term("fileName","词语"));    //  参数:  默认域  分词解析器    QueryParser queryParser = new QueryParser("fileContent", new IKAnalyzer());    //解析 ,如果不指定域,使用默认域  使用语法书写    Query parse = queryParser.parse("侯征 姚振 何毅");    Query parse1 = queryParser.parse("fileName:侯征 姚振 何毅");//指定域    Query parse2 = queryParser.parse("fileName:侯*");//匹配    Query parse3 = queryParser.parse("+fileName:游戏 fileName:新词语");//匹配    printResult(indexSearcher,parse3,10);    //关闭reader    indexSearcher.getIndexReader().close();}@Testpublic void test10() throws Exception{    /**     * 解析查询表达式     * MultiFieldQueryParser支持多默认域     */    IndexSearcher indexSearcher = getIndexSearcher();    // 指定多默认域数组    String[] arr=new String[]{"fileName","fileContent"};    //搜索时设置权重    Map
boosts = new HashMap
(); boosts.put("fileContent", 10.0f);//权重默认是1, 文件名字符合条件的排序在前面 MultiFieldQueryParser queryParser = new MultiFieldQueryParser(arr, new IKAnalyzer(),boosts);//指定搜索权重 //解析 ,如果不指定域,使用默认域 使用语法书写 Query parse = queryParser.parse("游戏");//查询所有默认域里有姚振的文档 printResult(indexSearcher,parse,10); //关闭reader indexSearcher.getIndexReader().close();}

还有一点,就是加权了,查询时设置权重,6.6版本后创建索引设置权重值已经被废弃,并提供了两种新的加权方式就是BoostsQuery 和语法查询时多默认域的扩展可以使用,上面例子里有,可自行选择。

索引维护

最后就是索引的更新,删除,恢复了,这个没多大区别,主要是创建,查询时候有恨多不一样的地方,还有一点就是文档的编码一定要对,否则会索引不到。

//按条件删除@Testpublic void testDelete() throws Exception{    IndexWriter indexWriter = getIndexWriter();    Query query=new TermQuery(new Term("fileContent","老阴逼"));    indexWriter.deleteDocuments(query);//可传入多个参数    indexWriter.close();}//全删除@Testpublic void testDeleteAll() throws Exception{    IndexWriter indexWriter = getIndexWriter();    indexWriter.deleteAll();    //强制删除,不会恢复    //indexWriter.forceMergeDeletes();    indexWriter.close();}//更新@Testpublic void testUpdate() throws Exception{    IndexWriter indexWriter = getIndexWriter();    Document doc =new Document();    doc.add(new TextField("","",Store.YES));    // .... 修改的内容    indexWriter.updateDocument(new Term("fileName","词语"), doc);    indexWriter.close();} //恢复,从回收站恢复@Testpublic void testDELETE() throws Exception{    IndexWriter indexWriter = getIndexWriter();    indexWriter.rollback();}

转自:

转载地址:http://icosi.baihongyu.com/

你可能感兴趣的文章
C#中ColorDialog需点两次确定才会退出的问题
查看>>
16、Memento 备忘录模式
查看>>
Java基础篇(一)
查看>>
数据库
查看>>
nginx反代 499 502 bad gateway 和timeout
查看>>
linux虚拟机安装tar.gz版jdk步骤详解
查看>>
python猜拳游戏
查看>>
python实现100以内自然数之和,偶数之和
查看>>
python数字逆序输出及多个print输出在同一行
查看>>
ESP8266 WIFI数传 Pixhaw折腾笔记
查看>>
苏宁产品经理面经
查看>>
百度产品经理群面
查看>>
去哪儿一面+平安科技二面+hr面+贝贝一面+二面产品面经
查看>>
element ui 弹窗在IE11中关闭时闪现问题修复
查看>>
vue 遍历对象并动态绑定在下拉列表中
查看>>
Vue动态生成el-checkbox点击无法选中的解决方法
查看>>
python __future__
查看>>
MySQL Tricks1
查看>>
python 变量作用域问题(经典坑)
查看>>
pytorch
查看>>