博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Lucene2.0简介
阅读量:7094 次
发布时间:2019-06-28

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

hot3.png

 Lucene是apache组织的一个用java实现全文搜索引擎的开源项目。其功能非常的强大,api也很简单。总得来说用Lucene来进行建立和搜索与操作数据库是差不多的,Document可以看作是数据库的一行记录,Field可以看作是数据库的字段。用lucene实现搜索引擎就像用JDBC实现连接数据库一样简单。

     值得一提的是:200661Lucene2.0发布,它与以前广泛应用和介绍的Lucene 1.4.3 并不兼容。 有了很大的改进和优化,这里只介绍的是Lucene 2.0

目前最新的版本是在2007年2月17号发布的Lucene2.1.0由于普遍用户使用的是2.0版本所以这里只介绍2.0

Lucene2.0的下载地址是 http://apache.justdn.org/lucene/java/
大家先看一个例子,通过这个例子来对lucene的一个大概的认识。 一个Junit测试用例:(为了让代码清晰好看,我们将异常都抛出)
a)    这是一个建立文件索引的例子

public void testIndexHello() throws IOException{         Date date1 = new Date();         //可以说是创建一个新的写入工具         //第一个参数是要索引建立在哪个目录里         //第二个参数是新建一个文本分析器,这里用的是标准的大家也可以自己写一个         //第三个参数如果是true,在建立索引之前先将c:\\index目录清空。         IndexWriter writer = new IndexWriter("c:\\index",new StandardAnalyzer(),true);         //这个是数据源的文件夹         File file = new File("c:\\file");         /**          * 例子主要是对C:\\file目录下的文件的内容建立索引,将文件路径作为搜索内容的附属         */         if(file.isDirectory()){          String[] fileList = file.list();           for (int i = 0; i < fileList.length; i++){               //建立一个新的文档,它可以看作是数据库的一行记录                 Document doc = new Document();                 File f = new File(file, fileList[i]);                 Reader reader = new BufferedReader(new FileReader(f));                 doc.add(new Field("file",reader));//为doument添加field                 doc.add(new Field("path",f.getAbsolutePath(),Field.Store.YES,Field.Index.NO));                 writer.addDocument(doc);           }         }         writer.close();//这一步是必须的,只有这样数据才会被写入索引的目录里         Date date2 = new Date();         System.out.println("用时"+(date2.getTime()-date1.getTime())+"毫秒"); }

注意:因为建立索引本来就是费时,所以说最后输出的用时会比较长,请不要奇怪。

b)一个通过索引来全文检索的例子

public void HelloSearch() throws IOException, ParseException{         //和上面的IndexWriter一样是一个工具         IndexSearcher indexSearcher = new IndexSearcher("c:\\index");        QueryParser queryParser = new QueryParser("file",new StandardAnalyzer());          //new StandardAnalyzer()这是一个分词器         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));         //这个地方Query是抽象类大家也注意一下,下面会讲到的        Query query = queryParser.parse(br.readLine());        Hits hits = indexSearcher.search(query);         Document doc = null;         System.out.print("正搜索................");         for (int i = 0; i < hits.length(); i++){             doc = hits.doc(i);             System.out.println("内容是:"+doc.get("file"));//注意这里输出的是什么             System.out.println("文件的路径是:" + doc.get("path"));         }     } 通过上面的两个例子应该可以看出Lucene还是比较简单的。 运行一下上面的两个例子,大家可能会说怎么doc.get(“file”);返回的是空呢,我们马上会讲到。      其实从上面的例子就可以看出建立索引就用到Document,IndexWriter,Field。 最简单的步骤就是:      首先分别new 一个Document,IndexWriter,Field,然后用Doument.add()方法加入Field.其次用IndexWrtier.addDocument() 方法加入Document。 最后调用一下IndexWriter.close()方法关闭输入索引,这一步非常的重要只有调用这个方法索引才会被 写入索引的目录里,而这是被很多初学的人所忽略的。 Document没有什么好介绍的,把它的作用看成数据库中的一行记录就行。  Field是一个比较重要的也是比较复杂的,看一下它的构造函数有5个: ( name, byte[] value,  store) ( name,  reader) ( name,  reader,  termVector) Field (String name, String value, Field.Store store, Field.Index index) Field (String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector) 在Field中有三个内部类:Field.Index,Field.Store,Field.termVector,而构造函数也用到了它们。 注意:termVector是Lucene 1.4新增的,它提供一种向量机制来进行模糊查询,这个不常用。它们的不同的组合,在全文检索 中有着不同的作用。看看下面的表吧:

Field.Index

 

 

Field.Store

 

 

说明

 

 

(分词)

 

 

 

 

被分词索引且存储

 

 

 

 

 

 

被分词索引但不存储

 

 

 

 

 

 

这是不能被搜索的,它只是被搜索内容的附属物。如URL等

 

 

 

 

YES/NO

 

 

不被分词,它作为一个整体被搜索,搜一部分是搜不出来的

 

 

 

 

 

 

没有这种用法

 

 

而对于 ( name, reader)

Field ( name, reader, termVector)
       他们是Field.Index.TOKENIZED和Field.Store.NO的。这就是为什么我们在上面的例子中会出现文章的内容为 null了。因为它只是被索引了,而并没有被存储下来。如果一定要看到文章的内容的话可以通过文章的路径得到。毕竟文章的路径是作为搜索的附属物被搜索出来了。而我们在Web开发的时候一般是将大数据放在数据库中,不会放在文件系统中,更不会放在索引目录里,因为它太大了操作会加大服务器的负担

下面介绍一下IndexWriter:

它就是一个写入索引的写入器,它的任务比较简单:
1.用addDocument()将已经准备好写入索引的document们加入
2.调用close()将索引写入索引目录
先看一下它的构造函数:
( d, a, boolean create)
( path, a, boolean create)
( path, a, boolean create)

可见构造它需要一个索引文件目录,一个分析器(一般用标准的这个),最后一个参数是标识是否清空索引目录

它有一些设置参数的功能如:设置Field的最大长度
看个例子:

public void IndexMaxField() throws IOException {         IndexWriter indexWriter= new IndexWriter("c:\\index",new StandardAnalyzer(),true);         Document doc1 = new Document();         doc1.add(new Field("name1","程序员之家",Field.Store.YES,Field.Index.TOKENIZED));         Document doc2 = new Document();        doc2.add(new Field("name2","Welcome to the Home of                                programers",Field.Store.YES,Field.Index.TOKENIZED));         indexWriter.setMaxFieldLength(5);         indexWriter.addDocument(doc1);        indexWriter.setMaxFieldLength(3);         indexWriter.addDocument(doc1);         indexWriter.setMaxFieldLength(0);         indexWriter.addDocument(doc2);         indexWriter.setMaxFieldLength(3);         indexWriter.addDocument(doc2);         indexWriter.close(); } public void SearcherMaxField() throws ParseException, IOException {         Query query = null;         Hits hits = null;         IndexSearcher indexSearcher= null;         QueryParser queryParser= null;         queryParser = new QueryParser("name1",new StandardAnalyzer());         query = queryParser.parse("程序员");         indexSearcher= new IndexSearcher("c:\\index");         hits = indexSearcher.search(query);         System.out.println("您搜的是:程序员");         System.out.println("找到了"+hits.length()+"个结果");         System.out.println("它们分别是:");         for (int i = 0; i < hits.length(); i++){             Document doc = hits.doc(i);             System.out.println(doc.get("name1"));         }       query = queryParser.parse("程序员之家");        indexSearcher= new IndexSearcher("c:\\index");        hits = indexSearcher.search(query);        System.out.println("您搜的是:程序员之家");        System.out.println("找到了"+hits.length()+"个结果");         System.out.println("它们分别是:");         for (int i = 0; i < hits.length(); i++){             Document doc = hits.doc(i);            System.out.println(doc.get("name1"));         }        queryParser = new QueryParser("name2",new StandardAnalyzer());        query = queryParser.parse("Welcome");        indexSearcher= new IndexSearcher("c:\\index");        hits = indexSearcher.search(query);        System.out.println("您搜的是:Welcome");        System.out.println("找到了"+hits.length()+"个结果");        System.out.println("它们分别是:");        for (int i = 0; i < hits.length(); i++){             Document doc = hits.doc(i);             System.out.println(doc.get("name2"));         }         query = queryParser.parse("the");        indexSearcher= new IndexSearcher("c:\\index");        hits = indexSearcher.search(query);        System.out.println("您搜的是:the");        System.out.println("找到了"+hits.length()+"个结果");        System.out.println("它们分别是:");        for (int i = 0; i < hits.length(); i++){             Document doc = hits.doc(i);             System.out.println(doc.get("name2"));         }        query = queryParser.parse("home");         indexSearcher= new IndexSearcher("c:\\index");         hits = indexSearcher.search(query);         System.out.println("您搜的是:home");         System.out.println("找到了"+hits.length()+"个结果");         System.out.println("它们分别是:");         for (int i = 0; i < hits.length(); i++) {             Document doc = hits.doc(i);             System.out.println(doc.get("name2"));        } } 它的运行结果为:

 

总结一下:

1.设置Field的长度限制只是限制了搜索。如果用了Field.Store.YES的话还是会
全部被保存进索引目录里的。
2.为什么搜the没有搜出来呢?是因为lucene分析英文的时候不会搜索the to of 等无用的词(搜这些词是无意义的)。
3.New StandardAnlayzer()对于英文的分词是按空格和一些无用的词,而中文呢是全部的单个的字。
4.设置Field的最大长度是以0开头和数组一样。
大家还可以试一下别的,以便加深一下印象

 

 

到现在我们已经可以用lucene建立索引了

下面介绍一下几个功能来完善一下:
1.索引格式
      其实索引目录有两种格式,一种是除配置文件外,每一个Document独立成为一个文件(这种搜索起来会影响速度)。另一种是全部Document成一个文件,这样属于复合模式就快了。

2.索引文件可放的位置:

    索引可以存放在两个地方1.硬盘,2.内存。放在硬盘上可以用FSDirectory(),放在内存的用RAMDirectory()不过一关机就没了。
FSDirectory. ( file, boolean create)
FSDirectory.( path, boolean create)两个工厂方法返回目录
New RAMDirectory() 就直接可以,再和( d, a, boolean create) 一配合就行了
如:
IndexWrtier indexWriter = new IndexWriter(FSDirectory.getDirectory(“c:\\index”,true),new StandardAnlyazer(),true);
IndexWrtier indexWriter = new IndexWriter(new RAMDirectory(),new StandardAnlyazer(),true);

3.索引的合并

    这个可用IndexWriter.([] dirs) 将目录加进去
来看个例子:

public void UniteIndex() throws IOException{        IndexWriter writerDisk = new IndexWriter(FSDirectory.getDirectory("c:\\indexDisk",           true),new StandardAnalyzer(),true);         Document docDisk = new Document();         docDisk.add(new Field("name","程序员之家",Field.Store.YES,Field.Index.TOKENIZED));         writerDisk.addDocument(docDisk);         RAMDirectory ramDir = new RAMDirectory();         IndexWriter writerRam = new IndexWriter(ramDir,new StandardAnalyzer(),true);         Document docRam = new Document();         docRam.add(new Field("name","程序员杂志",Field.Store.YES,Field.Index.TOKENIZED));         writerRam.addDocument(docRam);         writerRam.close();//这个方法非常重要,是必须调用的         writerDisk.addIndexes(new Directory[]{ramDir});        writerDisk.close();     }     public void UniteSearch() throws ParseException, IOException{         QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());         Query query = queryParser.parse("程序员");         IndexSearcher indexSearcher =new IndexSearcher("c:\\indexDisk");         Hits hits = indexSearcher.search(query);        System.out.println("找到了"+hits.length()+"结果");         for(int i=0;i< hits.length();i++){            Document doc = hits.doc(i);            System.out.println(doc.get("name"));          }    } 这个例子是将内存中的索引合并到硬盘上来. 注意:合并的时候一定要将被合并的那一方的IndexWriter的close()方法调用。 4.对索引的其它操作: IndexReader类是用来操作索引的,它有对Document,Field的删除等操作。 下面一部分的内容是:全文的搜索      全文的搜索主要是用:IndexSearcher,Query,Hits,Document(都是Query的子类),有的时候用QueryParser 主要步骤:   1.new QueryParser(Field字段,new 分析器)   2.Query query = QueryParser.parser(“要查询的字串”);这个地方我们可以用反射api看一下query究竟是什么类型   3.new IndexSearcher(索引目录).search(query);返回Hits   4.用Hits.doc(n);可以遍历出Document   5.用Document可得到Field的具体信息了。 其实1 ,2两步就是为了弄出个Query 实例,究竟是什么类型的看分析器了。  拿以前的例子来说吧     QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());     Query query = queryParser.parse("程序员"); //这里返回的就是org.apache.lucene.search.PhraseQuery     IndexSearcher indexSearcher =new IndexSearcher("c:\\indexDisk");     Hits hits = indexSearcher.search(query);      不管是什么类型,无非返回的就是Query的子类,我们完全可以不用这两步直接new个Query的子类的实例就ok了, 不过一般还是用这两步因为它返回的是PhraseQuery这个是非常强大的query子类,它可以进行多字搜索。用QueryParser可以 设置各个关键字之间的关系这个是最常用的了。 IndexSearcher:    其实IndexSearcher它内部自带了一个IndexReader用来读取索引的,IndexSearcher有个close()方法,这个方法不是用 来关闭IndexSearcher的是用来关闭自带的IndexReader。 QueryParser呢可以用parser.setOperator()来设置各个关键字之间的关系,它可以自动通过空格从字串里面将关键字分离出来。      注意:用QueryParser搜索的时候分析器一定的和建立索引时候用的分析器是一样的。  Query:  可以看一个lucene2.0的帮助文档有很多的子类: , , , , , , , , , , , ,    各自有用法看一下文档就能知道它们的用法了 下面一部分讲一下lucene的分析器:    分析器是由分词器和过滤器组成的,拿英文来说吧分词器就是通过空格把单词分开,过滤器就是把the,to,of等词去掉不被搜索和索引。    我们最常用的是StandardAnalyzer()它是lucene的标准分析器它集成了内部的许多的分析器。 最后一部分了:lucene的高级搜索了   1.排序    Lucene有内置的排序用IndexSearcher.search(query,sort)但是功能并不理想。我们需要自己实现自定义的排序。    这样的话得实现两个接口: ScoreDocComparator, SortComparatorSource              用IndexSearcher.search(query,new Sort(new SortField(String Field,SortComparatorSource)));

就看个例子吧:

这是一个建立索引的例子:

 

public void IndexSort() throws IOException {       IndexWriter writer = new IndexWriter("C:\\indexStore",new StandardAnalyzer(),true);       Document doc = new Document();       doc.add(new Field("sort","1",Field.Store.YES,Field.Index.TOKENIZED));       writer.addDocument(doc);       doc = new Document();       doc.add(new Field("sort","4",Field.Store.YES,Field.Index.TOKENIZED));       writer.addDocument(doc);       doc = new Document();       doc.add(new Field("sort","3",Field.Store.YES,Field.Index.TOKENIZED));       writer.addDocument(doc);       doc = new Document();       doc.add(new Field("sort","5",Field.Store.YES,Field.Index.TOKENIZED));       writer.addDocument(doc);       doc = new Document();       doc.add(new Field("sort","9",Field.Store.YES,Field.Index.TOKENIZED));       writer.addDocument(doc);       doc = new Document();       doc.add(new Field("sort","6",Field.Store.YES,Field.Index.TOKENIZED));       writer.addDocument(doc);       doc = new Document();       doc.add(new Field("sort","7",Field.Store.YES,Field.Index.TOKENIZED));        writer.addDocument(doc);         writer.close(); } 下面是搜索的例子: public void SearchSort1() throws IOException, ParseException {         IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");        QueryParser queryParser = new QueryParser("sort",new StandardAnalyzer());         Query query = queryParser.parse("4");        Hits hits = indexSearcher.search(query);         System.out.println("有"+hits.length()+"个结果");         Document doc = hits.doc(0);         System.out.println(doc.get("sort")); } public void SearchSort2() throws IOException, ParseException {         IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");         Query query = new RangeQuery(new Term("sort","1"),new Term("sort","9"),true);        //这个地方前面没有提到,它是用于范围的Query可以看一下帮助文档.         Hits hits = indexSearcher.search(query,new Sort(new SortField("sort",new MySortComparatorSource())));         System.out.println("有"+hits.length()+"个结果");         for(int i=0;i< hits.length();i++){             Document doc = hits.doc(i);             System.out.println(doc.get("sort"));         } } public class MyScoreDocComparator implements ScoreDocComparator {    private Integer[]sort;    public MyScoreDocComparator(String s,IndexReader reader, String fieldname)      throws IOException{        sort = new Integer[reader.maxDoc()];        for(int i = 0;i< reader.maxDoc();i++){             Document doc =reader.document(i);             sort[i]=new Integer(doc.get("sort"));        }     }     public int compare(ScoreDoc i, ScoreDoc j){        if(sort[i.doc]>sort[j.doc])             return 1;        if(sort[i.doc]< sort[j.doc])            return -1;        return 0;     }     public int sortType(){        return SortField.INT;     }     public Comparable sortValue(ScoreDoc i){      // TODO 自动生成方法存根      return new Integer(sort[i.doc]);     } } public class MySortComparatorSource implements SortComparatorSource {    private static final long serialVersionUID = -9189690812107968361L;    public ScoreDocComparator newComparator(IndexReader reader, String fieldname)       throws IOException{         if(fieldname.equals("sort"))             return new MyScoreDocComparator("sort",reader,fieldname);             return null;          } } SearchSort1()输出的结果没有排序,SearchSort2()就排序了。

2.多域搜索 MultiFieldQueryParser

1.如果想输入关键字而不想关心是在哪个Field里的就可以用MultiFieldQueryParser了。
用它的构造函数即可后面的和一个Field一样。
MultiFieldQueryParser. ([] queries, [] fields, [] flags, analyzer)
                                         

第三个参数比较特殊这里也是与以前lucene 1.4.3 不一样的地方,看一个例子就知道了。

String[] fields = {"filename", "contents", "description"};
BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
BooleanClause.Occur.MUST,//在这个Field里必须出现的
BooleanClause.Occur.MUST_NOT};//在这个Field里不能出现
MultiFieldQueryParser.parse("query", fields, flags, analyzer);

2.多索引搜索 MultiSearcher

在构造的时候传进去一个Searcher数组即可
3.过滤器Filter
看个例子:

public void FilterTest() throws IOException, ParseException {         IndexWriter indexWriter = new IndexWriter("C:\\FilterTest",new StandardAnalyzer(),true);         Document doc = new Document();         doc.add(new Field("name","程序员之家",Field.Store.YES,Field.Index.TOKENIZED));         indexWriter.addDocument(doc);         doc=new Document();         doc.add(new Field("name","程序员杂志",Field.Store.YES,Field.Index.TOKENIZED));         indexWriter.addDocument(doc);         indexWriter.close();         Query query = null;         Hits hits = null;         IndexSearcher indexSearcher = new IndexSearcher("C:\\FilterTest");         QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());         query = queryParser.parse("程序");         hits = indexSearcher.search(query,new Filter(){             @Override             public BitSet bits(IndexReader reader) throws IOException{                BitSet bit = new BitSet(reader.maxDoc());               for(int i=0;i< reader.maxDoc();i++){                     if(reader.document(i).get("name").enth("杂志"))//将以“杂志”后缀的过滤掉                         continue;                         bit.set(i);ks                      }                return bit;            }         });         System.out.println(hits.length());         for(int i=0;i< hits.length();i++){             doc =hits.doc(i);             System.out.println(doc.get("name"));         } } 这只是一个入门的文档Lucene 2.0的内容还有很多,这里只是介绍了一部分,其它的可以看帮助文档来学习。
原文链接:

转载于:https://my.oschina.net/moroseyu/blog/51477

你可能感兴趣的文章
烂泥:通过binlog恢复mysql备份之前的数据
查看>>
spring retry重试机制原理解析(手写原理)
查看>>
编译安装web服务
查看>>
PHP: CodeIgniter3中函数名不能与控制器名相同的问题
查看>>
SSDCRM紧急发布一个安全漏洞补丁
查看>>
如何看懂网站日志(apache日志)
查看>>
HTTP协议:签名和证书
查看>>
C#递归生成菜单树
查看>>
8.1.3 链路状态数据库
查看>>
I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)
查看>>
华章1-2月份新书简介(2018年)
查看>>
PreparedStatement的用法
查看>>
For多重循环 break continue
查看>>
BaseRecyclerViewAdapterHelper开源项目之点击事件源码学习
查看>>
JAVA解析JSON大全
查看>>
Java基础学习总结(23)——GUI编程
查看>>
让App的运行速度与响应速度趋于一流(iOS)
查看>>
大型网站技术架构(八)网站的安全架构
查看>>
Java基础学习总结(16)——Java制作证书的工具keytool用法总结
查看>>
学习Redis必须了解的N个常识
查看>>