Elasticsearch實戰篇——Spring Boot整合ElasticSearch

當前Spring Boot非常流行,包括我本身,也是在用Spring Boot集成其餘框架進行項目開發,因此這一節,咱們一塊兒來探討Spring Boot整合ElasticSearch的問題。php

本文主要講如下內容:html

第一部分,通讀文檔java

第二部分,Spring Boot整合ElasticSearchnode

第三部分,基本的CRUD操做git

第四部分,搜索github

第五部分,例子web

尚未學過Elasticsearch的朋友,能夠先學這個系列的第一節(這個系列共三節),若是你有不明白或者不正確的地方,能夠給我評論、留言或者私信。spring

第一步,通讀文檔

Spring Data Elasticsearch 官方文檔,這是當前最新的文檔。apache

關於repository

文檔一開始就介紹 CrudRepository ,好比,繼承 Repository,其餘好比 JpaRepositoryMongoRepository是繼承CrudRepository。也對其中的方法作了簡單說明,咱們一塊兒來看一下:json

public interface CrudRepository<T, ID extends Serializable>
  extends Repository<T, ID> {

// Saves the given entity.
  <S extends T> S save(S entity);      

// Returns the entity identified by the given ID.
  Optional<T> findById(ID primaryKey); 

// Returns all entities.
  Iterable<T> findAll();               

// Returns the number of entities.
  long count();                        

// Deletes the given entity.
  void delete(T entity);               

// Indicates whether an entity with the given ID exists.
  boolean existsById(ID primaryKey);   

  // … more functionality omitted.
}

好了,下面咱們看一下今天的主角 ElasticsearchRepository 他是怎樣的吧。

ElasticsearchRepository繼承圖

這說明什麼?

  • 用法和JPA同樣;
  • 再這他除了有CRUD的基本功能以外,還有分頁和排序。

清楚了這以後,是否是應該考慮該如何使用了呢?

如何用?

沒錯,接下來,開始說如何用,也寫了不少示例代碼。相對來講,仍是比較簡單,這裏就貼一下代碼就好了吧。

interface PersonRepository extends Repository<User, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

是否是這樣,就能夠正常使用了呢?

問題

固然能夠,可是若是錯了問題怎麼辦呢,官網寫了一個常見的問題,好比包掃描問題,沒有你要的方法。

interface HumanRepository {
  void someHumanMethod(User user);
}

class HumanRepositoryImpl implements HumanRepository {

  public void someHumanMethod(User user) {
    // Your custom implementation
  }
}

interface ContactRepository {

  void someContactMethod(User user);

  User anotherContactMethod(User user);
}

class ContactRepositoryImpl implements ContactRepository {

  public void someContactMethod(User user) {
    // Your custom implementation
  }

  public User anotherContactMethod(User user) {
    // Your custom implementation
  }
}

你也能夠本身寫接口,而且去實現它。

說完理論,做爲我,應該在實際的代碼中如何運用呢?

示例

官方也提供了不少示例代碼,咱們一塊兒來看看。

@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

這段代碼相對來講仍是十分經典的,我相信不少人都看到別人的代碼,可能都會問,它爲何會這麼用呢,答案或許就在這裏吧。

固然,這是之前的代碼,或許如今用不必定合適。

高級搜索

終於到高潮了!

學完個人第一節,你應該已經發現了,Elasticsearch搜索是一件十分複雜的事,爲了用好它,咱們不得不學好它。一塊兒加油。

到這裏,官方文檔咱們算是過了一遍了,大體明白了,他要告訴咱們什麼。其實,文檔還有不少內容,可能你遇到的問題都能在裏面找到答案。

最後,咱們繼續看一下官網寫的一段處理得十分優秀的一段代碼吧:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
    .withQuery(matchAllQuery())
    .withIndices(INDEX_NAME)
    .withTypes(TYPE_NAME)
    .withFields("message")
    .withPageable(PageRequest.of(0, 10))
    .build();

CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class);

List<SampleEntity> sampleEntities = new ArrayList<>();
while (stream.hasNext()) {
    sampleEntities.add(stream.next());
}

第二部分,Spring Boot整合ElasticSearch

添加依賴

implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

添加配置

spring:
  data:
    elasticsearch:
      cluster-nodes: localhost:9300
      cluster-name: es-wyf

這樣就完成了整合,接下來咱們用兩種方式操做。

Model

咱們先寫一個的實體類,藉助這個實體類呢來完成基礎的CRUD功能。

@Data
@Accessors(chain = true)
@Document(indexName = "blog", type = "java")
public class BlogModel implements Serializable {

    private static final long serialVersionUID = 6320548148250372657L;

    @Id
    private String id;

    private String title;

    //@Field(type = FieldType.Date, format = DateFormat.basic_date)
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date time;
}

注意id字段是必須的,能夠不寫註解@Id。

BlogRepository

public interface BlogRepository extends ElasticsearchRepository<BlogModel, String> {
}

第三部分,CRUD

基礎操做的代碼,都是在 BlogController 裏面寫。

@RestController
@RequestMapping("/blog")
public class BlogController {
    @Autowired
    private BlogRepository blogRepository;
}

添加

@PostMapping("/add")
public Result add(@RequestBody BlogModel blogModel) {
    blogRepository.save(blogModel);
    return Result.success();
}

咱們添加一條數據,標題是:Elasticsearch實戰篇:Spring Boot整合ElasticSearch,時間是:2019-03-06。咱們來測試,看一下成不成功。

POST http://localhost:8080/blog/add

{
    "title":"Elasticsearch實戰篇:Spring Boot整合ElasticSearch",
    "time":"2019-05-06"
}

獲得響應:

{
    "code": 0,
    "msg": "Success"
}

嘿,成功了。那接下來,咱們一下查詢方法測試一下。

查詢

  • 根據ID查詢
@GetMapping("/get/{id}")
public Result getById(@PathVariable String id) {
    if (StringUtils.isEmpty(id))
        return Result.error();
    Optional<BlogModel> blogModelOptional = blogRepository.findById(id);
    if (blogModelOptional.isPresent()) {
        BlogModel blogModel = blogModelOptional.get();
        return Result.success(blogModel);
    }
    return Result.error();
}

測試一下:

測試根據ID查詢

ok,沒問題。

  • 查詢全部
@GetMapping("/get")
public Result getAll() {
    Iterable<BlogModel> iterable = blogRepository.findAll();
    List<BlogModel> list = new ArrayList<>();
    iterable.forEach(list::add);
    return Result.success(list);
}

測試一下:

GET http://localhost:8080/blog/get

結果:

{
    "code": 0,
    "msg": "Success",
    "data": [
        {
            "id": "fFXTTmkBTzBv3AXCweFS",
            "title": "Elasticsearch實戰篇:Spring Boot整合ElasticSearch",
            "time": "2019-05-06"
        }
    ]
}

根據ID修改

@PostMapping("/update")
public Result updateById(@RequestBody BlogModel blogModel) {
    String id = blogModel.getId();
    if (StringUtils.isEmpty(id))
        return Result.error();
    blogRepository.save(blogModel);
    return Result.success();
}

測試:

POST http://localhost:8080/blog/update

{
    "id":"fFXTTmkBTzBv3AXCweFS",
    "title":"Elasticsearch入門篇",
    "time":"2019-05-01"
}

響應:

{
    "code": 0,
    "msg": "Success"
}

查詢一下:

修改數據成功

ok,成功!

刪除

  • 根據ID刪除
@DeleteMapping("/delete/{id}")
public Result deleteById(@PathVariable String id) {
    if (StringUtils.isEmpty(id))
        return Result.error();
    blogRepository.deleteById(id);
    return Result.success();
}

測試:

DELETE http://localhost:8080/blog/delete/fFXTTmkBTzBv3AXCweFS

響應:

{
    "code": 0,
    "msg": "Success"
}

咱們再查一下:

刪除數據成功

  • 刪除全部數據
@DeleteMapping("/delete")
public Result deleteById() {
    blogRepository.deleteAll();
    return Result.success();
}

第四部分,搜索

構造數據

爲了方便測試,咱們先構造數據

構造查詢數據

Repository查詢操做

搜索標題中的關鍵字

BlogRepository

List<BlogModel> findByTitleLike(String keyword);

BlogController

@GetMapping("/rep/search/title")
public Result repSearchTitle(String keyword) {
    if (StringUtils.isEmpty(keyword))
        return Result.error();
    return Result.success(blogRepository.findByTitleLike(keyword));
}

咱們來測試一下。

POST http://localhost:8080/blog/rep/search/title?keyword=java

結果:

{
    "code": 0,
    "msg": "Success",
    "data": [
        {
            "id": "f1XrTmkBTzBv3AXCeeFA",
            "title": "java實戰",
            "time": "2018-03-01"
        },
        {
            "id": "fVXrTmkBTzBv3AXCHuGH",
            "title": "java入門",
            "time": "2018-01-01"
        },
        {
            "id": "flXrTmkBTzBv3AXCUOHj",
            "title": "java基礎",
            "time": "2018-02-01"
        },
        {
            "id": "gFXrTmkBTzBv3AXCn-Eb",
            "title": "java web",
            "time": "2018-04-01"
        },
        {
            "id": "gVXrTmkBTzBv3AXCzuGh",
            "title": "java ee",
            "time": "2018-04-10"
        }
    ]
}

繼續搜索:

GET http://localhost:8080/blog/rep/search/title?keyword=入門

結果:

{
    "code": 0,
    "msg": "Success",
    "data": [
        {
            "id": "hFXsTmkBTzBv3AXCtOE6",
            "title": "Elasticsearch入門",
            "time": "2019-01-20"
        },
        {
            "id": "fVXrTmkBTzBv3AXCHuGH",
            "title": "java入門",
            "time": "2018-01-01"
        },
        {
            "id": "glXsTmkBTzBv3AXCBeH_",
            "title": "php入門",
            "time": "2018-05-10"
        }
    ]
}

爲了驗證,咱們再換一個關鍵字搜索:

GET http://localhost:8080/blog/rep/search/title?keyword=java入門

{
    "code": 0,
    "msg": "Success",
    "data": [
        {
            "id": "fVXrTmkBTzBv3AXCHuGH",
            "title": "java入門",
            "time": "2018-01-01"
        },
        {
            "id": "hFXsTmkBTzBv3AXCtOE6",
            "title": "Elasticsearch入門",
            "time": "2019-01-20"
        },
        {
            "id": "glXsTmkBTzBv3AXCBeH_",
            "title": "php入門",
            "time": "2018-05-10"
        },
        {
            "id": "gFXrTmkBTzBv3AXCn-Eb",
            "title": "java web",
            "time": "2018-04-01"
        },
        {
            "id": "gVXrTmkBTzBv3AXCzuGh",
            "title": "java ee",
            "time": "2018-04-10"
        },
        {
            "id": "f1XrTmkBTzBv3AXCeeFA",
            "title": "java實戰",
            "time": "2018-03-01"
        },
        {
            "id": "flXrTmkBTzBv3AXCUOHj",
            "title": "java基礎",
            "time": "2018-02-01"
        }
    ]
}

哈哈,有沒有以爲很眼熟。

那根據上次的經驗,咱們正好換一種方式解決這個問題。

@Query("{\"match_phrase\":{\"title\":\"?0\"}}")
List<BlogModel> findByTitleCustom(String keyword);

值得一提的是,官方文檔示例代碼多是爲了好看,出現問題。

官網文檔給的錯誤示例:

官網文檔錯誤

官網示例代碼:

官方示例代碼

官方示例代碼

另外,?0 代指變量的意思。

@GetMapping("/rep/search/title/custom")
public Result repSearchTitleCustom(String keyword) {
    if (StringUtils.isEmpty(keyword))
        return Result.error();
    return Result.success(blogRepository.findByTitleCustom(keyword));
}

測試一下:

測試成功示例

ok,沒有問題。

ElasticsearchTemplate

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;

@GetMapping("/search/title")
public Result searchTitle(String keyword) {
    if (StringUtils.isEmpty(keyword))
        return Result.error();
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(queryStringQuery(keyword))
            .build();
    List<BlogModel> list = elasticsearchTemplate.queryForList(searchQuery, BlogModel.class);
    return Result.success(list);
}

測試:

POST http://localhost:8080/blog/search/title?keyword=java入門

結果:

{
    "code": 0,
    "msg": "Success",
    "data": [
        {
            "id": "fVXrTmkBTzBv3AXCHuGH",
            "title": "java入門",
            "time": "2018-01-01"
        },
        {
            "id": "hFXsTmkBTzBv3AXCtOE6",
            "title": "Elasticsearch入門",
            "time": "2019-01-20"
        },
        {
            "id": "glXsTmkBTzBv3AXCBeH_",
            "title": "php入門",
            "time": "2018-05-10"
        },
        {
            "id": "gFXrTmkBTzBv3AXCn-Eb",
            "title": "java web",
            "time": "2018-04-01"
        },
        {
            "id": "gVXrTmkBTzBv3AXCzuGh",
            "title": "java ee",
            "time": "2018-04-10"
        },
        {
            "id": "f1XrTmkBTzBv3AXCeeFA",
            "title": "java實戰",
            "time": "2018-03-01"
        },
        {
            "id": "flXrTmkBTzBv3AXCUOHj",
            "title": "java基礎",
            "time": "2018-02-01"
        }
    ]
}

OK,暫時先到這裏,關於搜索,咱們後面會專門開一個專題,學習搜索。

第五部分,例子

咱們寫個什麼例子,想了好久,那就寫一個搜索手機的例子吧!

界面截圖

咱們先看下最後實現的效果吧

主頁效果:

主頁效果

分頁效果:

分頁效果

咱們搜索 「小米」:

全文搜索 - 「小米」

咱們搜索 「1999」:

全文搜索 - 「1999」

咱們搜索 「黑色」:

全文搜索 - 「黑色」

高級搜索頁面:

高級搜索 - "主頁面"

咱們使用高級搜索,搜索:「小米」、「1999」:

高級搜索 - 「小米 1999」

高級搜索 「小米」、「1999」 結果:

高級搜索 - 「小米 1999」 - 結果

上面的而且關係生效了嗎?咱們試一下搜索 「華爲」,「1999」:

高級搜索 - 「華爲 1999」 - 結果

最後,咱們嘗試搜索時間段:

高級搜索 - 「2019-03-19 01:44:53 ~ 2019-03-19 01:44:55」

看一下,搜索結果吧:

高級搜索 - 「2019-03-19 01:44:53 ~ 2019-03-19 01:44:55」 - 結果

說實話,這個時間搜索結果,我不是很滿意,ES 的時間問題,我打算在後面花一些時間去研究下。

搭建項目

基於Gradle搭建Spring Boot項目,把我折騰的受不了(若是哪位這方面有經驗,能夠給我指點指點),這個demo寫了好久,那天都跑的好好的,今早上起來,就跑步起來了,一氣之下,就改爲Maven了。

下面看一下個人依賴和配置

pom.xml 片斷

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<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.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--
    添加 JavaLib 支持
    用於接口返回
     -->
    <dependency>
        <groupId>com.github.fengwenyi</groupId>
        <artifactId>JavaLib</artifactId>
        <version>1.0.7.RELEASE</version>
    </dependency>

    <!--
    添加 webflux 支持
    用於編寫非阻塞接口
     -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!--
    添加 fastjson 的支持
    用於處理JSON格式數據
     -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.56</version>
    </dependency>

    <!--
    添加 Httpclient 的支持
    用於網絡請求
     -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.7</version>
    </dependency>

    <!--
    添加 jsoup 的支持
    用於解析網頁內容
     -->
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.10.2</version>
    </dependency>
</dependencies>

application.yml

server:
  port: 9090

spring:
  data:
    elasticsearch:
      cluster-nodes: localhost:9300
      cluster-name: es-wyf
      repositories:
        enabled: true

PhoneModel

@Data
@Accessors(chain = true)
@Document(indexName = "springboot_elasticsearch_example_phone", type = "com.fengwenyi.springbootelasticsearchexamplephone.model.PhoneModel")
public class PhoneModel implements Serializable {
    private static final long serialVersionUID = -5087658155687251393L;

    /* ID */
    @Id
    private String id;

    /* 名稱 */
    private String name;

    /* 顏色,用英文分號(;)分隔 */
    private String colors;

    /* 賣點,用英文分號(;)分隔 */
    private String sellingPoints;

    /* 價格 */
    private String price;

    /* 產量 */
    private Long yield;

    /* 銷售量 */
    private Long sale;

    /* 上市時間 */
    //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date marketTime;

    /* 數據抓取時間 */
    //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

}

PhoneRepository

public interface PhoneRepository extends ElasticsearchRepository<PhoneModel, String> {
}

PhoneController

@RestController
@RequestMapping(value = "/phone")
@CrossOrigin
public class PhoneController {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

}

後面接口,都會在這裏寫。

構造數據

個人數據是抓的 「華爲」 和 「小米」 官網

首先使用 httpclient 下載html,而後使用 jsoup 進行解析。

華爲 爲例:

private void huawei() throws IOException {
    CloseableHttpClient httpclient = HttpClients.createDefault(); // 建立httpclient實例
    HttpGet httpget = new HttpGet("https://consumer.huawei.com/cn/phones/?ic_medium=hwdc&ic_source=corp_header_consumer"); // 建立httpget實例

    CloseableHttpResponse response = httpclient.execute(httpget); // 執行get請求
    HttpEntity entity=response.getEntity(); // 獲取返回實體
    //System.out.println("網頁內容:"+ EntityUtils.toString(entity, "utf-8")); // 指定編碼打印網頁內容
    String content = EntityUtils.toString(entity, "utf-8");
    response.close(); // 關閉流和釋放系統資源

//        System.out.println(content);

    Document document = Jsoup.parse(content);
    Elements elements = document.select("#content-v3-plp #pagehidedata .plphidedata");
    for (Element element : elements) {
//            System.out.println(element.text());
        String jsonStr = element.text();
        List<HuaWeiPhoneBean> list = JSON.parseArray(jsonStr, HuaWeiPhoneBean.class);
        for (HuaWeiPhoneBean bean : list) {
            String productName = bean.getProductName();
            List<ColorModeBean> colorsItemModeList = bean.getColorsItemMode();

            StringBuilder colors = new StringBuilder();
            for (ColorModeBean colorModeBean : colorsItemModeList) {
                String colorName = colorModeBean.getColorName();
                colors.append(colorName).append(";");
            }

            List<String> sellingPointList = bean.getSellingPoints();
            StringBuilder sellingPoints = new StringBuilder();
            for (String sellingPoint : sellingPointList) {
                sellingPoints.append(sellingPoint).append(";");
            }

//                System.out.println("產品名:" + productName);
//                System.out.println("顏  色:" + color);
//                System.out.println("買  點:" + sellingPoint);
//                System.out.println("-----------------------------------");
            PhoneModel phoneModel = new PhoneModel()
                    .setName(productName)
                    .setColors(colors.substring(0, colors.length() - 1))
                    .setSellingPoints(sellingPoints.substring(0, sellingPoints.length() - 1))
                    .setCreateTime(new Date());
            phoneRepository.save(phoneModel);
        }
    }
}

全文搜索

全文搜索來講,仍是相對來講,比較簡單,直接貼代碼吧:

/**
 * 全文搜索
 * @param keyword 關鍵字
 * @param page 當前頁,從0開始
 * @param size 每頁大小
 * @return {@link Result} 接收到的數據格式爲json
 */
@GetMapping("/full")
public Mono<Result> full(String keyword, int page, int size) {
    // System.out.println(new Date() + " => " + keyword);

    // 校驗參數
    if (StringUtils.isEmpty(page))
        page = 0; // if page is null, page = 0
    
    if (StringUtils.isEmpty(size))
        size = 10; // if size is null, size default 10
    
    // 構造分頁類
    Pageable pageable = PageRequest.of(page, size);
    
    // 構造查詢 NativeSearchQueryBuilder
    NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder()
            .withPageable(pageable)
            ;
    if (!StringUtils.isEmpty(keyword)) {
        // keyword must not null
        searchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keyword));
    }
    
    /*
    SearchQuery
    這個很關鍵,這是搜索條件的入口,
    elasticsearchTemplate 會 使用它 進行搜索
     */
    SearchQuery searchQuery = searchQueryBuilder.build();

    // page search
    Page<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);
    
    // return
    return Mono.just(Result.success(phoneModelPage));
}

官網文檔也是這麼用的,因此相對來講,這仍是很簡單的,不過拆詞 和 搜索策略 搜索速度 可能在實際使用中要考慮。

高級搜索

先看代碼,後面咱們再來分析:

/**
 * 高級搜索,根據字段進行搜索
 * @param name 名稱
 * @param color 顏色
 * @param sellingPoint 賣點
 * @param price 價格
 * @param start 開始時間(格式:yyyy-MM-dd HH:mm:ss)
 * @param end 結束時間(格式:yyyy-MM-dd HH:mm:ss)
 * @param page 當前頁,從0開始
 * @param size 每頁大小
 * @return {@link Result}
 */
@GetMapping("/_search")
public Mono<Result> search(String name, String color, String sellingPoint, String price, String start, String end, int page, int size) {

    // 校驗參數
    if (StringUtils.isEmpty(page) || page < 0)
        page = 0; // if page is null, page = 0

    if (StringUtils.isEmpty(size) || size < 0)
        size = 10; // if size is null, size default 10
    
    // 構造分頁對象
    Pageable pageable = PageRequest.of(page, size);

    // BoolQueryBuilder (Elasticsearch Query)
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    if (!StringUtils.isEmpty(name)) {
        boolQueryBuilder.must(QueryBuilders.matchQuery("name", name));
    }

    if (!StringUtils.isEmpty(color)) {
        boolQueryBuilder.must(QueryBuilders.matchQuery("colors", color));
    }

    if (!StringUtils.isEmpty(color)) {
        boolQueryBuilder.must(QueryBuilders.matchQuery("sellingPoints", sellingPoint));
    }

    if (!StringUtils.isEmpty(price)) {
        boolQueryBuilder.must(QueryBuilders.matchQuery("price", price));
    }

    if (!StringUtils.isEmpty(start)) {
        Date startTime = null;
        try {
            startTime = DateTimeUtil.stringToDate(start, DateTimeFormat.yyyy_MM_dd_HH_mm_ss);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").gt(startTime.getTime()));
    }

    if (!StringUtils.isEmpty(end)) {
        Date endTime = null;
        try {
            endTime = DateTimeUtil.stringToDate(end, DateTimeFormat.yyyy_MM_dd_HH_mm_ss);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").lt(endTime.getTime()));
    }

    // BoolQueryBuilder (Spring Query)
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withPageable(pageable)
            .withQuery(boolQueryBuilder)
            .build()
            ;

    // page search
    Page<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);
    
    // return
    return Mono.just(Result.success(phoneModelPage));
}

無論spring如何封裝,查詢方式都同樣,以下圖:

es 搜索 語句

好吧,咱們懷着這樣的心態去看下源碼。

org.springframework.data.elasticsearch.core.query.SearchQuery

這個是咱們搜索須要用到對象

public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
        this.queryBuilder = queryBuilder;
        return this;
    }

OK,根據源碼,咱們須要構造這個 QueryBuilder,那麼問題來了,這個是個什麼東西,咱們要如何構造,繼續看:

org.elasticsearch.index.query.QueryBuilder

注意包名。

啥,怎麼又跑到 elasticsearch。

你想啊,你寫的東西,會讓別人直接操做嗎?

答案是不會的,咱們只會提供API,全部,無論Spring如何封裝,也只會經過API去調用。

query 包下得類

好吧,今天先到這裏,下一個專題,咱們再討論關於搜索問題。

連接

ElasticSearch 學習系列

代碼

Spring Boot整合Elasticsearch

Spring Boot結合Elasticsearch,實現手機信息搜索小例子

演示視頻

<iframe height=498 width=510 src='http://player.youku.com/embed...' frameborder=0 'allowfullscreen'></iframe>

若是沒法播放,請點擊這裏

相關文章
相關標籤/搜索