大型网站架构设计-Lucene 的使用

2014-12-26 13:08:20     21 人阅读    

          Lucene为搜索引擎提供了强大的、令人惊叹的API,在企业的垂直化搜索领域得到了极为 广泛的应用。为了学习搜索引擎的基本原理,有效地使用Lucene,并将其引入到我们的应用程 序当中,本节将介绍Lucene的一些常用的API和使用方法,以及索引的优化和分布式扩展。


1.构建索引
           在执行搜索之前,先要构建搜索的索引:

Directory dir = FSDirectory.open(new File(indexPath));
Analyzer analyzer = new StandardAnalyzer();
Document doc = new Document();
doc.add(new Field("name","zhansan",Store.Y£S,Index.ANALYZED)); doc.add(new Field("address","hangzhou",Store.YES,Index.ANALYZED)); doc.add(new Field("sex","man",Store.YES,Index.NOT_ANALYZED)); doc.add(new Field("introduce",ni am a coder,my name is zhansan",Store.YES, Index.NO));
IndexWriter indexWriter = new IndexWriter(dir,analyzer, MaxFieldLength. LZMJTED); indexWriter.addDocument(doc); indexWriter.close();

             首先需要构建索引存储的目录Directory,索引最终将被存放到该目录。然后初始化 Document,给Document添加Field,包括名称、地址、性别和个人介绍信息。Field的第一个参 数为Field的名称;第二个参数为Filed的值;第三个参数表示该Field是否会被存储。Store.NO 表示索引中不存储该Field; Store.YES表示索引中存储该Field;如果是Store.COMPRESS,则表示压缩存储。最后一个参数表示是否对该字段进行检索。Index.ANALYZED表示需对该字段 进行全文检索,该Field需要使用分词器进行分词;Index.NOT_ANALYZED表示不进行全文检 索,因此不需要分词;Index.NO表示不进行索引。创建一个IndexWriter,用来写入索引,初始 化时需要指定索引存放的目录,以及索引建立时使用的分词器,此处用的是Lucene自带的中文 分词器StandardAnalyzer,最后一个参数则用来指定是否限制Field的最大长度。


2.索引更新与删除
         很多情况下,在搜索引擎首次构建完索引之后,数据还有可能再次被更改,此时如果不将 最新的数据同步到搜索引擎,则有可能检索到过期的数据。遗憾的是,Lucene暂时还不支持对 于Document单个Field或者整个Document的更新,因此这里所说的更新,实际上是删除旧的Document,然后再向索引中添加新的Document。所添加的新的Document必须包含所有的Field,包括没有更改的Field:

IndexWriter indexWriter = new IndexWriter(dir,analyzer, MaxFieldLength. LUTED); indexWriter.deleteDocuments(new Term(nnamen,nzhansann)); indexWriter.addDocument(doc);
IndexWriter的deleteDocuments可以根据Term来删除Document。请注意Term匹配的准确 性,一个不正确的Term可能会导致搜索引擎的大量索引被误删。Lucene的IndexWriter也提供 经过封装的updateDocument方法,其实质仍然是先删除Term所匹配的索引,然后再新增对应 的 Document:
indexWriter.updateDocument(new Term("namen,nzhansan"), doc);

条件查询

           索引构建完之后,就需要对相关的内容进行查询:

String queryStr = "zhansan";
String[] fields = {"name","introduce"};
Analyzer analyzer = new StandardAnalyzer();
QueryParser queryPaser = new MultiFieldQueryParser(fields, analyzer); Query query = queryPaser.parse(queryStr);
IndexSearcher indexSearcher = new IndexSearcher(indexPath); Filter filter = null;
TopDocs topDocs = indexSearcher.search(query, filter, 10000); System.out.println("hits :" + topDocs.totalHits );
for(ScoreDoc scoreDoc : topDocs.scoreDocs){ int docNum = scoreDoc.doc;
Document doc = indexSearcher.doc(docNum); printDocumentInfo(doc);
}

             查询所使用的字符串为人名zhansan,查询的Field包括name和introduce。构建一个查询 MultiFieldQueryParser解析器,对查询的内容进行解析,生成Query;然后通过IndexSearcher 来对Query进行查询,查询将返回TopDocs,TopDocs中包含了命中的总条数与命中的Document的文档编号;最后通过IndexSearcher读取指定文档编号的文档内容,并进行输出。
Lucene支持多种查询方式,比如针对某个Field进行关键字查询:

Term term = new Term(nnamen,nzhansann);
Query termQuery = new TermQuery(term);

           Term中包含了查询的Field的名称与需要匹配的文本值,termQuery将命中名称为name的 Field中包含zhansan这个关键字的Document。
也可以针对某个范围对Field的值进行区间查询:

NumericRangeQuery numericRangeQuery
=NumericRangeQuery.newIntRange("size", 2, 100, true, true);

                 假设Document包含一个名称为size的数值型的Field,可以针对size进行范围查询,指定 查询的范围为2〜100,后面两个参数表示是否包含查询的边界值。
还可以通过通配符来对Field进行查询:

Term wildcardTerm = new Term("name","zhansa?");
WildcardQuery wildcardQuery = new WildcardQuery(wildcardTerm);

               通配符可以让我们使用不完整、缺少某些字母的项进行查询,但是仍然能够查询到匹配的 结果,如指定对name的查询内容为“zhansa?” ?表示0个或者一个字母,这将命中name的值 为zhansan的Document,如果使用*,则代表0个或者多个字母。
假设某一段落中包含这样一句话“I have a lovely white dog and a black lazy cat”即使不知 道这句话的完整写法,也可以通过PhraseQuery查找到包含dog和cat两个关键字,并且dog和 cat之间的距离不超过5个单词的document:

PhraseQuery phraseQuery = new PhraseQuery(); phraseQuery.add(new Term("content","dog")); phraseQuery.add(new Term("content","cat")); phraseQuery.setSlop(5);


                其中,content为查询对应的Field,dog和cat分别为查询的短语,而phraseQuery.setSlop(5)
表示两个短语之间最多不超过5个单词,两个Field之间所允许的最大距离称为slop。
除这些之外,Lucene还支持将不同条件组合起来进行复杂查询:

PhraseQuery query1 = new PhraseQuery(); query1.add(new Term("content","dog")); query1.add(new Term("content","cat")); query1.setSlop(5);
Term wildTerm = new Term(nnamen,nzhans?n); WildcardQuery query2 = new WildcardQuery(wildTerm);
BooleanQuery booleanQuery = new BooleanQuery(); booleanQuery.add(query1,Occur. MUST); booleanQuery.add(query2,Occur.MUST);


                queryl为前面所说的短语查询,而query2则为通配符查询,通过BooleanQuery将两个查 询条件组合起来。需要注意的是,Occur.MUST表示只有符合该条件的Document才会被包含在 查询结果中;Occur.SHOULD表示该条件是可选的;Occur.MUST_NOT表示只有不符合该条件的Document才能够被包含到查询结果中。

结果排序
                Lucene不仅支持多个条件的复杂查询,还支持按照指定的Field对查询结果进行排序:

String queryStr = "lishi";
String[] fields = {"name","address","size"};
Sort sort = new Sort();
SortField field = new SortField("size",SortField. IWT, true); sort.setSort(field);
Analyzer analyzer = new StandardAnalyzer();
QueryParser queryParse = new MultiFieldQueryParser(fields, analyzer); Query query = queryParse.parse(queryStr);
IndexSearcher indexSearcher = new IndexSearcher(indexPath);
Filter filter = null;
TopDocs topDocs = indexSearcher.search(query, filter, 100, sort);
for(ScoreDoc scoreDoc : topDocs.scoreDocs){ int docNum = scoreDoc.doc;
Document doc = indexSearcher.doc(docNum); printDocumentInfo(doc);
}

                通过新建一个Sort,指定排序的Field为size,Field的类型为SortField.INT,表示按照整数类型进行排序,而不是字符串类型,SortField的第三个参数用来指定是否对排序结果进行反转。 在查询时,使用IndexSearcher的一个重构方法,带上Sort参数,则能够让查询的结果按照指定 的字段进行排序

 

                 假设查询串中包含zhansan和shanghai两个查询串,设置Field name的权重为4,而设置 Field address的权重为2,如按照Field的权重进行查询排序,那么同时包含zhansan和shanghai 的Document将排在最前面,其次是name为zhansan的Document,最后是address为shanghai 的 Document:


本文出自   《大型分布式网站架构设计与实践试读样章》 试读,如果感觉到对你有帮助可以去购买该书


原文地址:http://www.itmmd.com/201412/386.html
该文章由 萌萌的IT人 整理发布,转载须标明出处。

linux命令详解(23)编辑文本文件 JOE 编辑器 入门   上一篇
下一篇  产品经理学习笔记(14)-搜集用户反馈

精彩回复
发表评论
姓名:       

《程序员app》专门为程序员量身定做!