• 22-07-27 西安 ElasticSearch(02)


    Spring Data Elasticsearch

    Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API 进行封装 。

    官方网站:Spring Data Elasticsearch

    1.Spring Data ElasticSearch 入门

    创建工程 elasticsearch-springdata-es

    1.1引入依赖

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

    1.2 配置文件application.properties

    1. # es服务地址
    2. elasticsearch.host=127.0.0.1
    3. # es服务端口
    4. elasticsearch.port=9200
    5. # 配置日志级别,开启debug日志
    6. logging.level.com.atguigu=debug

    1.3主启动

    1. @SpringBootApplication
    2. public class SpringDataESApplication {
    3. public static void main(String[] args) {
    4. SpringApplication.run(SpringDataESApplication.class,args);
    5. }
    6. }

    1.4配置类

    ElasticsearchRestTemplate基于RestHighLevelClient客户端的。需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象

    1. @ConfigurationProperties(prefix = "elasticsearch")
    2. @Configuration
    3. @Getter
    4. @Setter
    5. public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
    6. private String host ;
    7. private Integer port ;
    8. //重写父类方法
    9. @Override
    10. public RestHighLevelClient elasticsearchClient() {
    11. //高级客户端
    12. RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
    13. RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
    14. return restHighLevelClient;
    15. }
    16. }

    1.5实体类

    这要搁平时来说,实体类屁用没有,都懒得提,提了都降价。这次不一样了,Spring Data ES中

    实体类可是顶大用啊,主要体现在里面注解就老多了,跟我们创建的索引库息息相关

    1. @Data
    2. @NoArgsConstructor
    3. @AllArgsConstructor
    4. @Document(indexName = "item",shards = 1, replicas = 1)
    5. public class Item {
    6. @Id
    7. private Long id;
    8. @Field(type = FieldType.Text, analyzer = "ik_max_word")
    9. private String title; //标题
    10. @Field(type = FieldType.Keyword)
    11. private String category;// 分类
    12. @Field(type = FieldType.Keyword)
    13. private String brand; // 品牌
    14. @Field(type = FieldType.Double)
    15. private Double price; // 价格
    16. @Field(index = false, type = FieldType.Keyword)
    17. private String images; // 图片地址
    18. }

    @Document 作用在类,标记实体类为文档对象,属性解释如下:

    • indexName:对应索引库名称
    • shards:主分片数量
    • replicas:复制分片数量


    @Id 作用在成员变量,标记一个字段作为id主键
     

    @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:

    • type:字段类型,取值是枚举:FieldType
    • index:是否索引,布尔类型,默认是true
    • store:是否存储,布尔类型,默认是false
    • analyzer:分词器名称:ik_max_word

    1.6写一个测试类

    1. @RunWith(SpringRunner.class)
    2. @SpringBootTest
    3. public class SpringDataESTest {
    4. @Autowired
    5. private ElasticsearchRestTemplate elasticsearchTemplate;
    6. @Test
    7. public void test0(){
    8. System.out.println(elasticsearchTemplate);
    9. }
    10. }

    运行,控制台打印


    2.索引和文档操作

    2.1创建索引库

    1. @Test
    2. public void testCreate() {
    3. // 创建索引库,会根据Item类的@Document注解信息来创建
    4. elasticsearchTemplate.createIndex(Item.class);
    5. // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
    6. elasticsearchTemplate.putMapping(Item.class);
    7. }

    Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。【不用在类上加任何的注解,我说的】

    1. public interface ItemDao extends ElasticsearchRepository {
    2. }

    2.2 添加文档

    1. @Autowired
    2. private ItemDao itemDao;
    3. @Test
    4. public void testAdd() {
    5. Item item = new Item(1L, "联想笔记本电脑小新Pro14 英特尔Evo平台", " 笔记本", "联想", 4599.00, "http://image.leyou.com/13123.jpg");
    6. //添加文档
    7. itemDao.save(item);
    8. }

     2.3 修改文档

    1. @Test
    2. public void testUpdate() {
    3. Item item = new Item(1L, "小米手机", " 手机", "小米", 9499.00, "http://image.leyou.com/13123.jpg");
    4. //文档修改【id存在就是修改,否则就是插入】
    5. itemDao.save(item);
    6. }

     2.4批量添加文档

    1. @Test
    2. public void testBatchADD() {
    3. List list = new ArrayList<>();
    4. list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
    5. list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
    6. // 接收对象集合,实现批量新增文档
    7. itemDao.saveAll(list);
    8. }

     2.5删除文档

    1. @Test
    2. public void testDelete() {
    3. //删除文档
    4. itemDao.deleteById(1L);
    5. }

    2.6根据id查询文档

    1. @Test
    2. public void testQueryByID(){
    3. Optional optional = itemDao.findById(2L);
    4. //根据id查询文档
    5. System.out.println(optional.get());
    6. }

    2.7查询全部并按照价格降序排序

    1. @Test
    2. public void testFindALL(){
    3. // 查询全部,并按照价格降序排序
    4. Iterable items = itemDao.findAll(Sort.by(Sort.Direction.DESC, "price"));
    5. items.forEach(item-> System.out.println(item));
    6. }

    3.自定义方法

    Spring Data 的另一个强大功能,是根据方法名称自动实现功能。

    比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类

    测试类中测试方法

    1. @Test
    2. public void testFindByTitile(){
    3. // 查询全部,并按照价格降序排序
    4. Iterable items = itemDao.findByTitle("手机");
    5. items.forEach(item-> System.out.println(item));
    6. }

    首先,要知道索引库中所有的文档如下:

     运行后控制台打印如下:效果他不就有了吗

    但是方法名称要符合一定的约定

    Keyword

    Sample方法命名

    Elasticsearch Query String执行方式

    And

    findByNameAndPrice

    {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}

    Or

    findByNameOrPrice

    {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}

    Is

    findByName

    {"bool" : {"must" : {"field" : {"name" : "?"}}}}

    Not

    findByNameNot

    {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}

    Between

    findByPriceBetween

    {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

    LessThanEqual

    findByPriceLessThan

    {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

    GreaterThanEqual

    findByPriceGreaterThan

    {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}

    Before

    findByPriceBefore

    {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

    After

    findByPriceAfter

    {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}

    Like

    findByNameLike

    {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}

    StartingWith

    findByNameStartingWith

    {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}

    EndingWith

    findByNameEndingWith

    {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}

    Contains/Containing

    findByNameContaining

    {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}

    In

    findByNameIn(Collectionnames)

    {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}

    NotIn

    findByNameNotIn(Collectionnames)

    {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}

    Near

    findByStoreNear

    Not Supported Yet !

    True

    findByAvailableTrue

    {"bool" : {"must" : {"field" : {"available" : true}}}}

    False

    findByAvailableFalse

    {"bool" : {"must" : {"field" : {"available" : false}}}}

    OrderBy

    findByAvailableTrueOrderByNameDesc

    {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

    虽然基本查询和自定义方法已经很强大了,但是如果是复杂查询(模糊、通配符、词条查询等)就显得力不从心了。此时,我们只能使用原生查询。


    ES Java高级客户端

    在这之前,我要吐槽。Elasticsearch 的Java客户端用起来费老劲了,昨天用Kibana他不香吗,这是越整合越倒流了。增加了学习的成本不说,还整合的像坨粑粑。

    人家jedis整合redis,方法名就是命令名,多简单。人家JDBC整合mysql,都是自己写sql,然后用占位符赋值。结果你它吗整合ES就整合成这个鬼样子。

    建议直接看5、6,还算有点用的。其他的就

    1.搭建环境测试

    2.索引操作

    2.1添加索引

    1. @Test
    2. public void addIndex() throws Exception {
    3. //1.使用client获取操作索引的对象 indicesClient 操作索引库的客户端
    4. IndicesClient indicesClient = client.indices();
    5. //2.请求对象
    6. CreateIndexRequest createRequest = new CreateIndexRequest("abc");
    7. //发起创建索引库的请求
    8. CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
    9. //3.根据返回值判断结果
    10. System.out.println(response.isAcknowledged());
    11. }

    2.2 添加索引和映射

    1. /**
    2. * 添加索引 创建索引库并指定映射
    3. */
    4. @Test
    5. public void addIndexAndMapping() throws IOException {
    6. //1.使用client获取操作索引的对象
    7. IndicesClient indicesClient = client.indices();
    8. //2.具体操作,获取返回值
    9. CreateIndexRequest createRequest = new CreateIndexRequest("aaa");
    10. //2.1 设置mappings
    11. String mapping = "{\n" +
    12. " \"properties\" : {\n" +
    13. " \"address\" : {\n" +
    14. " \"type\" : \"text\",\n" +
    15. " \"analyzer\" : \"ik_max_word\"\n" +
    16. " },\n" +
    17. " \"age\" : {\n" +
    18. " \"type\" : \"long\"\n" +
    19. " },\n" +
    20. " \"name\" : {\n" +
    21. " \"type\" : \"keyword\"\n" +
    22. " }\n" +
    23. " }\n" +
    24. " }";
    25. createRequest.mapping(mapping,XContentType.JSON);
    26. CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
    27. //3.根据返回值判断结果
    28. System.out.println(response.isAcknowledged());
    29. }
    30. }

    2.3查询索引

    2.4删除索引

    2.5判断索引是否存在

    3.文档操作

    3.1 添加文档(使用map作为数据)

    3.2添加文档(使用对象作为数据)

    3.3修改文档

    3.4根据id查询文档

    3.5根据id删除文档

    3.6批量操作脚本

    Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数(带宽)

    4.查询操作

    4.1查询所有matchAll

    4.2term查询

    4.3matchQuery词条分词查询

    4.4模糊查询-脚本

    4.5范围查询-脚本

    4.6queryString查询-脚本

    4.7布尔查询

    4.8聚合查询

    5.批量导入Mysql到ES中

    目的:将数据库中Goods表的数据导入到ElasticSearch中

    1. 创建Goods表并导入数据
    2. 创建索引

    6.重建索引

    ElasticSearch的索引一旦创建,只允许添加字段,不允许改变字段。因为改变字段,需要重建倒排索引,影响内部缓存结构,性能太低。

    解决办法:重建一个新的索引,并将原有索引的数据导入到新索引中。
    原索引库 :student_index_v1
    新索引库 :student_index_v2

    1. # 2:将student_index_v1 数据拷贝到 student_index_v2
    2. POST _reindex
    3. {
    4. "source": {
    5. "index": "student_index_v1"
    6. },
    7. "dest": {
    8. "index": "student_index_v2"
    9. }
    10. }

    这里我也提出过一个问题,虽然我知道答案

    问题:老师,索引库拷贝的时候,课堂上演示的是把date类型的“2020-11-11”拷贝到了新的索引库的Text类型。那要是把Text类型的"2020年11月11号",拷贝到新的索引库的date类型,是不可以的吗

    肯定是会报错的。不得不说,Elasticsearch中值得诟病的地方真它奈奈的多。除了查到快点,我都想给它一大嘴巴子!

  • 相关阅读:
    scanf输入时的小细节
    6个优化策略,助你降低K8S成本
    MongoDB-基本常用命令
    设计模式-Bridge模式(桥模式)
    Kruise Rollout v0.2.0 版本发布:支持 Gateway API、StatefulSet 分批发布等能力
    云扩RPA研习社 | 浅析自动化原理(上)
    linux安装mysql8参考指引
    C++ 实现定时器的两种方法(线程定时和时间轮算法修改版)
    达梦数据库错误代码汇总
    从生成到调试:大型语言模型的自我演进之旅
  • 原文地址:https://blog.csdn.net/m0_56799642/article/details/126020583