一捺氢、簡介安裝
官方網(wǎng)址:https://www.elastic.co/cn/
安裝elasticsearch:下載并解壓
安裝kibana:下載并解壓
下載ik分詞器:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v8.2.0
1、適配kibana中kibana.yml文件
elasticsearch.hosts: ["http://localhost:9200"]
2、解壓ik分詞器包放在elasticsearch的plugins的新建ik文件夾下
官方網(wǎng)址:https://www.elastic.co/cn/
3挽唉、操作網(wǎng)址:http://localhost:5601/app/dev_tools#/console
二、ik分詞解析器
1朵锣、分詞最少切分
POST /_analyze
{
"text": "河馬生鮮,阿里巴巴出品",
"analyzer": "ik_smart"
}
2、分詞最細(xì)切分
POST /_analyze
{
"text": "河馬生鮮火邓,阿里巴巴出品",
"analyzer": "ik_max_word"
}
3、擴(kuò)展ik分詞器
在IKAnalyzer.xml文件中設(shè)置擴(kuò)展配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴(kuò)展配置</comment>
<!--用戶可以在這里配置自己的擴(kuò)展字典 -->
<entry key="ext_dict">ext.dic</entry>
<!--用戶可以在這里配置自己的擴(kuò)展停止詞字典-->
<entry key="ext_stopwords">stopword.dic</entry>
<!--用戶可以在這里配置遠(yuǎn)程擴(kuò)展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用戶可以在這里配置遠(yuǎn)程擴(kuò)展停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
在ext.dic塑顺、stopword.dic文件中添加詞匯
三须误、操作索引庫
1、mapping常見屬性
type:數(shù)據(jù)類型(是否參與搜索)
index:是否索引
analyzer:分詞器
properties:子字段
2旭绒、創(chuàng)建索引庫(spring索引庫名稱)
PUT /spring
{
"mappings": {
"properties": {
"info": {
"analyzer": "ik_smart",
"type": "text"
},
"email":{
"type": "keyword",
"index": false
},
"name":{
"type": "object",
"properties": {
"lastName": {
"type": "keyword"
}
}
}
}
}
}
3鸟妙、查詢索引庫
GET /spring
4焦人、刪除索引庫
DELETE /spring
5、修改索引庫
PUT /spring/_mapping
{
"properties": {
"新字段名": {
"type": "keyword"
"index": false
}
}
}
四圆仔、操作文檔
1垃瞧、插入文檔(spring索引庫名稱,1文檔id)
POST /spring/_doc/1
{
"info": "文本內(nèi)容分詞",
"email": "123456@163.com",
"name": {
"lastName": "趙"
}
}
2坪郭、查詢文檔
GET /spring/_doc/1
3个从、刪除文檔
DELETE /spring/_doc/1
4、全量修改
PUT /spring/_doc/1
{
"info": "阿里巴巴出品歪沃,等于白嫖了嗦锐,真的是奧利給",
"email": "ALIBB@COM",
"name": {
"lastName": "趙"
}
}
5、局部修改
POST /spring/_update/1
{
"doc": {
"email": "alibaba@ali.cn"
}
}
五沪曙、代碼操作ES(RestClient)
1奕污、官方文檔地址
https://www.elastic.co/guide/en/elasticsearch/client/index.html
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
2、mapping映射字符串
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"address": {
"type": "keyword",
"index": false
},
"price": {
"type": "integer"
},
"score": {
"type": "integer"
},
"brand": {
"type": "keyword",
"copy_to": "name"
},
"city": {
"type": "keyword"
},
"starName": {
"type": "keyword"
},
"business": {
"type": "keyword",
"copy_to": "name"
},
"location": {
"type": "geo_point"
},
"pic": {
"type": "keyword",
"index": false
}
}
}
}
3液走、在pom文件中引入
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
4碳默、索引庫操作
public class HotelIndexTest {
private RestHighLevelClient restHighLevelClient;
@Test
void createHotelIndex() throws IOException {
//創(chuàng)建Request對(duì)象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//DSL語句
request.source(MAPPING_TEMPLATE, XContentType.JSON);
//發(fā)送請(qǐng)求
restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
}
//刪除索引庫
@Test
void deleteHotelIndex() throws IOException {
//創(chuàng)建Request對(duì)象
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
//發(fā)送請(qǐng)求
restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
}
//索引庫是否存在
@Test
void existsHotelIndex() throws IOException {
//創(chuàng)建Request對(duì)象
GetIndexRequest request = new GetIndexRequest("hotel");
//發(fā)送請(qǐng)求
boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
}
@BeforeEach
void setUp() {
this.restHighLevelClient = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://localhost:9200")));
}
@AfterEach
void tearDown() throws IOException {
restHighLevelClient.close();
}
}
設(shè)置MAPPING_TEMPLATE常量
public class HotelConstants {
public static final String MAPPING_TEMPLATE = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"name\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"address\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"price\": {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"score\": {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"brand\": {\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"city\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"starName\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"business\": {\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"location\": {\n" +
" \"type\": \"geo_point\"\n" +
" },\n" +
" \"pic\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"all\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
}
5、文檔操作
@SpringBootTest
public class HotelDocumentTest {
@Autowired
private IHotelService hotelService;
private RestHighLevelClient client;
//插入文檔數(shù)據(jù)
@Test
public void testAddDocument() throws IOException {
//根據(jù)ID查詢酒店數(shù)據(jù)
Hotel hotel = hotelService.getById(38665L);
//轉(zhuǎn)換為文檔類型
HotelDoc hotelDoc = new HotelDoc(hotel);
//創(chuàng)建Request對(duì)象
IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
//準(zhǔn)備json文檔
request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
//發(fā)送請(qǐng)求
client.index(request, RequestOptions.DEFAULT);
}
//獲取文檔數(shù)據(jù)
@Test
public void testGetDocumentById() throws IOException {
//創(chuàng)建Request對(duì)象
GetRequest request = new GetRequest("hotel", "61083");
//發(fā)送請(qǐng)求
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
String sourceAsString = documentFields.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
}
//更新文檔
@Test
public void testUpdateDocumentById() throws IOException {
//創(chuàng)建Request對(duì)象
UpdateRequest request = new UpdateRequest("hotel", "61083");
request.doc("price",888);
//發(fā)送請(qǐng)求
client.update(request, RequestOptions.DEFAULT);
}
//刪除文檔
@Test
public void testDeleteDocumentById() throws IOException {
//創(chuàng)建Request對(duì)象
DeleteRequest request = new DeleteRequest("hotel", "61083");
//發(fā)送請(qǐng)求
client.delete(request, RequestOptions.DEFAULT);
}
//批量導(dǎo)入
@Test
public void testBulk() throws IOException {
//根據(jù)ID查詢酒店數(shù)據(jù)
List<Hotel> list = hotelService.list();
//創(chuàng)建Request對(duì)象
BulkRequest request = new BulkRequest();
for (Hotel hotel : list) {
//轉(zhuǎn)換為文檔類型
HotelDoc hotelDoc = new HotelDoc(hotel);
request.add(new IndexRequest("hotel").id(hotel.getId().toString()).source(JSON.toJSONString(hotelDoc), XContentType.JSON));
}
//發(fā)送請(qǐng)求
client.bulk(request, RequestOptions.DEFAULT);
}
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://localhost:9200")));
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
}
六缘眶、DSL查詢語句
查詢所有:查詢出所有數(shù)據(jù)嘱根,一般測(cè)試用
match_all
全文檢索:利用分詞器對(duì)用戶輸入內(nèi)容分詞,然后去倒排索引庫中匹配
match_query巷懈、multi_match_query
精準(zhǔn)查詢:根據(jù)精確詞條值查詢數(shù)據(jù)该抒,一般查詢keyword、數(shù)值顶燕、日期凑保、boolean等
ids、range涌攻、term
地理查詢:根據(jù)經(jīng)緯度查詢
geo_distance欧引、geo_bounding_box
復(fù)合查詢:復(fù)合查詢可以將上述各種查詢條件組合起來,合并查詢條件
bool恳谎、function_score
1维咸、查詢所有
GET /hotel/_search
{
"query": {
"match_all": {
}
}
}
#全文查詢
GET /hotel/_search
{
"query": {
"match": {
"name": "地區(qū)"
}
}
}
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "外灘如家",
"fields": ["brand","name","business"]
}
}
}
2、精確查詢
#根據(jù)詞條精確查詢
GET /hotel/_search
{
"query": {
"term": {
"city": {
"value": "北京"
}
}
}
}
3惠爽、根據(jù)值的范圍查詢
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 300
}
}
}
}
4癌蓖、地理查詢
GET /hotel/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 40.73,
"lon": -74.1
},
"bottom_right": {
"lat": 40.717,
"lon": -73.99
}
}
}
}
}
GET /hotel/_search
{
"query": {
"geo_distance": {
"distance": "3km",
"location": {
"lat": 31.21,
"lon": 121.5
}
}
}
}
5、復(fù)合查詢
GET /hotel/_search
{
"query": {
"function_score": {
"query": {
"match": {
"name": "上海外灘"
}
},
"functions": [
{
"filter": {
"term": {
"brand": "如家"
}
},
"weight": 10
}
],
"boost_mode": "multiply"
}
}
}
6婚肆、組合查詢
#must:必須匹配每個(gè)子查詢租副,類似“與”
#should:選擇性匹配子查詢,類似“或”
#must_not:必須不匹配较性,不參與算分用僧,類似“非”
#filter:必須匹配结胀,不參與算分
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"city": {
"value": "上海"
}
}
}
],
"should": [
{
"term": {
"brand": {
"value": "皇冠假日"
}
}
},
{
"term": {
"brand": {
"value": "華美達(dá)"
}
}
}
],
"must_not": [
{
"range": {
"price": {
"lte": 500
}
}
}
],
"filter": [
{
"range": {
"score": {
"gte": 45
}
}
}
]
}
}
}
7、排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": {
"order": "desc"
}
},
{
"price": {
"order": "asc"
}
}
]
}
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 31.034661,
"lon": 121.612282
},
"order": "asc",
"unit": "km"
}
}
]
}
八责循、代碼實(shí)現(xiàn)查詢語句
public class HotelSearchTest {
private RestHighLevelClient client;
//查詢所有
@Test
void searchMatchAll() throws IOException {
MatchAllQueryBuilder query = QueryBuilders.matchAllQuery();
search(query);
}
//全文查詢
@Test
void searchMatch() throws IOException {
MatchQueryBuilder query = QueryBuilders.matchQuery("name", "上海外灘");
search(query);
}
@Test
void searchMultiMatch() throws IOException {
MultiMatchQueryBuilder query = QueryBuilders.multiMatchQuery("北京", "name", "brand");
search(query);
}
//精準(zhǔn)查詢
@Test
void searchTerm() throws IOException {
TermQueryBuilder query = QueryBuilders.termQuery("city", "深圳");
search(query);
}
@Test
void searchRange() throws IOException {
RangeQueryBuilder query = QueryBuilders.rangeQuery("price").gte(100).lte(500);
search(query);
}
/**
* must:必須匹配每個(gè)子查詢糟港,類似“與”
* should:選擇性匹配子查詢,類似“或”
* must_not:必須不匹配院仿,不參與算分秸抚,類似“非”
* filter:必須匹配,不參與算分
*/
//組合查詢
@Test
void searchBool() throws IOException {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.must(QueryBuilders.termQuery("city", "上海"));
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(500));
search(boolQuery);
}
//查詢語句抽取
public void search(QueryBuilder queryBuilder) throws IOException {
//準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//準(zhǔn)備DSL
request.source().query(queryBuilder);
//發(fā)送請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析響應(yīng)
handleResponse(response);
}
//分頁查詢
@Test
void searchPageAndSort() throws IOException {
int page = 1;
//準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//準(zhǔn)備DSL
request.source().query(QueryBuilders.matchAllQuery());
request.source().sort("price", SortOrder.ASC);
request.source().from((page - 1) * 20).size(20);
//發(fā)送請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析響應(yīng)
handleResponse(response);
}
//高亮查詢
@Test
void searchHighlight() throws IOException {
//準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//準(zhǔn)備DSL
request.source().query(QueryBuilders.matchQuery("name", "如家"));
request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
//發(fā)送請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析響應(yīng)
handleResponse(response);
}
//聚合查詢
@Test
void searchAggregation() throws IOException {
//準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//準(zhǔn)備DSL
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(20));
//發(fā)送請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析響應(yīng)
System.out.println(response);
//解析聚合結(jié)果
Aggregations aggregations = response.getAggregations();
//根據(jù)名稱獲取聚合結(jié)果
Terms brandAgg = aggregations.get("brandAgg");
//獲取桶
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
//遍歷
for (Terms.Bucket bucket :buckets){
String brandName = bucket.getKeyAsString();
long docCount = bucket.getDocCount();
System.out.println(brandName + docCount);
}
}
//解析json抽取
public void handleResponse(SearchResponse response) {
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "條");
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
//獲取高亮結(jié)果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (!CollectionUtils.isEmpty(highlightFields)) {
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
System.out.println(hotelDoc);
}
}
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://localhost:9200")));
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
}
九歹垫、實(shí)例練習(xí)
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
@Override
public PageResult search(RequestParams params) throws IOException {
//準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//Query查詢
buildBasicQuery(request, params);
//分頁
if (params.getPage() != null && params.getSize() != null)
request.source().from(0).size(5);
else
request.source().from((params.getPage() - 1) * params.getSize()).size(params.getSize());
//距離排序
if (params.getLocation() != null && !params.getLocation().equals(""))
request.source().sort(SortBuilders.
geoDistanceSort("location", new GeoPoint(params.getLocation())).
order(SortOrder.ASC).
unit(DistanceUnit.KILOMETERS));
//發(fā)送請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
return handleResponse(response);
}
private void buildBasicQuery(SearchRequest request, RequestParams params) {
//1剥汤、組合查詢
BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
//輸入框內(nèi)容
if (params.getKey() == null || params.getKey().equals(""))
queryBuilder.must(QueryBuilders.matchAllQuery());
else
queryBuilder.must(QueryBuilders.matchQuery("name", params.getKey()));
//地區(qū)
if (params.getCity() != null && !params.getCity().equals(""))
queryBuilder.filter(QueryBuilders.termQuery("city", params.getCity()));
//品牌
if (params.getBrand() != null && !params.getBrand().equals(""))
queryBuilder.filter(QueryBuilders.termQuery("brand", params.getBrand()));
//星級(jí)
if (params.getStarName() != null && !params.getStarName().equals(""))
queryBuilder.filter(QueryBuilders.termQuery("starName", params.getStarName()));
//價(jià)格查詢
if (params.getMinPrice() != null && params.getMaxPrice() != null)
queryBuilder.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
//2、算分控制
FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
//原始查詢
queryBuilder,
//function數(shù)組
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
//其中一個(gè)function元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//過濾條件
QueryBuilders.termQuery("isAD", true),
//算分函數(shù)
ScoreFunctionBuilders.weightFactorFunction(10)
)
});
//將條件查詢封裝
request.source().query(functionScoreQuery);
}
//解析json抽取
public PageResult handleResponse(SearchResponse response) {
PageResult pageResult = new PageResult();
//解析響應(yīng)
SearchHits searchHits = response.getHits();
//獲取總條數(shù)
long total = searchHits.getTotalHits().value;
pageResult.setTotal(total);
//獲取數(shù)據(jù)集合
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> list = new ArrayList<>();
for (SearchHit hit : hits) {
//獲取單個(gè)數(shù)據(jù)
String json = hit.getSourceAsString();
//反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
//獲取排序值,距離
Object[] sortValues = hit.getSortValues();
if (sortValues != null && sortValues.length > 0) {
Object value = sortValues[0];
hotelDoc.setDistance(value);
}
//獲取高亮結(jié)果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (!CollectionUtils.isEmpty(highlightFields)) {
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
list.add(hotelDoc);
}
pageResult.setHotels(list);
return pageResult;
}
/**
* 聚合結(jié)果
*/
@Override
public Map<String, List<String>> filters(RequestParams params) throws IOException {
Map<String, List<String>> map = new HashMap<>();
//準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//準(zhǔn)備DSL
//Query查詢
buildBasicQuery(request, params);
//聚合查詢
buildAggregation(request);
//發(fā)送請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析響應(yīng)
System.out.println(response);
//解析聚合結(jié)果
Aggregations aggregations = response.getAggregations();
//根據(jù)名稱獲取聚合結(jié)果
List<String> cityList = getAggByName(aggregations, "cityAgg");
List<String> brandList = getAggByName(aggregations, "brandAgg");
List<String> starList = getAggByName(aggregations, "starAgg");
map.put("城市", cityList);
map.put("品牌", brandList);
map.put("星級(jí)", starList);
return map;
}
private void buildAggregation(SearchRequest request) {
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(100));
request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(100));
request.source().aggregation(AggregationBuilders.terms("starAgg").field("starName").size(100));
}
public List<String> getAggByName(Aggregations aggregations, String agg) {
//聚合集合
List<String> list = new ArrayList<>();
//根據(jù)聚合名稱獲取聚合數(shù)據(jù) m
Terms terms = aggregations.get(agg);
//獲取聚合數(shù)據(jù)結(jié)果集
List<? extends Terms.Bucket> buckets = terms.getBuckets();
//遍歷
for (Terms.Bucket bucket : buckets) {
String brandName = bucket.getKeyAsString();
list.add(brandName);
}
return list;
}
}