一、什么是全文检索?
全文检索首先将要查询的目标文档中的词提取出来,组成索引(相当于书的目录),通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索。
二、安装 Lucene
官网安装后解压;jdk要求:1.7以上
三、建立索引
四、查询索引
五、案例引导
(一)获取到原始内容【信息采集】
(二)创建文档
创建原始内容的目的是为了索引,在索引钱需要将原始内容创建成文档,文档中包含一个一个的域【field】,域中存储内容;
这里我们可以将磁盘上的一个文件当成一个document,document中包含一些field【file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容】;
(三)分析文档
将原始内容创建为包含域【field】的文档,需要对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元【语汇单元理解为一个一个的单词】。
(四)索引文档
对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到document;
注意: 创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构
传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。
六、Lucene入门程序
1、开发环境
jdk:1.7 Lucene包【Lucene-core-5.2.jar、Lucene-analyzers-common-5.2.jar、lucene-queryparser-5.2.jar】 其他包【commons-io-2.4.jar、junit-4.9.jar】
2、创建数据源目录
该目录存放要搜索的原始文件。
3、创建索引目录
该目录存放创建的索引文件
4、Field常用类型
下面列出了开发中常用的field类型,注意field的属性,并根据需求选择:
5、代码实现–导入索引
public void importIndex() throws IOException {
//获得索引库
Path path = Paths.get("D:\\java\\Luence\\index_io");
//打开索引库
FSDirectory dir = FSDirectory.open(path);
//创建分词器
Analyzer al = new IKAnalyzer();
//创建索引的写入配置对象
IndexWriterConfig iwc = new IndexWriterConfig(al);
//创建索引的Writer
IndexWriter iw = new IndexWriter(dir, iwc);
//采集原始文档
File files = new File("D:\\java\\Luence\\searchsource");
//获得文件夹下的所有文件
File[] file = files.listFiles();
//遍历每一个文件
for(File fl : file) {
//获得文件的属性
String fileName = fl.getName();
String content = FileUtils.readFileToString(fl);
//获得文件大小
long size = FileUtils.sizeOf(fl);
//获取路径
String path1 = fl.getPath();
Field fName = new TextField("fileName", fileName, Store.YES);
Field fcontent = new TextField("content", content, Store.YES);
Field fsize = new LongField("size", size, Store.YES);
Field fpath = new TextField("path", path1, Store.YES);
//创建文档对象
Document doc = new Document();
//把域加入到文档中
doc.add(fName);
doc.add(fcontent);
doc.add(fsize);
doc.add(fpath);
//把文档写入到索引库
iw.addDocument(doc);
}
//提交
iw.commit();
//关闭资源
iw.close();
}
七、Luke
Luke作为Lucene工具包中的一个工具,用于查询、修改Lucene的索引文件。
打开luke的方法: cmd运行: java -jar lukeall-4.10.3.jar
如果需要加载第三方分词器,如果需要加载第三方分词器,需通过java.ext.dirs加载jar包:可简单的将第三方分词器和lukeall放在一块儿,cmd下运行:java -Djava.ext.dirs=. -jar lukeall-4.10.3.jar
7.1使用luke查看索引
八、Analyzer分词器
8.1创建索引时使用Analyzer
输入关键字进行搜索,当需要让该关键字与文档域内容所包含的词进行匹配时需要对文档域内容进行分析,需要经过Analyzer分析器处理生成语汇单元(Token).分词器分析的对象是文档中的field。当field的属性tokenized(是否分词)为true时会对field值进行分析。
对于一些field课余不用分析:
①不作为查询条件的内容,比如文件路径;
②不是匹配内容中的词而匹配field的整体内容,比如订单号、身份证号等。
8.2Analyzer执行过程
Analyzer是一个抽象类,在Lucene的lucene-analyzers-common包中提供了很多分析器,比如:org.apache.lucene.analysis.standard.standardAnalyzer标准分词器,它是Lucene的核心分词器,它对分析文本进行分词、大写转成小写、去除停用词、去除标点符号等操作过程。
从一个Reader字符流开始,创建一个基于Reader的Tokenizer分词器,经过三个TokenFilter生成语汇单元Token.
8.3 TokenStream
TokenStream是语汇单元流,tokenStream是一个抽象类,它是所有分析器的基类;
TokenStream是分词器,负责将Reader转换为语汇单元即进行分词,Lucene提供了很多的分词器,也可以使用第三方的分词,比如IKAnalyzer一个中文分词器。
tokenFileter是分词过滤器,负责对语汇单元进行过滤,tokenfilter可以是一个过滤器链儿,Lucene提供了很多的分词器过滤器,比如大小写转换,去除停用词等。
8.4 分词器的使用
@Test
public void importAnalyzer() throws IOException {
//创建分词器
//Analyzer al = new StandardAnalyzer();
//Analyzer al = new CJKAnalyzzer();
Analyzer al = new IKAnalyzer();
//分词
TokenStream stream = al.tokenStream("content", "Serving Web Content with Spring mvc 中国红太阳升红红火火恍恍惚惚明明白白");
//分词对象的重置
stream.reset();
//获得每一个语汇的偏移量的属性对象
OffsetAttribute oa = stream.addAttribute(OffsetAttribute.class);
//获得分词的语汇属性
CharTermAttribute ca = stream.addAttribute(CharTermAttribute.class);
//遍历分词的语汇流
while(stream.incrementToken()) {
System.out.println("----------------");
System.out.println("开始索引:"+oa.startOffset()+"结束索引:"+oa.endOffset());
System.out.println(ca);
}
}
九、索引维护
9.1添加索引
步骤: (1)创建存放索引的目录Directory;(2)创建索引器配置管理类IndexWriterConfig;(3)使用索引目录和配置管理类创建索引器;(4)使用索引器将Document写到索引文件中;
@Test
public void importIndex() throws IOException {
IndexWriter iw = getIndexWriter();
//采集原始文档
File fl = new File("D:\\java\\Luence\\searchsource\\hibernate.txt");
//获得文件的属性
String fileName = fl.getName();
String content = FileUtils.readFileToString(fl);
//获得文件大小
long size = FileUtils.sizeOf(fl);
//获取路径
String path1 = fl.getPath();
//域
Field fName = new TextField("fileName", fileName, Store.YES);
Field fcontent = new TextField("content", content, Store.YES);
Field fsize = new LongField("size", size, Store.YES);
Field fpath = new TextField("path", path1, Store.YES);
//创建文档对象
Document doc = new Document();
//把域加入到文档中
doc.add(fName);
doc.add(fcontent);
doc.add(fsize);
doc.add(fpath);
//把文档写入到索引库
iw.addDocument(doc);
//提交
iw.commit();
//关闭资源
iw.close();
}
public IndexWriter getIndexWriter() throws IOException {
//获得索引库
Path path = Paths.get("D:\\java\\Luence\\index_io");
//打开索引库
FSDirectory dir = FSDirectory.open(path);
//创建分词器
Analyzer al = new StandardAnalyzer();
//创建索引的写入配置对象
IndexWriterConfig iwc = new IndexWriterConfig(al);
//创建索引的Writer
IndexWriter iw = new IndexWriter(dir, iwc);
return iw;
}
9.2删除索引
@Test
public void deleteIndex() throws IOException {
IndexWriter iw = getIndexWriter();
//删除所有
iw.deleteAll();
//提交
iw.commit();
//关闭
iw.close();
}
@Test
public void deleteIndexByQuery() throws Exception {
IndexWriter iw = getIndexWriter();
//创建语汇单元项
Term term = new Term("fileName","spring");
//创建根据语汇单元的查询对象
TermQuery query = new TermQuery(term);
iw.deleteDocuments(query);
//提交
iw.commit();
//关闭
iw.close();
}
9.3增加索引(一)
@Test
public void queryIndex() throws IOException {
//获得索引库
Path path = Paths.get("D:\\java\\Luence\\index_io");
FSDirectory open = FSDirectory.open(path);
//创建索引库的读取对象
DirectoryReader reader = DirectoryReader.open(open);
//创建索引库的搜索对象
IndexSearcher is = new IndexSearcher(reader);
//创建语汇单元的对象
Term term = new Term("fileName","教育");
//创建分词的语汇查询对象
TermQuery tq = new TermQuery(term);
//查询
TopDocs result = is.search(tq, 10);
//总记录数
int totalHits = result.totalHits;
System.out.println(totalHits);
for(ScoreDoc sd:result.scoreDocs) {
//获得文档的id
int id = sd.doc;
//获得文档对象
Document doc = is.doc(id);
String fileName = doc.get("fileName");
String size = doc.get("size");
String content = doc.get("content");
String path1 = doc.get("path");
System.out.println("文件名:"+fileName);
System.out.println("大小:"+size);
System.out.println("内容:"+content);
System.out.println("路径:"+path1);
System.out.println("-------------------------");
}
}
9.4增加索引(二)
通过Query搜索
NumericRangeQuery,指定数字范围查询,如下://文件大小在0到1024的文件
@Test
public void queryIndex() throws Exception {
IndexSearcher is = getDirReader();
//创建语汇单元的对象
//Term term = new Term("fileName","教育");
//创建分词的语汇查询对象
//TermQuery tq = new TermQuery(term);
//创建数值范围查询对象 注意0l和100l最后一位都是小写的L
Query tq = NumericRangeQuery.newLongRange("size", 0l, 100l, true, true);
System.out.println(tq);
printDoc(is, tq);
}
public static IndexSearcher getDirReader() throws Exception {
//获得索引库
Path path = Paths.get("D:\\java\\Luence\\index_io");
FSDirectory open = FSDirectory.open(path);
//创建索引库的读取对象
DirectoryReader reader = DirectoryReader.open(open);
//创建索引库的搜索对象
IndexSearcher is = new IndexSearcher(reader);
return is;
}
public static void printDoc(IndexSearcher is,Query tq) throws Exception {
//查询
TopDocs result = is.search(tq, 10);
//总记录数
int totalHits = result.totalHits;
System.out.println(totalHits);
for(ScoreDoc sd:result.scoreDocs) {
//获得文档的id
int id = sd.doc;
//获得文档对象
Document doc = is.doc(id);
String fileName = doc.get("fileName");
String size = doc.get("size");
String content = doc.get("content");
String path1 = doc.get("path");
System.out.println("文件名:"+fileName);
System.out.println("大小:"+size);
System.out.println("内容:"+content);
System.out.println("路径:"+path1);
System.out.println("-------------------------");
}
}
9.5增加索引(三)QueryParser查询
QueryParser使用方法:
//f是默认搜索的域
QueryParser queryParser = new QueryParser("f", analyzer);
// 指定查询语法 ,如果不指定fileName就搜索默认的域
Query query2 = queryParser.parse("fileName:springmvc.txt");
或者
Query query2 = queryPArser.parse("spring AND web");
案例:
@Test
public void queryIndex2() throws Exception {
IndexSearcher is = getDirReader();
//创建分词器
Analyzer al = new IKAnalyzer();
//创建查询解析器
QueryParser qp = new QueryParser("fileName", al);
//通过qp来解析查询对象
Query query = qp.parse("今天我们来学习全文检索技术Lucene");
System.out.println("打印查询条件:"+query);
printDoc(is, query);
}
@Test
public void queryIndex3() throws Exception {
IndexSearcher is = getDirReader();
//创建分词器
Analyzer al = new IKAnalyzer();
//创建查询解析器
QueryParser qp = new QueryParser("fileName", al);
//通过qp来解析查询对象
Query query = qp.parse("fileName:solr OR content:java");
System.out.println("打印查询条件:"+query);
printDoc(is, query);
}
9.6MultiFieldQueryParser组合域查询
通过MuliFieldQueryParse对多个域查询,比如商品信息查询,输入关键字需要从商品名称和商品内容中查询。
@Test
public void queryIndex5() throws Exception {
IndexSearcher is = getDirReader();
//创建分词器
Analyzer al = new IKAnalyzer();
//定义多个域
String[] fields = {"fileName","content"};
//创建查询解析对象
MultiFieldQueryParser mp = new MultiFieldQueryParser(fields, al);
Query query = mp.parse("今天我们学习全文检索技术Lucene");
Query tq = NumericRangeQuery.newLongRange("size", 0l, 1024l, true, true);
BooleanQuery bq = new BooleanQuery();
bq.add(query, Occur.MUST);
bq.add(tq, Occur.MUST);
System.out.println("打印查询条件:"+bq);
printDoc(is, bq);
}