Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。作为 Elastic Stack
的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。
What
Elasicsearch是什么?
Elasticsearch 是一个基于 Lucene 的实时的分布式搜索分析引擎,开箱即用,整合了全文检索、结构化搜索、分析三大功能。为什么不直接用 Lucene ?Lucene 只是一个全文检索引擎的架构,提供了大量可用的 API,但其并不是一个完整的全文检索引擎,使用 Lucene 时,你还需要自己写代码,自己去封装成全文检索引擎。
Elasticsearch基本概念
Field:字段。
Document :文档,一条数据,用 json 格式表示。一个Document 包含多个field,json 中的 key 即 field 。
Type:类型,一个 Document 分组,和 mysql 中的 table 类似,但又不完全相同。一个 Type 包含多个Document,同一个 Type 中的 Document 所拥有的 field 可以不同,但最好保持一致。
Index :索引,类似于 mysql 中的 database。一个 Index 包含多个 Type。默认情况下,Document 中的所有 field 都会被索引,这样这些 field 才会被搜索到。Elasticsearch 中有一个倒排索引(Inverted Index)的概念,可以实现 mysql 中 B+Tree索引加速检索的目的。
shard:分片。可以将一个 Index 中的数据切分为多个 shard,然后将之存储在多台服务器上,以增大一个 Index 可以存储的数据量,加速检索能力,提升系统性能。
replica :副本。replica 与 shard 存储的数据是相同的,replica 起到备份的作用。当 shard 发生故障时,可以从 replica 中读取数据,保证系统不受影响。
Node:节点,单个 Elasticsearch 实例。节点名称默认随机分配。
Cluster:集群,一组 Elasticsearch 实例。默认集群名称为 elasticsearch。
How
安装Elasticsearch
这里使用docker-compose部署
1 2 3 4 5 6 7 8 9 version: '3' services: elasticsearch: image: elasticsearch:6.8.8 container_name: elasticsearch environment: discovery.type: single-node ports: - "9200:9200"
使用curl http://localhost:9200
可以测试安装结果,出现类似以下结果为正常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "name" : "Q8-z3Bf" , "cluster_name" : "docker-cluster" , "cluster_uuid" : "H56Jga63RRWDwtu1D2OaEw" , "version" : { "number" : "6.8.8" , "build_flavor" : "default" , "build_type" : "docker" , "build_hash" : "2f4c224" , "build_date" : "2020-03-18T23:22:18.622755Z" , "build_snapshot" : false , "lucene_version" : "7.7.2" , "minimum_wire_compatibility_version" : "5.6.0" , "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" }
为Elasticsearch安装ik分词器
在线安装
1 2 3 4 docker exec -it es容器id bash ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip exit docker restart es容器id
集成springboot
pom文件
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <?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" > <modelVersion > 4.0.0</modelVersion > <groupId > org.example</groupId > <artifactId > elasticsearch-springboot-test</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > jar</packaging > <name > elasticsearch-springboot-test</name > <description > elasticsearch-springboot-test</description > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.2.6.RELEASE</version > <relativePath /> </parent > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > <java.version > 1.8</java.version > <lombok.version > 1.18.12</lombok.version > </properties > <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-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > ${lombok.version}</version > <scope > provided</scope > </dependency > </dependencies > </project >
添加配置文件application.yml
1 2 3 4 5 6 server: port: 13000 spring: elasticsearch: rest: uris: http://192.168.3.30:9200
定义domain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Document(indexName = UserIndexConstant.INDEX_NAME,shards = 1,replicas = 0) @Data @AllArgsConstructor @NoArgsConstructor @ToString public class UserCO { @Id private Long id; @Field(type = FieldType.Text) private String name; @Field(type = FieldType.Integer) private Integer age; }
定义常量
1 2 3 4 public class UserIndexConstant { public static final String INDEX_NAME = "test_index"; public static final String TYPE = "user"; }
定义操作接口
1 2 3 4 5 6 7 8 public interface IUserEsIndexService { public void insert (List<UserCO> userCOs) ; public void delete (UserCO userCO) ; public void update (List<UserCO> userCOs) ; public void deleteAll () ; public <T> List<T> queryForList (SearchQuery query, Class<T> clazz) ; public <T> Page<T> queryForPage (SearchQuery query, Class<T> clazz) ; }
操作接口实现类
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Service @AllArgsConstructor public class UserEsIndexServiceImpl implements IUserEsIndexService { private ElasticsearchOperations elasticsearchOperations; @Override public void insert (List<UserCO> userCOs) { if (!elasticsearchOperations.indexExists(UserCO.class)) { elasticsearchOperations.createIndex(UserCO.class); } List<IndexQuery> collect = userCOs.stream().map(userCO -> { IndexQuery indexQuery = new IndexQuery(); indexQuery.setId(userCO.getId().toString()); indexQuery.setObject(userCO); return indexQuery; }).collect(Collectors.toList()); elasticsearchOperations.bulkIndex(collect); } @Override public void delete (UserCO userCO) { elasticsearchOperations.delete(UserIndexConstant.INDEX_NAME, UserIndexConstant.TYPE, userCO.getId().toString()); } @Override public void update (UserCO userCO) { this .delete(userCO); List<UserCO> list = new ArrayList<UserCO>(); list.add(userCO); this .insert(list); } @Override public void deleteAll () { elasticsearchOperations.deleteIndex(UserIndexConstant.INDEX_NAME); } @Override public <T> List<T> queryForList (SearchQuery query, Class<T> clazz) { return this .elasticsearchOperations.queryForList(query,clazz); } @Override public <T> Page<T> queryForPage (SearchQuery query, Class<T> clazz) { return this .elasticsearchOperations.queryForPage(query,clazz); } }
启动类
1 2 3 4 5 6 @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class,args); } }
测试类
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @RunWith(SpringRunner.class) @SpringBootTest(classes={Application.class}) public class UserIndexTest { @Autowired private IUserEsIndexService userEsIndexService; @Test public void testInsert () { String[] name1 = new String[]{"张" , "王" , "刘" , "李" , "白" , "郝" , "马" }; String[] name2 = new String[]{"小" , "大" , "海" , "镇" , "天" , "时" , "里" }; String[] name3 = new String[]{"渡" , "嗄" , "无" , "图" , "与" , "可" , "真" }; List<UserCO> userCOList = new ArrayList<>(); for (int i = 0 ; i < 100 ; i++) { int i1 = Math.abs(new Random().nextInt()) % 7 ; int i2 = Math.abs(new Random().nextInt()) % 7 ; int i3 = Math.abs(new Random().nextInt()) % 7 ; int age = (Math.abs(new Random().nextInt()) % 50 ) + 20 ; UserCO userCO = new UserCO((long ) i, name1[i1] + name2[i2] + name3[i3], age); System.out.println(userCO); userCOList.add(userCO); } userEsIndexService.insert(userCOList); } @Test public void testQuery () { SearchQuery searchQuery = new NativeSearchQuery(new QueryStringQueryBuilder("1" )); List<UserCO> userCOS = userEsIndexService.queryForList(searchQuery, UserCO.class); userCOS.forEach(System.out::println); } @Test public void testDelete () { UserCO userCO = new UserCO(); userCO.setId(1L ); userEsIndexService.delete(userCO); } @Test public void testUpdate () throws InterruptedException { SearchQuery searchQuery = new NativeSearchQuery(new QueryStringQueryBuilder("2" )); List<UserCO> userCOS = userEsIndexService.queryForList(searchQuery, UserCO.class); userCOS.forEach(System.out::println); UserCO userCO = new UserCO(2L ,"测试变更" +System.currentTimeMillis(),100 ); userEsIndexService.update(userCO); Thread.sleep(500 ); List<UserCO> userCOS2 = userEsIndexService.queryForList(searchQuery, UserCO.class); userCOS2.forEach(System.out::println); } }
坑
截止到2020年5月7日 SpringData最高支持到Elasticsearch 6.8.8版本,所以如果用最新版的es,将会一直报错。
备注