Lucene架构学习
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 【两遍】
实现我给的接口
作业讲解
/*** 将所有文章数据添加至索引库*/@Testpublic 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();}/*** 查询上师文章*/@Testpublic 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);
// 日期转StringString 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 = "百知教育,上师,窃书不能算偷……窃书!……读书人的事,能算偷么?”接连便是难懂的话";/*** 测试分词规则 标准分词器* 单个分词 每个字儿都是关键词**/@Testpublic void test1() throws IOException {test(new StandardAnalyzer(Version.LUCENE_44),text);}/*** 智能中文分词器*/@Testpublic void test2() throws IOException {test(new SmartChineseAnalyzer(Version.LUCENE_44),text);}/*** 中日韩分词器* 分词规则 两两分词*/@Testpublic void test3() throws IOException {test(new CJKAnalyzer(Version.LUCENE_44),text);}/*** ik分词器 庖丁分词器*/@Testpublic 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端方案:搜索页面—>搜索结果展示页面【来自索引库】—>结果详情页【来自数据库】
- 索引的创建
补充参考资料
- 第三阶段补充课程 资料
- 博客 全文检索的原理 【了解】
Lucene架构学习相关推荐
- javaweb k8s_K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程...
K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程 课程内容是关于Kubernetes微服务架构学习课程,基于K8S开展ASP.NET核心进行微 ...
- ZT Android4.2蓝牙基础架构学习
Android4.2蓝牙基础架构学习 分类: Jellybean Bluetooth Bluetooth 2013-10-13 23:58 863人阅读 评论(3) 收藏 举报 androidblue ...
- 时序图 分支_BOOM微架构学习(1)——取指单元与分支预测
之前在RISC-V的"Demo"级项目--Rocket-chip一文中曾经简介过BOOM处理器的流水线,这次我们开始一个系列,深入学习一下BOOM的微架构,这样对于乱序执行的超标量 ...
- bpmn2.0业务过程模型和符号_IT帮业务架构学习小组学习内容
关于学习内容,担心大家学完的可能不多,所以考虑是先选择一小部分来学.但考虑到业务架构属于组织级学科,本身就要求体系全面,所以还是决定把全套内容放入到本期学习.下面我列举一下我们这8个月在业务架构自主学 ...
- SpringCloud微服务架构学习(二)常见的微服务架构
SpringCloud微服务架构学习(二)常见的微服务架构 1.Dubbo 阿里开源微服务框架 官网地址:http://dubbo.apache.org/en-us/ 简介: Dubbo是阿里巴巴SO ...
- 《InsideUE4》GamePlay架构学习_Level和World
<InsideUE4>GamePlay架构学习 Level和World 前话 Unity To UE 思考 为什么AWorldSettings[0]的位置,而ALevelScriptAct ...
- MIPS架构学习笔记
MIPS架构学习笔记 来源: ChinaUnix博客 日期: 2007.03.13 23:18 (共有条评论) 我要评论 MIPS架构学习笔记 ...
- 高性能架构学习路线图-分布式架构演进,mybatis一对一一对多面试题
架构演进一: 早期雏形 架构演进二: 数据库开发(LAMP特长) 架构演进三: javaweb的雏形 架构演进四: javaweb的集群发展 架构演进五: javaweb的分布式发展 架构演进 ...
- 高性能架构学习路线图-分布式架构演进
目录 一.分布式架构学习路线图 二.计算机软件发展历史 三.技术架构演进史 架构演进一: 早期雏形 架构演进二: 数据库开发(LAMP特长) 架构演进三: javaweb的雏形 架构演进四: ja ...
最新文章
- 内嵌IE网页窗口中消除IE默认脚本设置影响的方法
- FFmpeg再学习 -- FFmpeg+SDL+MFC实现图形界面视频播放器
- apache应用进阶
- D3DCOLOR与D3DXCOLOR
- mysql 日志节点恢复_基于binlog二进制日志的MySQL恢复笔记
- 最后一公里极速配送 - 阿里云算法大赛总结
- 2018: 跑图(深搜)
- C语言 · 9-1九宫格
- POJ3080 ZOJ2784 UVALive3628 Blue Jeans题解
- 深度优先遍历(DFS)- Letter CasePermutation - Combinations
- 微信扫一扫服务器地址,微信扫一扫
- .NET-3.Xamarin学习与总结
- 计算机408考研 思维导图 知识整理
- SQL SERVER数据库三种数据插入方式
- 桌面计算机地址栏在哪,电脑窗口地址栏清理
- cond怎么读_cond condition是什么意思
- 网页不能自动播放视频、音频的解决方案
- php 判断字符串乱码,php如何检测乱码字符
- 例3.2、计算存款利息。有1000元,想存一年。有三种方法可选:(1)活期,年利率为r1;(2)一年期定期,年利率为r2;(3)存两次半年定期,年利率为r3。请分别计算出一年后按3种方法所得到的本息和
- 最不可思议的巧合,这些电影一定是穿越者拍的了!
热门文章
- 利用伪元素给图片在鼠标悬停时添加背景图片
- 【激光雷达点云障碍物检测】(一)滤波部分
- 超简单 CameraX 人脸识别效果封装
- 计算机网络基本概念,计算机网络基本概念【笔记】
- Docsify保姆级教程
- Nature雄文指引绿色金融研究-内附丰富低碳数据
- 服务器共享文件怎么自动备份,如何用批处理把域的文件服务器中的共享资源备份到另一台电脑中...
- 解决vue渲染时闪烁{{}}的问题
- windows/NBTSTAT,linux/nmblookup命令详解,查询NetBIOS名
- endnote参考文献格式