1. 在springboot基础上搭建ES环境
  2. 源码地址

下载ES服务器并运行

到es官网下载es服务器并运行。

初始化数据库

执行数据库脚本,文件路径(db/es_.sql),demo中从数据库初始化引擎数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- Dumping database structure for master
CREATE DATABASE IF NOT EXISTS `es_` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `es_`;

-- Dumping structure for table master.user
CREATE TABLE IF NOT EXISTS `es_goods` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`goods_name` varchar(100) DEFAULT NULL COMMENT '商品名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='商品';

-- Dumping data for table master.es_goods: ~0 rows (大约)
DELETE FROM `es_goods`;

INSERT INTO `es_goods` (`goods_name`) VALUES ('测试面膜');
INSERT INTO `es_goods` (`goods_name`) VALUES ('MEDIHEAL/美迪惠尔可莱丝针剂水库面膜贴10片');
INSERT INTO `es_goods` (`goods_name`) VALUES ('SNP海洋燕窝补水美白面膜10片');
INSERT INTO `es_goods` (`goods_name`) VALUES ('Paparecipe春雨 天然蜂蜜补水面膜 10片装');
INSERT INTO `es_goods` (`goods_name`) VALUES ('RAY 银色蚕丝面膜 10片(泰国)');
INSERT INTO `es_goods` (`goods_name`) VALUES ('RAY 金色蚕丝面膜 10片(泰版)');
INSERT INTO `es_goods` (`goods_name`) VALUES ('补水测试面膜');
INSERT INTO `es_goods` (`goods_name`) VALUES ('华为畅享7 Plus 3GB 32GB 移动联通电信4G手机 双卡双待');
INSERT INTO `es_goods` (`goods_name`) VALUES ('华为畅享7 Plus 4GB 64GB 移动联通电信4G手机 双卡双待');
INSERT INTO `es_goods` (`goods_name`) VALUES ('华为 nova 2 4GB 64GB移动联通电信4G手机');

在springboot项目中添加es所需库

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

另附demo中所需其它库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

配置yml

主要是数据库连接方面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
datasource:
hikari:
pool-name: HikariCP
auto-commit: false
url: jdbc:mysql://localhost:3306/es_?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: dreamlyn
driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
tenant-enable: true
mapper-locations: classpath:/mapper/*Mapper.xml
global-config:
banner: false
db-config:
id-type: auto
field-strategy: NOT_EMPTY

添加ES相关配置

在此对es进行配置,主要是es服务器的ip和端口。

更多配置请参考官方文档

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {

@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}

附官方配置说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
HttpHeaders httpHeaders = new HttpHeaders();
// Define default headers, if they need to be customized
httpHeaders.add("some-header", "on every request");

ClientConfiguration clientConfiguration = ClientConfiguration.builder()
//Use the builder to provide cluster addresses, set default HttpHeaders or enable SSL
.connectedTo("localhost:9200", "localhost:9291")
//Optionally enable SSL.
.usingSsl()
//Optionally set a proxy.
.withProxy("localhost:8888")
//Optionally set a path prefix, mostly used when different clusters a behind some reverse proxy.
.withPathPrefix("ela")
//Set the connection timeout. Default is 10 sec.
.withConnectTimeout(Duration.ofSeconds(5))
//Set the socket timeout. Default is 5 sec.
.withSocketTimeout(Duration.ofSeconds(3))
//Optionally set headers.
.withDefaultHeaders(defaultHeaders)
//Add basic authentication.
.withBasicAuth(username, password)
//A Supplier<Header> function can be specified which is called every time before a request is sent to Elasticsearch - here, as an example, the current time is written in a header.
.withHeaders(() -> {
HttpHeaders headers = new HttpHeaders();
headers.add("currentTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
return headers;
})
// ... other options
.build();

添加商品对象实体类

注解意义可参考官方文档

1
2
3
4
5
6
7
8
9
10
@Document(indexName = "es_goods")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ESGoodsDocument implements Serializable {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
private String goodsName;
}

附官方文档:

The MappingElasticsearchConverter uses metadata to drive the mapping of objects to documents. The metadata is taken from the entity’s properties which can be annotated.

The following annotations are available:

  • @Document: Applied at the class level to indicate this class is a candidate for mapping to the database. The most important attributes are:
  1. indexName: the name of the index to store this entity in
  2. type: the mapping type. If not set, the lowercased simple name of the class is used. (deprecated since version 4.0)
  3. shards: the number of shards for the index.
  4. replicas: the number of replicas for the index.
  5. refreshIntervall: Refresh interval for the index. Used for index creation. Default value is “1s”.
  6. indexStoreType: Index storage type for the index. Used for index creation. Default value is “fs”.
  7. createIndex: Configuration whether to create an index on repository bootstrapping. Default value is true.
  8. versionType: Configuration of version management. Default value is EXTERNAL.
  • @Id: Applied at the field level to mark the field used for identity purpose.
  • @Transient: By default all fields are mapped to the document when it is stored or retrieved, this annotation excludes the field.

  • @PersistenceConstructor: Marks a given constructor - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved Document.

  • @Field: Applied at the field level and defines properties of the field, most of the attributes map to the respective Elasticsearch Mapping definitions (the following list is not complete, check the annotation Javadoc for a complete reference):

  1. name: The name of the field as it will be represented in the Elasticsearch document, if not set, the Java field name is used.
  2. type: the field type, can be one of Text, Keyword, Long, Integer, Short, Byte, Double, Float, Half_Float, Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type. See Elasticsearch Mapping Types
  3. format and pattern definitions for the Date type. format must be defined for date types.
  4. store: Flag wether the original field value should be store in Elasticsearch, default value is false.
  5. analyzer, searchAnalyzer, normalizer for specifying custom custom analyzers and normalizer.
  • @GeoPoint: marks a field as geo_point datatype. Can be omitted if the field is an instance of the GeoPoint class.

创建实体类对应的dao、service、controller文件

贴出关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//根据关键字查询
@Service
public class BaseSearchServiceImpl<T> implements BaseSearchService<T> {
private Logger log = LoggerFactory.getLogger(getClass());
@Resource
private ElasticsearchOperations searchOperations;
@Override
public List<T> query(String keyword, Class<T> clazz) {
Query searchQuery = new NativeSearchQueryBuilder()
.withQuery(new QueryStringQueryBuilder(keyword).field("goodsName"))
.withSort(SortBuilders.scoreSort().order(SortOrder.DESC))
// .withSort(new FieldSortBuilder("createTime").order(SortOrder.DESC))
.build();
SearchHitsIterator<T> stream = searchOperations
.searchForStream(searchQuery, clazz);
List<T> list = new ArrayList<>();
while (stream.hasNext()) {
list.add(stream.next().getContent());
}
stream.close();
return list;
}
}

其余逻辑请参考代码自行参悟。

最终效果