Springboot integrated ElasticSearch quick start demo

HBLOG
5 min readFeb 2, 2024

--

一、ElasticSearch introduce

elasticsearch is an distributed search engine which use java language ,build on Apache Lucene .Lucene is an open source search full-text toolkit. it only a toolkit,not an complete working engine. elasticsearch is based on REST API,So any application developed in any development language can manage an elasticsearch cluster through HTTP requests in JSON format. elasticsearch encapsulate and sextends luncene, make storage、index and searching more faster、more easier。besides ,elasticsearch can also store data intact,even can be used directly as a nosql database with search capabilities. Its data is represented in the form of documents.Elasticsearch will be referred to as es below.

二、ElasticSearch enviroment setup

use docker-compose to build,the specific configuration as follows:

version: '3'
# birdge es -> Facilitate mutual communication
networks:
es:
services:
elasticsearch:
image: registry.cn-hangzhou.aliyuncs.com/zhengqing/elasticsearch:7.14.1 # old image`elasticsearch:7.14.1`
container_name: elasticsearch # docker name 'elasticsearch'
restart: unless-stopped #
volumes:
- "./elasticsearch/data:/usr/share/elasticsearch/data"
- "./elasticsearch/logs:/usr/share/elasticsearch/logs"
- "./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml"
# - "./elasticsearch/config/jvm.options:/usr/share/elasticsearch/config/jvm.options"
- "./elasticsearch/plugins/ik:/usr/share/elasticsearch/plugins/ik" #
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
TAKE_FILE_OWNERSHIP: "true" # authority
discovery.type: single-node
ES_JAVA_OPTS: "-Xmx512m -Xms512m"
#ELASTIC_PASSWORD: "123456" # elastic
ports:
- "9200:9200"
- "9300:9300"
networks:
- es
kibana:
image: registry.cn-hangzhou.aliyuncs.com/zhengqing/kibana:7.14.1 # oringinal`kibana:7.14.1`
container_name: kibana
restart: unless-stopped
volumes:
- ./elasticsearch/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
ports:
- "5601:5601"
depends_on:
- elasticsearch
links:
- elasticsearch
networks:
- es

deployment

# start
docker-compose -f docker-compose-elasticsearch.yml -p elasticsearch up -d
# after run,Grant permissions (read, write, execute) to all files in the current directory
#chmod -R 777 ./elasticsearch
  1. ES access url:ip:9200 default username/password:elastic/123456
  2. kibana access url:ip:5601/app/dev_tools#/console default username/password:elastic/123456

set ES password

# entry docker
docker exec -it elasticsearch /bin/bash
#set password-genenal random password
# elasticsearch-setup-passwords auto
# set password-set mannul password
elasticsearch-setup-passwords interactive
# access
curl 127.0.0.1:9200 -u elastic:123456

三、code project

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>elasticsearch</artifactId>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

start class

package com.et59.elaticsearch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EssearchApplication {
public static void main(String[] args) {
SpringApplication.run(EssearchApplication.class, args);
}
}

service

package com.et59.elaticsearch.service;
import com.et59.elaticsearch.document.ProductDocument;
import java.util.List;
/**
* @author zhoudong
* @version 0.1
* @date 2018/12/13 15:32
*/
public interface EsSearchService extends BaseSearchService<ProductDocument> {
/**
* save
* @auther: zhoudong
* @date: 2018/12/13 16:02
*/
void save(ProductDocument... productDocuments);
/**
* delete
* @param id
*/
void delete(String id);
/**
* clear index
*/
void deleteAll();
/**
* query by id
* @param id
* @return
*/
ProductDocument getById(String id);
/**
* query all
* @return
*/
List<ProductDocument> getAll();
}
package com.et59.elaticsearch.service.impl;
import com.alibaba.fastjson.JSON;
import com.et59.elaticsearch.document.ProductDocument;
import com.et59.elaticsearch.repository.ProductDocumentRepository;
import com.et59.elaticsearch.service.EsSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
/**
* elasticsearch search engine service implete
* @author zhoudong
* @version 0.1
* @date 2018/12/13 15:33
*/
@Service
public class EsSearchServiceImpl extends BaseSearchServiceImpl<ProductDocument> implements EsSearchService {
private Logger log = LoggerFactory.getLogger(getClass());
@Resource
private ElasticsearchTemplate elasticsearchTemplate;
@Resource
private ProductDocumentRepository productDocumentRepository;
@Override
public void save(ProductDocument ... productDocuments) {
elasticsearchTemplate.putMapping(ProductDocument.class);
if(productDocuments.length > 0){
/*Arrays.asList(productDocuments).parallelStream()
.map(productDocumentRepository::save)
.forEach(productDocument -> log.info("【save data】:{}", JSON.toJSONString(productDocument)));*/
log.info("【save index】:{}",JSON.toJSONString(productDocumentRepository.saveAll(Arrays.asList(productDocuments))));
}
}
@Override
public void delete(String id) {
productDocumentRepository.deleteById(id);
}
@Override
public void deleteAll() {
productDocumentRepository.deleteAll();
}
@Override
public ProductDocument getById(String id) {
return productDocumentRepository.findById(id).get();
}
@Override
public List<ProductDocument> getAll() {
List<ProductDocument> list = new ArrayList<>();
productDocumentRepository.findAll().forEach(list::add);
return list;
}
}

document

package com.et59.elaticsearch.document;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.Mapping;
import java.io.Serializable;
import java.util.Date;
/**
* product entity
* @author zhoudong
* @version 0.1
* @date 2018/12/13 15:22
*/
@Document(indexName = "orders", type = "product")
@Mapping(mappingPath = "productIndex.json") //solve the problem that ik word segmentation can't be userd
public class ProductDocument implements Serializable {
@Id
private String id;
//@Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String productName;
//@Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String productDesc;
private Date createTime;
private Date updateTime;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}

repository

package com.et59.elaticsearch.repository;
import com.et59.elaticsearch.document.ProductDocument;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
/**
* @author zhoudong
* @version 0.1
* @date 2018/12/13 17:35
*/
@Component
public interface ProductDocumentRepository extends ElasticsearchRepository<ProductDocument,String> {
}

application.properties

# elasticsearch.yml file's cluster.name
spring.data.elasticsearch.cluster-name=docker-cluster
# elasticsearch call address,separate by “,”
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
#spring.data.elasticsearch.repositories.enabled=true
#spring.data.elasticsearch.username=elastic
#spring.data.elasticsearch.password=123456
#spring.data.elasticsearch.network.host=0.0.0.0

productIndex.json

{
"properties": {
"createTime": {
"type": "long"
},
"productDesc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"productName": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"updateTime": {
"type": "long"
}
}
}

code repository

四、check

package com.et59.elaticsearch;
import com.alibaba.fastjson.JSON;
import com.et59.elaticsearch.document.ProductDocument;
import com.et59.elaticsearch.document.ProductDocumentBuilder;
import com.et59.elaticsearch.service.EsSearchService;
import com.et59.elaticsearch.page.Page;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class EssearchApplicationTests {
private Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private EsSearchService esSearchService;
@Test
public void save() {
log.info("【the index count】:{}",esSearchService.getAll().size());
ProductDocument productDocument = ProductDocumentBuilder.create()
.addId(System.currentTimeMillis() + "01")
.addProductName("无印良品 MUJI 基础润肤化妆水")
.addProductDesc("无印良品 MUJI 基础润肤化妆水 高保湿型 200ml")
.addCreateTime(new Date()).addUpdateTime(new Date())
.builder();
ProductDocument productDocument1 = ProductDocumentBuilder.create()
.addId(System.currentTimeMillis() + "02")
.addProductName("荣耀 V10 尊享版")
.addProductDesc("荣耀 V10 尊享版 6GB+128GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待")
.addCreateTime(new Date()).addUpdateTime(new Date())
.builder();
ProductDocument productDocument2 = ProductDocumentBuilder.create()
.addId(System.currentTimeMillis() + "03")
.addProductName("资生堂(SHISEIDO) 尿素红罐护手霜")
.addProductDesc("日本进口 资生堂(SHISEIDO) 尿素红罐护手霜 100g/罐 男女通用 深层滋养 改善粗糙")
.addCreateTime(new Date()).addUpdateTime(new Date())
.builder();
esSearchService.save(productDocument,productDocument1,productDocument2);
log.info("【index ID】:{},{},{}",productDocument.getId(),productDocument1.getId(),productDocument2.getId());
log.info("【all count after create index】:{}",esSearchService.getAll().size());
}
@Test
public void getAll(){
esSearchService.getAll().parallelStream()
.map(JSON::toJSONString)
.forEach(System.out::println);
}
@Test
public void deleteAll() {
esSearchService.deleteAll();
}
@Test
public void getById() {
log.info("【query by id】:{}", JSON.toJSONString(esSearchService.getById("154470178213401")));
}
@Test
public void query() {
log.info("【query by keyword】:{}", JSON.toJSONString(esSearchService.query("无印良品荣耀",ProductDocument.class)));
}
@Test
public void queryHit() {
String keyword = "联通尿素";
String indexName = "orders";
List<Map<String,Object>> searchHits = esSearchService.queryHit(keyword,indexName,"productName","productDesc");
log.info("【Search content based on keywords, highlight hit parts, and return content】:{}", JSON.toJSONString(searchHits));
//[{"highlight":{"productDesc":"<span style='color:red'>无印良品</span> MUJI 基础润肤化妆水 高保湿型 200ml","productName":"<span style='color:red'>无印良品</span> MUJI 基础润肤化妆水"},"source":{"productDesc":"无印良品 MUJI 基础润肤化妆水 高保湿型 200ml","createTime":1544755966204,"updateTime":1544755966204,"id":"154475596620401","productName":"无印良品 MUJI 基础润肤化妆水"}},{"highlight":{"productDesc":"<span style='color:red'>荣耀</span> V10 尊享版 6GB+128GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待","productName":"<span style='color:red'>荣耀</span> V10 尊享版"},"source":{"productDesc":"荣耀 V10 尊享版 6GB+128GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待","createTime":1544755966204,"updateTime":1544755966204,"id":"154475596620402","productName":"荣耀 V10 尊享版"}}]
}
@Test
public void queryHitByPage() {
String keyword = "联通尿素";
String indexName = "orders";
Page<Map<String,Object>> searchHits = esSearchService.queryHitByPage(1,1,keyword,indexName,"productName","productDesc");
log.info("【Search content based on keywords, highlight hit parts, and return content】:{}", JSON.toJSONString(searchHits));
//[{"highlight":{"productDesc":"<span style='color:red'>无印良品</span> MUJI 基础润肤化妆水 高保湿型 200ml","productName":"<span style='color:red'>无印良品</span> MUJI 基础润肤化妆水"},"source":{"productDesc":"无印良品 MUJI 基础润肤化妆水 高保湿型 200ml","createTime":1544755966204,"updateTime":1544755966204,"id":"154475596620401","productName":"无印良品 MUJI 基础润肤化妆水"}},{"highlight":{"productDesc":"<span style='color:red'>荣耀</span> V10 尊享版 6GB+128GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待","productName":"<span style='color:red'>荣耀</span> V10 尊享版"},"source":{"productDesc":"荣耀 V10 尊享版 6GB+128GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待","createTime":1544755966204,"updateTime":1544755966204,"id":"154475596620402","productName":"荣耀 V10 尊享版"}}]
}
@Test
public void deleteIndex() {
log.info("【delete index repository】");
esSearchService.deleteIndex("orders");
}
}

五、参考

--

--