全文搜索引擎的概念
全文搜索引擎是目前廣泛應(yīng)用的主流搜索引擎。它的工作原理是計(jì)算機(jī)索引程序通過(guò)掃描文章中的每一個(gè)詞蚀乔,對(duì)每一個(gè)詞建立一個(gè)索引蒋纬,指明該詞在文章中出現(xiàn)的次數(shù)和位置泊愧,當(dāng)用戶查詢時(shí)村视,檢索程序就根據(jù)事先建立的索引進(jìn)行查找官套,并將查找的結(jié)果反饋給用戶的檢索方式。這個(gè)過(guò)程類似于通過(guò)字典中的檢索字表查字的過(guò)程蚁孔。
常見的全文搜索引擎及對(duì)比
①Lucene
Lucene是一個(gè)Java全文搜索引擎奶赔,完全用Java編寫。Lucene不是一個(gè)完整的應(yīng)用程序杠氢,而是一個(gè)代碼庫(kù)和API站刑,可以很容易地用于向應(yīng)用程序添加搜索功能
優(yōu)點(diǎn):比較成熟的解決方案,具有活躍的社區(qū)鼻百,經(jīng)過(guò)優(yōu)化笛钝,可以支持10億+的搜索
缺點(diǎn):所有的擴(kuò)展质况,分布式,可靠性等都需要自己實(shí)現(xiàn)玻靡;非實(shí)時(shí),從建索引到可以搜索中間有一個(gè)時(shí)間延遲中贝。
②solar
Solr是一個(gè)基于名為L(zhǎng)ucene的Java庫(kù)構(gòu)建的開源搜索平臺(tái)囤捻。它以用戶友好的方式提供Apache Lucene的搜索功能。作為一個(gè)行業(yè)參與者近十年邻寿,它是一個(gè)成熟的產(chǎn)品蝎土,擁有強(qiáng)大而廣泛的用戶社區(qū)。它提供分布式索引绣否,復(fù)制誊涯,負(fù)載平衡查詢以及自動(dòng)故障轉(zhuǎn)移和恢復(fù)。如果它被正確部署然后管理得好蒜撮,它就能夠成為一個(gè)高度可靠暴构,可擴(kuò)展且容錯(cuò)的搜索引擎
優(yōu)點(diǎn):Solr有一個(gè)更大、更成熟的用戶段磨、開發(fā)和貢獻(xiàn)者社區(qū)取逾;支持多種文件的索引,如JSON,XML,CSV,PDF,HTML,WORD苹支;不考慮建立索引砾隅,搜索速度更快。
缺點(diǎn):建立索引耗時(shí)較長(zhǎng)债蜜,實(shí)時(shí)搜索的效率不高
③Elasticsearch
Elasticsearch(elastic)是一個(gè)基于Apache Lucene庫(kù)構(gòu)建的RESTful搜索引擎,它提供了一個(gè)分布式晴埂,多租戶能力的全文搜索引擎,具有HTTP Web界面(REST)和無(wú)架構(gòu)JSON文檔寻定。分布式搜索引擎包括可以劃分為分片的索引儒洛,并且每個(gè)分片可以具有多個(gè)副本。每個(gè)Elasticsearch節(jié)點(diǎn)都可以有一個(gè)或多個(gè)分片特姐,其引擎也可以充當(dāng)協(xié)調(diào)器晶丘,將操作委派給正確的分片。
優(yōu)點(diǎn):Elasticsearch是分布式的唐含,可以實(shí)時(shí)分發(fā)浅浮;支持Lucene接近實(shí)時(shí)的搜索;處理多租戶(multienancy)不需要特殊的配置,而slor則需要更多高級(jí)的配置
缺點(diǎn):社區(qū)沒有solar那么活躍捷枯;由于發(fā)布出來(lái)時(shí)間較短滚秩,較solar不穩(wěn)定;不夠自動(dòng)淮捆,不適應(yīng)當(dāng)前新的index warmup API(索引預(yù)熱)
Elasticsearch基本概念
①Index:索引郁油,Elastic 數(shù)據(jù)管理的頂層單位本股,寫入數(shù)據(jù)后經(jīng)處理會(huì)生成倒排索引(Inverted Index)
②Document:文檔,Index里面的單條數(shù)據(jù)記錄
③Type:Document可以進(jìn)行分組桐腌,Type就是這種分組拄显,使用Type可以對(duì)Document進(jìn)行過(guò)濾
④Cluster:集群,Elastic是一個(gè)分布式數(shù)據(jù)庫(kù)案站,眾多的節(jié)點(diǎn)組成了一個(gè)集群
⑤Node:單個(gè)elastic實(shí)例為一個(gè)節(jié)點(diǎn)躬审,節(jié)點(diǎn)分為主節(jié)點(diǎn)跟分節(jié)點(diǎn)。當(dāng)一個(gè)節(jié)點(diǎn)被選舉成為主節(jié)點(diǎn)時(shí)蟆盐,它將負(fù)責(zé)管理集群范圍內(nèi)的所有變更承边,例如增加、刪除索引石挂,或者增加博助、刪除節(jié)點(diǎn)等,任何節(jié)點(diǎn)都有可能被選舉成主節(jié)點(diǎn)
⑥分片:數(shù)據(jù)的容器痹愚,分為主分片跟副分片富岳。一個(gè)主分片理論上可以存儲(chǔ)Integer.MAX_VALUE - 128 個(gè)文檔,索引建立時(shí)已經(jīng)確定了主分片的數(shù)量里伯。副分片作為主分片的拷貝城瞎,作為硬件故障時(shí)保護(hù)數(shù)據(jù)不丟失的冗余備份。分片可以分布在同一節(jié)點(diǎn)疾瓮,但是為了做到數(shù)據(jù)備份跟負(fù)載均衡脖镀,一般會(huì)將不同分片分布在不同的節(jié)點(diǎn)上。
全文搜索引擎基本原理-倒排索引
當(dāng)往Elasticsearch插入數(shù)據(jù)時(shí)狼电,會(huì)建立對(duì)應(yīng)的文檔來(lái)存儲(chǔ)數(shù)據(jù)蜒灰,并且根據(jù)分詞規(guī)則對(duì)數(shù)據(jù)進(jìn)行拆分,然后建立不同的詞條跟所在文檔的倒排索引肩碟。當(dāng)進(jìn)行搜索時(shí)强窖,會(huì)對(duì)搜索關(guān)鍵字進(jìn)行拆分,并且匹配倒排索引中維護(hù)的詞條削祈,最終查找出對(duì)應(yīng)的文檔翅溺。
如現(xiàn)在存在兩個(gè)文檔,每個(gè)文檔中的content包括以下內(nèi)容:
1.本文作者是吃雪糕也放辣椒
2.讀完本文給作者點(diǎn)個(gè)贊
然后根據(jù)分詞規(guī)則將文本拆分成詞條髓抑,并且建立倒排索引咙崎,如下所示:
當(dāng)輸入“讀完本文”關(guān)鍵字時(shí),會(huì)匹配到以下結(jié)果:
從上面匹配結(jié)果來(lái)看吨拍,文檔2的匹配度更加高褪猛。如果我們使用僅計(jì)算匹配詞條數(shù)量的簡(jiǎn)單 相似性算法 ,那么羹饰,我們可以說(shuō)伊滋,對(duì)于我們查詢的相關(guān)性來(lái)講碳却,第二個(gè)文檔比第一個(gè)文檔更佳。
Elastic的簡(jiǎn)單使用
①環(huán)境的搭建
1.安裝1.8版本及以上JDK
2.安裝elastic,直接上官網(wǎng)下載解壓即可笑旺,下載鏈接>https://www.elastic.co/cn/downloads/elasticsearch
解壓完后昼浦,cd /安裝目錄/elasticsearch-6.5.4/bin
運(yùn)行 elasticsearch.bat,可以看到elastic已經(jīng)以9300的端口啟動(dòng):
http端口可以在 /安裝目錄/elasticsearch-6.5.4/config/elasticsearch.yml中進(jìn)行修改
3.安裝IK分詞器,下載鏈接>https://github.com/medcl/elasticsearch-analysis-ik/releases
解壓在 /安裝目錄/elasticsearch-6.5.4/plugins即可
4.安裝可視化界面kibana,下載鏈接>https://www.elastic.co/cn/downloads/kibana
下載完后直接解壓即可筒主,cd /安裝目錄/kibana-6.5.4-windows-x86_64/config,在kibana.yml可以設(shè)置elastic的ip及端口:
然后cd /安裝目錄/kibana-6.5.4-windows-x86_64/bi,進(jìn)入kibana.bat啟動(dòng)
②SpringBoot整合Elasticsearch
1.導(dǎo)入elastic依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2.配置節(jié)點(diǎn)
#application.properties
# 配置集群名稱座柱,名稱寫錯(cuò)會(huì)連不上服務(wù)器,默認(rèn)elasticsearch
spring.data.elasticsearch.cluster-name=elasticsearch
# 配置集群節(jié)點(diǎn)
spring.data.elasticsearch.cluster-nodes=localhost:9300
#是否開啟本地存儲(chǔ)
spring.data.elasticsearch.repositories.enabled=true
3.建立模型Demo
@Data
@Document(indexName="shop")
public class Product {
@Id
private String id;
/**
* searchAnalyzer:分詞器
* type:字段類型
* fielddata:預(yù)加載
*/
@Field(analyzer="ik_smart",searchAnalyzer="ik_smart",type = FieldType.Text,fielddata = true)
private String title;
private Integer price;
@Field(analyzer="ik_smart",searchAnalyzer="ik_smart",type = FieldType.Text,fielddata = true)
private String intro;
@Field(type= FieldType.Keyword)
private String brand;
}
4.創(chuàng)建Repository
public interface EsRepository extends ElasticsearchRepository<Product, String> {
}
5.創(chuàng)建業(yè)務(wù)接口
/**
* @author wangsj
*/
public interface EsProduct {
/**
* 添加數(shù)據(jù)
* @param product 商品對(duì)象
* @return 返回添加的商品對(duì)象
*/
Product create(Product product);
/**
* 魔魂查詢
* @param keyword 名字
* @return 商品集合
*/
List<Product> search(String keyword);
/**
* 高亮查詢
* @param keyword 搜索關(guān)鍵字
* @return AggregatedPage
*/
AggregatedPage<Product> highLightSearch(String keyword);
}
6.業(yè)務(wù)接口實(shí)現(xiàn)類
public class EsProductImpl implements EsProduct {
@Autowired
private EsRepository esRepository;
@Autowired
private ElasticsearchTemplate template;
@Override
public Product create(Product product) {
Product pro = esRepository.save(product);
return pro;
}
@Override
public List<Product> search(String keyword) {
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.multiMatchQuery(keyword, "title", "intro")
);
builder.withPageable(PageRequest.of(0, 100, Sort.Direction.DESC, "brand"));
Page<Product> search = esRepository.search(builder.build());
List<Product> pros = search.getContent();
return pros;
}
@Override
public AggregatedPage<Product> highLightSearch(String keyword) {
// Java與JSON互轉(zhuǎn)的工具對(duì)象
ObjectMapper mapper = new ObjectMapper();
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
// 設(shè)置查詢哪個(gè)索引中的哪個(gè)類型
builder.withIndices("shop");
builder.withQuery(
QueryBuilders.multiMatchQuery(keyword,
"title", "intro")
);
builder.withHighlightFields(
new HighlightBuilder.Field("title")
.preTags("<span style='color:red'>").postTags("</span>"),
new HighlightBuilder.Field("intro")
.preTags("<span style='color:red'>").postTags("</span>")
);
AggregatedPage<Product> page = template.queryForPage(builder.build(), Product.class,
new SearchResultMapper(){
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<T> list = new ArrayList<>();
for (SearchHit hit : response.getHits().getHits()) {
list.add(mapSearchHit(hit, clazz));
}
long total = response.getHits().totalHits;
return new AggregatedPageImpl<>(list, pageable, total);
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
T t = null;
try {
t = mapper.readValue(searchHit.getSourceAsString(), type);
for (HighlightField field : searchHit.getHighlightFields().values()) {
// 替換需要高亮顯示的字段物舒,用到Apache的BeanUtils工具
BeanUtils.setProperty(t, field.getName(), field.getFragments()[0].string());
}
}catch (Exception e){
e.printStackTrace();
return null;
}
return t;
}
});
return page;
}
}
7.實(shí)例測(cè)試
(1)新增數(shù)據(jù)
@Test
public void testCreate(){
Product product = new Product();
product.setBrand("三星");
product.setIntro("性價(jià)比不高的手機(jī)");
product.setTitle("貴手機(jī)");
product.setId("3");
esProduct.create(product);
}
打開kibana,可以看到elastic中已經(jīng)新建立了一條數(shù)據(jù)
(2)模糊查詢
@Test
public void testSearch(){
List<Product> pros = esProduct.search("甩");
System.out.println(pros);
}
輸入搜索關(guān)鍵字為“甩”戏锹,運(yùn)行結(jié)果:
(3)高亮查詢
@Test
public void testHighLightSearch(){
AggregatedPage<Product> page = esProduct.highLightSearch("甩");
page.forEach(System.out::println);
}
輸入搜索關(guān)鍵字為“甩”冠胯,運(yùn)行結(jié)果:
在頁(yè)面直觀的效果類似于往百度輸入搜索關(guān)鍵字,然后返回結(jié)果會(huì)將關(guān)鍵字渲染成紅色
③Elasticsearch進(jìn)階
elastic可以進(jìn)行復(fù)雜的聚合運(yùn)算锦针,如桶聚合荠察,還可以進(jìn)行統(tǒng)計(jì)、排序奈搜、分頁(yè)等等悉盆,進(jìn)一步學(xué)習(xí)可以參考官方文檔:>https://www.elastic.co/guide/en/elasticsearch/reference/6.0/search-aggregations.html