Lucene
为什么使用Lucene(全文检索)
应用场景:搜索引擎(全文检索)
站内搜索:淘宝站内商品的搜索,博客园找找看 等
基本概念
什么是全文检索
全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程
建立索引的过程:全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置
查询过程:当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程
全文检索(Full-Text Retrieval)以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。
全文检索的特点
-
只处理文本数据
-
不处理语义,数据的匹配,不做语义分析
-
搜索时英文不区分大小写
-
结果列表有相关度排序
相关度分数:搜索关键词和结果数据 匹配程度 Lucene自动计算 可以人为修改
全文检索和模糊匹配的区别
模糊匹配的缺点
-
数据不够准备 模糊匹配要求 搜索关键词的每一个字以及顺序 都需要和数据库中的数据 一一对应 才能够找结果
需求:因为用户输入的关键词 千奇百怪 不可能做到和数据库的原始数据 一一对应 所以通过模糊匹配很难的到结果
-
几乎不能使用上索引
除了右模糊 都使用不了索引【执行计划是确定一条sql能不能使用索引的标准】
全文检索的优点
-
搜索结果更加准确 全面
-
效率更高
文检索的速度大大快于SQL的like搜索的速度。这是因为查询方式不同造成的,以查字典举例:数据库的like就是一页一页的翻,一行一行的找,而全文检索是先查目录,得到结果所在的页码,再直接翻到这一页
-
相关度排序
查出的结果没有相关度排序,不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果
基本使用
导入依赖
<!--核心依赖-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.4.0</version>
</dependency>
<!--分词器 对文本做分词处理-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.4.0</version>
</dependency>
<!--智能分词器-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>4.4.0</version>
</dependency>
<!--查询-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.4.0</version>
</dependency>
索引的创建
/**
* 创建索引
*/
@Test
public void test1() throws IOException {
Integer id = 1;
String title = "背影";
String author = "朱自清";
String content = "你站在这里不要动,我去给你买几个橘子";
/**
* 1.准备文章数据 将数据封装在Document对象 文档对象
* Document add方法封装数据 参数为Field对象
* 八种基本类型和StringField 暂时可以认为不分词
* TextField 文本Field 会做分词处理 需要被查询的数据 要定义为文本Field
* Field
* 参数1 属性名
* 参数2 属性值
* 参数3 Field.Store.YES 固定写法
*/
Document document = new Document();
document.add(new IntField("id",id, Field.Store.YES));
document.add(new TextField("title",title, Field.Store.YES));
document.add(new TextField("author",author, Field.Store.YES));
document.add(new TextField("content",content, Field.Store.YES));
/**
* 2.通过程序扫描文章数据创建索引
* IndexWriter 索引写出对象(相当于一个流) 把文档对象写出到索引库 在写出的过程中会自动做分词处理 并且 创建索引
* IndexWriter 对象
* 参数1 定义索引库的位置 FSDirectory directory = FSDirectory.open(new File("E://lucene"));
* 参数2 定义索引创建的配置 索引配置对象
* IndexWriterConfig
* 参数1 当前Lucene的版本号
* 参数2 分词器对象 分词器:对文本做分词处理 StandardAnalyzer 标准分词器 StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
*/
FSDirectory directory = FSDirectory.open(new File("E://lucene"));
StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_44,analyzer);
IndexWriter indexWriter = new IndexWriter(directory,config);
/**
* 将文章数据 给 索引写出对象
*/
indexWriter.addDocument(document);
/**
* 将文章数据提交到索引库 会自动做分词处理 并且 创建索引
*/
indexWriter.commit();
/**
* 释放资源
*/
indexWriter.close();
}
通过搜索关键词查询
@Test
public void test2() throws ParseException, IOException {
/**
* 1.准备搜索关键词
*/
String keywords = "朱自清的文章";
/**
* 2.处理搜索关键词
* MultiFieldQueryParser 多列属性查询处理的对象
* 参数1 Lucene的版本号
* 参数2 属性名的数组
* 参数3 分词器对象 需要保证和创建索引时候用的一个分词器
* parse() 处理关键词 得到一个Query对象
*/
String[] fields = {"title","author","content"};
StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44);
MultiFieldQueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_44,fields,analyzer);
Query query = queryParser.parse(keywords);
/**
* 3.通过处理好的关键词 去 索引库中查询 得到索引(索引词+最终数据的位置信息id)
*/
DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("E://lucene")));
IndexSearcher searcher = new IndexSearcher(reader);
/**
* search 方法
* 参数1 Query 对象
* 参数2 期望的结果条数
*
* TopDocs 结果集 包含了结果的数据(索引)
*/
TopDocs topDocs = searcher.search(query, 10);
/**
* 处理结果集
* scoreDocs 包含有分数 和 文档对象的id(原始数据)(位置信息)
*/
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
/**
* 4.通过位置信息 得到数据
*/
for (ScoreDoc scoreDoc : scoreDocs) {
System.out.println(scoreDoc.score);
/**
* docId 文档对象在索引库中的位置信息
*/
int docId = scoreDoc.doc;
/**
* 通过位置信息 得到数据 Document
*/
Document document = searcher.doc(docId);
String id = document.get("id");
System.out.println(id);
String title = document.get("title");
System.out.println(title);
String author = document.get("author");
System.out.println(author);
String content = document.get("content");
System.out.println(content);
}
}
删除索引库
第一天作业
-
练习demo 【两遍】
-
实现我给的接口
作业讲解
/**
* 将所有文章数据添加至索引库
*/
@Test
public void test4() throws IOException {
// 文章数据
List<CmfzArticle> cmfzArticles = cmfzArticleDao.selectList(null);
/**
* 1.封装数据 到文档对象中 List<Document>
*/
List<Document> documents = new ArrayList<>();
for (CmfzArticle cmfzArticle : cmfzArticles) {
Document document = new Document();
document.add(new IntField("articleId",cmfzArticle.getArticleId(), Field.Store.YES));
/**
* StringField 不分词
* TextField 会分词处理 需要被查询的数据 要定义为文本Field
*/
document.add(new TextField("articleName",cmfzArticle.getArticleName(), Field.Store.YES));
document.add(new TextField("articleContent",cmfzArticle.getArticleContent(), Field.Store.YES));
/**
* 时间 可以 long 类型 String
*/
document.add(new LongField("articleDate",cmfzArticle.getArticleDate().getTime(), Field.Store.YES));
String s = DateTools.dateToString(cmfzArticle.getArticleDate(), DateTools.Resolution.DAY);
System.out.println(s);
documents.add(document);
}
/**
* 2.通过索引写出对象 写出到索引库
*/
FSDirectory directory = FSDirectory.open(new File("E://lucene"));
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_44,new StandardAnalyzer(Version.LUCENE_44));
IndexWriter indexWriter = new IndexWriter(directory,config);
indexWriter.addDocuments(documents);
indexWriter.commit();
indexWriter.close();
}
/**
* 查询上师文章
*/
@Test
public void test5() throws ParseException, IOException {
/**
* 1.准备搜索关键词
*/
String keyWords = "曾经有一位上师";
/**
* 2.处理关键词
*/
String[] ss = {"articleName","articleContent"};
MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_44,ss,new StandardAnalyzer(Version.LUCENE_44));
Query query = parser.parse(keyWords);
/**
* 3.查询
*/
DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("E://lucene")));
IndexSearcher indexSearcher = new IndexSearcher(reader);
TopDocs topDocs = indexSearcher.search(query, 10);
/**
* 4.处理结果集
*/
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
/**
* 通过id 查询数据
*/
Document document = indexSearcher.doc(docId);
String articleId = document.get("articleId");
String articleName = document.get("articleName");
String articleContent = document.get("articleContent");
String articleDate = document.get("articleDate");
CmfzArticle cmfzArticle = new CmfzArticle();
cmfzArticle.setArticleId(Integer.parseInt(articleId));
cmfzArticle.setArticleName(articleName);
cmfzArticle.setArticleContent(articleContent);
// Long类型转时间
long l = Long.parseLong(articleDate);
Date date = new Date(l);
cmfzArticle.setArticleDate(date);
// 日期转String
String s = DateTools.dateToString(date, DateTools.Resolution.DAY);
System.out.println(s);
System.out.println(cmfzArticle);
}
}
索引库的基本结构
封装工具类
package com.baizhi.util;
import com.baizhi.entity.CmfzArticle;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class LuceneUtil {
private static FSDirectory directory = null;
private static Analyzer analyzer = null;
/**
* 做成员变量的初始化
*/
static {
try {
directory = FSDirectory.open(new File("E://lucene"));
analyzer = new StandardAnalyzer(Version.LUCENE_44);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取 IndexWriter
* @return
*/
public static IndexWriter getIndexWriter(){
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_44,analyzer);
IndexWriter indexWriter = null;
try {
indexWriter = new IndexWriter(directory,config);
} catch (IOException e) {
e.printStackTrace();
}
return indexWriter;
}
/**
* 文章对象 转换 文档对象
*/
public static List<Document> cmfzArticleToDocument(List<CmfzArticle> cmfzArticles){
List<Document> documents = new ArrayList<>();
for (CmfzArticle cmfzArticle : cmfzArticles) {
Document document = new Document();
document.add(new IntField("articleId",cmfzArticle.getArticleId(), Field.Store.YES));
/**
* StringField 不分词
* TextField 会分词处理 需要被查询的数据 要定义为文本Field
*/
document.add(new TextField("articleName",cmfzArticle.getArticleName(), Field.Store.YES));
document.add(new TextField("articleContent",cmfzArticle.getArticleContent(), Field.Store.YES));
/**
* 时间 可以 long 类型 String
*/
document.add(new LongField("articleDate",cmfzArticle.getArticleDate().getTime(), Field.Store.YES));
documents.add(document);
}
return documents;
}
/**
* 处理关键词
* @param keyWords
* @return
*/
public static Query parsekeyWords(String keyWords){
String[] ss = {"articleName","articleContent"};
MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_44,ss,analyzer);
Query query = null;
try {
query = parser.parse(keyWords);
} catch (ParseException e) {
e.printStackTrace();
}
return query;
}
/**
* 获取IndexSearcher
*/
public static IndexSearcher getIndexSearcher(){
DirectoryReader reader = null;
try {
reader = DirectoryReader.open(directory);
} catch (IOException e) {
e.printStackTrace();
}
return new IndexSearcher(reader);
}
/**
* 处理结果集 得到文章对象集合
*/
public static List<CmfzArticle> scoreDocToCmfzArticle(ScoreDoc[] scoreDocs,IndexSearcher indexSearcher){
List<CmfzArticle> cmfzArticles = new ArrayList<>();
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
/**
* 通过id 查询数据
*/
Document document = null;
try {
document = indexSearcher.doc(docId);
} catch (IOException e) {
e.printStackTrace();
}
// 获取数据
String articleId = document.get("articleId");
String articleName = document.get("articleName");
String articleContent = document.get("articleContent");
String articleDate = document.get("articleDate");
// 封装对象
CmfzArticle cmfzArticle = new CmfzArticle();
cmfzArticle.setArticleId(Integer.parseInt(articleId));
cmfzArticle.setArticleName(articleName);
cmfzArticle.setArticleContent(articleContent);
// Long类型转时间
long l = Long.parseLong(articleDate);
Date date = new Date(l);
cmfzArticle.setArticleDate(date);
// 封装集合
cmfzArticles.add(cmfzArticle);
}
return cmfzArticles;
}
}
作业:
- 封装工具类
- 使用id 删除一个上师的数据
删除一个 和 修改
/**
* 删除一个 按照id int 需要使用查询Int的Query
*/
@Test
public void test6() throws IOException {
IndexWriter indexWriter = LuceneUtil.getIndexWriter();
/**
* Term 词语 术语 学期 在Lucene指 一个不能被分词的词语 最小单位 上师 针对的是StringField进行查询
* Query
*
* 方法1:通过int删除
* Range方法 是一个范围
* 参数1 属性名
* 参数2 开始id
* 参数3 结束id
* 参数4 true false 包不包含开始id
* 参数5 true false 包不包含结束id
*
* 方法2:通过String删除 假设id=123 "123"
* 1.存索引的时候 需要保证Id用的是 StringField
* 2.删除的时候 使用TermQuery来进行删除
*/
NumericRangeQuery<Integer> query = NumericRangeQuery.newIntRange("articleId", 1, 1, true, true);
TermQuery termQuery = new TermQuery(new Term("articleId", "1"));
indexWriter.deleteDocuments(termQuery);
indexWriter.commit();
indexWriter.close();
}
/**
* 修改
*/
@Test
public void test7() throws IOException {
IndexWriter indexWriter = LuceneUtil.getIndexWriter();
/**
* 参数1 Term对象 要修改那个id对应的数据
* 参数2 Document对象 要更新的数据
* 参数3 分词器
*/
Term articleId = new Term("articleId", "2");
Document document = new Document();
document.add(new StringField("articleId","2", Field.Store.YES));
/**
* StringField 不分词
* TextField 会分词处理 需要被查询的数据 要定义为文本Field
*/
document.add(new TextField("articleName","上师对弟子的评价与期望", Field.Store.YES));
document.add(new TextField("articleContent","春眠不觉晓", Field.Store.YES));
/**
* 时间 可以 long 类型 String
*/
document.add(new LongField("articleDate",new Date().getTime(), Field.Store.YES));
indexWriter.updateDocument(articleId,document,LuceneUtil.analyzer);
indexWriter.commit();
indexWriter.close();
}
分词器讲解
分词器:按照一定的规则,解析文本,提取关键词
分词规则:
- 关键词 会被创建为索引的词 例如:橘子 背影 等
- 停用词 不需要被创建索引的词 例如:的 呢 了 呵 等
什么是关键词 什么是停用词 是自定义的
不同的分词器 有不同的分词规则
测试常见的分词器
package com.baizhi.lucene;
import org.apache.lucene.analysis.Analyzer;
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.CharTermAttribute;
import org.apache.lucene.util.Version;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.IOException;
import java.io.StringReader;
/**
*
*/
public class AnalyzerTest {
private String text = "百知教育,上师,窃书不能算偷……窃书!……读书人的事,能算偷么?”接连便是难懂的话";
/**
* 测试分词规则 标准分词器
* 单个分词 每个字儿都是关键词
*
*/
@Test
public void test1() throws IOException {
test(new StandardAnalyzer(Version.LUCENE_44),text);
}
/**
* 智能中文分词器
*/
@Test
public void test2() throws IOException {
test(new SmartChineseAnalyzer(Version.LUCENE_44),text);
}
/**
* 中日韩分词器
* 分词规则 两两分词
*/
@Test
public void test3() throws IOException {
test(new CJKAnalyzer(Version.LUCENE_44),text);
}
/**
* ik分词器 庖丁分词器
*/
@Test
public void test4() throws IOException {
IKAnalyzer ikAnalyzer = new IKAnalyzer();
test(ikAnalyzer,text);
}
/**
* 该方法会把分词的结果打印出来
* @param analyzer 分词器对象
* @param text 文本 要进行分词的文本
* @throws IOException
*/
public static void test(Analyzer analyzer, String text) throws IOException {
System.out.println("当前分词器:--->"+analyzer.getClass().getName());
TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text));
tokenStream.addAttribute(CharTermAttribute.class);
tokenStream.reset();
while(tokenStream.incrementToken()){
CharTermAttribute attribute = tokenStream.getAttribute(CharTermAttribute.class);
System.out.println(attribute.toString());
}
tokenStream.end();
int a = 1;
}
}
自定义分词规则(使用IK分词器)
-
导入依赖
<!--ik中文分词器--> <dependency> <groupId>com.janeluo</groupId> <artifactId>ikanalyzer</artifactId> <version>2012_u6</version> </dependency>
-
导入配置文件
- 在配置文件(词典)中自定义分词规则
- 直接在项目中使用
高亮展示
问题:
- 如何实现表示?
<font color='red'>上师</font>
- 高亮展示添加给谁? 搜索关键词处理的结果
- 在什么地方给 关键词 添加 标签? 数据查询出来之后
代码实现
-
导入依赖
<!--高亮器--> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-highlighter</artifactId> <version>4.4.0</version> </dependency>
-
修改查询的代码
-
在查询过程中创建一个高亮器
-
使用高亮器处理查询结果
-
示例代码【没有封装工具类】
/** * 搜索 * @throws ParseException * @throws IOException */ @Test public void test2() throws ParseException, IOException, InvalidTokenOffsetsException { /** * 1.准备搜索关键词 */ String keywords = "朱自清的文章"; /** * 2.处理搜索关键词 * MultiFieldQueryParser 多列属性查询处理的对象 * 参数1 Lucene的版本号 * 参数2 属性名的数组 * 参数3 分词器对象 需要保证和创建索引时候用的一个分词器 * parse() 处理关键词 得到一个Query对象 */ String[] fields = {"title","author","content"}; StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_44); MultiFieldQueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_44,fields,analyzer); Query query = queryParser.parse(keywords); /** * 3.通过处理好的关键词 去 索引库中查询 得到索引(索引词+最终数据的位置信息id) */ DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("E://lucene"))); IndexSearcher searcher = new IndexSearcher(reader); /** * search 方法 * 参数1 Query 对象 * 参数2 期望的结果条数 * * TopDocs 结果集 包含了结果的数据(索引) */ TopDocs topDocs = searcher.search(query, 10); /** * 处理结果集 * scoreDocs 包含有分数 和 文档对象的id(原始数据)(位置信息) */ ScoreDoc[] scoreDocs = topDocs.scoreDocs; /** * 创建高亮器 * 参数1 高亮规则 要添加的样式 `<font color='red'></font>` * 参数2 搜索关键词分词的结果 再处理 QueryScorer:处理搜索关键词 */ SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>"); QueryScorer queryScorer = new QueryScorer(query); Highlighter highlighter = new Highlighter(formatter,queryScorer); /** * 4.通过位置信息 得到数据 */ for (ScoreDoc scoreDoc : scoreDocs) { System.out.println(scoreDoc.score); /** * docId 文档对象在索引库中的位置信息 */ int docId = scoreDoc.doc; /** * 通过位置信息 得到数据 Document */ Document document = searcher.doc(docId); String id = document.get("id"); System.out.println(id); String title = document.get("title"); System.out.println(title); String author = document.get("author"); /** * 通过高亮器 处理搜索结果 * 参数1 分词器 * 参数2 属性名 * 参数3 被处理文本 */ String author1 = highlighter.getBestFragment(LuceneUtil.analyzer, "author", author); System.out.println(author); System.out.println(author1); String content = document.get("content"); System.out.println(content); String date = document.get("date"); long l = Long.parseLong(date); Date date1 = new Date(l); System.out.println(date1); String s = DateTools.dateToString(date1, DateTools.Resolution.DAY); System.out.println(s); } }
-
高亮的问题
解决:非空判断
项目集成
作业:
- 将之前的分词器替换IK分词器
- 练习高亮的代码
- 完成项目集成 方案1
方案1【作业】:
- 重置索引库功能
- 高级检索功能 就是全文检索 将查询的结果展示在table中(不需要考虑分页)
方案2【了解】:
- 搜索引擎的入口
- 搜索结果展示页 【内容来自索引库】
- 结果详情页 【内容来自数据库】
问题解决
总结
- 应用场景出发:用户输入的关键词 千奇百怪 不可能做到和数据库的原始数据 一一对应 所以通过模糊匹配很难的到结果
- 模糊匹配的缺点
- 全文检索
- 索引创建
- 查询过程
- 全文特点
- 代码环节
- 索引的创建
- 准备数据 将数据封装在 Document 对象 文档对象
- 将数据写出到 索引库 在写的同时Lucene会自动的做分词处理 并且 创建索引
- 索引库位置的定义
- 索引写出的参数的定义 版本 分词器
- 将文档对象交给 索引写出对象
- 提交到索引库
- 释放资源
- 通过关键词查询
- 获取搜索关键词
- 处理搜索关键词
- 需要定义 要查询哪些属性 使用什么分词器
- 需要保证 查询时的分词器 和 创建索引时的分词器 是同一个
- 通过索引查询对象 读取索引库 直接调用方法查询 得到结果集
- 处理结果集 得到 文档对象的id (位置信息)
- 通过文档对象的id 再次查询 得到原始的数据 (Document 对象 )
- 索引库的数据结构
- 两个区域:索引区和元数据区
- Field.Store.YES 和 Field.Store.NO 区别
- 封装工具类 【常规操作】
- 删除和修改
- 根据 id 删除
- IntField
- StringField
- 根据 id 删除
- 分词器
- 分词规则
- 关键词
- 停用词
- IK分词器
- 分词规则
- 高亮展示 (搜索结果中关键词变色处理)
- 项目集成
- 后台集成方案:表格展示
- web端方案:搜索页面—>搜索结果展示页面【来自索引库】—>结果详情页【来自数据库】
- 索引的创建
补充参考资料
- 第三阶段补充课程 资料
- 博客 全文检索的原理 【了解】
全文检索
- 索引创建
- 查询过程
- 全文特点
- 代码环节
- 索引的创建
- 准备数据 将数据封装在 Document 对象 文档对象
- 将数据写出到 索引库 在写的同时Lucene会自动的做分词处理 并且 创建索引
- 索引库位置的定义
- 索引写出的参数的定义 版本 分词器
- 将文档对象交给 索引写出对象
- 提交到索引库
- 释放资源
- 通过关键词查询
- 获取搜索关键词
- 处理搜索关键词
- 需要定义 要查询哪些属性 使用什么分词器
- 需要保证 查询时的分词器 和 创建索引时的分词器 是同一个
- 通过索引查询对象 读取索引库 直接调用方法查询 得到结果集
- 处理结果集 得到 文档对象的id (位置信息)
- 通过文档对象的id 再次查询 得到原始的数据 (Document 对象 )
- 索引库的数据结构
- 两个区域:索引区和元数据区
- Field.Store.YES 和 Field.Store.NO 区别
- 封装工具类 【常规操作】
- 删除和修改
- 根据 id 删除
- IntField
- StringField
- 根据 id 删除
- 分词器
- 分词规则
- 关键词
- 停用词
- IK分词器
- 分词规则
- 高亮展示 (搜索结果中关键词变色处理)
- 项目集成
- 后台集成方案:表格展示
- web端方案:搜索页面—>搜索结果展示页面【来自索引库】—>结果详情页【来自数据库】
- 索引的创建
补充参考资料
- 第三阶段补充课程 资料
- 博客 全文检索的原理 【了解】