一 .什么是Lucene
lucene是一款高性能的、可扩展,纯java语言编写的信息检索(IR)工具库
它适合几乎任何需要全文本搜索(特别是跨平台)的应用程序
http://lucene.apache.org/java
二 .Lucene的原理
lucene是基于关键词索引和查询
全文分析:把文本解析为一个个关键字存储到索引文件中倒排索引: (英语:Invertedindex),也常被称为反向索引、置入档案或反向档案,是一种索引方法,
被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构
设有两篇文章1和2
文章1的内容为:
Tom lives in Guangzhou,I live in Guangzhoutoo.
文章2的内容为:
He once lived inShanghai.
)全文分析
首先我们要取得这两篇文章的关键词,通常我们需要如下处理措施
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经过倒排后变成
通常仅知道关键词在哪些文章中出现还不够,我们还需要知道关键词在文章中出现次数和出现的位置,通常有两种位置:
a)字符位置,即记录该词是文章中第几个字符(优点是关键词亮显时定位快);
b)关键词位置,即记录该词是文章中第几个关键词(优点是节约索引空间、词组(phase)查询快),lucene中记录的就是这种位置。
频率来分析,文章1中出现了2次,那么“2,5”就表示live在文章1中出现的两个位置,文章2中出现了一次,剩下的“2”就表示live是文章2中第2个关键字
三.简单的Lucene的调用
创建maven项目 ,在pom.xml中配置分词器的架包
<dependencies>
<!-- 添加一个分词器的jar包 -->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
简单的搜索和创建索引库
package cn.et;
import java.io.File;
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class IndexDemo {
static String drc="E:\\index";
//定义存储目录
static Analyzer aly= new IKAnalyzer();
public static void main(String[] args) throws Exception {
search();
}
//搜索
public static void search() throws Exception {
Directory dr= FSDirectory.open(new File(drc));
//读取存储目录
DirectoryReader ird=DirectoryReader.open(dr);
//搜索类
IndexSearcher isearcher=new IndexSearcher(ird);
//Lucene查询解析 用于指定查询的属性名和分词器
QueryParser parser = new QueryParser(Version.LUCENE_47,"userDesc", aly);
//开始搜索
Query query=parser.parse("来");
//获取搜索的结果指定返回的docuemnt个数
ScoreDoc[] hits= isearcher.search(query, null, 10).scoreDocs;
for(int i=0;i<hits.length;i++){
//获取单独的document
Document hitDoc = isearcher.doc(hits[i].doc);
System.out.println(hitDoc.getField("userName").stringValue());
}
ird.close();
dr.close();
}
//创建索引库
public static void write() throws IOException{
//索引库的存储目录
Directory dr= FSDirectory.open(new File(drc));
//管联Lucene版本和当前分词器
IndexWriterConfig cfi= new IndexWriterConfig(Version.LUCENE_47,aly);
//传入目录和分词器
IndexWriter iwriter = new IndexWriter(dr,cfi);
//document对象Field属性
Document doc= new Document();
//TextField.TYPE_STORED 写入存储目录
Field field= new Field("userName","张三",TextField.TYPE_STORED);
doc.add(field);
field= new Field("userDesc","张三来自永州,喜欢吃永州血鸭",TextField.TYPE_STORED);
doc.add(field);
Document doce= new Document();
//TextField.TYPE_STORED 写入存储目录
Field field1= new Field("userName","李四",TextField.TYPE_STORED);
doce.add(field1);
field1= new Field("userDesc","李四来自北京,喜欢吃烤鸭",TextField.TYPE_STORED);
doce.add(field1);
//写入存储目录
iwriter.addDocument(doc);
iwriter.addDocument(doce);
iwriter.commit();
iwriter.close();
}
}
添加高亮pom.xml依赖
<!-- 加入高亮的jar包 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.7.2</version>
</dependency>
/**
* 搜索
*/
@Override
public List<Map> search(String field, String value) throws Exception{
Directory directory = FSDirectory.open(new File(dir));
//读取索引库的存储目录
DirectoryReader ireader = DirectoryReader.open(directory);
//搜索类
IndexSearcher isearcher = new IndexSearcher(ireader);
//lucence查询解析 用于指定查询的属性名和分词器
QueryParser parser = new QueryParser(Version.LUCENE_47, field, analyzer);
//开始搜索
Query query = parser.parse(value);
//最终结果被分词后添加前缀和后缀的处理页<B></B>
SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter("<font color=red>","</font>");
//将高亮搜索的词 添加到高亮处理器中
Highlighter highlighter = new Highlighter(htmlFormatter, new QueryScorer(query));
//获取搜索的结果 指定返回的docuemnt个数
ScoreDoc[] hits = isearcher.search(query, null, 10).scoreDocs;
List<Map> list=new ArrayList<Map>();
for (int i = 0; i < hits.length; i++) {
int id = hits[i].doc;
Document hitDoc = isearcher.doc(hits[i].doc);
Map map=new HashMap();
map.put("foodid", hitDoc.get("foodid"));
String foodname=hitDoc.get("foodname");
//将查询的结果和搜索词匹配 匹配到添加前缀和后缀高亮
TokenStream tokenStream = TokenSources.getAnyTokenStream(isearcher.getIndexReader(), id, "foodname", analyzer);
//传入的第二个参数是查询的值
TextFragment[] frag = highlighter.getBestTextFragments(tokenStream, foodname, false, 200);
String foodnameHign="";
for (int j = 0; j < frag.length; j++) {
if ((frag[j] != null) && (frag[j].getScore() > 0)) {
foodnameHign=((frag[j].toString()));
}
}
map.put("foodname",foodnameHign);
map.put("price", hitDoc.get("price"));
map.put("imagepath", hitDoc.get("imagepath"));
list.add(map);
}
ireader.close();
directory.close();
return list;
}