ElasticSearch入门(一)

ElasticSearch 入门DEMO

  • ElasticSearch环境准备
  • 基本概念
  • 与SpringBoot集成
  • 单元测试
    • 索引创建
    • 增加或者更新
    • 更新
    • 删除
    • 获取
    • 批量操作
    • 查询
  • 后记

ElasticSearch环境准备

  1. ElasticSearch 解压安装,/bin目录下对应名字的文件启动,本地启动默认为127.0.0.1:9200
  2. 集群管理工具 cerebro。不想安装node+head插件的可以用这个。解压后直接使用,输入ES的地址127.0.0.1:9200可以查看集群当前状态
  3. ES插件IK分词器。增强中文分词能力,下载后解压到/ElasticSearch/plugins/ik 这样的路径下面,重启ES生效

基本概念

  • 索引index:含有相同属性的文档集合。类似MySQL的database。
  • 类型type:索引可以定义一个或者多个类型。文档必须属于一个类型。类似MySQL的Table。
  • 文档doc:可以被索引的基本数据单位。类似于MySQL表中的一行记录。

与SpringBoot集成

本笔记写于2020年2月,使用的ES版本为7.5.2,SpringBoot的版本为2.2.4.RELEASE。在这个时间节点,ES官方推荐使用elasticsearch-rest-high-level-client作为ES的客户端访问工具。因此maven的pom文件中只需要添加以下依赖即可。

  1. pom.xml添加es必要依赖spring-boot-starter-data-elasticsearch,其他都不需要
		
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        
		
  1. 写ElasticSearchConfig配置类,在容器中定义bean——RestHighLevelClient
    由于TrasnsportClient即将在新版的ElasticSearch废除,官方建议使用RestHighLevelClient
@Configuration
public class ElasticSearchConfig {

    private static final Logger log = LoggerFactory.getLogger(ElasticSearchConfig.class);

    private static final int ADDRESS_LENGTH = 2;
    private static final String HTTP_SCHEME = "http";

	// ES的访问地址从配置文件中读入
    @Value("${elasticsearch.ip}")
    String[] ipAddress;

    @Autowired
    RestClientBuilder restClientBuilder;

    @Bean
    public RestClientBuilder restClientBuilder() {
        HttpHost[] hosts = Arrays.stream(ipAddress)
                .map(this::makeHttpHost)
                .filter(Objects::nonNull)
                .toArray(HttpHost[]::new);
        return RestClient.builder(hosts);
    }

    @Bean(name = "highLevelClient")
    public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) {
        //TODO 此处可以进行其它操作
        return new RestHighLevelClient(restClientBuilder);
    }

    HttpHost makeHttpHost(String s) {
        assert StringUtils.isNotEmpty(s);
        String[] address = s.split(":");
        if (address.length == ADDRESS_LENGTH) {
            String ip = address[0];
            int port = Integer.parseInt(address[1]);
            log.info("ElasticSearch: " + ip + ":" + port);
            return new HttpHost(ip, port, HTTP_SCHEME);
        } else {
            return null;
        }
    }
}
  1. 写一个测试用的实体类。可使用spring.data.elasticsearch提供的注解定义该类,方便后续使用。
@Document(indexName = "item",type = "docs", shards = 5, replicas = 1)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {
    @Id
    private Long id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title; //标题

    @Field(type = FieldType.Keyword)
    private String category;// 分类

    @Field(type = FieldType.Keyword)
    private String brand; // 品牌

    @Field(type = FieldType.Double)
    private Double price; // 价格

    //默认情况下分词,一般默认分词就好,除非这个字段你确定查询时不会用到
    @Field(index = false, type = FieldType.Keyword)
    private String images; // 图片地址
}
  1. 为了方便还可以使用spring.data提供的ElasticsearchRepository简化对ES的一般操作,需要先声明接口
import com.example.demo.domain.Item;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface ItemRepository extends ElasticsearchRepository<Item, Long> {
}

完成上述步骤之后,就可以在代码中读写ES了。

单元测试

通过单元测试来实践创建索引和增删查改等常规操作。首先需要引入客户端等

@SpringBootTest
class ElasticSearchStudyApplicationTests {

    @Autowired
    private RestHighLevelClient highLevelClient;

    @Autowired
    private ItemRepository itemRepository;

	…………………………其他代码…………………………
}

索引创建

可以借助ElasticsearchRepository简化索引创建操作,在save某个实体之后会自动创建索引并写入数据。

    @Test
    public void addIndex() throws IOException {
        Item it1 = new Item();
        it1.setId(1L);
        it1.setBrand("brand");
        it1.setCategory("category");
        it1.setTitle("title");
        it1.setImages("images");
        it1.setPrice(1.00);
        // 使用Repository写入数据的时候会自动创建index和type
        itemRepository.save(it1);
    }

增加或者更新

    @Test
    public void insertOrUpdateOne() {
        // 构造记录
        Item it1 = new Item();
        it1.setId(3L);
        it1.setBrand("品牌3");
        it1.setCategory("种类3");
        it1.setTitle("标题3");
        it1.setImages("图片3");
        it1.setPrice(3.00);
        // 发送请求
        IndexRequest request = new IndexRequest("item");// 指定index
        request.type("docs");//指定type
        request.id("3");// 指定id,如果不指定则是自动生成.如果指定了,记录存在则是更新
        request.source(JSON.toJSONString(it1), XContentType.JSON);

        try {
            IndexResponse indexResponse = highLevelClient.index(request, RequestOptions.DEFAULT);
            out.println(indexResponse);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

更新

    @Test
    public void update() throws IOException {
        // 模拟入参一个item
        Item it1 = new Item();
        it1.setId(3L);
        it1.setBrand("品牌");
        it1.setCategory("种类3");
        it1.setTitle("标题3");
        it1.setImages("图片3");
        it1.setPrice(3.00);

        UpdateRequest request = new UpdateRequest("item", "docs", "2");

        request.doc(JSON.toJSONString(it1), XContentType.JSON);
        UpdateResponse updateResponse = highLevelClient.update(request, RequestOptions.DEFAULT);
        System.out.println("update: " + JSON.toJSONString(updateResponse));
    }

删除

提供id删除对应文档

/**
     * 删除记录
     */
    @Test
    public void delete() throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest("item", "docs", "2");
        DeleteResponse response = highLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println("delete: " + JSON.toJSONString(response));
    }

获取

    @Test
    public void getByRepository() {
        Iterable<Item> all = itemRepository.findAll();

        // 格式化为普通的List返回
        ArrayList<Item> items = Lists.newArrayList(all);

        out.println(items);
    }

    @Test
    public void getById() throws Exception {
        GetRequest getRequest = new GetRequest("item", "docs", "1");
        GetResponse getResponse = highLevelClient.get(getRequest, RequestOptions.DEFAULT);
        Item item = JSON.parseObject(getResponse.getSourceAsString(), new TypeReference<Item>() {
        });
        out.println("get: " + JSON.toJSONString(getResponse));
        out.println("转换成Item实体: " + item);
    }

批量操作

@Test
    public void bulkAdd() throws IOException {
        Item it1 = new Item(1L, "围城", "图书", "小说", 2.00, "image");
        Item it2 = new Item(2L, "Java编程思想", "图书", "技术", 3.00, "image");
        Item it3 = new Item(3L, "范特西", "音乐", "流行", 10.00, "image");
        Item it4 = new Item(4L, "数据库设计", "图书", "技术", 15.00, "image");
        Item it5 = new Item(5L, "华为手机", "数码", "华为", 11.00, "image");

        List<Item> testsList = Lists.newArrayList(it1, it2, it3, it4, it5);

        // 批量增加
        BulkRequest bulkAddRequest = new BulkRequest();
        for (int i = 0; i < testsList.size(); i++) {
            Item tests = testsList.get(i);
            IndexRequest indexRequest = new IndexRequest("item", "docs", tests.getId().toString());
            indexRequest.source(JSON.toJSONString(tests), XContentType.JSON);
            bulkAddRequest.add(indexRequest);
        }
        BulkResponse bulkAddResponse = highLevelClient.bulk(bulkAddRequest, RequestOptions.DEFAULT);
        System.out.println("bulkAdd: " + JSON.toJSONString(bulkAddResponse));
    }

    @Test
    public void bulkUpdate() throws IOException {
        Item it1 = new Item(1L, "东方快车谋杀案", "图书", "小说", 2.00, "image");
        Item it2 = new Item(2L, "Java编程思想", "图书", "技术", 3.00, "image");
        Item it3 = new Item(3L, "范特西", "音乐", "流行", 10.00, "image");
        Item it4 = new Item(4L, "数据库设计", "图书", "技术", 15.00, "image");
        Item it5 = new Item(5L, "华为手机", "数码", "华为", 11.00, "image");
        List<Item> testsList = Lists.newArrayList(it1, it2, it3, it4, it5);

        // 批量更新
        BulkRequest bulkUpdateRequest = new BulkRequest();
        for (int i = 0; i < testsList.size(); i++) {
            Item tests = testsList.get(i);
            tests.setImages(tests.getImages() + "_updated");
            UpdateRequest updateRequest = new UpdateRequest("item", "docs", tests.getId().toString());
            updateRequest.doc(JSON.toJSONString(tests), XContentType.JSON);
            bulkUpdateRequest.add(updateRequest);
        }
        BulkResponse bulkUpdateResponse = highLevelClient.bulk(bulkUpdateRequest, RequestOptions.DEFAULT);
        System.out.println("bulkUpdate: " + JSON.toJSONString(bulkUpdateResponse));
    }

    @Test
    public void bulkDelete() throws IOException {
        List<Item> testsList = new ArrayList<>();
        // 批量删除
        BulkRequest bulkDeleteRequest = new BulkRequest();
        for (int i = 0; i < testsList.size(); i++) {
            Item tests = testsList.get(i);
            DeleteRequest deleteRequest = new DeleteRequest("item", "docs", tests.getId().toString());
            bulkDeleteRequest.add(deleteRequest);
        }
        BulkResponse bulkDeleteResponse = highLevelClient.bulk(bulkDeleteRequest, RequestOptions.DEFAULT);
        System.out.println("bulkDelete: " + JSON.toJSONString(bulkDeleteResponse));
    }

查询

    /**
     * 查询
     */
    @Test
    public void search() throws IOException {
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
        // 这里可以根据字段进行搜索,must表示符合条件的,相反的mustnot表示不符合条件的
        boolBuilder.should(QueryBuilders.matchQuery("title", "Java"));
        boolBuilder.should(QueryBuilders.matchQuery("brand", "技术"));

        // boolBuilder.must(QueryBuilders.matchQuery("id", tests.getId().toString()));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        sourceBuilder.query(boolBuilder);

        // 分页
        sourceBuilder.from(0);
        sourceBuilder.size(100); // 获取记录数,默认10

        // 第一个是获取字段,第二个是过滤的字段,默认获取全部
        //sourceBuilder.fetchSource(new String[] { "id", "name" }, new String[] {});

        SearchRequest searchRequest = new SearchRequest("item");
        searchRequest.types("docs");
        searchRequest.source(sourceBuilder);
        SearchResponse response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println("search: " + JSON.toJSONString(response));
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            System.out.println("search -> " + hit.getSourceAsString());
        }
    }

后记

  • 本文记录了SpringBoot集成ElasticSearch的个人实践,至今还没有机会能在实际项目中应用,并不是最佳实践。如果您有提议或者好的想法,欢迎留言。
  • ES提供Restful风格接口,可以通过PostMan等Http客户端进行访问。URL和参数等可以参照官方文档。
  • ES 入门(二)打算总结Elasticsearch的查询,并在代码中实践。

你可能感兴趣的:(Java开发)