`

全文检索---lucene学习笔记 有案例

阅读更多
    最近要做检索了,特来学习复习lucene,原理跟数据库的索引一样!
     1、注意false和true区别
IndexWriter writer = new IndexWriter(indexpath, getAnalyzer(),false);
IndexWriter writer = new IndexWriter(indexpath, getAnalyzer(),true);

IndexReader ir=IndexReader.open(indexpath);


     2、为了对文档进行索引,Lucene 提供了五个基础的类 (核心索引类 )
public class IndexWriter              org.apache.lucene.index.IndexWriter
public abstract class Directory       org.apache.lucene.store.Directory
public abstract class Analyzer        org.apache.lucene.analysis.Analyzer
public final class Document           org.apache.lucene.document.Document
public final class Field              org.apache.lucene.document.Field


      3、Document和Field
doc.add(new Field("path", f.getPath(),Field.Store.YES, Field.Index.UN_TOKENIZED));

Field(String name, String value, Field.Store store, Field.Index index)

Field.Index 表示Field的索引方式
NO 表示该Field不需要索引,也就是用户不需要去查找该Field的值
NO_NORMS 表示对该Field进行索引,但是不使用Analyzer,同时禁止它参加评分,主要是为了减少内存的消耗
TOKENIZED 表示该Field先被分词再索引
UN_TOKENIZED 像链接地址URL、文件系统路径信息、时间日期、人名、居民身份证、电话号码等等通常将被索引并且完整的存储在索引中,但一般不需要切分词

Field.Store 表示Field的存储方式
COMPRESS压缩存储
NO 原文不存储在索引文件中,搜索结果命中后,再根据其他附加属性如文件的Path,数据库的主键等,重新连接打开原文,适合原文内容较大的情况。
YES索引文件本来只存储索引数据, 此设计将原文内容直接也存储在索引文件中,如文档的标题。


      4、创建一个索引的大致过程
IndexWriter writer = new IndexWriter(INDEX_DIR, new StandardAnalyzer(), true);
Document doc = new Document();
doc.add(new Field(***));
writer.addDocument(doc);
writer.optimize();//合并索引并优化
writer.close();


      5、理解核心搜索类
只需要几个类来执行基本的搜索操作:
public class IndexSearcher        org.apache.lucene.search.IndexSearcher extends Searcher
public final class Term           org.apache.lucene.index.Term
public abstract class Query       org.apache.lucene.search.Query
public class TermQuery            org.apache.lucene.search.TermQuery extends Query
public final class Hits           org.apache.lucene.search.Hits


      6、Term
Term是搜索的基本单元。一个Term对象有两个String类型的域组成:字段的名称和字段的值。
在搜索时,你可能创建Term对象并和TermQuery同时使用。其中第一个参数代表了要在文档的哪一个Field上进行查找,第二个参数代表了要查询的关键词。 
Query q = new TermQuery(new Term(“fieldName”, “queryWord ”));
Hits hits = sercher.search(q);
这段代码使Lucene找出在fieldName字段中含有单词queryWord的所有文档。因为TermQuery对象继承自它的抽象父类Query,你可以在等式的左边用Query类型。


      7、关键词搜索的大致过程
最简单的接受单个Query对象做为参数并返回一个Hits对象。这个方法的典型应用类似这样:
IndexSearcher sercher = new IndexSearcher( INDEX_DIR);
Query q = new TermQuery(new Term(“contents”, “lucene”));
Hits hits = sercher.search(q);
for (int i = 0; i < hits.length(); i++) {
Document doc = hits.doc(i);
String summary = doc.get(“title");
}

      8、BooleanQuery布尔搜索
BooleanQuery是实际开发过程中经常使用的一种Query。
它其实是一个组合的Query,在使用时可以把各种Query对象添加进去并标明它们之间的逻辑关系。
BooleanQuery是可以嵌套的(BooleanQuery是一个布尔子句的容器)
一个BooleanQuery可以成为另一个BooleanQuery的条件子句。
布尔型Query的子句数目不能超过1024。

BooleanClause布尔搜索
public void add(Query query, BooleanClause.Occur occur)
BooleanClause用于表示布尔查询子句关系的类,包括:
BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。
有以下6种组合:
(1).MUST和MUST:取得连个查询子句的交集。
(2).MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
(3).MUST_NOT和MUST_NOT:无意义,检索无结果。
(4).SHOULD与MUST、SHOULD与MUST_NOT:
SHOULD与MUST连用时,无意义,结果为MUST子句的检索结果。
SHOULD与MUST_NOT连用时, SHOULD功能同MUST,相当于MUST和 MUST NOT的检索结果。
(5).SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。


      9、RangeQuery范围搜索
public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive)
布尔型的参数表示是否将2个临界值也加入到搜索中
查找所有书号在000001到000005之间的图书,并且不包括000001和000005
IndexSearcher searcher = new IndexSearcher(PATH);
Term begin = new Term("booknumber","000001");
Term end = new Term("booknumber","000005");
RangeQuery query = new RangeQuery(begin,end,false);
Hits hits = searcher.search(query);


      10、PrefixQuery 前缀搜索
《钢铁是怎样炼成的》《英雄儿女》《篱笆女人和狗》《女人是水做的》《我的兄弟和女儿》《白毛女》《钢的世界》《钢铁战士》
《钢铁是怎样炼成的》《钢的世界》《钢铁战士》

IndexSearcher searcher = new IndexSearcher(PATH);
Term prefix = new Term("bookname","钢");
PrefixQuery query = new PrefixQuery(prefix);
Hits hits = searcher.search(query);


      11、PhraseQuery短语搜索
《钢铁是怎样炼成的》 , 《钢铁战士》 , 《钢和铁是两种金属元素》 , 《钢要比铁有更多的碳元素》 , 《铁和钢是两种重要的金属》 , 《铁钢是两种重要的金属》
《钢铁战士》,《钢铁是怎样炼成的》
IndexSearcher searcher = new IndexSearcher(PATH);
PhraseQuery query = new PhraseQuery();
query.add(new Term("bookname","钢"));
query.add(new Term("bookname","铁"));
Hits hits = searcher.search(query);

可以看出,搜索的结果都是“钢”和“铁”两字相连,而且顺序也一致的文档,即严格包含有“钢铁”这个短语的文档
PhraseQuery提供了一种为“坡度”的参数,用于表示词组的两个字之间可以插入无关单字的个数。
Public void setSlop(int s)
如果坡度值为1,则《钢和铁是两种重要的金属》也被搜索出来了


      12、FuzzyQuery模糊搜索
word,work,world,seed,sword,ford
workwork,word
FuzzyQuery(Term term)           Calls FuzzyQuery(term, 0.5f, 0).
FuzzyQuery(Term term, float minimumSimilarity)           Calls FuzzyQuery(term, minimumSimilarity, 0).
minimumSimilarity参数代表:最小相似度。默认为0.5。数值越小,文档数量越多。相似度为1时, FuzzyQuery变成了TermQuery。
FuzzyQuery(Term term, float minimumSimilarity, int prefixLength)
prefixLength参数代表:要有多少个前缀字母必须完全其配。


      13、WildcardQuery通配符搜索
*代表0到多个字符,?代表一个单一的字符
IndexSearcher searcher = new IndexSearcher(PATH);
Term t = new Term("content","?o*");
WildcardQuery query = new WildcardQuery(t);
Hits hits = searcher.search(query);


      14、SpanQuery跨度搜索
Man always remember love because of romance only
每个term均有一个位置:Man是1,always是2,remember是3……
如果跨度为3,则应该包括Man always remember 3个term。
在某种跨度范围内,查找关键词并匹配文档,称为跨度搜索
SpanQuery是一个抽象类,实际的搜索功能由它的子类完成。


      15、RegexQuery正则表达式搜索
涉及2个包:
Package org.apache.lucene.search.regex
Package org.apache.regexp
注意:
/contrib/regex/lucene-regex-2.2.0.jar放入工程。
jakarta-regexp-1.5.jar
http://jakarta.apache.org/site/downloads/downloads_regexp.cgi

String regex = "http://[a-z]{1,3}\\.abc\\.com/.*";
Term t = new Term("url",regex);
RegexQuery query = new RegexQuery(t);


      16、MultiFieldQueryParser 多域搜索
org.apache.lucene.queryParser.MultiFieldQueryParser
在不同的Field上进行不同的查找
public static Query parse(String[] queries, String[] fields, Analyzer analyzer) throws ParseException
在不同的Field上进行同一个查找,指定他们之间的布尔关系
public static Query parse(String query, String[] fields, BooleanClause.Occur[] flags, Analyzer analyzer) throws ParseException
在不同的Field上进行不同的查找,指定他们之间的布尔关系
public static Query parse(String[] queries, String[] fields, BooleanClause.Occur[] flags, Analyzer analyzer) throws ParseException


      17、MultiSearcher多索引搜索
IndexSearcher searcher1 = new IndexSearcher(PATH1);
IndexSearcher searcher2 = new IndexSearcher(PATH2);
IndexSearcher [] searchers = {searcher1,searcher2};
MultiSearcher searcher = new MultiSearcher(searchers);
Hits hits = searcher.search(query);


      18、ParallelMultiSearcher多线程搜索
IndexSearcher searcher1 = new IndexSearcher(PATH1);
IndexSearcher searcher2 = new IndexSearcher(PATH2);
IndexSearcher [ ] searchers = {searcher1,searcher2};
ParallelMultiSearcher searcher = new ParallelMultiSearcher(searchers);
Hits hits = searcher.search(query);


      19、在索引中清除Document
尽管大多程序关心的是添加Document到Lucene索引中,一些也需要清除它们。例如,报纸出版社可能只想在可搜索的索引中保留最近一个周的有价值的新闻。另外的程序可能想清除所有包含特定单词的Document。
Document的删除是由IndexReader来完成的。这个类并不立即从索引中删除Document。它只做个删除的标志,等待IndexReader的close()方法调用时真正的Document删除。

IndexReader reader = IndexReader.open(dir);
reader.delete(1);
reader.isDeleted(1)
reader.hasDeletions()
reader.maxDoc();
reader.numDocs();

maxDoc()和numDocs()
IndexReader经常混淆的两个方法的不同:maxDoc()和numDocs()。
maxDoc()返回下一个可用的内部Document号,
numDocs()返回索引中的Document的数目。
numDocs()能够立即感知到Document的删除,而maxDoc()不能。

每个Lucene的Document有个唯一的内部编号。这些编码不是永久分配的,因为Lucene索引分配时在内部重新分配Document的编号。因此,你不能假定一个给定的Document总是拥有同一个Document编号。

delete(Term)
除了我们通过指定Document编号来删除单个Document之外,你可以用IndexReader的delete(Term)方法删除多个Document。使用这个删除方法,允许你删除所有包含指定Term的Document。
例如,为了删除city字段中包含单词Amsterdam的Document,你可以这样用IndexReader: 
IndexReader reader = IndexReader.open(dir);
reader.delete(new Term(“city”, “Amsterdam”));
reader.close();

恢复Document
因为Document的删除延迟到IndexReader实例关闭时才执行,Lucene允许程序改变想法并恢复已做删除标记的Document。
对IndexReader的undeleteAll()方法的调用通过清除索引目录中的.del文件来恢复所有删除的Document。所以在关闭IndexReader实例关闭之后Document就保留在索引中了。
只能使用与删除Document时同一个IndexReader实例,才能调用undeleteAll()来恢复Document。


      20、更新索引中的Document
“如何才能更新索引中的文档?”是一个在Lucene用户邮件列表中经常问的问题。Lucene并没有提供更新方法;Document必须首先从索引中删除然后再重新添加它
如果你需要删除和添加多个Document,最好是进行批操作。按以下步骤:
1. 打开IndexReader。
2. 删除所有你要删除的Document。
3. 关闭IndexReader。
4. 打开IndexWriter。
5. 添加你要添加的所有Document。
6. 关闭IndexWriter。


      21、Document增量
默认情况下,所有的Document都没有增量――或者更恰当地说,它们都有相同的增量因数1.0。通过改变某个Document的增量因数,你可能让Lucene认为它比索引中的其他Document更重要或不重要。
执行这些的API只需一个方法,setBoost(float)
Document doc = new Document();
……
doc.setBoost(1.5);
……
writer.addDocument(doc);

Field增量
就象你可以增量Document一样,你也可以增量个别的字段。
当你增量Document时,Lucene内部使用相同的增量因数增量它的每个字段。
field.setBoost(1.2);


      22、Lucene默认按照文档得分进行排序
Lucene uses this formula to determine a document score based on a query.
tf(t in d)词条t在文档d中出现的词频
idf( t )词条t在文档中的倒排词频
boost(t.field in d)在索引过程中设置的字段参数
lengthNorm(t.field in d)字段的标准化值,表明在字段中存储了多少词条,这个数值是在索引过程中计算出来的,并且也存储在索引中
coord(q, d)协调因子,它的计算是基于文档d中所包含的所有可供查询的词条数量
queryNorm(q)在给出每个查询条目的方差和后,计算某查询的标准化值

explain方法
public Explanation explain(Query query, int doc)
该方法返回一个Explanation 类型的对象。 Explanation 类的toString方法提供的信息,能否将一个文档的得分情况详细的例举出来。
String explain = searcher.explain(query, hits.id(i)).toString();
System.out.println(explain);

改变文档的得分
除了内置的得分算法外,Lucene还提供了一种方法来改变每个文档的得分。
初始化Document后,可以使用Document的setBoost方法来改变一下文档的boost因子。这种做法的实际目的是将文档的得分乘以这个因子,以这个新的数作为文档的得分。
public void setBoost(float boost)
Sets a boost factor for hits on any field of this document. This value will be multiplied into the score of all hits on this document. Values are multiplied into the value of Fieldable.getBoost() of each field in this document. Thus, this method in effect sets a default boost for the fields of this document.

sort排序
如何对某个特定的field进行排序?
实例化一个Sort对象,并使用Searcher的Search(Query,Sort)方法。
org.apache.lucene.search.Searcher
search(Query query, Sort sort)           Returns documents matching query sorted by sort.
org.apache.lucene.search.Sort
Sort(String field)           Sorts by the terms in field then by index order (document number).
Sort(String field, boolean reverse)           Sorts possibly in reverse by the terms in field then by index order (document number).
Sort(String[] fields)           Sorts in succession by the terms in each field.

SortField
SortField构造方法
public SortField(String field, int type, boolean reverse)
org.apache.lucene.search.Sort
Sort(SortField field)          Sorts by the criteria in the given SortField.
Sort(SortField[] fields)           Sorts in succession by the criteria in each SortField.


      23、搜索的过滤器
搜索的过滤器可以减小搜索的范围,即使搜索的结果匹配,但由于文档已经被过滤,所以仍然不会返回给客户。
比如可以用它来实现一种机制,保护某些文档没法被检索到。
所有的过滤器都来自一个抽象的基类org.apache.lucene.search.Filter
public abstract BitSet bits(IndexReader reader) throws IOException
java.util.BitSet此类实现了一个按需增长的位向量。位 set 的每个组件都有一个 boolean 值 .
java.util.BitSet 类提供了一个public BitSet(int nbits)构造方法 创建一个位 set,它的初始大小足以显式表示索引范围在 0 到 nbits-1 的位。所有的位初始均为 false。
Lucene以两种取值(true、false)来代表文档是否被过滤

一个简单的Filter
共设置3中安全级别,要求将安全级别最高的文档过滤掉
SECURITY_ADVANCED = 0,SECURITY_MIDDLE 1,SECURITY_NORMAL = 2,
public class AdvancedSecurityFilter extends Filter {
  public static final int SECURITY_ADVANCED = 0; // 安全级别的常量
  public BitSet bits(IndexReader reader) throws IOException {
    final BitSet bits = new BitSet(reader.maxDoc()); // 首先初始化一个BitSet对象
    bits.set(0, bits.size() - 1); // 先将整个集合置为true,表示当前集合内的所有文档都是可以检索到的.
    Term term = new Term("securitylevel", SECURITY_ADVANCED + ""); // 最高安全级别.
    TermDocs termDocs = reader.termDocs(term); // 从索引中取出最高安全级别的文档
    while (termDocs.next()) {
      bits.set(termDocs.doc(), false); // 遍历每一个文档,将相应的set置为false
    }
    return bits;
  }
}

一个简单的Filter的另一种实现方法
上一个例子中,使用了IndexReader的较底层的API,还可以在bits方法中进行一次查询,来获得想要的结果.
public class AdvancedSecurityFilter extends Filter {
public static final int SECURITY_ADVANCED = 0;//安全级别的常量
public BitSet bits(IndexReader reader) throws IOException {
final BitSet bits = new BitSet(reader.maxDoc());//首先初始化一个BitSet对象
bits.set(0, bits.size() - 1);//先将整个集合置为true,表示当前集合内的所有文档都是可以检索到的.
Term term = new Term("securitylevel", SECURITY_ADVANCED + "");//最高安全级别.
// 初始化一个IndexSearcher对象,
//查找securitylevel这个field的值是SECURITY_ADVANCED的文档
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.search(new TermQuery(term));
for (int i=0;i<hits.length();i++){
bits.set(hits.id(i), false);//遍历每一个文档,将相应的set置为false
}
return bits;
}
}

一个简单的Filter:在搜索时应用过滤器
org.apache.lucene.search.Searcher 提供了在检索中应用Filter的方法
public Hits search(Query query, Filter filter) ……
public Hits search(Query query, Filter filter, Sort sort)
Hits hits = searcher.search(q,new AdvancedSecurityFilter()) ;

内置的过滤器
org.apache.lucene.search.Filter 提供了几个内置的过滤器
Direct Known Subclasses:
BooleanFilter, CachingWrapperFilter, ChainedFilter, ModifiedEntryFilter, PrefixFilter, QueryWrapperFilter, RangeFilter, RemoteCachingWrapperFilter, TermsFilter

RangeFilter
RangeFilter用于将检索结果限定在某个给定的Field值的范围内
public RangeFilter(String fieldName, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper)
fieldName - field 名称
lowerTerm – 范围下界
upperTerm – 范围上届
includeLower – 下届是否包含在范围内
includeUpper – 上届是否包含在范围内
RangeFilter提供了静态方法来得到”无上边界/无下边界”的RangeFilter.
public static RangeFilter Less(String fieldName, String upperTerm)
public static RangeFilter More(String fieldName, String lowerTerm)
RangeFilter filter = new RangeFilter("publishdate","1970-01-01","1990-01-01",true,true);

QueryFilter结果中查询
QueryFilter使用很简单,其构造函数接受一个Query对象,该Query对象可以看作是前一次查询,只要在本次查询时将所构造的QueryFilter对象作为参数传入即可
Deprecated. use a CachingWrapperFilter with QueryWrapperFilter

Term begin = new Term("publishdate","1970-01-01");
Term end = new Term("publishdate","1990-01-01");
RangeQuery q = new RangeQuery(begin,end,true);

QueryFilter filter = new QueryFilter(q);

Term normal = new Term("securitylevel",SECURITY_ADVANCED+"");
TermQuery query = new TermQuery(normal);

IndexSearcher searcher = new IndexSearcher(PATH);
Hits hits = searcher.search(query,filter);

  首先去http://lucene.apache.org/  下载jar 包哦

下面是最常用的代码, 1 读取文件   2 创建索引
public static void main(String[] args) throws Exception {  
        /* 指明要索引文件夹的位置,这里是C盘的S文件夹下 */ 
        File fileDir = new File("c:\\s");  
 
        /* 这里放索引文件的位置 */ 
        File indexDir = new File("c:\\index");  
        Analyzer luceneAnalyzer = new StandardAnalyzer();  
        IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,  
                true);  
        File[] textFiles = fileDir.listFiles();  
        long startTime = new Date().getTime();  
          
        //增加document到索引去  
        for (int i = 0; i < textFiles.length; i++) {  
            if (textFiles[i].isFile()  
                    && textFiles[i].getName().endsWith(".txt")) {  
                System.out.println("File " + textFiles[i].getCanonicalPath()  
                        + "正在被索引....");  
                String temp = FileReaderAll(textFiles[i].getCanonicalPath(),  
                        "GBK");  
                System.out.println(temp);  
                Document document = new Document();  
                Field FieldPath = new Field("path", textFiles[i].getPath(),  
                        Field.Store.YES, Field.Index.NO);  
                Field FieldBody = new Field("body", temp, Field.Store.YES,  
                        Field.Index.TOKENIZED,  
                        Field.TermVector.WITH_POSITIONS_OFFSETS);  
                document.add(FieldPath);  
                document.add(FieldBody);  
                indexWriter.addDocument(document);  
            }  
        }  
        //optimize()方法是对索引进行优化  
        indexWriter.optimize();  
        indexWriter.close();  
          
        //测试一下索引的时间  
        long endTime = new Date().getTime();  
        System.out  
                .println("这花费了" 
                        + (endTime - startTime)  
                        + " 毫秒来把文档增加到索引里面去!" 
                        + fileDir.getPath());  
    }  
 
    public static String FileReaderAll(String FileName, String charset)  
            throws IOException {  
        BufferedReader reader = new BufferedReader(new InputStreamReader(  
                new FileInputStream(FileName), charset));  
        String line = new String();  
        String temp = new String();  
          
        while ((line = reader.readLine()) != null) {  
            temp += line;  
        }  
        reader.close();  
        return temp;  
    }  

然后再建立索引的情况下,开始检索吧
  public static void main(String[] args) throws IOException, ParseException {  
        Hits hits = null;  
        String queryString = "中华";  
        Query query = null;  
        IndexSearcher searcher = new IndexSearcher("c:\\index");  
 
        Analyzer analyzer = new StandardAnalyzer();  
        try {  
            QueryParser qp = new QueryParser("body", analyzer);  
            query = qp.parse(queryString);  
        } catch (ParseException e) {  
        }  
        if (searcher != null) {  
            hits = searcher.search(query);  
            if (hits.length() > 0) {  
                System.out.println("找到:" + hits.length() + " 个结果!");  
            }  
        }  
    }  
 
分享到:
评论
1 楼 a450065208 2012-04-11  
研究一下最新的lucene版本啊。关于排序的就好了

相关推荐

Global site tag (gtag.js) - Google Analytics