lucene入门体会
- 格式:doc
- 大小:208.50 KB
- 文档页数:31
lucene笔记-视频配套笔记传智播客 Java学院传智.入云龙全文检索技术之Lucene1 课程计划1、什么全文检索数据类型:结构化数据和非结构化数据非结构化数据查询方法全文检索的概念2、什么是Lucene3、Lucene实现全文检索的流程(重点)4、Lucene的入门程序(重点)5、分析器Analyzer(重点)7、索引库的维护(重点)i. 索引库的增加索引ii. 删除索引iii. 修改索引8、索引库的查询(重点)i. 使用Query的子类查询ii. 使用QueryParser查询8、相关的排序2 什么全文检索结构化数据:数据的长度类型是固定的。
数据库中的数据非结构化数据:长度不固定格式不固定。
Word文档、excel、pdf、txt。
2.1 结构化数据的查询使用sql语句查询就可以2.2 非结构化数据的查询需求:在文件中找出包含java字符的文档。
传智播客 Java学院传智.入云龙2.2.1 实现方法1、目测2、顺序扫描。
如果文件量大的话比如说20G,顺序扫描会变的很慢。
3、非结构化的数据变结构化先从文件中找出文件所有的词汇,形成一个列表。
查询的时候直接查询列表,找到关键词后根据这词找到相应的文档。
一次创建多次使用。
2.3 全文检索的概念这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
2.4 全文检索应用领域1、搜索引擎,百度、谷歌2、站内搜索a) 微博搜索b) 论坛搜索3、电商搜索,搜索的是商品信息。
3 什么是Lucene3.1 Lucene的概念Lucene是apache下的一个开放源代码的全文检索引擎工具包。
提供了完整的查询引擎和索引引擎,部分文本分析引擎。
Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能。
3.2 Lucene和搜索引擎的区别Lucene是一个工具包:需要进行编程才能实现全文检索的功能呢搜索引擎:一套独立运行的系统,提供了全文检索功能。
lucene原理Lucene是Apache软件基金会下的一个开放源代码的全文信息检索的开发工具包,它实现了完整的文档搜索引擎。
它提供两种索引类型:结构化索引和文档索引,两种索引类型都有它们各自的优势和缺点,取决于实际需要。
Lucene提供了一个组件化的架构,它利用一个高效的索引系统来实现搜索。
此外,Lucene还提供了许多的文本处理功能,如词法分析,摘要,跟踪搜索日志,等等。
而且,Lucene和其他全文搜索系统不同,它允许用户定制自己的索引和结构,从而满足特定的搜索需求。
Lucene的核心是索引机制,它可以对一系列文档进行检索、搜索、高级搜索。
它利用微机二进制索引结构可以快速访问准确的结果,还可以在全文检索时进行模糊处理,识别文档中的同义词等。
Lucene还跟踪文档更新,可以检测何时需要重组全文索引,从而实现快速响应搜索需要。
除此之外,Lucene还可以搜索特定的文档,文本,页面,网页或者指定的网站。
Lucene的设计出发点是提供全文搜索的性能,而不仅仅是提供精确的搜索词语。
这意味着Lucene可以提供精确的搜索,使用的是数据结构和算法来实现搜索,搜索的结果可以按照权重排序,并且可以对搜索结果进行筛选,从而更好地满足搜索用户的需求。
Lucene通过提供文档过滤器和搜索过滤器,可以用来限定搜索结果的范围。
此外,Lucene 还提供了一系列的分析器,来处理原始的文档,包括不同类型的文件,如Word文档,PDF文档,HTML文档等等。
基于Lucene的搜索服务可以满足各种不同的搜索需求。
用户可以根据自己的关键字设置搜索条件,也可以应用不同类型的条件,如限制搜索结果的数量,搜索结果的排序等。
Lucene利用高效的计算方法和索引技术,能够提供快速准确的搜索结果,并对不同类型的数据进行处理,进一步提高搜索效率。
1Lucene学习资料大纲:了解搜索引擎基础知识Lucene快速入门常用查询技术亮点:搜索引擎面试问题:索引为什么能提高搜索效率lucene中的倒序索引与数据库索引的区别1.1搜索引擎基础知识1.1.1什么是索引[index] 是根据一定需要,把书刊中的主要内容或各种题名摘录下来,标明出处、页码,按一定次序分条排列,以供人查阅的资料。
索引是一本科技书籍的重要组成部分,它把书中的重要名词罗列出来,并给出它们相应的页码,方便读者快速查找该名词的定义和含义。
1.1.2数据库语义在关系数据库中,索引是一种与表有关的数据库结构,它可以使对应于表的SQL语句执行得更快。
当表中有大量记录时,若要对表进行查询,第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量数据库系统时间,并造成大量磁盘I/O操作;第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的ROWID(相当于页码)快速找到表中对应的记录。
但是,考虑一下如下情形,按照商品名称(Title)来进行搜索,这时,用户输入的不可能是商品的完整名称,而只能是名称之中的一个词:面试小问题:使用like查询是否能使用索引?提示,mysql 中可以使用explain查看sql语句的执行计划这时如果按照传统索引技术,索引整个Title字段,仍然不能达到快速搜索的效果1.1.3倒排索引倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。
它是文档检索系统中最常用的数据结构。
通过倒排索引,可以根据单词快速获取包含这个单词的文档列表1.1.4全文检索的一般过程包括了数据收集(Gather Data)、建立索引、从索引中搜索这些部分1.1.5Gather Data输入的来源可能有:1)文件系统2)数据库3)网页4)人工输入1.1.6创建索引步骤:1)分词:将文档分成一一个单词去除标点符号去除停用词(Stop word)经过语言处理组件进行处理例如对于英语变为小写将单词缩减或转变为词根,例如复数=>单数过去式=>原型注意:lucene的默认分词并没有上述实现2)中文分词:分词算法可分为三大类:基于字典、词库匹配的分词方法;基于词频度统计的分词方法和基于知识理解的分词方法。
一、lucene模糊匹配原理概述lucene是一个开源的全文检索引擎工具,提供了强大的文本搜索和分析功能。
在实际应用中,经常需要进行模糊匹配,以处理用户输入的错别字、拼写错误或者同义词。
模糊匹配是lucene中非常重要的功能,它可以帮助用户找到相关的文档,提高搜索的准确性和全面性。
二、lucene模糊匹配的算法原理1. Levenshtein Distance算法Levenshtein Distance是衡量两个字符串相似程度的一种算法,也称为编辑距离。
在lucene中,模糊匹配主要使用Levenshtein Distance算法来实现。
该算法通过计算两个字符串之间的距离,从而确定它们的相似程度。
具体来说,它通过插入、删除和替换操作,将一个字符串转换成另一个字符串所需的最小步骤数来衡量相似度。
2. 模糊查询的实现方式在lucene中,模糊查询可以通过FuzzyQuery类来实现。
利用FuzzyQuery,可以指定一个最大编辑距离,从而允许匹配到具有一定相似度的文档。
FuzzyQuery会基于Levenshtein Distance算法来进行模糊匹配,找到编辑距离小于等于指定值的文档。
三、模糊匹配的应用场景1. 处理用户输入错误当用户在搜索框中输入错别字或者拼写错误时,模糊匹配可以帮助系统找到相关的文档,并提供纠正建议,提高搜索的准确性和用户体验。
2. 同义词匹配在自然语言处理中,同一个概念可能有多种不同的表达方式。
通过模糊匹配,可以将具有相似含义的词语进行匹配,从而提高搜索的全面性。
3. 解决词形变化问题词形变化是自然语言中常见的现象,同一个词可能有不同的变形形式。
通过模糊匹配,可以将不同词形的单词对应起来,使得搜索更加全面和准确。
四、模糊匹配的优化策略1. 设置合适的编辑距离阈值在使用模糊匹配时,需要根据具体的应用场景来设置合适的编辑距离阈值。
如果编辑距离过小,可能会产生大量的不必要匹配;如果编辑距离过大,可能会包含过多的无关文档。
es的lucene作用Es的Lucene作用Lucene是一个开源的全文搜索引擎库,被广泛应用于各种编程语言和领域,其中包括Elasticsearch(简称Es),是一个基于Lucene 构建的分布式搜索与分析引擎。
本文将重点介绍Es的Lucene作用,并探讨其在搜索引擎领域中的重要性。
一、Lucene的基本概念和原理Lucene是一个高效、可扩展的全文搜索引擎库,它提供了一套简单而强大的API,可以用于创建索引、搜索和生成文本摘要。
其核心原理是将文本数据分析、索引和搜索的过程进行分离,以实现高效的全文搜索。
1. 数据分析(Analysis):Lucene提供了一系列的文本分析器(Analyzer),用于将输入的文本进行分词、词干提取、大小写转换等处理。
分析器的作用是将原始文本转化为一组有意义的词条(Term),以便于后续的索引和搜索操作。
2. 索引(Indexing):Lucene使用倒排索引(Inverted Index)的方式来存储和管理文本数据。
倒排索引是一种将词条映射到文档的数据结构,它可以快速地根据词条进行搜索,并找到包含该词条的文档。
3. 搜索(Searching):Lucene提供了丰富的搜索API,可以根据关键词、短语、通配符等进行检索,并按照相关度对搜索结果进行排序。
搜索过程利用倒排索引来定位匹配的文档,并根据各种算法计算文档与查询的相关度。
二、Es中的Lucene应用Es是一个基于Lucene的分布式搜索与分析引擎,它在Lucene的基础上进行了功能扩展和性能优化,提供了更强大的分布式搜索和数据分析能力。
1. 分布式搜索:Es将数据分片存储在多个节点上,并使用分布式索引的方式来实现高性能的搜索。
当用户发起搜索请求时,Es会将查询分发到各个节点,并将结果进行合并和排序,最后返回给用户。
2. 数据分析与聚合:Es提供了丰富的数据聚合功能,可以对文档进行分组、统计、排序等操作。
用户可以通过聚合操作获取关于数据的各种统计信息,如平均值、最大值、最小值等,以及根据条件进行数据筛选和分析。
lucene算法原理Lucene算法原理Lucene是一个开源的全文检索引擎工具包,采用Java语言编写,被广泛应用于各类信息检索系统中。
它的核心思想是将文本信息转化为可被计算机理解和处理的数据结构,以实现高效的文本搜索和检索功能。
本文将介绍Lucene算法的原理及其核心组成部分。
一、倒排索引Lucene的核心数据结构是倒排索引(Inverted Index),它是一种将文档中的单词映射到文档的数据结构。
传统的索引方式是顺序索引,即根据文档顺序逐个记录单词。
而倒排索引则是根据单词逐个记录文档,将每个单词对应的文档存储在一个倒排列表中。
这种方式可以极大地提高搜索效率,因为它能够快速地找到包含某个特定单词的文档。
二、分词器在构建倒排索引前,需要将文本进行分词处理。
分词器(Tokenizer)将文本切分成一个个独立的词项(Term),并去除停用词等无关信息。
Lucene提供了多种分词器,如标准分词器(StandardTokenizer)、简单分词器(SimpleTokenizer)等,用户也可以自定义分词器以适应不同的需求。
三、索引构建索引构建是指将文档转化为倒排索引的过程。
首先,需要创建一个索引目录(Index Directory),用于存储索引文件;然后,通过Analyzer对文档进行分词处理;最后,将分词结果按照倒排索引的结构存储到索引目录中。
四、搜索过程Lucene的搜索过程主要包括查询解析、查询扩展和评分排序三个步骤。
1. 查询解析:用户输入的查询语句经过查询解析器(Query Parser)处理,将其解析为一个个查询表达式。
查询解析器支持多种查询语法,如布尔查询、模糊查询、范围查询等。
2. 查询扩展:为了提高搜索的准确性和覆盖率,Lucene支持查询扩展功能。
通过分析用户查询的上下文,自动为查询语句添加相关的词项,从而扩展查询范围。
3. 评分排序:Lucene使用TF-IDF算法对搜索结果进行评分排序。
一步一步跟我学习lucene(8)---lucene搜索之索引的查询原理和查询工具类昨天我们了解了lucene搜索之IndexSearcher构建过程(/wuyinggui10000/article/details/4569866 7),对lucene的IndexSearcher有一个大体的了解,知道了怎么创建IndexSearcher,就要开始学会使用IndexSearcher进行索引的搜索,本节我们学习索引的查询原理和根据其相关原理写索引查询的工具类的编写;IndexSearcher常用方法IndexSearcher提供了几个常用的方法:•IndexSearcher.doc(int docID) 获取索引文件中的第n个索引存储的相关字段,返回为Document类型,可以据此读取document 中的Field.STORE.YES的字段;•IndexSearcher.doc(int docID, StoredFieldVisitor fieldVisitor) 获取StoredFieldVisitor指定的字段的document,StoredFieldVisitor定义如下[java] view plain copy1.StoredFieldVisitor visitor = new DocumentStoredFieldVisi tor(String... fields);•IndexSearcher.doc(int docID, Set<String> fieldsToLoad) 此方法同上边的IndexSearcher.doc(int docID, StoredFieldVisitor fieldVisitor) ,其实现如下图•IndexSearcher.count(Query query) 统计符合query条件的document个数•IndexSearcher.searchAfter(final ScoreDoc after, Queryquery, int numHits) 此方法会返回符合query查询条件的且在after 之后的numHits条记录;其实现原理为:先读取当前索引文件的最大数据条数limit,然后判断after是否为空和after对应的document的下标是否超出limit的限制,如果超出的话抛出非法的参数异常;设置读取的条数为numHits和limit中最小的(因为有超出最大条数的可能,避免超出限制而造成的异常)接下来创建一个CollectorManager类型的对象,该对象定义了要返回的T opDocs的个数,上一页的document的结尾(after),并且对查询结果进行分析合并最后调用search(query,manager)来查询结果•IndexSearcher.search(Query query, int n) 查询符合query条件的前n个记录•IndexSearcher.search(Query query, Collector results) 查询符合collector的记录,collector定义了分页等信息•IndexSearcher.search(Query query, int n,Sort sort, boolean doDocScores, boolean doMaxScore) 实现任意排序的查询,同时控制是否计算hit score和max score是否被计算在内,查询前n条符合query条件的document;•IndexSearcher.search(Query query, CollectorManager<C, T>collectorManager) 利用给定的collectorManager获取符合query 条件的结果,其执行流程如下:先判断是否有ExecutorService执行查询的任务,如果没有executor,IndexSearcher会在单个任务下进行查询操作;如果IndexSearcher有executor,则会由每个线程控制一部分索引的读取,而且查询的过程中采用的是future机制,此种方式是边读边往结果集里边追加数据,这种异步的处理机制也提升了效率,其执行过程如下:编码实践我中午的时候写了一个SearchUtil的工具类,里边添加了多目录查询和分页查询的功能,经测试可用,工具类和测试的代码如下:[java] view plain copy1.package com.lucene.search.util;2.3.import java.io.File;4.import java.io.IOException;5.import java.nio.file.Paths;6.import java.util.Set;7.import java.util.concurrent.ExecutorService;8.9.import org.apache.lucene.document.Document;10.import org.apache.lucene.index.DirectoryReader;11.import org.apache.lucene.index.IndexReader;12.import org.apache.lucene.index.MultiReader;13.import org.apache.lucene.search.BooleanQuery;14.import org.apache.lucene.search.IndexSearcher;15.import org.apache.lucene.search.Query;16.import org.apache.lucene.search.ScoreDoc;17.import org.apache.lucene.search.TopDocs;18.import org.apache.lucene.search.BooleanClause.Occur ;19.import org.apache.lucene.store.FSDirectory;20.21./**lucene索引查询工具类22.* @author lenovo23.*24.*/25.public class SearchUtil {26./**获取IndexSearcher对象27.* @param indexPath28.* @param service29.* @return30.* @throws IOException31.*/32.public static IndexSearcher getIndexSearcherByParent Path(String parentPath,ExecutorService service) throws IOExcep tion{33.MultiReader reader = null;34.//设置35.try {36.File[] files = new File(parentPath).listFiles();37.IndexReader[] readers = new IndexReader[files.length] ;38.for (int i = 0 ; i < files.length ; i ++) {39.readers[i] = DirectoryReader.open(FSDirectory.open(P aths.get(files[i].getPath(), new String[0])));40.}41.reader = new MultiReader(readers);42.} catch (IOException e) {43.// TODO Auto-generated catch block44. e.printStackTrace();45.}46.return new IndexSearcher(reader,service);47.}48./**根据索引路径获取IndexReader49.* @param indexPath50.* @return51.* @throws IOException52.*/53.public static DirectoryReader getIndexReader(String i ndexPath) throws IOException{54.return DirectoryReader.open(FSDirectory.open(Paths.g et(indexPath, new String[0])));55.}56./**根据索引路径获取IndexSearcher57.* @param indexPath58.* @param service59.* @return60.* @throws IOException61.*/62.public static IndexSearcher getIndexSearcherByIndex Path(String indexPath,ExecutorService service) throws IOExcepti on{63.IndexReader reader = getIndexReader(indexPath);64.return new IndexSearcher(reader,service);65.}66.67./**如果索引目录会有变更用此方法获取新的IndexSearcher这种方式会占用较少的资源68.* @param oldSearcher69.* @param service70.* @return71.* @throws IOException72.*/73.public static IndexSearcher getIndexSearcherOpenIfC hanged(IndexSearcher oldSearcher,ExecutorService service) thr ows IOException{74.DirectoryReader reader = (DirectoryReader) oldSearch er.getIndexReader();75.DirectoryReader newReader = DirectoryReader.openIf Changed(reader);76.return new IndexSearcher(newReader, service);77.}78.79./**多条件查询类似于sql in80.* @param querys81.* @return82.*/83.public static Query getMultiQueryLikeSqlIn(Query ...querys){84.BooleanQuery query = new BooleanQuery();85.for (Query subQuery : querys) {86.query.add(subQuery,Occur.SHOULD);87.}88.return query;89.}90.91./**多条件查询类似于sql and92.* @param querys93.* @return94.*/95.public static Query getMultiQueryLikeSqlAnd(Query .. . querys){96.BooleanQuery query = new BooleanQuery();97.for (Query subQuery : querys) {98.query.add(subQuery,Occur.MUST);99.}100.return query;101.}102./**根据IndexSearcher和docID获取默认的document 103.* @param searcher104.* @param docID105.* @return106.* @throws IOException107.*/108.public static Document getDefaultFullDocument(Inde xSearcher searcher,int docID) throws IOException{109.return searcher.doc(docID);110.}111./**根据IndexSearcher和docID112.* @param searcher113.* @param docID114.* @param listField115.* @return116.* @throws IOException117.*/118.public static Document getDocumentByListField(Inde xSearcher searcher,int docID,Set<String> listField) throws IOExc eption{119.return searcher.doc(docID, listField);120.}121.122./**分页查询123.* @param page 当前页数124.* @param perPage 每页显示条数125.* @param searcher searcher查询器126.* @param query 查询条件127.* @return128.* @throws IOException129.*/130.public static TopDocs getScoreDocsByPerPage(int pa ge,int perPage,IndexSearcher searcher,Query query) throws IOE xception{131.TopDocs result = null;132.if(query == null){133.System.out.println(" Query is null return null ");134.return null;135.}136.ScoreDoc before = null;137.if(page != 1){138.TopDocs docsBefore = searcher.search(query, (page-1)*perPage);139.ScoreDoc[] scoreDocs = docsBefore.scoreDocs;140.if(scoreDocs.length > 0){141.before = scoreDocs[scoreDocs.length - 1];142.}143.}144.result = searcher.searchAfter(before, query, perPage);145.return result;146.}147.public static TopDocs getScoreDocs(IndexSearcher se archer,Query query) throws IOException{148.TopDocs docs = searcher.search(query, getMaxDocId(s earcher));149.return docs;150.}151./**统计document的数量,此方法等同于matchAllDocsQuery查询152.* @param searcher153.* @return154.*/155.public static int getMaxDocId(IndexSearcher searcher ){156.return searcher.getIndexReader().maxDoc();157.}158.159.}相关测试代码如下:[java] view plain copy1.package com.lucene.index.test;2.3.import java.io.IOException;4.import java.util.HashSet;5.import java.util.Set;6.import java.util.concurrent.ExecutorService;7.import java.util.concurrent.Executors;8.9.import org.apache.lucene.document.Document;10.import org.apache.lucene.index.Term;11.import org.apache.lucene.search.IndexSearcher;12.import org.apache.lucene.search.Query;13.import org.apache.lucene.search.ScoreDoc;14.import org.apache.lucene.search.TermQuery;15.import org.apache.lucene.search.TopDocs;16.17.import com.lucene.search.util.SearchUtil;18.19.public class TestSearch {20.public static void main(String[] args) {21.ExecutorService service = Executors.newCachedThrea dPool();22.try {23.24.IndexSearcher searcher = SearchUtil.getIndexSearcher ByParentPath("index",service);25.System.out.println(SearchUtil.getMaxDocId(searcher));26.Term term = new Term("content", "lucene");27.Query query = new TermQuery(term);28.TopDocs docs = SearchUtil.getScoreDocsByPerPage(2, 20, searcher, query);29.ScoreDoc[] scoreDocs = docs.scoreDocs;30.System.out.println("所有的数据总数为:"+docs.totalHits);31.System.out.println("本页查询到的总数为:"+scoreDocs.length);32.for (ScoreDoc scoreDoc : scoreDocs) {33.Document doc = SearchUtil.getDefaultFullDocument(s earcher, scoreDoc.doc);34.//System.out.println(doc);35.}36.System.out.println("\n\n");37.TopDocs docsAll = SearchUtil.getScoreDocs(searcher, query);38.Set<String> fieldSet = new HashSet<String>();39.fieldSet.add("path");40.fieldSet.add("modified");41.for (int i = 0 ; i < 20 ; i ++) {42.Document doc = SearchUtil.getDocumentByListField(s earcher, docsAll.scoreDocs[i].doc,fieldSet);43.System.out.println(doc);44.}45.46.} catch (IOException e) {47.// TODO Auto-generated catch block48. e.printStackTrace();49.}finally{50.service.shutdownNow();51.}52.}53.54.}。
2012/2/26一、摘要本文档是在完成山东农信知识库项目的基础上编写的,简要介绍了lucene技术的原理、分词技术、索引技术、文档权重、文档排重技术等,本文档的定位是lucene初级开发人员,如果您已经是lucene专家或者是搜索引擎的大牛,就没必要来看本文档了。
本文档的所有编码是基于lucene 3.0.4完成的,使用的是标准分析器,对于不同的lucene版本或者不同的分析器,代码会有一定的差异。
二、Lucene全文检索的实现Lucene是一个高效的全文检索库。
相信各位同事在项目中通过数据库检索的案例很多,数据库检索大家一定非常熟悉了,我们知道在正常情况下数据库查询搜索能够满足我们的需求,那为什么还要用到lucene全文检索呢?在学习lucene之前,我们先了解一下全文检索吧。
数据分为两种:结构化数据和全文数据(又叫做非结构化数据)。
•结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。
•全文数据:指不定长或无固定格式的数据,如邮件,word文档等。
对结构化数据的搜索,我们一般用到数据库比较多。
那对于全文数据的搜索呢?我们需要针对全文数据制定全文搜索的方式,其中像lucene这样,先建立索引,再对索引进行搜索就是其中的一种。
Lucene是一个基于java的全文索引引擎工具包,它提供了各种借口,可以方便的此文档讲述了lucent技术的基本用法,包括分词、索引、排重等技术。
嵌入到各种应用中,通过对接口的实现满足我们全文索引或者检索功能。
在对非结构化数据索引之前,我们需要先将数据进行分词处理,对于英文分词比较简单,一个空格间隔就是一个单词,而对于中文,我们如何将中文的词提取出来再进行索引难度就比较大了。
Lucene提供了多种语言分析器,在本文档的第四节中,我们将会对lucene分词技术做详细的讲解。
在对文字进行关键字提取之后,下一步就需要对内容进行索引了。
索引是通过document存储的,document可以设定多个field字段,根据需要按照规则索引或者存储字段。
一步一步跟我学习lucene(14)---lucene搜索之facet查询原理和facet查询实例Facet说明我们在浏览网站的时候,经常会遇到按某一类条件查询的情况,这种情况尤以电商网站最多,以天猫商城为例,我们选择某一个品牌,系统会将该品牌对应的商品展示出来,效果图如下:如上图,我们关注的是品牌,选购热点等方面,对于类似的功能我们用lucene的term查询当然可以,但是在数据量特别大的情况下还用普通查询来实现显然会因为FSDirectory.open等耗时的操作造成查询效率的低下,同时普通查询是全部document都扫描一遍,这样显然造成了查询效率低;lucene提供了facet查询用于对同一类的document进行聚类化,这样在查询的时候先关注某一个方面,这种显然缩小了查询范围,进而提升了查询效率;facet模块提供了多个用于处理facet的统计和值处理的方法;要实现facet的功能,我们需要了解facetField,FacetField定义了dim和此field对应的path,需要特别注意的是我们在做facetField索引的时候,需要事先调用FacetsConfig.build(Document);FacetField的indexOptions设置为了DOCS_AND_FREQS_AND_POSITIONS的,即既索引又统计出现的频次和出现的位置,这样做主要是为了方便查询和统计;相应的在存储的时候我们需要利用FacetsConfig和DirectoryTaxonomyWriter;DirectoryTaxonomyWriter用来利用Directory来存储T axono 信息到硬盘;DirectoryTaxonomyWriter的构造器如下:[java] view plain copy1.public DirectoryTaxonomyWriter(Directory directory, Ope nMode openMode,2.TaxonomyWriterCache cache) throws IOException {3.4.dir = directory;5.IndexWriterConfig config = createIndexWriterConfig(ope nMode);6.indexWriter = openIndexWriter(dir, config);7.8.// verify (to some extent) that merge policy in effect would preserve category docids9.assert !(indexWriter.getConfig().getMergePolicy() instanc eof TieredMergePolicy) :10."for preserving category docids, merging none-adjacent segments is not allowed";11.12.// after we opened the writer, and the index is locked, it's safe to check13.// the commit data and read the index epoch14.openMode = config.getOpenMode();15.if (!DirectoryReader.indexExists(directory)) {16.indexEpoch = 1;17.} else {18.String epochStr = null;19.Map<String, String> commitData = readCommitData( directory);20.if (commitData != null) {21.epochStr = commitData.get(INDEX_EPOCH);22.}23.// no commit data, or no epoch in it means an old tax onomy, so set its epoch to 1, for lack24.// of a better value.25.indexEpoch = epochStr == null ? 1 : Long.parseLong( epochStr, 16);26.}27.28.if (openMode == OpenMode.CREATE) {29.++indexEpoch;30.}31.32.FieldType ft = new FieldType(TextField.TYPE_NOT_STO RED);33.ft.setOmitNorms(true);34.parentStreamField = new Field(Consts.FIELD_PAYLOAD S, parentStream, ft);35.fullPathField = new StringField(Consts.FULL, "", Field.S tore.YES);36.37.nextID = indexWriter.maxDoc();38.39.if (cache == null) {40.cache = defaultTaxonomyWriterCache();41.}42.this.cache = cache;43.44.if (nextID == 0) {45.cacheIsComplete = true;46.// Make sure that the taxonomy always contain the ro ot category47.// with category id 0.48.addCategory(new FacetLabel());49.} else {50.// There are some categories on the disk, which we ha ve not yet51.// read into the cache, and therefore the cache is inco mplete.52.// We choose not to read all the categories into the ca che now,53.// to avoid terrible performance when a taxonomy ind ex is opened54.// to add just a single category. We will do it later, afte r we55.// notice a few cache misses.56.cacheIsComplete = false;57.}58.}由上述代码可知,DirectoryTaxonomyWriter先打开一个IndexWriter,在确保indexWriter打开和locked的前提下,读取directory对应的segments中需要提交的内容,如果读取到的内容为空,说明是上次的内容,设置indexEpoch为1,接着对cache进行设置;判断directory中是否还包含有document,如果有设置cacheIsComplete为false,反之为true;时候不早了,今天先写到这里,明天会在此基础上补充,大家见谅编程实践我对之前的读取文件夹内容的做了个facet索引的例子对BaseIndex修改了facet的设置,相关代码如下[java] view plain copy1.package com.lucene.index;2.3.4.5.import java.io.File;6.import java.io.IOException;7.import java.nio.file.Paths;8.import java.text.ParseException;9.import java.util.List;10.import java.util.concurrent.CountDownLatch;11.12.import org.apache.lucene.document.Document;13.import org.apache.lucene.document.Field;14.import org.apache.lucene.facet.FacetResult;15.import org.apache.lucene.facet.Facets;16.import org.apache.lucene.facet.FacetsCollector;17.import org.apache.lucene.facet.FacetsConfig;18.import org.apache.lucene.facet.taxonomy.FastTaxono myFacetCounts;19.import org.apache.lucene.facet.taxonomy.directory.Dir ectoryTaxonomyReader;20.import org.apache.lucene.facet.taxonomy.directory.Dir ectoryTaxonomyWriter;21.import org.apache.lucene.index.IndexOptions;22.import org.apache.lucene.index.IndexWriter;23.import org.apache.lucene.index.Term;24.import org.apache.lucene.search.IndexSearcher;25.import org.apache.lucene.search.MatchAllDocsQuery;26.import org.apache.lucene.search.Query;27.import org.apache.lucene.store.Directory;28.import org.apache.lucene.store.FSDirectory;29.import org.apache.lucene.store.RAMDirectory;30.31.import com.lucene.search.SearchUtil;32.33.34.public abstract class BaseIndex<T> implements Run nable{35./**36.* 父级索引路径37.*/38.private String parentIndexPath;39./**40.* 索引编写器41.*/42.private IndexWriter writer;43.private int subIndex;44./**45.* 主线程46.*/47.private final CountDownLatch countDownLatch1;48./**49.*工作线程50.*/51.private final CountDownLatch countDownLatch2;52./**53.* 对象列表54.*/55.private List<T> list;56./**57.* facet查询58.*/59.private String facet;60.protected final FacetsConfig config = new FacetsConf ig();61.protected final static String indexPath = "index1";62.protected final static DirectoryTaxonomyWriter taxo Writer;63.static{64.try {65.Directory directory = FSDirectory.open(Paths.get(inde xPath, new String[0]));66.taxoWriter = new DirectoryTaxonomyWriter(directory) ;67.} catch (IOException e) {68.throw new ExceptionInInitializerError("BaseIndex initi alizing error");69.}70.}71.public BaseIndex(IndexWriter writer,CountDownLatch countDownLatch1, CountDownLatch countDownLatch2,72.List<T> list, String facet){73.super();74.this.writer = writer;75.this.countDownLatch1 = countDownLatch1;76.this.countDownLatch2 = countDownLatch2;77.this.list = list;78.this.facet = facet;79.}80.public BaseIndex(String parentIndexPath, int subIndex ,81.CountDownLatch countDownLatch1, CountDownLatch countDownLatch2,82.List<T> list) {83.super();84.this.parentIndexPath = parentIndexPath;85.this.subIndex = subIndex;86.try {87.//多目录索引创建88.File file = new File(parentIndexPath+"/index"+subInd ex);89.if(!file.exists()){90.file.mkdir();91.}92.this.writer = IndexUtil.getIndexWriter(parentIndexPath +"/index"+subIndex, true);93.} catch (IOException e) {94.// TODO Auto-generated catch block95. e.printStackTrace();96.};97.this.subIndex = subIndex;98.this.countDownLatch1 = countDownLatch1;99.this.countDownLatch2 = countDownLatch2;100.this.list = list;101.}102.public BaseIndex(String path,CountDownLatch count DownLatch1, CountDownLatch countDownLatch2,103.List<T> list) {104.super();105.try {106.//单目录索引创建107.File file = new File(path);108.if(!file.exists()){109.file.mkdir();110.}111.this.writer = IndexUtil.getIndexWriter(path,true);112.} catch (IOException e) {113.// TODO Auto-generated catch block114. e.printStackTrace();115.};116.this.countDownLatch1 = countDownLatch1;117.this.countDownLatch2 = countDownLatch2;118.this.list = list;119.}120.121./**创建索引122.* @param writer123.* @param carSource124.* @param create125.* @throws IOException126.* @throws ParseException127.*/128.public abstract void indexDoc(IndexWriter writer,T t) throws Exception;129./**批量索引创建130.* @param writer131.* @param t132.* @throws Exception133.*/134.public void indexDocs(IndexWriter writer,List<T> t) th rows Exception{135.for (T t2 : t) {136.indexDoc(writer,t2);137.}138.}139./**带group的索引创建140.* @param writer141.* @param docs142.* @throws IOException143.*/144.public void indexDocsWithGroup(IndexWriter writer,S tring groupFieldName,String groupFieldValue,List<Document> docs) throws IOException{145.Field groupEndField = new Field(groupFieldName, gro upFieldValue, Field.Store.NO, Field.Index.NOT_ANALYZED);146.docs.get(docs.size()-1).add(groupEndField);147.//148.writer.updateDocuments(new Term(groupFieldName, groupFieldValue),docs);mit();150.writer.close();151.}152.@Override153.public void run() {154.try {155.countDownLatch1.await();156.System.out.println(writer);157.indexDocs(writer,list);158.} catch (InterruptedException e) {159.// TODO Auto-generated catch block160. e.printStackTrace();161.} catch (Exception e) {162.// TODO Auto-generated catch block163. e.printStackTrace();164.}finally{165.countDownLatch2.countDown();166.try {mit();168.writer.close();169.} catch (IOException e) {170.// TODO Auto-generated catch block171. e.printStackTrace();172.}173.}174.}175.}相应得,document的索引需要利用DirectoryTaxonomyWriter 来进行原有document的处理[java] view plain copy1.package com.lucene.index;2.3.import java.util.List;4.import java.util.concurrent.CountDownLatch;5.6.import org.apache.lucene.document.Document;7.import org.apache.lucene.document.Field;8.import org.apache.lucene.document.LongField;9.import org.apache.lucene.document.StringField;10.import org.apache.lucene.document.TextField;11.import org.apache.lucene.facet.FacetField;12.import org.apache.lucene.index.IndexWriter;13.import org.apache.lucene.index.IndexWriterConfig;14.import org.apache.lucene.index.Term;15.16.import com.lucene.bean.FileBean;17.18.public class FileBeanIndex extends BaseIndex<FileBe an>{19.private static String facet;20.21.public FileBeanIndex(IndexWriter writer, CountDownL atch countDownLatch12, CountDownLatch countDownLatch1,22.List<FileBean> fileBeans, String facet1) {23.super(writer, countDownLatch12, countDownLatch1, fi leBeans, facet);24.facet = facet1;25.}26.@Override27.public void indexDoc(IndexWriter writer, FileBean t) t hrows Exception {28.Document doc = new Document();29.String path = t.getPath();30.System.out.println(t.getPath());31.doc.add(new StringField("path", path, Field.Store.YES)) ;32.doc.add(new LongField("modified", t.getModified(), Fi eld.Store.YES));33.doc.add(new TextField("content", t.getContent(), Field. Store.YES));34.doc.add(new FacetField("filePath", new String[]{facet}) );35.//doc = config.build(taxoWriter,doc);36.if (writer.getConfig().getOpenMode() == IndexWriterC onfig.OpenMode.CREATE){37.//writer.addDocument(doc);38.writer.addDocument(this.config.build(taxoWriter, doc) );39.}else{40.writer.updateDocument(new Term("path", t.getPath()), this.config.build(taxoWriter, doc));41.}mit();43.}44.45.46.}测试facet功能的测试类:[java] view plain copy1.package com.lucene.search;2.3.import java.io.IOException;4.import java.nio.file.Paths;5.import java.util.ArrayList;6.import java.util.List;7.8.import org.apache.lucene.facet.FacetResult;9.import org.apache.lucene.facet.Facets;10.import org.apache.lucene.facet.FacetsCollector;11.import org.apache.lucene.facet.FacetsConfig;12.import belAndValue;13.import org.apache.lucene.facet.taxonomy.FastTaxono myFacetCounts;14.import org.apache.lucene.facet.taxonomy.TaxonomyR eader;15.import org.apache.lucene.facet.taxonomy.directory.Dir ectoryTaxonomyReader;16.import org.apache.lucene.index.DirectoryReader;17.import org.apache.lucene.search.IndexSearcher;18.import org.apache.lucene.search.MatchAllDocsQuery;19.import org.apache.lucene.search.Query;20.import org.apache.lucene.store.Directory;21.import org.apache.lucene.store.FSDirectory;22.import org.junit.Test;23.24.public class TestSearchFacet {25.public static Directory directory;26.public static Directory taxoDirectory;27.public static TaxonomyReader taxoReader;28.protected final static FacetsConfig config = new Face tsConfig();29.static {30.try {31.directory = FSDirectory.open(Paths.get("index", new S tring[0]));32.taxoDirectory = FSDirectory.open(Paths.get("index1", new String[0]));33.taxoReader = new DirectoryTaxonomyReader(taxoDire ctory);34.} catch (IOException e) {35.// TODO Auto-generated catch block36. e.printStackTrace();37.}38.}39.40.public static void testSearchFacet() {41.try {42.DirectoryReader indexReader = DirectoryReader.open( directory);43.IndexSearcher searcher = new IndexSearcher(indexRe ader);44.FacetsCollector fc = new FacetsCollector();45.FacetsCollector.search(searcher, new MatchAllDocsQu ery(), indexReader.maxDoc(), fc);46.Facets facets = new FastTaxonomyFacetCounts(taxoRe ader, config, fc);47.List<FacetResult> results =facets.getAllDims(100);48.for (FacetResult facetResult : results) {49.System.out.println(facetResult.dim);belAndValue[] values = belValues;51.for (LabelAndValue labelAndValue : values) {52.System.out.println("\t"+bel +" "+l abelAndValue.value);53.}54.55.}56.57.indexReader.close();58.taxoReader.close();59.} catch (IOException e) {60.// TODO Auto-generated catch block61. e.printStackTrace();62.}63.}64.65.public static void main(String[] args) {66.testSearchFacet();67.}68.69.}。
前沿写这点文字是面向lucene初中级用户,站在一个编辑加工的角度,对lucene进行解释,使抽象的概念更容易被大家理解,也尽可能节省大家搜集资料之苦,在这里广纳资源,采其精华,供大家分享。
一、背景简介Lucene 是一个基于 Java 的全文检索工具包,你可以利用它来为你的应用程序加入索引和检索功能。
Lucene 目前是著名的 Apache Jakarta 家族中的一个开源项目/lucene/,很多Java项目都使用了Lucene作为其后台的全文索引引擎,比较著名的有:∙Jive:WEB论坛系统;∙Eyebrows:邮件列表HTML归档/浏览/查询系统,本文的主要参考文档“TheLucene search engine: Powerful, flexible, and free”作者就是EyeBrows系统的主要开发者之一,而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。
∙Cocoon:基于XML的web发布框架,全文检索部分使用了Lucene ∙Eclipse:基于Java的开放开发平台,帮助部分的全文索引使用了Lucene∙IBM社区:网站的搜索功能。
对于中文用户来说,最关心的问题是其是否支持中文的全文检索。
但通过后面对于Lucene的结构的介绍,你会了解到由于Lucene良好架构设计,对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。
二、demo运行1、demo的配置目前最新的版本是lucene-2.2.0 ,下载解压后(或附件包中)看到luceneweb.war,lucene-demos-2.2.0.jar,lucene-core-2.2.0.jar三个文件。
0).自持demo运行:在环境变量classpath里面配置加入lucene-demos-2.2.0.jar,lucene-core-2.2.0.jar的存放路径。
手工在C盘根目录下面建立目录luceneindex(存放索引文件),demo(用来测试的文件,放入一些html、text 文件)。
放到其他位置也可以,只是在下面执行命令的路径处作相应更改。
转到cmd工作模式下,打入命令:(创建测试文件的索引文件)java org.apache.lucene.demo.IndexHTML –create –index ./c://luce neindex ./ c://demoOK!如果看到adding /usr/local/man/man1/mysql.1...........adding /usr/local/man/man1/cvs.11614 total milliseconds就会将demo目录下的文件建立索引,并将索引文件存到luceneindex目录下面。
打入命令:(检索索引文件)java org.apache.lucene.demo.SearchFiles出现Query:OK!Lucene自待的demo运行成功,输入检索值,如果你的测试文档中有相关关键字即可出现如下类似结果Query: passwordSearching for: password7 total matching documents0. /usr/local/man/man1/mysql.1......6. /usr/local/man/man1/mysqlshow.11)、luceneweb.war的运行:luceneweb.war复制到tomcat的webapp目录下,假定tomcat装在$TOMCATHOME目录下,具体应用时用真实的目录替换$TOMCATHOME。
cd $TOMCATHOME/webappsmkdir lucenedbcd lucenedbjava org.apache.lucene.demo.IndexHTML -create -index$TOMCAT/webapps/lucenedb ../examples<--用相对路径“..”,一来指明被索引的文件的位置,二来用来显示被索引文件的URL,因为检索的jsp程序在luceneweb子目录下.examples可用其它的真实应用的目录名来替换cd ..cp~/lucene-1.2/luceneweb.war . <--luceneweb.war在你解压缩生成的lucene-1.2目录下../bin/shudown.sh../bin/startup.sh然后通过客户端访问:8080/luceneweb,如果顺利浏览器应出现右边所示的内容。
.再到服务器端cd lucenewebvi configuration.jsp <--将indexLocation 的值改为"$TOMCATHOME/webapps/lucenedb";cd ..jar -ur luceneweb.war luceneweb再到客户端,刷新刚才的页面,然后就可以输入单词进行检索了。
2、原理分析0)设有两篇文章1和2文章1的内容为:Tom lives in Guangzhou,I live in Guangzhou too.文章2的内容为:He once lived in Shanghai.1)全文分析:由于lucene是基于关键词索引和查询的,首先我们要取得这两篇文章的关键词,通常我们需要如下处理措施a.我们现在有的是文章内容,即一个字符串,我们先要找出字符串中的所有单词,即分词。
英文单词由于用空格分隔,比较好处理。
中文单词间是连在一起的需要特殊的分词处理。
b.文章中的”in”, “once” “too”等词没有什么实际意义,中文中的“的”“是”等字通常也无具体含义,这些不代表概念的词可以过滤掉c.用户通常希望查“He”时能把含“he”,“HE”的文章也找出来,所以所有单词需要统一大小写。
d.用户通常希望查“live”时能把含“lives”,“lived”的文章也找出来,所以需要把“lives”,“lived”还原成“live”e.文章中的标点符号通常不表示某种概念,也可以过滤掉在lucene中以上措施由Analyzer类完成经过上面处理后文章1的所有关键词为:[tom] [live] [guangzhou] [i] [live] [guangzhou] 文章2的所有关键词为:[he] [live] [shanghai]2) 倒排索引:有了关键词后,我们就可以建立倒排索引了。
上面的对应关系是:“文章号”对“文章中所有关键词”。
倒排索引把这个关系倒过来,变成:“关键词”对“拥有该关键词的所有文章号”。
文章1,2经过倒排后变成关键词文章号guangzhou 1he 2i 1live 1,2shanghai 2tom 1通常仅知道关键词在哪些文章中出现还不够,我们还需要知道关键词在文章中出现次数和出现的位置,通常有两种位置:a)字符位置,即记录该词是文章中第几个字符(优点是关键词亮显时定位快);b)关键词位置,即记录该词是文章中第几个关键词(优点是节约索引空间、词组(phase)查询快),lucene 中记录的就是这种位置。
加上“出现频率”和“出现位置”信息后,我们的索引结构变为:以live 这行为例我们说明一下该结构:live在文章1中出现了2次,文章2中出现了一次,它的出现位置为“2,5,2”这表示什么呢?我们需要结合文章号和出现频率来分析,文章1中出现了2次,那么“2,5”就表示live在文章1中出现的两个位置,文章2中出现了一次,剩下的“2”就表示live是文章2中第 2个关键字。
以上就是lucene索引结构中最核心的部分。
我们注意到关键字是按字符顺序排列的(lucene没有使用B树结构),因此lucene可以用二元搜索算法快速定位关键词。
实现时 lucene将上面三列分别作为词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)保存。
其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。
Lucene中使用了field的概念,用于表达信息所在位置(如标题中,文章中,url 中),在建索引中,该field信息也记录在词典文件中,每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。
为了减小索引文件的大小,Lucene对索引还使用了压缩技术。
首先,对词典文件中的关键词进行了压缩,关键词压缩为<前缀长度,后缀>,例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>。
其次大量用到的是对数字的压缩,数字只保存与上一个值的差值(这样可以减小数字的长度,进而减少保存该数字需要的字节数)。
例如当前文章号是16389(不压缩要用3个字节保存),上一文章号是16382,压缩后保存7(只用一个字节)。
注意是“上一个词”。
由于词典是按顺序排列的,这种压缩方法的效果会非常显著。
下面我们可以通过对该索引的查询来解释一下为什么要建立索引。
假设要查询单词“live”,lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。
词典通常非常小,因而,整个过程的时间是毫秒级的。
而用普通的顺序匹配算法,不建索引,而是对所有文章的内容进行字符串匹配,这个过程将会相当缓慢,当文章数目很大时,时间往往是无法忍受的。
全文检索框架的实现机制:Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表==>记录==>字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。
总体上看:可以先把Lucene当成一个支持全文索引的数据库系统。
比较一下Lucene和数据库:全文检索≠ like "%keyword%"由于数据库索引不是为全文索引设计的,因此,使用like "%keyword%"时,数据库索引是不起作用的,在使用like查询时,搜索过程又变成类似于一页页翻书的遍历过程了,所以对于含有模糊查询的数据库服务来说,LIKE对性能的危害是极大的。
如果是需要对多个关键词进行模糊匹配:like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。
通常比较厚的书籍后面常常附关键词索引表(比如:北京:12, 34页,上海:3,77页……),它能够帮助读者比较快地找到相关内容的页码。
而数据库索引能够大大提高查询的速度原理也是一样,想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高,另外一个原因是它是排好序的。