您的当前位置:首页正文

lucene2.0学习文档

2024-11-07 来源:个人技术集锦

文章来源:

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

Field( name, byte[] value, store)
( name, reader)
Field( 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

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

 

 



没有这种用法

 

 

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"));
        } 
} 

它的运行结果为:

 

 

 

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的内容还有很多,这里只是介绍了一部分,其它的可以看帮助文档来学习。

 

 

转自:

 

另:(关于开源框架的一些blog,有些很不错)

Top