ElasticSearch经典入门(五) ElasticSearch Java实战

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: ElasticSearch Java实战

前言

今天开始我们学习如何使用Java来操作ES,这里会讲两种操作方式,一是使用ES提供的jar包来操作,二是使用SpringBootData来操作,第二种方式是企业里面使用的较多的方式了。

Java 操作ES

环境集成

创建一个普通的Java工程,首先我们需要导入ES的jar包

<dependency>
   <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>6.8.6</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>compile</scope>
</dependency>

然后创建ES的客户端

public class ESClientUtil {
   
   

    public static TransportClient getClient(){
   
   
        TransportClient client = null;
        Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
        try {
   
   
            client = new PreBuiltTransportClient(settings).addTransportAddress(
            new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
        } catch (UnknownHostException e) {
   
   
            e.printStackTrace();
        }
        return client;
    }

}

添加文档

编写测试类 ,往指定的索引库中添加文档

@Test
public void testAdd() {
   
   
  //获取客户端对象
  TransportClient client = ESClientUtil.getClient();

  //创建索引
  IndexRequestBuilder indexRequestBuilder = client.prepareIndex("orders", "_doc", "1");
  Map<String,Object> data = new HashMap<>();
  data.put("id",1);
  data.put("title","买了一个鼠标");
  data.put("count",1);
  data.put("amount",100.00);
  data.put("status",1);
  //获取结果
  IndexResponse indexResponse = indexRequestBuilder.setSource(data).get();
  //打印给过
  System.out.println(indexResponse);
  client.close();
}

打印结果如下 :

IndexResponse[index=orders,type=_doc,id=1,version=18,result=updated,seqNo=19,primaryTerm=3,shards={"total":2,"successful":1,"failed":0}]

获取一个文档

@Test
public void testGet() {
   
   
    //获取客户端对象
    TransportClient client = ESClientUtil.getClient();
    //获取文档
    GetResponse response = client.prepareGet("orders", "_doc", "1").get();
    //打印结果
    System.out.println(response);
}

打印结果如下:{amount=100.0, count=1, id=1, title=买了一个鼠标, status=1}

更新一个文档

@Test

public void testUpdate(){
   
   
    //获取客户端对象
    TransportClient client = ESClientUtil.getClient();

    //修改索引
    UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate("orders", "_doc", "1");
    Map<String,Object> data = new HashMap<>();
    data.put("id",1);
    data.put("title","买了一个鼠标");
    data.put("count",2);
    data.put("amount",100.00);
    //data.put("status",1);
    //获取结果设置修改内容
    UpdateResponse updateResponse = updateRequestBuilder.setDoc(data).get();

    System.out.println(updateResponse);
    client.close();
}

打印结果如下:UpdateResponse[index=orders,type=_doc,id=1,version=19,seqNo=20,primaryTerm=3,result=updated,shards=ShardInfo{total=2, successful=1, failures=[]}]

注意:这种修改方式是不会影响到未修改的字段的,比如上面注释掉status字段,status字段不会被修改。也就是说这种方式是局部修改。

删除一个文档

 @Test
 public void testDelete() {
   
   
      //获取客户端对象
      TransportClient client = ESClientUtil.getClient();
      //执行删除
      DeleteResponse response = client.prepareDelete("orders", "_doc", "1").get();
      //打印结果
      System.out.println(response);
  }

打印结果:DeleteResponse[index=orders,type=_doc,id=1,version=20,result=deleted,shards=ShardInfo{total=2, successful=1, failures=[]}]

批量操作

同时提交多个操作

@Test
public void testBuilkAdd(){
   
   
   //获取客户端对象
   TransportClient client = ESClientUtil.getClient();

   BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();

   Map<String,Object> data1 = new HashMap<>();
   data1.put("id",1);
   data1.put("title","买了一个鼠标");
   data1.put("count",2);
   data1.put("amount",100.00);

   //添加操作
   bulkRequestBuilder.add(client.prepareIndex("orders", "_doc", "1").setSource(data1));

   Map<String,Object> data2 = new HashMap<>();
   data2.put("id",2);
   data2.put("title","买了一个鼠标");
   data2.put("count",2);
   data2.put("amount",100.00);
   //添加操作
   bulkRequestBuilder.add(client.prepareIndex("orders", "_doc", "2").setSource(data2));

   Map<String,Object> data3 = new HashMap<>();
   data3.put("id",2);
   data3.put("title","买了一个鼠标");
   data3.put("count",2);
   data3.put("amount",200.00);

   //修改操作
   bulkRequestBuilder.add(client.prepareUpdate("orders", "_doc", "2").setDoc(data3));


   BulkResponse bulkItemResponses = bulkRequestBuilder.get();
   Iterator<BulkItemResponse> iterator = bulkItemResponses.iterator();
   while(iterator.hasNext()){
   
   
       BulkItemResponse next = iterator.next();
       System.out.println(next.getResponse());
   }
   client.close();
 }

打印结果
IndexResponse[index=orders,type=_doc,id=1,version=1,result=created,seqNo=22,primaryTerm=3,shards={"total":2,"successful":1,"failed":0}]
IndexResponse[index=orders,type=_doc,id=2,version=4,result=updated,seqNo=3,primaryTerm=3,shards={"total":2,"successful":1,"failed":0}]
UpdateResponse[index=orders,type=_doc,id=2,version=5,seqNo=4,primaryTerm=3,result=updated,shards=ShardInfo{total=2, successful=1, failures=[]}]

DSL查询

案例:查询订单标题中包含:鼠标,价格在10-2000之间,状态为1,按照价格正排序

@Test
    public void testSearch(){
   
   
        //获取客户端对象
        TransportClient client = ESClientUtil.getClient();

        SearchRequestBuilder searchRequestBuilder = client.prepareSearch("orders");
        //设置分页
        searchRequestBuilder.setFrom(0);
        searchRequestBuilder.setSize(10);
        searchRequestBuilder.addSort("amount", SortOrder.ASC);

        //查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //dsl查询
        List<QueryBuilder> must = boolQueryBuilder.must();
        must.add(QueryBuilders.matchQuery("title" , "鼠标"));

        //dsl过滤
        List<QueryBuilder> filter = boolQueryBuilder.filter();
        filter.add(QueryBuilders.rangeQuery("amount").lte(2000).gte(10));
        filter.add(QueryBuilders.termQuery("status",1));

        searchRequestBuilder.setQuery(boolQueryBuilder);

        SearchResponse searchResponse = searchRequestBuilder.get();

        SearchHits hits = searchResponse.getHits();

        System.out.println("条数:"+hits.getTotalHits());
        for (SearchHit hit : hits.getHits()) {
   
   
            System.out.println(hit.getSourceAsMap());

        }

        client.close();
    }

SpringBootData操作ES

环境集成

第一步:导入SpringBoot提整合ES的依赖 spring-boot-starter-data-elasticsearch

<!--SpringBoot-->
<parent>
   <groupId> org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.2.5.RELEASE</version>
</parent>

<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-test</artifactId>
     </dependency>
      <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
</dependencies>

第二步:然后需要在yml中对ES进行配置 , 如果是集群配置增加uri即可,单个配置如下:

spring:
  elasticsearch:
    rest:
      uris:
        - http://localhost:9200

第三步:编写启动类

@SpringBootApplication
public class SearchStart {
   
   

    public static void main(String[] args) {
   
   
        SpringApplication.run(SearchStart.class);
    }
}

创建Document对象

第三步:编写Document对象 ,该对象是对存储到ES中的数据的封装,同时文档映射也是通过它来实现


//标记该对象是ES的文档对象
//indexName 索引库
//type 类型
@Document(indexName = "orders",type = "_doc")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderDoc {
   
   

    //标记为文档ID,该ID的值会作为document的id值
    @Id
    private Long id;
    /**
     * 标题需要分词,指定为text;并使用IK分词器
     * 一般需要作为关键字搜索的字段都要指定为text,因为需要分词且创建索引
     */
    @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    //@Field(type = FieldType.Keyword)
    private String title;

    /**
     * 指定为integer类型
     */
    @Field(type = FieldType.Integer)
    private int count;
    /**
     * 状态指定为 integer类型
     */
    @Field(type = FieldType.Integer)
    private int status;

    /**
     * 金额
     */
    @Field(type = FieldType.Double)
    private BigDecimal amount;

}

创建Repository

第四步:SpringBootData提供了ElasticsearchRepository 来操作ES,该接口中包含了针对ES的CRUD方法,我们编写接口集成它即可使用

@Repository
public interface OrderRepository extends ElasticsearchRepository<OrderDoc,Long> {
   
   
}

注意:这里的泛型是当前Repository所要管理的实体类,也就是OrderDoc,Long是实体类ID的类型

创建索引和映射

编写一个基于SpringBoot的测试类如下:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SearchStart.class)
public class OrderESTest {
   
   
    //操作ES的template模板
    @Autowired
    private ElasticsearchRestTemplate template;

    @Autowired
    private OrderRepository orderRepository;

    @Test
    public void test(){
   
   
           //创建索引
        template.createIndex(CourseDoc.class);
        //创建映射
        template.putMapping(CourseDoc.class);
    }
}

这里我注入了一个 ElasticsearchRestTemplate 工具类,通过它的 createIndex 可以创建索引库,SpringBoot指定解析 CourseDoc上的@Document注解中的index来确定索引库的名字。创建映射也是同样的道理。

基础CRUD

在SpringBoot中对ES的Crud显得极其简单,直接调用repository内置方法即可完成

@Test
    public void testAdd(){
   
   
        orderRepository.save(new OrderDoc(1L,"买了一个表",1,1,new BigDecimal("200")));
    }

    @Test
    public void testGet(){
   
   
        Optional<OrderDoc> optional = orderRepository.findById(1l);
        System.out.println(optional.get());
    }

    @Test
    public void testDelete(){
   
   
        orderRepository.deleteById(1L);
    }

注意:这里并没有update方法,因为save对象的时候如果ID已经存在,就会执行update操作。repository中还有很多的方法可以使用,你可以自己去调试。

DSL查询

在SpringBoot中我们通过 NativeSearchQueryBuilder 来构建查询条件,调用repository.search来执行查询。
案例:查询标题中包含鼠标 ,状态为1,金额在10-2000之间,按照金额倒排,查询第2页数据,每页10条

 @Test
public void testSearch(){
   
   

    //查询构建器
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

    //设置分页: 第2页 (0开始), 每页10数
    builder.withPageable(PageRequest.of(1,10));
    //设置排序 : 金额倒排
    builder.withSort(SortBuilders.fieldSort("amount").order(SortOrder.DESC));

    //构建组合查询
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

    //标题包含鼠标
    boolQuery.must(QueryBuilders.matchQuery("title","鼠标"))
            //状态值查询
            .filter(QueryBuilders.termQuery("status",1))
            //金额范围查询
            .filter(QueryBuilders.rangeQuery("amount").gte(10).lte(2000));

    //添加查询条件
    builder.withQuery(boolQuery);

    //执行搜索
    Page<OrderDoc> page = orderRepository.search(builder.build());
    //获取条数
    System.out.println("总元素个数:"+page.getTotalElements());
    //打印列表
    page.getContent().forEach(System.out::print);

}

这里需要根据业务需求组装查询条件,我们之前见过的查询条件都可以通过它来构建,如:
image.png

打印效果

条数:3
{amount=200, count=1, id=2, title=买了一个鼠标, status=1}
{amount=200, count=1, id=1, title=买了一个鼠标, status=1}
{amount=200, count=1, id=3, title=买了一个鼠标, status=1}

文章结束,如果对你有所帮助,请点赞收藏加评论

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
5天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
11天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
17天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
55 5
|
15天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
29 1
|
21天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
43 3
|
22天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
23天前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
19 1
|
9天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
18天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
5天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
25 9