天天看點

《ElasticSearch6.x實戰教程》之簡單搜尋、Java用戶端(上)

《ElasticSearch6.x實戰教程》之簡單搜尋、Java用戶端(上)

第五章-簡單搜尋

衆裡尋他千百度

搜尋是ES的核心,本節講解一些基本的簡單的搜尋。

掌握ES搜尋查詢的RESTful的API猶如掌握關系型資料庫的SQL語句,盡管Java用戶端API為我們不需要我們去實際編寫RESTful的API,但在生産環境中,免不了線上上執行查詢語句做資料統計供産品經理等使用。

資料準備

首先建立一個名為user的Index,并建立一個student的Type,Mapping映射一共有如下幾個字段:

建立名為user的Index PUT

http://localhost:9200/user

建立名為student的Type,且指定字段name和address的分詞器為ik_smart。

POST

http://localhost:9200/user/student/_mapping

{

"properties":{

"name":{
     "type":"text",
     "analyzer":"ik_smart"
 },
 "age":{
     "type":"short"
 }           

}

經過上一章分詞的學習我們把text類型都指定為ik_smart分詞器。

插入以下資料。

POST localhost:9200/user/student

"name":"kevin",
"age":25           
"name":"kangkang",
"age":26           
"name":"mike",
"age":22           
"name":"kevin2",
"age":25           
"name":"kevin yu",
"age":21           

按查詢條件數量次元

無條件搜尋

GET

http://localhost:9200/user/student/_search?pretty

檢視索引user的student類型資料,得到剛剛插入的資料傳回:

單條件搜尋

ES查詢主要分為term精确搜尋、match模糊搜尋。

term精确搜尋

我們用term搜尋name為“kevin”的資料。

"query":{
    "term":{
        "name":"kevin"
    }
}           

既然term是精确搜尋,按照非關系型資料庫的了解來講就等同于=,那麼搜尋結果也應該隻包含1條資料。然而出乎意料的是,搜尋結果出現了兩條資料:name="kevin"和name="keivin yu",這看起來似乎是進行的模糊搜尋,但又沒有搜尋出name="kevin2"的資料。我們先繼續觀察match的搜尋結果。

match模糊搜尋

同樣,搜尋name為“kevin”的資料。

"query":{
    "match":{
        "name":"kevin"
    }
}           

match的搜尋結果竟然仍然是兩條資料:name="kevin"和name="keivin yu"。同樣,name="kevin2"也沒有出現在搜尋結果中。

原因在于term和match的精确和模糊針對的是搜尋詞而言,term搜尋不會将搜尋詞進行分詞後再搜尋,而match則會将搜尋詞進行分詞後再搜尋。例如,我們對name="kevin yu"進行搜尋,由于term搜尋不會對搜尋詞進行搜尋,是以它進行檢索的是"kevin yu"這個整體,而match搜尋則會對搜尋詞進行分詞搜尋,是以它進行檢索的是包含"kevin"和"yu"的資料。而name字段是text類型,且它是按照ik_smart進行分詞,就算是"kevin yu"這條資料由于被分詞後變成了"kevin"和"yu",是以term搜尋不到任何結果。

如果一定要用term搜尋name="kevin yu",結果出現"kevin yu",辦法就是在定義映射Mapping時就為該字段設定一個keyword類型。

為了下文的順利進行,删除DELETE

http:localhost:9200/user/student

重新按照開頭建立索引以及插入資料吧。唯一需要修改的是在定義映射Mapping時,name字段修改為如下所示:

"properties":{
      "name":{
          "type":"text",
          "analyzer":"ik_smart",
          "fields":{
              "keyword":{
                  "type":"keyword",
        "ignore_abore":256
              }
          }
      },
"age":{
    "type":integer
}
}           

待我們重新建立好索引并插入資料後,此時再按照term搜尋name="kevin yu"。

http://localhost:9200/user/student/_search
"query":{
    "term":{
        "name.keyword":"kevin yu"
    }
}           

傳回一條name="kevin yu"的資料。按照match搜尋同樣出現name="kevin yu",因為name.keyword無論如何都不會再分詞。

在已經建立索引且定義好映射Mapping的情況下,如果直接修改name字段,此時能修改成功,但是卻無法進行查詢,這與ES底層實作有關,如果一定要修改要麼是新增字段,要麼是重建索引。

是以,與其說match是模糊搜尋,倒不如說它是分詞搜尋,因為它會将搜尋關鍵字分詞;與其将term稱之為模糊搜尋,倒不如稱之為不分詞搜尋,因為它不會将搜尋關鍵字分詞。

match查詢還有很多更為進階的查詢方式:match_phrase短語查詢,match_phrase_prefix短語比對查詢,multi_match多字段查詢等。将在複雜搜尋一章中詳細介紹。

類似like的模糊搜尋

wildcard通配符查詢。

"query": {

"wildcard": {
  "name": "*kevin*"
}           

ES傳回結果包括name="kevin",name="kevin2",name="kevin yu"。

fuzzy更智能的模糊搜尋

fuzzy也是一個模糊查詢,它看起來更加”智能“。它類似于搜狗輸入法中允許文法錯誤,但仍能搜出你想要的結果。例如,我們查詢name等于”kevin“的文檔時,不小心輸成了”kevon“,它仍然能查詢出結構。

"fuzzy": {
  "name": "kevin"
}           

ES傳回結果包括name="kevin",name="kevin yu"。

多條件搜尋

上文介紹了單個條件下的簡單搜尋,并且介紹了相關的精确和模糊搜尋(分詞與不分詞)。這部分将介紹多個條件下的簡單搜尋。

當搜尋需要多個條件時,條件與條件之間的關系有”與“,”或“,“非”,正如非關系型資料庫中的”and“,”or“,“not”。

在ES中表示”與“關系的是關鍵字must,表示”或“關系的是關鍵字should,還有表示表示”非“的關鍵字must_not。

must、should、must_not在ES中稱為bool查詢。當有多個查詢條件進行組合查詢時,此時需要上述關鍵字配合上文提到的term,match等。

精确查詢(term,搜尋關鍵字不分詞)name="kevin"且age="25"的學生。

"query":{
    "bool":{
        "must":[{
            "term":{
                "name.keyword":"kevin"
            }
        },{
            "term":{
                "age":25
            }
        }]
    }
}           

傳回name="kevin"且age="25"的資料。

精确查詢(term,搜尋關鍵字不分詞)name="kevin"或age="21"的學生。

"query":{
    "bool":{
        "should":[{
            "term":{
                "name.keyword":"kevin"
            }
        },{
            "term":{
                "age":21
            }
        }]
    }
}           

傳回name="kevin",age=25和name="kevin yu",age=21的資料

精确查詢(term,搜尋關鍵字不分詞)name!="kevin"且age="25"的學生。

"query":{
    "bool":{
        "must":[{
            "term":{
                "age":25
            }
        }],
        "must_not":[{
            "term":{
                "name.keyword":"kevin"
            }
        }]
    }
}           

傳回name="kevin2"的資料。

如果查詢條件中同時包含must、should、must_not,那麼它們三者是"且"的關系

多條件查詢中查詢邏輯(must、should、must_not)與查詢精度(term、match)配合能組合成非常豐富的查詢條件。

按等值、範圍查詢次元

上文中講到了精确查詢、模糊查詢,已經"且","或","非"的查詢。基本上都是在做等值查詢,實際查詢中還包括,範圍(大于小于)查詢(range)、存在查詢(exists)、~不存在查詢(missing)。

範圍查詢

範圍查詢關鍵字range,它包括大于gt、大于等于gte、小于lt、小于等于lte。

查詢age>25的學生。

"query":{
    "range":{
        "age":{
            "gt":25
        }
    }
}           

傳回name="kangkang"的資料。

查詢age >= 21且age < 26的學生。

http://localhost:9200/user/search/_search?pretty
"query":{
    "range":{
        "age":{
            "gte":21,
            "lt":25
        }
    }
}           

查詢age >= 21 且 age < 26且name="kevin"的學生

"query":{
    "bool":{
        "must":[{
            "term":{
                "name":"kevin"
            }
        },{
            "range":{
                "age":{
                    "gte":21,
                    "lt":25
                }
            }
        }]
    }
}           

存在查詢

存在查詢意為查詢是否存在某個字段。

查詢存在name字段的資料。

"query":{
    "exists":{
        "field":"name"
    }   
}           

不存在查詢

不存在查詢顧名思義查詢不存在某個字段的資料。在以前ES有missing表示查詢不存在的字段,後來的版本中由于must not和exists可以組合成missing,故去掉了missing。

查詢不存在name字段的資料。

"query":{
    "bool":{
        "must_not":{
            "exists":{
                "field":"name"
            }
        }
    }   
}           

分頁搜尋

談到ES的分頁永遠都繞不開深分頁的問題。但在本章中暫時避開這個問題,隻說明在ES中如何進行分頁查詢。

ES分頁查詢包含from和size關鍵字,from表示起始值,size表示一次查詢的數量。

查詢資料的總數

傳回文檔總數。

分頁(一頁包含1條資料)模糊查詢(match,搜尋關鍵字不分詞)name="kevin"

"query":{
    "match":{
        "name":"kevin"
    }
},
"from":0,
"size":1           

結合文檔總數即可傳回簡單的分頁查詢。

分頁查詢中往往我們也需要對資料進行排序傳回,MySQL中使用order by關鍵字,ES中使用sort關鍵字指定排序字段以及降序升序。

分頁(一頁包含1條資料)查詢age >= 21且age <=26的學生,按年齡降序排列。

"query":{
    "range":{
        "age":{
            "gte":21,
            "lte":26
        }
    }
},
"from":0,
"size":1,
"sort":{
    "age":{
        "order":"desc"
    }
}           

ES預設升序排列,如果不指定排序字段的排序),則sort字段可直接寫為"sort":"age"。

第六章-Java用戶端(上)

ES提供了多種方式使用Java用戶端:

TransportClient,通過Socket方式連接配接ES叢集,傳輸會對Java進行序列化

RestClient,通過HTTP方式請求ES叢集

目前常用的是TransportClient方式連接配接ES服務。但ES官方表示,在未來TransportClient會被永久移除,隻保留RestClient方式。

同樣,Spring Boot官方也提供了操作ES的方式Spring Data ElasticSearch。本章節将首先介紹基于Spring Boot所建構的工程通過Spring Data ElasticSearch操作ES,再介紹同樣是基于Spring Boot所建構的工程,但使用ES提供的TransportClient操作ES。

Spring Data ElasticSearch

本節完整代碼(配合源碼使用更香):

https://github.com/yu-linfeng/elasticsearch6.x_tutorial/tree/master/code/spring-data-elasticsearch

使用Spring Data ElasticSearch後,你會發現一切變得如此簡單。就連連接配接ES服務的類都不需要寫,隻需要配置一條ES服務在哪兒的資訊就能開箱即用。

作為簡單的API和簡單搜尋兩章節的啟下部分,本節示例仍然是基于上一章節的示例。

通過IDEA建立Spring Boot工程,并且在建立過程中選擇Spring Data ElasticSearch,主要步驟如下圖所示:

第一步,建立工程,選擇Spring Initializr。

第二步,選擇SpringBoot的依賴NoSQL -> Spring Data ElasticSearch。

idea-springboot-es

建立好Spring Data ElasticSearch的Spring Boot工程後,按照ES慣例是定義Index以及Type和Mapping。在Spring Data ElasticSearch中定義Index、Type以及Mapping非常簡單。ES文檔資料實質上對應的是一個資料結構,也就是在Spring Data ElasticSearch要我們把ES中的文檔資料模型與Java對象映射關聯。

定義StudentPO對象,對象中定義Index以及Type,Mapping映射我們引入外部json檔案(json格式的Mapping就是在簡單搜尋一章中定義的Mapping資料)。

package com.coderbuff.es.easy.domain;

import lombok.Getter;

import lombok.Setter;

import lombok.ToString;

import org.springframework.data.annotation.Id;

import org.springframework.data.elasticsearch.annotations.Document;

import org.springframework.data.elasticsearch.annotations.Field;

import org.springframework.data.elasticsearch.annotations.FieldType;

import org.springframework.data.elasticsearch.annotations.Mapping;

import java.io.Serializable;

/**

  • ES mapping映射對應的PO
  • Created by OKevin on 2019-06-26 22:52

    */

@Getter

@Setter

@ToString

@Document(indexName = "user", type = "student")

@Mapping(mappingPath = "student_mapping.json")

public class StudentPO implements Serializable {

private String id;

/**
 * 姓名
 */
private String name;

/**
 * 年齡
 */
private Integer age;           

Spring Data ElasticSearch為我們屏蔽了操作ES太多的細節,以至于真的就是開箱即用,它操作ES主要是通過ElasticsearchRepository接口,我們在定義自己具體業務時,隻需要繼承它,擴充自己的方法。

package com.coderbuff.es.easy.dao;

import com.coderbuff.es.easy.domain.StudentPO;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import org.springframework.stereotype.Repository;

  • Created by OKevin on 2019-06-26 23:45

@Repository

public interface StudentRepository extends ElasticsearchRepository {

ElasticsearchTemplate可以說是Spring Data ElasticSearch最為重要的一個類,它對ES的Java API進行了封裝,建立索引等都離不開它。在Spring中要使用它,必然是要先注入,也就是執行個體化一個bean。而Spring Data ElasticSearch早為我們做好了一切,隻需要在application.properties中定義spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300,就可大功告成(網上有人的教程還在使用applicationContext.xml定義一個bean,事實證明,受到了Spring多年的“毒害”,Spring Boot遠比我們想象的智能)。

單元測試建立Index、Type以及定義Mapping。

package com.coderbuff.es;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;

import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)

@SpringBootTest

public class SpringDataElasticsearchApplicationTests {

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;

/**
 * 測試建立Index,type和Mapping定義
 */
@Test
public void createIndex() {
    elasticsearchTemplate.createIndex(StudentPO.class);
    elasticsearchTemplate.putMapping(StudentPO.class);
}           

使用GET

請求指令,可看到通過Spring Data ElasticSearch建立的索引。

索引建立完成後,接下來就是定義操作student文檔資料的接口。在StudentService接口的實作中,通過組合StudentRepository類對ES進行操作。StudentRepository類繼承了ElasticsearchRepository接口,這個接口的實作已經為我們提供了基本的資料操作,儲存、修改、删除隻是一句代碼的事。就算查詢、分頁也為我們提供好了builder類。"最難"的實際上不是實作這些方法,而是如何構造查詢參數SearchQuery。建立SearchQuery執行個體,有兩種方式:

建構NativeSearchQueryBuilder類,通過鍊式調用構造查詢參數。

建構NativeSearchQuery類,通過構造方法傳入查詢參數。

這裡以"不分頁range範圍和term查詢age>=21且age<26且name=kevin"為例。

SearchQuery searchQuery = new NativeSearchQueryBuilder()

.withQuery(QueryBuilders.boolQuery()
                    .must(QueryBuilders.rangeQuery("age").gte(21).lt(26))
                    .must(QueryBuilders.termQuery("name", "kevin"))).build();           

搜尋條件的構造一定要對ES的查詢結構有比較清晰的認識,如果是在了解了簡單的API和簡單搜尋兩章的前提下,學習如何構造多加練習一定能掌握。這裡就不一一驗證前面章節的示例,一定要配合代碼使用練習(

https://github.com/yu-linfeng/elasticsearch6.x_tutorial/tree/master/code/spring-data-elasticsearch)

原文位址

https://www.cnblogs.com/yulinfeng/p/11219786.html