一、Lucene:是一个世界上最流行的开源的全文检索框架java
官网网址:http://lucene.apache.orgmysql
版本:7.3.3sql
Jdk要求:1.8数据库
一、好比购物商城:假设经过传统的SQL语句进行书籍查询的时候 ,输入关键字‘Lucene实战’,进行查询的时候,查询字段中的数据必须有 ‘Lucene实战’这些关键字必须连在一块。 这是经过sql语句搞不定的apache
二、性能问题:在对大数据进行检索时,lucene的检索速度明显快于传统的sql检索api
三、实现高亮效果oracle
四、 作系统的站内检索,即对一个系统内的资源进行搜索。框架
如BBS、BLOG中的文章搜索,网上商店中的商品搜索、OA系统中公文检索、邮件检索……post
一、解压下载的Lucene压缩包,获得以下文件(Lucene/api/lucene-7.3.0)性能
analysis:分词器
core:Lucene核心jar包
demo:Lucene示例
docs:Lucene使用文档
highlighter:实现高亮效果
2、Lucene与mysql、oracle等数据库的比较
-一、mysql、oracle等数据库须要创建本身的数据库以及表
-二、Lucene须要创建本身的索引库
/** *添加索引(单字分词器) */ private static void addIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//建立分词器 单字分词器 Analyzer analyzer=new StandardAnalyzer(); //建立IndexWriterConfig实例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,则建立。存在,则追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引库中添加记录 Lucene中一个document实例表明数据表中的一条记录 Document document=new Document(); /* *StringFiled:不会对关键字进行分词 *TextFiled:会对关键字进行分词 * * Store.YES:会将数据进行存储并分词 * Store.NO:不会将数据进行存储,不会分词,索引仍是会建立。有索引,没有内容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "生活不止眼前的苟且", Store.YES)); document.add(new TextField("content", "妈妈坐在门前,哼着花儿与少年,虽已隔多年。", Store.YES));
indexWriter.addDocument(document);
//提交事务 indexWriter.commit(); //关闭事务 indexWriter.close();
System.out.println("添加成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 更新索引(单字分词器) */ private static void updateIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//建立分词器 单字分词器 Analyzer analyzer=new StandardAnalyzer(); //建立IndexWriterConfig实例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,则建立。存在,则追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引库中添加记录 Lucene中一个document实例表明数据表中的一条记录 Document document=new Document(); /* *StringFiled:不会对关键字进行分词 *TextFiled:会对关键字进行分词 * * Store.YES:会将数据进行存储并分词 * Store.NO:不会将数据进行存储,不会分词,索引仍是会建立。有索引,没有内容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "幽幽而来", Store.YES)); document.add(new TextField("content", "这世界真好呀", Store.YES));
//更新的原理:先删除以前的索引,再建立新的索引====删除与添加两个动做的合集 indexWriter.updateDocument(new Term("articalId","0001"), document);
//提交事务 indexWriter.commit(); //关闭事务 indexWriter.close();
System.out.println("更新成功"); } catch (Exception e) { e.printStackTrace(); } }
|
/** * 删除索引(单字分词器) */ private static void deleteIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//建立分词器 单字分词器 Analyzer analyzer=new StandardAnalyzer(); //建立IndexWriterConfig实例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,则建立。存在,则追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//删除索引库中指定的索引 indexWriter.deleteDocuments(new Term("articalId","0001"));
//删除索引库中所有的索引 //indexWriter.deleteAll();
//提交事务 indexWriter.commit(); //关闭事务 indexWriter.close();
System.out.println("删除成功"); } catch (Exception e) { e.printStackTrace(); } } |
a. IndexWriter : 对索引库进行CUD操做.
b. Directory : 存储的目录.
-- FSDirectory : 文件磁盘目录。
-- RAMDirectory: 内存中。
c. IndexWriterConfig: 指定建立索引须要的配置信息.
d. Analyzer : 分词器
e. OpenMode : 指定打开索引库的模式。
-- OpenMode.CREATE : 建立(每次都会建立).
-- OpenModel.APPEND : 追加模式
-- OpenMode.CREATE_OR_APPEND: 建立或追加模式。
f. Document: 文档
g. Store : 是否存储
-- YES: 存储
-- NO: 不存储
/** * 全文检索(单字分词器) */ private static void searchIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定须要读取的索引库信息,并返回相应的实例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher实例,经过IndexSearcher实例进行全文检索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//TermQuery:指定了查询的关键字以及查询哪个字段;不会对关键字进行分词 Query query=new TermQuery(new Term("title","生")); /*经过indexSearch进行检索,并指定两个参数 *第一个参数:封装查询的相关信息,好比查询的关键字、分词器,是否须要分词,或者须要分词的话,采起什么分词器 *第二个参数:最多只要多少条记录 * *查询数据,最终都封装在TopDocs的实例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //经过topDocs获取匹配所有记录 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("获取到的记录数"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //获取记录的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//获取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } } |
package org.crdit.com;
import java.util.Arrays;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.cjk.CJKAnalyzer; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; import org.apache.lucene.util.BytesRef;
/** * @author crd * 分词效果演示 */ public class AnalyzerTest { public static void main(String[] args) throws Exception { String str = "广西桂林好玩的地方";
//单字分词 //Analyzer analyzer = new StandardAnalyzer();
// 二分法分词 //Analyzer analyzer = new CJKAnalyzer();
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar //Analyzer analyzer = new SmartChineseAnalyzer();
// 词库分词 智能分词器 Analyzer analyzer = new SmartChineseAnalyzer(new CharArraySet(Arrays.asList("的", "了","啊"), true));//使用自定义的停用词集合
// 对指定的字符串进行分词 TokenStream ts = analyzer.tokenStream(null, str);
ts.reset(); // 先要对tokenStream执行reset
// 采用循环,不断地获取下一个token while(ts.incrementToken()) { // 获取其中token信息 TermToBytesRefAttribute attr = ts.getAttribute(TermToBytesRefAttribute.class); final BytesRef bytes = attr.getBytesRef(); System.out.println(bytes.utf8ToString()); } ts.close(); } }
|
英文分词器:
a. 按空格来分词。welcome to fkjava
b. 去掉停用词(stop word).
is、an、a、the、of、的、地、吗、嘛、了、得、标识符号(, ; :)
c. 大写字母转成小写字母。
中文分词器:
a、去掉停用词(stop word).
is、an、a、the、of、的、地、吗、嘛、了、得、标识符号(, ; :)
中华人民共和国,采用不一样分词器效果以下
单字分词器:(StandardAnalyzer):中|华|人|民|共|和|国
二分法分词(CJKAnalyzer):中华|华人|人民|民共|共和|和国
智能分词器(SmartChineseAnalyzer):中华|华人|人民|共和国
以上项目代码在code/01下
/** * 添加索引 */ private static void addIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb")); //词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer();
//建立IndexWriterConfig实例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,则建立。存在,则追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引库中添加记录 Lucene中一个document实例表明数据表中的一条记录 Document document=new Document(); /* *StringFiled:不会对关键字进行分词 *TextFiled:会对关键字进行分词 * * Store.YES:会将数据进行存储并分词 * Store.NO:不会将数据进行存储,不会分词,索引仍是会建立。有索引,没有内容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "桂林好玩的地方", Store.YES)); document.add(new TextField("content", "能够竹筏游漓江,观美景", Store.YES));
indexWriter.addDocument(document);
//提交事务 indexWriter.commit(); //关闭事务 indexWriter.close();
System.out.println("添加成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 更新索引 */ private static void updateIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer();
//建立IndexWriterConfig实例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,则建立。存在,则追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引库中添加记录 Lucene中一个document实例表明数据表中的一条记录 Document document=new Document(); /* *StringFiled:不会对关键字进行分词 *TextFiled:会对关键字进行分词 * * Store.YES:会将数据进行存储并分词 * Store.NO:不会将数据进行存储,不会分词,索引仍是会建立。有索引,没有内容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "幽幽而来", Store.YES)); document.add(new TextField("content", "这世界真好呀", Store.YES));
//更新的原理:先删除以前的索引,再建立新的索引====删除与添加两个动做的合集 indexWriter.updateDocument(new Term("articalId","0001"), document);
//提交事务 indexWriter.commit(); //关闭事务 indexWriter.close();
System.out.println("更新成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 删除索引 */ private static void deleteIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer();
//建立IndexWriterConfig实例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,则建立。存在,则追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//删除索引库中指定的索引 indexWriter.deleteDocuments(new Term("articalId","0001"));
//删除索引库中所有的索引 //indexWriter.deleteAll();
//提交事务 indexWriter.commit(); //关闭事务 indexWriter.close();
System.out.println("删除成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 全文检索 */ private static void searchIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定须要读取的索引库信息,并返回相应的实例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher实例,经过IndexSearcher实例进行全文检索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser实例 经过QueryParser能够指定须要查询的字段以及分词器信息 QueryParser queryParser=new QueryParser("title", analyzer); //经过queryParser实例获得query实例,并指定查询的关键字 Query query =queryParser.parse("桂林有哪些好玩的地方"); /*经过indexSearch进行检索,并指定两个参数 *第一个参数:封装查询的相关信息,好比查询的关键字、分词器,是否须要分词,或者须要分词的话,采起什么分词器 *第二个参数:最多只要多少条记录 * *查询数据,最终都封装在TopDocs的实例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //经过topDocs获取匹配所有记录 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("获取到的记录数"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //获取记录的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//获取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } } |
/** * 全文检索 */ private static void searchIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定须要读取的索引库信息,并返回相应的实例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher实例,经过IndexSearcher实例进行全文检索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser实例 经过MultiFieldQueryParser能够指定须要查询的多个字段以及分词器信息 QueryParser queryParser=new MultiFieldQueryParser(new String[]{"title","content"}, analyzer); //经过queryParser实例获得query实例,并指定查询的关键字 Query query =queryParser.parse("桂林");
/*经过indexSearch进行检索,并指定两个参数 *第一个参数:封装查询的相关信息,好比查询的关键字、分词器,是否须要分词,或者须要分词的话,采起什么分词器 *第二个参数:最多只要多少条记录 * *查询数据,最终都封装在TopDocs的实例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //经过topDocs获取匹配所有记录 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("获取到的记录数"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //获取记录的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//获取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } }
|
// 广州 默认Field里包含"广州"关键词 Query query1 = queryParser.parse("广州"); // 景区 OR 广州 默认Field里包含"景区"或 "广州" Query query2 = queryParser.parse("景区 广州"); //+白云山 +广州 (白云山 AND 广州) AND必须为大写 默认Field里包含"广州"和"白云山" Query query3 = queryParser.parse("广州 AND 白云山"); // title:广州 title字段中包含广州 Query query4 = queryParser.parse("title:广州"); //title:广州 -content:广州|景区 (title:广州 AND NOT content:广州|景区 ) //(title里包含广州,但content里不能包含"广州 或者 景区") Query query5 = queryParser.parse("title:广州 -content:广州|景区"); |
导包:
lucene-highlighter-7.3.0.jar
lucene-memory-7.3.0.jar
/** * 全文检索 */ private static void searchIndex() { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定须要读取的索引库信息,并返回相应的实例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher实例,经过IndexSearcher实例进行全文检索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser实例 经过MultiFieldQueryParser能够指定须要查询的多个字段以及分词器信息 QueryParser queryParser=new MultiFieldQueryParser(new String[]{"title","content"}, analyzer); //经过queryParser实例获得query实例,并指定查询的关键字 Query query =queryParser.parse("桂林");
//经过格式器指定咋样对关键字进行处理 Formatter formatter=new SimpleHTMLFormatter("<font color='red'>", "</font>"); //经过Scorer包装query Scorer fragmentScorer=new QueryScorer(query); //建立高亮器 Highlighter highlighter=new Highlighter(formatter, fragmentScorer);
//建立格式化片断 Fragmenter fragmenter=new SimpleFragmenter(10);
//将格式化片断实例与高亮器关联,最终经过高亮器对文本信息进行处理 highlighter.setTextFragmenter(fragmenter);
/*经过indexSearch进行检索,并指定两个参数 *第一个参数:封装查询的相关信息,好比查询的关键字、分词器,是否须要分词,或者须要分词的话,采起什么分词器 *第二个参数:最多只要多少条记录 * *查询数据,最终都封装在TopDocs的实例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //经过topDocs获取匹配所有记录 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("获取到的记录数"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //获取记录的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//获取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id);
String articalId= document.get("articalId"); //获取标题信息,高亮前title String title= document.get("title"); //获取内容信息,高亮前的content String content= document.get("content"); System.out.println("articalId:"+articalId+" 高亮前title:"+title+" 高亮前content:"+content);
/*经过高亮器对title和content进行高亮处理 *三个参数的含义: * 第一个:分词器 * 第二个:哪个字段进行高亮 * 第三个:须要进行高亮的文本 * 备注:若是须要高亮的文本信息不包括查询的关键字则getBestFragment会返回null */ String postTitle=highlighter.getBestFragment(analyzer, "title", title); String postContent=highlighter.getBestFragment(analyzer, "content", content);
System.out.println("articalId:"+articalId+" 高亮后title:"+postTitle+" 高亮后content:"+postContent);
} } catch (Exception e) { e.printStackTrace(); } }
|
/** * 全文检索 * @param pageIndex 当前页码 * @param pageSize 每页显示的记录数 */ private static void searchIndex(int pageIndex,int pageSize) { try { //指定索引所在目录 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定须要读取的索引库信息,并返回相应的实例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher实例,经过IndexSearcher实例进行全文检索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//词库分词 须要导jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser实例 经过MultiFieldQueryParser能够指定须要查询的多个字段以及分词器信息 QueryParser queryParser=new MultiFieldQueryParser(new String[]{"title","content"}, analyzer); //经过queryParser实例获得query实例,并指定查询的关键字 Query query =queryParser.parse("桂林");
/*经过indexSearch进行检索,并指定两个参数 *第一个参数:封装查询的相关信息,好比查询的关键字、分词器,是否须要分词,或者须要分词的话,采起什么分词器 *第二个参数:最多只要多少条记录 * *查询数据,最终都封装在TopDocs的实例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //经过topDocs获取匹配所有记录 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("获取到的记录数"+scoreDocs.length);
//定义查询的起始记录号以及结束的记录号 int startSize=(pageIndex-1)*pageSize; int endSize=pageIndex*pageSize>scoreDocs.length?scoreDocs.length:pageIndex*pageSize;
for (int i = startSize; i < endSize; i++) { //获取记录的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//获取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } } |