elasticsearch
Mapping字段映射
映射定義文檔如何被存儲和檢索的
核心資料類型
(1)字元串
text ⽤于全⽂索引,搜尋時會自動使用分詞器進⾏分詞再比對
keyword 不分詞,搜尋時需要比對完整的值
(2)數值型
整型: byte,short,integer,long
浮點型: float, half_float, scaled_float,double
(3)日期類型:date
(4)範圍型
integer_range, long_range, float_range,double_range,date_range
gt是大于,lt是小于,e是equals等于。
age_limit的區間包含了此值的文檔都算是比對。
(5)布爾
boolean
(6)二進制
binary 會把值當做經過 base64 編碼的字元串,預設不存儲,且不可搜尋
複雜資料類型
(1)對象
object一個對象中可以嵌套對象。
(2)數組
Array
嵌套類型
nested 用于json對象數組
映射
Mapping(映射)是用來定義一個文檔(document),
以及它所包含的屬性(field)是如何存儲和索引的。
比如:使用maping來定義:
哪些字元串屬性應該被看做全文本屬性(full text fields);
哪些屬性包含數字,日期或地理位置;
文檔中的所有屬性是否都嫩被索引(all 配置);
日期的格式;
自定義映射規則來執行動态添加屬性;
檢視mapping資訊:GET bank/_mapping
結果:
{
"bank" : {
"mappings" : {
"properties" : {
"account_number" : {
"type" : "long" # long類型
},
"address" : {
"type" : "text", # 文本類型,會進行全文檢索,進行分詞
"fields" : {
"keyword" : { # addrss.keyword
"type" : "keyword", # 該字段必須全部比對到
"ignore_above" : 256
}
}
},
"age" : {
"type" : "long"
},
"balance" : {
"type" : "long"
},
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"email" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"employer" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"firstname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"gender" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"lastname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"state" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
新版本改變
ElasticSearch7-去掉type概念
關系型資料庫中兩個資料表示是獨立的,
即使他們裡面有相同名稱的列也不影響使用,
但ES中不是這樣的。elasticsearch是基于Lucene開發的搜尋引擎,
而ES中不同type下名稱相同的filed最終在Lucene中的處理方式是一樣的。
兩個不同type下的兩個user_name,
在ES同一個索引下其實被認為是同一個filed,
你必須在兩個不同的type中定義相同的filed映射。
否則,不同type中的相同字段名稱就會在進行中出現沖突的情況,
導緻Lucene處理效率下降。
去掉type就是為了提高ES處理資料的效率。
Elasticsearch 7.x URL中的type參數為可選。
比如,索引一個文檔不再要求提供文檔類型。
Elasticsearch 8.x 不再支援URL中的type參數。
解決:
将索引從多類型遷移到單類型,每種類型文檔一個獨立索引
将已存在的索引下的類型資料,全部遷移到指定位置即可。詳見資料遷移
建立映射PUT /my_index
第一次存儲資料的時候es就猜出了映射
第一次存儲資料前可以指定映射
建立索引并指定映射
PUT /my_index
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword" # 指定為keyword
},
"name": {
"type": "text" # 全文檢索。儲存時候分詞,檢索時候進行分詞比對
}
}
}
}
添加新的字段映射PUT /my_index/_mapping
PUT /my_index/_mapping
{
"properties": {
"employee-id": {
"type": "keyword",
"index": false # 字段不能被檢索。檢索
}
}
}
##這裡的 “index”: false,表明新增的字段不能被檢索,隻是一個備援字段。
不能更新映射
對于已經存在的字段映射,我們不能更新。
更新必須建立新的索引,進行資料遷移。
資料遷移
先建立new_twitter的正确映射。
然後使用如下方式進行資料遷移。
6.0以後寫法
POST reindex
{
"source":{
"index":"twitter"
},
"dest":{
"index":"new_twitters"
}
}
老版本寫法
POST reindex
{
"source":{
"index":"twitter",
"twitter":"twitter"
},
"dest":{
"index":"new_twitters"
}
}
分詞
一個tokenizer(分詞器)接收一個字元流,
将之分割為獨立的tokens(詞元,通常是獨立的單詞),然後輸出tokens流。
例如:whitespace tokenizer遇到空白字元時分割文本。
它會将文本"Quick brown fox!"分割為[Quick,brown,fox!]
該tokenizer(分詞器)還負責記錄各個terms(詞條)的順序
或position位置(用于phrase短語和word proximity詞近鄰查詢),
以及term(詞條)所代表的原始word(單詞)的start(起始)
和end(結束)的character offsets(字元串偏移量)(用于高亮顯示搜尋的内容)。
elasticsearch提供了很多内置的分詞器(标準分詞器),
可以用來建構custom analyzers(自定義分詞器)。
POST _analyze
{
"analyzer": "standard",
"text": "The 2 Brown-Foxes bone."
}
安裝ik分詞器
所有的語言分詞,預設使用的都是“Standard Analyzer”,
但是這些分詞器針對于中文的分詞,并不友好。為此需要安裝中文的分詞器。
在前面安裝的elasticsearch時,
我們已經将elasticsearch容器的“/usr/share/elasticsearch/plugins”目錄,
映射到主控端的“ /mydata/elasticsearch/plugins”目錄下,
是以比較友善的做法就是下載下傳“/elasticsearch-analysis-ik-7.4.2.zip”檔案,
然後解壓到該檔案夾下即可。安裝完畢後,需要重新開機elasticsearch容器。
下載下傳位址https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip 下載下傳後解壓到/mydata/elasticsearch/plugins目錄下
然後
chmod -R 777 elasticsearch-analysis-ik-7.4.2/
修改權限,重新開機就可以了
測試分詞器
POST _analyze
{
"analyzer": "standard",
"text": "我是中國人"
}
POST _analyze
{
"analyzer": "ik_smart",
"text": "我是中國人"
}
POST _analyze
{
"analyzer": "ik_max_word",
"text": "我是中國人"
}
自定義詞庫
安裝Nginx
首先在Linux裡面建立好目錄
cd /mydata
mkdir nginx
随便啟動一個 nginx 執行個體,隻是為了複制出配置
docker run -p 80:80 --name nginx -d nginx:1.10
将容器内的配置檔案拷貝到目前目錄(mydata):
docker container cp nginx:/etc/nginx .
修改檔案名稱:mv nginx conf 把這個 conf 移動到/mydata/nginx下
[root@jane mydata]# ls
elasticsearch mysql nginx redis
[root@jane mydata]# mv nginx conf
[root@jane mydata]# mkdir nginx
[root@jane mydata]# mv conf nginx/
删掉之前的容器,建立過新的
docker run -p 80:80 --privileged=true --name nginx \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.10
給 nginx 的 html 下面放的所有資源可
我在裡面放了html/esfenci.txt,在裡面寫上詞語
然後修改elasticsearch/plugins/ik/config/中的 IKAnalyzer.cfg.xml
cd /mydata/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.2/config/
vi IKAnalyzer.cfg.xml
修改成下面這樣
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴充配置</comment>
<!--使用者可以在這裡配置自己的擴充字典 -->
<entry key="ext_dict"></entry>
<!--使用者可以在這裡配置自己的擴充停止詞字典-->
<entry key="ext_stopwords"></entry>
<!--使用者可以在這裡配置遠端擴充字典 -->
<entry key="remote_ext_dict">http://192.168.80.129/es/fenci.txt</entry>
<!--使用者可以在這裡配置遠端擴充停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
更新完成後,es 隻會對新增的資料用新詞分詞。
曆史資料是不會重新分詞的。
如果想要曆史資料重新分詞。需要執行:
POST my_index/_update_by_query?conflicts=proceed
Elasticsearch-Rest-Client
有兩種方式進行用戶端連接配接
1)、9300:TCP
spring-data-elasticsearch:transport-api.jar;
springboot 版本不同, transport-api.jar 不同,不能适配 es 版本
7.x 已經不建議使用,8 以後就要廢
2)、9200:HTTP
JestClient:非官方,更新慢
RestTemplate:模拟發 HTTP 請求,ES 很多操作需要自己封裝,麻煩
HttpClient:同上
Elasticsearch-Rest-Client:官方 RestClient,封裝了 ES 操作,API 層次分明,上手簡單
最終選擇 Elasticsearch-Rest-Client(elasticsearch-rest-high-level-client)
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
導入依賴
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.4.2</elasticsearch.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependency>
<groupId>org.elasticsearch.client </groupId>
<artifactId > elasticsearch-rest-high-level-client </artifactId>
<version>7.4.2</version>
</dependency>
請求測試項,比如es添加了安全通路規則,
通路es需要添加一個安全頭,就可以通過requestOptions設定
官方建議把requestOptions建立成單執行個體
package com.jane.shop.search.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author jane
* @create 2021-05-19 16:11
* 1.導入依賴
* 2.編寫配置,給容器注入一個RestHighLevelClient
*/
@Configuration
public class ShopElasticSearchConfig
{
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
COMMON_OPTIONS = builder.build();
}
@Bean
public RestHighLevelClient esRestClient() {
// RestHighLevelClient client = new RestHighLevelClient(
// RestClient.builder(
// new HttpHost("localhost", 9200, "http"),
// new HttpHost("localhost", 9201, "http")));
RestClientBuilder builder = null;
// 可以指定多個es
builder = RestClient.builder(new HttpHost("192.168.80.129", 9200, "http"));
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
商品的ES
ES在記憶體中,是以在檢索中優于mysql。ES也支援叢集,資料分片存儲。
需求:
上架的商品才可以在網站展示。
上架的商品需要可以被檢索。
如何存儲
1)、檢索的時候輸入名字,是需要按照sku的title進行全文檢索的
2)、檢素使用商品規格,規格是spu的公共屬性,每個spu是一樣的
3)、按照分類id進去的都是直接列出spu的,還可以切換。
4〕、我們如果将sku的全量資訊儲存到es中(包括spu屬性〕就太多字段了
想法一
{
skuId:1
spuId:11
skyTitile:華為xx
price:999
saleCount:99
attr:[
{尺寸:5},
{CPU:高通945},
{分辨率:全高清}
]
缺點:如果每個sku都存儲規格參數(如尺寸),會有備援存儲,
因為每個spu對應的sku的規格參數都一樣
sku索引
{
spuId:1
skuId:11
}
attr索引
{
skuId:11
attr:[
{尺寸:5},
{CPU:高通945},
{分辨率:全高清}
]
}
先找到4000個符合要求的spu,再根據4000個spu查詢對應的屬性,
封裝了4000個id,long 8B*4000=32000B=32KB
10000個人檢索,就是320MB
結論:如果将規格參數單獨建立索引,會出現檢索時出現大量資料傳輸的問題,會引起網絡網絡
nested嵌入式對象
屬性是"type": “nested”,因為是内部的屬性進行檢索
數組類型的對象會被扁平化處理(對象的每個屬性會分别存儲到一起)
user.name=["aaa","bbb"]
user.addr=["ccc","ddd"]
這種存儲方式,可能會發生如下錯誤:
錯誤檢索到{aaa,ddd},這個組合是不存在的
數組的扁平化處理會使檢索能檢索到本身不存在的,
為了解決這個問題,就采用了嵌入式屬性,
數組裡是對象時用嵌入式屬性(不是對象無需用嵌入式屬性)