天天看点

新一篇: lucene学习----创建索引 |

Lucene 学习笔记 一、环境 需要导入 lucene.jar 包(在 lucene.apache.org 下载) 二、基本概念 1 . Lucene 的工作流程: (1)  使用 IndexWriter ,在指定的目录建立索引的文件

  (2)  将需要检索的数据转换位 Document 的 Filed 对象,然后将 Document 用 IndexWriter 添加倒索引的文件中

   (3)  处理索引信息,关闭 IndexWriter 流

   (4)  创建搜索的 Query

   (5)  给 IndexSearcher 2 . Lucene 的字段类型 Lucene 有四种不同的字段类型: Keyword , UnIndexed , UnStored 和 Text ,用于指定建立最佳索引。

?        Keyword 字段是指不需要分析器 解析但需要被编入索引并保存到索引中的部分。 JavaSourceCodeIndexer 类使用该字段来保存导入类的声明。

?        UnIndexed 字段是既不被分析也不被索引,但是要被逐字逐句的将其值保存到索引中。由于我们一般要存储文件的位置但又很少用文件名作为关键字来搜索,所以用该字段来索引 Java 文件名。

?        UnStored 字段和 UnIndexed 字段相反。该类型的 Field 要被分析并编入索引,但其值不会被保存到索引中。由于存储方法的全部源代码需要大量的空间。所以用 UnStored 字段来存储被索引的方法源代码。可以直接从 Java 源文件中取出方法的源代码,这样作可以控制我们的索引的大小。

?        Text 字段在索引过程中是要被分析、索引并保存的。类名是作为 Text 字段来保存。下表展示了 JavaSourceCodeIndexer 类使用 Field 字段的一般情况。   3 .基本概念(与传统表的对比):

Lucene 传统表 说明
IndexWriter table
Document 一条记录
Field 每个字段 分为可被索引的,可切分的,不可被切分的,不可被索引的几种组合类型
Hits RecoreSet 结果集

  IndexWriter 提供了一些参数可供设置,列表如下

属性 默认值 说明
mergeFactor org.apache.lucene.mergeFactor 10 控制 index 的大小和频率 , 两个作用 1. 一个段有多少 document 2. 多少个段合成一个大段
maxMergeDocs org.apache.lucene.maxMergeDocs Integer.MAX_VALUE 限制一个段中的 document 数目
minMergeDocs org.apache.lucene.minMergeDocs 10 缓存在内存中的 document 数目,超过他以后会写入到磁盘
maxFieldLength 1000 一个 Field 中最大 Term 数目,超过部分忽略,不会 index 到 field 中,所以自然也就搜索不到

这些参数的的详细说明比较复杂: mergeFactor 有双重作用 (1) 设置每 mergeFactor 个 document 写入一个段,比如每 10 个 document 写入一个段 (2) 设置每 mergeFacotr 个小段合并到一个大段,比如 10 个 document 的时候合并为 1 小段,以后有 10 个小段以后合并到一个大段,有 10 个大段以后再合并,实际的 document 数目会是 mergeFactor 的指数 简单的来说 mergeFactor 越大,系统会用更多的内存,更少磁盘处理,如果要打批量的作 index ,那么把 mergeFactor 设置大没错, mergeFactor 小了以后, index 数目也会增多, searhing 的效率会降低, 但是 mergeFactor 增大一点一点,内存消耗会增大很多 ( 指数关系 ), 所以要留意不要” out of memory”

把 maxMergeDocs 设置小,可以强制让达到一定数量的 document 写为一个段,这样可以抵消部分 mergeFactor 的作用 .

minMergeDocs 相当于设置一个小的 cache, 第一个这个数目的 document 会留在内存里面,不写入磁盘。这些参数同样是没有最佳值的, 必须根据实际情况一点点调整。

maxFieldLength 可以在任何时刻设置, 设置后,接下来的 index 的 Field 会按照新的 length 截取,之前已经 index 的部分不会改变。可以设置为 Integer.MAX_VALUE   4 .几种查询方式       

查询方式 说明
TermQuery 条件查询 例如: TermQuery tquery=new TermQuery(new Term("name","jerry")); name: 字段名 jerry: 要搜索的字符串
MultiTermQuery 多个字段进行同一关键字的查询 Query query= null; Query =MultiFieldQueryParser.parse(" 我 ",new String[]
新一篇: lucene学习----创建索引 |

{"title","content"},analyzer); Searcher searcher=new IndexSearcher(indexFilePath);

 Hits hits=searcher.search(query);

BooleanQuery 例如: BooleanQuery bquery=new BooleanQuery();
新一篇: lucene学习----创建索引 |

 bquery.add(query,true,false);

   bquery.add(mquery,true,false);

   bquery.add(tquery,true,false);

   Searcher searcher=new IndexSearcher(indexFilePath);

    Hits hits=searcher.search(bquery);

WildcardQuery 语义查询(通配符查询) 例: Query query= new WildcardQuery(new Term("sender","*davy*"));
PhraseQuery 短语查询
PrefixQuery 前缀查询
PhrasePrefixQuery 短语前缀查询
FuzzyQuery 模糊查询
RangeQuery 范围查询
SpanQuery 范围查询

在全文检索时建议大家先采用语义时的搜索,先搜索出有意义的内容,之后再进行模糊之类的搜索 (1) 联合两个索引查询,已解决:

IndexSearcher[] searchers = new IndexSearcher[2]; 

searchers[0] = new IndexSearcher(m_indexpath);

searchers[1] = new IndexSearcher(m_outindexpath);

MultiSearcher multiSearcher = new MultiSearcher(searchers);

(2) 还有个进行多条件搜索 and 与 or 的操作————

用 MultiFieldQueryParser

建议重新封装

MultiFieldQueryParser.Parser(p[],d[],f[],analyer)   成 or 与 and 操作合一

或者

BooleanQuery m_BooleanQuery = new BooleanQuery();

Query query = QueryParser.Parse(m_SearchText, "INSTRUMENT_NAME", analyzer);

Query query2 = QueryParser.Parse(m_SearchText2, "INSTRUMENT_NAME2", analyzer);

m_BooleanQuery.Add(query, true, false);

m_BooleanQuery.Add(query2, true, false); (3) 复合查询(多种查询条件的综合查询) Query query=MultiFieldQueryParser.parse(" 索引 ”,new String[]

新一篇: lucene学习----创建索引 |

{"title","content"},analyzer);

Searcher searcher=new IndexSearcher(indexFilePath);

Hits hits=searcher.search(query);

for (int i = 0; i < hits.length(); i++) 

新一篇: lucene学习----创建索引 |

{

            System.out.println(hits.doc(i).get("name"));

}   5. 为查询优化索引 (index) Indexwriter.optimize() 方法可以为查询优化索引( index ),之前提到的参数调优是为 indexing 过程本身优化,而这里是为查询优化,优化主要是减少 index 文件数,这样让查询的时候少打开文件,优化过程中, lucene 会拷贝旧的 index 再合并,合并完成以后删除旧的 index ,所以在此期间,磁盘占用增加, IO 符合也会增加,在优化完成瞬间,磁盘占用会是优化前的 2 倍 , 在 optimize 过程中可以同时作 search 。       4.org.apache.lucene.document.Field

         即上文所说的“字段”,它是 Document 的片段 section 。         Field 的构造函数:        Field(String name, String string, boolean store, boolean index, boolean token) 。         Indexed :如果字段是 Indexed 的,表示这个字段是可检索的。         Stored :如果字段是 Stored 的,表示这个字段的值可以从检索结果中得到。         Tokenized :如果一个字段是 Tokenized 的,表示它是有经过 Analyzer 转变后成为一个 tokens 序列,在这个转变过程 tokenization 中,  Analyzer 提取出需要进行索引的文本,而剔除一些冗余的词句(例如: a , the,they 等,详见  org.apache.lucene.analysis.StopAnalyzer.ENGLISH_STOP_WORDS 和  org.apache.lucene.analysis.standard.StandardAnalyzer(String[] stopWords) 的 API )。 Token 是索引时候的 .  

类型 Analyzed Indexed Stored 说明
Field.Keyword(String,String/Date) N Y Y 这个 Field 用来储存会直接用来检索的比如 ( 编号 , 姓名 , 日期等 )
Field.UnIndexed(String,String) N N Y 不会用来检索的信息 , 但是检索后需要显示的 , 比如 , 硬件序列号 , 文档的 url 地址
Field.UnStored(String,String) Y Y N 大段文本内容 , 会用来检索 , 但是检索后不需要从 index 中取内容 , 可以根据 url 去 load 真实的内容
Field.Text(String,String) Y Y Y 检索 , 获取都需要的内容 , 直接放 index 中 , 不过这样会增大 index
Field.Text(String,Reader) Y Y N 如果是一个 Reader, lucene 猜测内容比较多 , 会采用 Unstored 的策略 .

    5.Lucene  的检索结果排序         Lucene 的排序主要是对 org.apache.lucene.search.Sort 的使用。 Sort 可以直接根据字段 Field 生成,也可以根据标准的 SortField 生成,但是作为 Sort 的字段,必须符合以下的条件:唯一值以及 Indexed 。可以对 Integers, Floats, Strings 三种类型排序。

         对整数型的 ID 检索结果排序只要进行以下的简单操作: Sort sort = new Sort("id");

 Hits hits = searcher.search(query, sort); 用户还可以根据自己定义更加复杂的排序,详细请参考 API 。   6 .分析器 Lucene 使用分析器来处理被索引的文本。在将其存入索引之前,分析器用于将文本标记化、摘录有关的单词、丢弃共有的单词、处理派生词(把派生词还原到词根形式,意思是把 bowling 、 bowler 和 bowls 还原为 bowl )和完成其它要做的处理。 Lucene 提供的通用分析器是:

        SimpleAnalyzer :用字符串标记一组单词并且转化为小写字母。

        StandardAnalyzer :用字符串标记一组单词,可识别缩写词、 email 地址、主机名称等等。并丢弃基于英语的 stop words (a, an, the, to) 等、处理派生词。    ChineseAnalyzer.class,它是一个单字分析法,它把句子中的词全部分成一个一个的字符,以单个字为单位存储。 CJKAnalyzer.class,它是双字分析法,它把中文以双字为单位拆分得到结果,从而建立词条。当然这些得到的双字词中会有很多不符合中文语义单位的双字被送进索引。   十、需要注意的问题: 1 .IndexWriter 在添加新的 document 后,需要重新建立 Index ,则需要调用 writer.optimize(); 方法

2. Lucene 没有 update 索引的方法,需要删除后重新建立,参考 remove 方法

3 . 用 IndexReader 删除 Document 后,需要重新用 IndexWriter 进行整理,否则无法在进行搜索(不知道是不是我设置问题) 4.Lucene 先在内存中进行索引操作,并根据一定的批量进行文件的写入。这个批次的间隔越大,文件的写入次数越少,但占用内存会很多。反之占用内存少,但文件 IO 操作频繁,索引速度会很慢。在 IndexWriter 中有一个 MERGE_FACTOR 参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件的操作。根据我的使用经验:缺省 Indexer 是每 20 条记录索引后写入一次,每将 MERGE_FACTOR 增加 50 倍,索引速度可以提高 1 倍左右。 5 .并发操作 Lucene (1) 所有只读操作都可以并发 (2) 在 index 被修改期间,所有只读操作都可以并发 (3) 对 index 修改操作不能并发,一个 index 只能被一个线程占用 (4)ndex 的优化,合并,添加都是修改操作 (5) 但需要注意的是 , 在创建搜索的时候用 : searcher = new IndexSearcher(IndexReader.open("E://lucene//test4//index")); searcher.close(); 这时候是不能关闭 searcher 的 . 如果想让 searcher 能关闭 , 就不要用 IndexReader 了 :

  searcher = new IndexSearcher("E://lucene//test4//index"); 6 . Locking 机制  lucence 内部使用文件来 locking , 默认的 locking 文件放在 java.io.tmpdir, 可以通过 -Dorg.apache.lucene.lockDir=xxx 指定新的 dir ,有 write.lock commit.lock 两个文件, lock 文件用来防止并行操作 index ,如果并行操作, lucene 会抛出异常,可以通过设置 -DdisableLuceneLocks=true 来禁止 locking ,这样做一般来说很危险,除非你有操作系统或者物理级别的只读保证,比如把 index 文件刻盘到 CDROM 上。 十一、 2.0 中新增特性 1. 新增类: org.apache.lucene.index.IndexModifier ,它合并了     IndexWriter 和 IndexReader ,好处是我们可以增加和删除文档的时候不同担心 synchronisation/locking 的问题了。 2. 增加对 contrib/highlighter 的 NullFragmenter , 这对全文本加亮很有用。 3. 增加了新类 MatchAllDocsQuery 用来匹配所有文档。 4.. 增加 ParallelReader ,这个一种 IndexReader 他合并多个单独的索引到一个单独的虚拟索引上。  5. 增加 Hits.iterator() 方法和相应的 HitIterator 和 Hit 对象。

他提供了对 Hits 对象标准的 java.util.Iterator 叠代操作。

每个 iterator's next() 方法返回一个   Hit 对象。   6. 在 term vectors 中增加了 位置和偏移信息。 (Grant Ingersoll & Christoph)   7. 增加了一个新的 DateTools 。允许用户格式化日期到一种更可读的格式,以便于更好的适应索引。 DateTools 不像 DateFields 类,它允许日期指定到 1970 年以前,但必须使用指定的日期格式。这样,在 RangeQuerys 中使用就更加有效率了。 8. 增加了对压缩字段存储的支持。 (patch #29370)

           实例: 1. 判断索引文件是否存在 :

    public static boolean indexExist(String indexDir)

    {

        return IndexReader.indexExists(indexDir);

    }

 private IndexWriter getWriter(String indexFilePath) throws Exception

新一篇: lucene学习----创建索引 |

{

        boolean append=true;

        File file=new File(indexFilePath+File.separator+"segments");

        if(file.exists())

            append=false; 

        return new IndexWriter(indexFilePath,analyzer,append);

    }   2. 删除索引

    public static void deleteIndex(Term aTerm, String indexDir)

    {

        List aList = new ArrayList();

        aList.add(aTerm);

        deleteIndex(aList, indexDir);

    }    

    public static void deleteIndex(List terms, String indexDir)

    {

        if (null == terms) {

            return;

        }

        if(!indexExist(indexDir)) { return; }         IndexReader reader = null;

        try {

            reader = IndexReader.open(indexDir);

            for (int i = 0; i < terms.size(); i++){

                Term aTerm = (Term) terms.get(i);

                if (null != aTerm){

                    reader.delete(aTerm);

                }

            }

        } catch (IOException e){

            LogMan.warn("Error in Delete Index", e);

        } finally {

            try{

                if (null != reader){

                    reader.close();

                }

            }catch (IOException e){

                LogMan.warn("Close reader Error");

            }

        }

    }   删除索引需要一个条件,类似数据库中的字段条件,例如删除一条新闻的代码如下:

     public static void deleteNewsInfoIndex(int nid)

     {

         Term aTerm = new Term("nid", String.valueOf(nid));

         deleteIndex(aTerm,indexDir);

     }   

注:本文有些知识是1.4下的,如果你用的是2.0可能这些例子不能很好的运行。不过我觉得看了以上的东西,再结合一些例子就能对lucene有一定的理解了,最起码可以开始干活了。在2.0版本中创建索引和进行多种搜索的例子我会陆继写出来与大家一起学习。