ElasticSearch之基於SpringBoot使用SpringData操作ES

7. SpringData操作ES

參考https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.mapping

7.1 創建項目、導入依賴、編寫配置文件

一定要注意版本的問題:版本不一致,會帶來很多問題:

Spring Data Elasticsearch Elasticsearch Spring Boot
3.2.x 6.8.4 2.2.x
3.1.x 6.2.2 2.1.x
3.0.x 5.5.0 2.0.x
2.1.x 2.4.0 1.5.x

首先導入關鍵dependency

<!--關鍵依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--lombok插件-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
# cluster-name不要寫錯
spring.data.elasticsearch.cluster-name=elasticsearch
# Java調用時端口是9300,restfu調用端口是9200,注意區分
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s

7.2 編寫實體類型

然後編寫實體類型type

/**
 * @author Honyelchak
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
// 若ES中沒有指定的索引,會自動創建。
// shards 分片數  replicas 副本數 refreshInterval 刷新間隔
@Document(indexName = "hello_world", type="user", shards = 2, replicas = 3)
public class Item implements Serializable {
    @Id
    private String id;
    private String userName;
    @Field(type = FieldType.Keyword)
    private String passWord;
    @Field(type = FieldType.Integer)
    private Integer age;
    // text 類型的會分詞
    @Field(type = FieldType.Text, analyzer = "")
    private String comment;
}
  • @Document(indexName = "hello_world",type = "user",shards = 5,replicas = 1) : 標註在實體類上,聲明存儲的索引和類型剛纔,

    • indexName: 索引名稱
    • type:索引類型
    • shards:分片的數量,默認爲·5
    • replicas:副本的數量,默認爲1
    • refreshInterval: 刷新間隔,默認爲1s
    • indexStoreType:索引文件存儲類型,默認爲fs
  • @Field標註在屬性上,用來指定屬性的類型。其中的屬性如下:

    • analyzer:指定分詞器,es中默認使用的標準分詞器,比如我們需要指定中文IK分詞器,可以指定值爲ik_max_word
    • type: 指定該屬性在es中的類型,其中的值是FileType類型的值,比如FileType.Text類型對應es中的text類型
    • index:指定該詞是否需要索引,默認爲true
    • store:指定該屬性內容是否存儲在ES,默認爲false
    • fielddata :指定該屬性能否進行排序,因爲es中的text類型是不能進行排序(已經分詞了)
    • searchAnalyzer : 指定搜索使用的分詞器
    • name:字段名稱,可以在ES文檔中表示,若未設置,則使用Java字段名稱。

7.3 使用方法:

  1. 直接在 dao接口繼承 ElasticsearchRepository

  2. 直接在 Service/Controller 中引入 ElasticsearchTemplate

7.3.1 繼承ElasticsearchRepository

7.3.1.1 SpringData重要接口

img

Spring Data的中央接口是Repository

  • 它需要實體類以及實體類的ID作爲類型參數來管理。
  • 該接口主要用作標記接口(內無實現),以捕獲要使用的類型並幫助您發現擴展該接口的接口
  • CrudRepository規定對於正在管理的實體類複雜的CRUD功能。
public interface CrudRepository<T, ID> extends Repository<T, ID> {

  <S extends T> S save(S entity);      

  Optional<T> findById(ID primaryKey); 

  Iterable<T> findAll();               

  long count();                        

  void delete(T entity);               

  boolean existsById(ID primaryKey);   

  // … more functionality omitted.
}

ES中的PagingAndSortingRepository接口 繼承了CrudRepository。簡化了分頁操作和排序操作:

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

而在SpringBoot項目中,常用的是ElasticsearchRepository,他在ElasticsearchCrudRepository的基礎上補充了一些與查詢相關的方法。

7.3.1.2 根據方法名查詢
interface BookRepository extends Repository<Book, String> {
  List<Book> findByNameAndPrice(String name, Integer price);
}
7.3.1.3 @Query(json)查詢

上面的方法名稱將轉換爲以下Elasticsearch json查詢,

{
    "query": {
        "bool" : {
            "must" : [
                { "query_string" : { "query" : "?", "fields" : [ "name" ] } },
                { "query_string" : { "query" : "?", "fields" : [ "price" ] } }
            ]
        }
    }
}

還可以用@Query註解來自定義查詢語句,例如:

@Query("{\n" +
       "    \"bool\": {\n" +
       "      \"must\": [\n" +
       "        {\n" +
       "          \"match\": {\n" +
       "            \"userName\": \"?0\"\n" +
       "          }\n" +
       "        }\n" +
       "      ]\n" +
       "    }\n" +
       "  }")
List<User> selectByUserName(String userName);

PS: 其中的?是佔位符,0表示第一個參數

對應表:

關鍵詞 樣品 Elasticsearch查詢字符串
And findByNameAndPrice { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
Or findByNameOrPrice { "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
Is findByName { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
Not findByNameNot { "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
Between findByPriceBetween { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
LessThan findByPriceLessThan { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}
LessThanEqual findByPriceLessThanEqual { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
GreaterThan findByPriceGreaterThan { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}
GreaterThanEqual findByPriceGreaterThan { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
Before findByPriceBefore { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
After findByPriceAfter { "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
Like findByNameLike { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
StartingWith findByNameStartingWith { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
EndingWith findByNameEndingWith { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
Contains/Containing findByNameContaining { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
In findByNameIn(Collectionnames) { "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
NotIn findByNameNotIn(Collectionnames) { "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
Near findByStoreNear Not Supported Yet !
True findByAvailableTrue { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}
False findByAvailableFalse { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}
OrderBy findByAvailableTrueOrderByNameDesc { "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }

7.3.2 引入ElasticsearchTemplate

img

	@Autowired
	private ElasticsearchTemplate elasticsearchTemplate;
	@GetMapping("createIndex")
    public boolean createIndex(String indexName) {
        return elasticsearchTemplate.createIndex(indexName);
    }

    @GetMapping("deleteIndex")
    public boolean deleteIndex(String indexName) {
        return elasticsearchTemplate.deleteIndex(indexName);
    }

    @GetMapping("indexIsExist")
    public boolean indexIsExist(String indexName) {
        return elasticsearchTemplate.indexExists(indexName);
    }

    @GetMapping("typeIsExist")
    public boolean typeIsExist(String indexName, String type) {
        return elasticsearchTemplate.typeExists(indexName, type);
    }

    @GetMapping("getMapping")
    public Map getMapping(String indexName, String type) {
        return elasticsearchTemplate.getMapping(indexName, type);
    }

    @GetMapping("getSetting")
    public Map getSetting(String indexName) {
        return elasticsearchTemplate.getSetting(indexName);
    }

7.4 高級查詢

  • QueryBuilders 構建查詢關鍵詞
  • SortBuilders 構建對關鍵字的排序
  • NativeSearchQueryBuilder 對前兩個條件進行封裝,
  • Repository : 進行查詢
// 分詞查詢 :comment中包含中國的用戶,並且以age倒序返回。
@Test
public void searchByCommentSortByAge() {
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.should(QueryBuilders.termQuery("comment", "中國"));
    FieldSortBuilder sortBuilder = SortBuilders.fieldSort("age").order(SortOrder.DESC);

    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
    nativeSearchQueryBuilder.withSort(sortBuilder);

    NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
    Page<Item> itemsPage = itemRepository.search(searchQuery);

    if (itemsPage != null) {
        List<Item> items = itemsPage.getContent();
        items.forEach(System.out::println);
    } else {
        System.out.println("can not find that!");
    }

}
// 查詢userName爲fanqi的用戶,並且以age倒序返回。
@Test
public void searchByUserNameSortByAge() {
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.should(QueryBuilders.termQuery("userName", "fanqi"));
    FieldSortBuilder sortBuilder = SortBuilders.fieldSort("age").order(SortOrder.DESC);

    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
    nativeSearchQueryBuilder.withSort(sortBuilder);

    NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
    Page<Item> itemsPage = itemRepository.search(searchQuery);

    if (itemsPage != null) {
        List<Item> items = itemsPage.getContent();
        items.forEach(System.out::println);
    } else {
        System.out.println("can not find that!");
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章