lucene 搜索实践

基本流程
1、 初始化IndexSearcher :Searcher searcher = new IndexSearcher(indexDir);//indexDir为索引存放路径
2、 生成Query对象: QueryParser类的parse()方法返回Query类形对象
3、 使用search(Query)方法,开始搜索,并返回Hits对象
4、 使用Hits对象的方法检索结果:
length();//结果总数量
doc(int n);//返回第n个文档;Document对象
id(int n);//第n个文档的内部ID
score(n);//第n个文档的分值,用于排序

例子:
IndexSearcher searcher = new IndexSearcher(“c://index”);
Query query = QueryParser.parse(“key1”,”name”,new StandardAnalyzer());
Hits hits = searcher.search(query);
If(hits.length()==0){
 Out.println(“not found”);
}
else{
 for(int i=0;i  Document d  = hits.doc(i);
 Out.println(d.get(“title”));
}
}

文档搜索结果的主要排序依据是文档的分值。改变文档分值的方法是建立索引时,在初始化Document对象后,使用Document的setBoost方法来改变文档的boost因子(分值的倍数):
Document doc = new Document();
Doc.add(Field.Text(“contents”,”text1 text2”));
Doc.setBoost(1.5f); //改变因子,如0.3f,1.0f等

构建Query:
Query是search包中的一个抽象类,它有许多子类,代表不同类形的查询

关键字TermQuery
Term term = new Term(“contents”,”java”);//实质也是一个键/值对
//构造TermQuery
Query query = new TermQuery(term);

组合查询BooleanQuery
BooleanQuery是一个查询容器,提供专门的方法往其中加入查询,并标明条件的关系
IndexSearcher searcher = new IndexSearcher(“c://index”);
Query q1 = new TermQuery(new Term(“name”,”tim”));
Query q2 = new TermQuery(new Term(“name”,”fly”));
//构造容器
Query query = new BooleanQuery();
query.add(q1,true,false);
query..add(q2,true,false);
Hits hits = searcher.search(query);

BooleanQuery的add方法的第二,第三参数说明:第二参数表示是否必须满足,第三参数表示是否不需要满足,组合一起有以下三种情况:
True,false:当前查询条件是必须要满足的
False ,true:当前查询是不可以满足的
False,false:条件是可选的
True,true:这种情况是错误的
以上表明,上述例子的两个条件都是必须满足的,是“与(and)”的关系
如果查进行“或(or)”运算,相应地可改为:
query.add(q1,false,false);
query.add(q2,false,false)

范围搜索RangeQuery
RangeQuery query = new RangeQuery(begin,end,included);//最后参数为是否包含边界
例:
Term begin = new Term(“time”,”20050101”);
Term end = new Term(“time”,”20050202”);
RangeQuery query = new RangeQuery(begin,end,false);

前缀PrefixQuery
PrefixQuery query = new PrefixQuery(new Term(“name”,”ti”));//name字段中以ti开头

多关键字PhraseQuery
PhraseQuery query = new PhraseQuery();
Query.add(new Term(“content”,”hello”));
Query.add(new Term(“content”,”world”));
Query.setSlop(2);
 //设置以上加上的两个关键字坡度,此值代表两个关键字之间无关词的个数.对于两个紧连的关键字,无论将坡度设为多少都能找到,如果两个词不紧连,且坡度值小于它们之间的无关词的数量,则无法找到

短词缀PhrasePrefixQuery
Term word1 = new Term(“content”,”david”);
Term word2 = new Term(“content”,”mary”);
Term word3 = new Term(“content”,”robert”);
PhrasePrefixQuery query = new PhrasePrefixQuery();
//加入不确定的词
Query.add(new Term[]{word1,word2});//不确定,第一个关键词在Term数组中产生
//加入确定的词
query.add(word3);
query.setSlop(2);//设置坡度
//以上查询中,david robert和mary robert都会出现在查询结果,同样地,mary and robert也会被击中

相近词FuzzyQuery
FuzzyQuery query = new FuzzyQuery(new Term(“content”,”david”));

通配符WildcardQuery
WildcardQuery query = new WildcardQuery(new Term(“content”,”*im”));
WildcardQuery query = new WildcardQuery(new Term(“content”,”t?m??”));
//*代表0个或多个字符,?一个字符

QueryParser类,把用户各种输入转为Query
Query query = QueryParser.parse(keywords,filedName,new StandardAnalyer());
关键字格式:
“tim” //在默认字段搜索tim
“name:tim”;//在name字段搜tim
“tim wong”或tim or wong;//在默认字段搜搜关键字tim 或wong
“+tim +wong”或”tim and wong” //搜索tim和 wong
”tim*”//在默认字段中搜前缀为tim的
“name:tim is a programer”;//在name字段中包含短语tim is a programer
“(tim or wong) and php” //在默认字段中搜含有tim 或wong的关键字,但一定要包含php
“name:tim –title:php” //name字段搜tim,并且title不含有php

另外,QueryParser构造时需要一个分析器,它应该与建立索引时的分析器一样

QueryParser的“与”和“或”
当输入两个关键字时,默认关系是或,要改变这种关系,方法是:
QueryParser parser = new QueryParser(keyword,fieldNmae,new StandardAnalyzer());
Parser.setOperator(QueryParser.DEFAULT_OPERATOR_AND);

 

排序及多字段查找

默认情况下,IndexSearcher类的search方法返回查询结果时,是按文档的分值排序的,可以使用重载的search方法对结果排序

IndexSearcher.search(Query,Sort);

new Sort() 和 Sort.RELEVANCE,以及null一样,采用默认排序,要定义排序字段,方法是将字段传入Sort对象

Sort sort = new Sort(String field);

也可以对多个字段排序Sort sort = new Sort(String[] fields);

例:

Sort sort = new Sort(new SortField[]{new SortField(“title”),new SortField(“name”)});

Hits hits=searcher.search(query,Sort);

 

多字段查找MultiFieldQueryParser

只在某些Term中查找,不关心在哪个字段

Query query = new MultiFieldQueryParser.parse(“word”,new String[]{“title”,”content”},analyzer); //在title和content中找word

多字段时默认是OR关系,要改变它,使用以下方法:

Query query = MultiFieldQueryParser.parse(“word”,new String[]{“title”,”content”},new int[]{MultiFieldQueryParser.REQUIRED_FIELD,MultiFieldQueryParser.PROHIBITED_FIELD},analyzer);

其中:

REQUIRED_FIELD 表示该条件必须有

PROHIBITED_FIELD 表示必须不含

 

索多个索引文件MultiSearcher

1)      建立多个索引:使用不同的索引目录,实例化不同的IndexWriter

2)      建立多索引搜索器:

Searcher[] searchers = new SEARCHER[2];

Searchers[0] = new IndexSearcher(dir1); //搜索索引目录一

Searchers[1]= new IndexSearcher(dir2);//搜索索引目录二

Searcher searcher = new MultiSearcher(serarchers);

3) 开始查询:Hits hits = searcher.search(query);

复合查询(多种查询条件的综合查询)

Query query=MultiFieldQueryParser.parse("索引",new String[]{"title","content"},analyzer);
Query mquery
=new WildcardQuery(new Term("sender","bluedavy*"));
TermQuery tquery
=new TermQuery(new Term("name","jerry"));
BooleanQuery bquery
=new BooleanQuery();

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

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

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

  }


高级篇

一、环境
需要导入 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[] {"title","content"},analyzer);
Searcher searcher=new IndexSearcher(indexFilePath);
 Hits hits=searcher.search(query);
BooleanQuery
例如: BooleanQuery bquery=new BooleanQuery();
 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[] {"title","content"},analyzer);
Searcher searcher=new IndexSearcher(indexFilePath);
Hits hits=searcher.search(query);
for (int i = 0; i < hits.length(); i++)  {
            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 的策略 .

 
 

你可能感兴趣的:(lucene 搜索实践)