SpringBoot integrates ElasticSearch

Spring Boot 集成 ElasticSearch

Friends who are relatively new to ElasticSearch can first take a look at the overview of ElasticSearch. ElasticSearch installation, startup, operation and concept introduction.
Good start~

1. Basic operation

1.1. Import dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
  • New version configuration method (recommended)

    The new configuration method uses the High Level REST Client method to replace the previous Transport Client method, which uses HTTP requests, and uses the 9200 port of Elasticsearch like Kibana.

1.2, custom configuration class

In this configuration scheme, what you use is not a configuration file, but a custom configuration class:

/**
 * 你也可以不继承 AbstractElasticsearchConfiguration 类,而将 ESConfig 写成一般的配置类的型式。
 * 不过继承 AbstractElasticsearchConfiguration 好处在于,它已经帮我们配置好了 elasticsearchTemplate 直接使用。
 */
@Configuration
public class ESConfig extends AbstractElasticsearchConfiguration {
    
    

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

1.3. Entity class

PO classes in Elasticsearch:

@Document(indexName = "books",shards = 1,replicas = 0)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ESBook {
    
    

    @Id
    @Field(type = FieldType.Keyword)
    private String id;

    @Field(type = FieldType.Text)
    private String title;

    @Field(type = FieldType.Keyword)
    private String language;

    @Field(type = FieldType.Keyword)
    private String author;

    @Field(type = FieldType.Float)
    private Float price;

    @Field(type = FieldType.Text)
    private String description;
}

  • @Document : The annotation will index all properties in the entity;
  • indexName = "books" : means to create an index named "books";
  • shards = 1 : Indicates that only one shard is used;
  • replicas = 0 : Indicates that no replication backup is used;
  • @Field(type = FieldType.Keyword) : Used to specify the data type of the field.

2. Create the Repository for the operation

@Repository
//看实体类Id索引是什么类型 我这里是String
public interface ESBookRepstitory extends ElasticsearchRepository<ESBook, String> {
    
    
    
}

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-5wSibzg8-1681375145182)(assets\spring-boot-es-02.png)]

Our custom CustomerRepository interface inherits a large number of ready-made methods from its ancestors. In addition, it can also define specific methods according to the rules of spring data.

3. Test CustomerRepository

// 创建索引
@Test
public void indexList() {
    
    
   System.out.println("创建索引");
}
// 删除索引
@Test
public void indexList() {
    
    
	restTemplate.indexOps(IndexCoordinates.of("books")).delete();
    System.out.println("删除索引");
}

[External link image transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the image and upload it directly (img-2HQkNX8j-1681375145182) (C:\Users\lps\AppData\Roaming\Typora\typora-user-images\ image-20230413111252346.png)]

4. CRUD operation

4.1, add in batches

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-xP3YE5M7-1681375145183) (C:\Users\lps\AppData\Roaming\Typora\typora-user-images\ image-20230413142657737.png)]

   @Autowired
   private ESBookRepstitory  bookByESRepstitory;
  @Test
    public void indexList() {
    
    
        List<ESBook> lists = new ArrayList<>();
        lists.add(new ESBook("1", "Java 程序设计", "汉语", "盖伦",
                18.88F, "哈哈嗨"));
        lists.add(new ESBook("2", "Python程序设计", "英语", "赵信",
                66.88F, "陷阵之至有死无生"));
        lists.add(new ESBook("3", "PHP 程序设计", "俄语", "宝石",
                88.88F, "我曾踏足山巅,也曾跌入低谷"));
        bookByESRepstitory.saveAll(lists);
    }

If the id is repeated, it will overwrite the previous one~~~

4.2. Modification

Modification and addition are the same interface, and the basis for distinguishing is id, which is similar to when we initiate a PUT request on a page.

     ESBook ESBook = new ESBook("3", "宝石 程序设计", "俄语", "宝石",
                88.88F, "我曾踏足山巅,也曾跌入低谷");
        bookByESRepstitory.save(ESBook);

//由于上面的id = 3 已经存在,故再次save 就是修改

4.3. Delete

@Test
public void test2(){
    bookByESRepstitory.deleteById("1");
    bookByESRepstitory.deleteAll();
}

4.4. Basic query

1. ElasticsearchRepository provides some basic query methods:

@Test
public void testQuery(){
    
    
     Optional<BookByES> optionalById = this.bookByESRepstitory.findById("1");
     System.out.println(optionalById.get());
}

 @Test
    public void testFind(){
    
    
        // 查询全部,并按照价格降序排序
        //写法一: 
        Iterable<BookByES> items = this.bookByESRepstitory.findAll(Sort.by(Sort.Direction.DESC,
                "price"));
        //写法二: 
        Iterable<BookByES> items1 = this.bookByESRepstitory.findAll(Sort.by(Sort.Order.desc("price")));
    }

2. Paging query

The paging scheme that comes with Spring Data:

    @Test
    public void testByPage(){
    
    
        Sort sort = Sort.by(Sort.Direction.DESC,"id");
        //分页
        PageRequest pageRequest = PageRequest.of(0, 2, sort);
        Page<BookByES> all = bookByESRepstitory.findAll(pageRequest);
        for (BookByES bookByES : all) {
    
    
            System.out.println(bookByES);
        }
    }

[External link image transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the image and upload it directly (img-Q7SZLIEI-1681375145183) (C:\Users\lps\AppData\Roaming\Typora\typora-user-images\ image-20230413121434941.png)]

4.5. Custom method query

Another powerful feature of Spring Data is the automatic implementation of functions based on method names.

For example: your method name is: findByTitle, then it knows that you are querying based on the title, and then automatically completes it for you without writing an implementation class.

Of course, the method name must conform to certain conventions

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(Collection<String>names) {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotIn findByNameNotIn(Collection<String>names) {"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}}}}

like:

import com.springsecurity.domain.ESBook;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author 阿水
 * @create 2023-04-13 11:05
 */
@Repository
//看实体类Id索引是什么类型 我这里是String
public interface ESBookRepstitory extends ElasticsearchRepository<ESBook, String> {
    
    
    /**
     * 根据描述查询书籍,带分页的
     * @param description
     * @return
     */
    List<ESBook> findESBookByDescription(String description, Pageable variable);
    /**
     * 根据作者和描述和标题查
     */
    List<ESBook> queryESBookByAuthorAndDescriptionOrTitle(String author,String description,String title);
    /**
     * 根据书的价格范围查询
     */
    List<ESBook> queryESBookByPriceBetween(Float price1,Float price2);
}
@Test
void esQueryCondition() {
    
    
    Sort sort = Sort.by(Sort.Order.desc("id"));
    PageRequest pageRequest = PageRequest.of(0, 2, sort);
    List<ESBook> EBooks = bookByESRepstitory.findESBookByDescription("我",pageRequest);
    for (ESBook bookByE : EBooks) {
    
    
        System.out.println(bookByE);
    }
}
@Test
void esQueryCondition2() {
    
    
    List<ESBook> esBooks = bookByESRepstitory.queryESBookByAuthorAndDescriptionOrTitle("盖伦", "哈嗨", "程序");
    for (ESBook book : esBooks) {
    
    
        System.out.println(book);
    }
}

@Test
void esQueryCondition3() {
    
    
    List<ESBook> esBooks = bookByESRepstitory.queryESBookByPriceBetween(18.88F,77.88F);
    for (ESBook book : esBooks) {
    
    
        System.out.println(book);
    }
}

4.6. Using Native Search Query

@Autowired
private ElasticsearchRestTemplate restTemplate;
QueryBuilders.queryStringQuery() #指定字符串作为关键词查询,关键词支持分词
QueryBuilders.queryStringQuery("华为手机").defaultField("description");
//不指定feild,查询范围为所有feild
QueryBuilders.queryStringQuery("华为手机");
//指定多个feild
QueryBuilders.queryStringQuery("华为手机").field("title").field("description");

QueryBuilders.boolQuery          #子方法must可多条件联查
QueryBuilders.termQuery          #精确查询指定字段不支持分词
QueryBuilders.termQuery("description", "华为手机")
QueryBuilders.matchQuery         #按分词器进行模糊查询支持分词
QueryBuilders.matchQuery("description", "华为手机")    
QueryBuilders.rangeQuery         #按指定字段进行区间范围查询

- `QueryBuilders.boolQuery()`
- `QueryBuilders.boolQuery().must()`:相当于 and
- `QueryBuilders.boolQuery().should()`:相当于 or
- `QueryBuilders.boolQuery().mustNot()`:相当于 not 
- ——————————————————————————————————————————————————————————————————————————————————————————————————————

  @Test
    void naticeQuery() {
    
    
        NativeSearchQuery nativeSearchQuery =
                new NativeSearchQueryBuilder()
                        //.withQuery(QueryBuilders.queryStringQuery("山巅哈哈").defaultField("description"))
                        //多条件查询
                        .withQuery(QueryBuilders.boolQuery()
                                .must(QueryBuilders.queryStringQuery("山巅哈哈").defaultField("description"))
                                .should(QueryBuilders.queryStringQuery("宝石").defaultField("title"))
                        )
                        .withPageable(PageRequest.of(0, 2))
                        .build();
        SearchHits<ESBook> search = restTemplate.search(nativeSearchQuery, ESBook.class);
        List<SearchHit<ESBook>> searchHits = search.toList();
        for (SearchHit<ESBook> searchHit : searchHits) {
    
    
            System.out.println(searchHit);
        }

    }

for example:

[External link image transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the image and upload it directly (img-zbyhHOcz-1681375145184) (C:\Users\lps\AppData\Roaming\Typora\typora-user-images\ image-20230413160753038.png)]

5. ES scene

Scenario 1: Use es for externally exposed data (a large amount of data). If you do not need to be exposed to the outside world and do not need full-text search, then you can directly search from the data, so the data for project analysis is divided into 2 pieces (which data needs to be placed in es, Check from es, which ones are not needed)

Scenario 2: As an external index of mysql, put the column data as the database query condition into es, so that when querying, first query the id that meets the conditions from es, and then search the database according to the id, the data maintenance is large, Once es is down, it will be troublesome

Guess you like

Origin blog.csdn.net/lps12345666/article/details/130134109