【ELK】4.spring boot 2.X集成ES spring-data-ES 進行CRUD操做 完整版+kibana管理ES的index操做

spring boot 2.X集成ES 進行CRUD操做  完整版html

 

內容包括:

=========================================================================================java

1.CRUD:單字段查詢、複合查詢、分頁查詢、評分查詢√node

2.時間範圍查詢√web

3.GET方法傳入時間類型解析不了的問題√spring

4.term和match查詢的區別√docker

5.filter+query查詢的區別√json

6.自定義ES的mapping,自定義settings√api

7.解決@Field註解 設置分詞器無效的問題、解決@Document註解 設置分區 以及備份無效的問題√緩存

8.pinyin查詢以及繁簡體轉化查詢的集成√服務器

9.同一個字段設置多種分詞器的解決方案√

10.不一樣分詞器的區別。讀時分詞和寫時分詞√

11.索引數據遷移

12.keyword與text類型區別以及引出的相關問題√

13.index建立的索引狀態爲yellow以及啓動集羣后對於index狀態、分片、備份的影響

=======================================================================================

要求:

spring boot 2.0.1

elasticsearch 6.5.4

spring-boot-starter-data-elasticsearch 

 

es中要求已經安裝了ik分詞器、pingyin分詞器、繁簡體轉化分詞器[安裝步驟]

=======================================================================================

註明:

下文中紅色字體部分,即爲集成過程當中解決的問題。

=======================================================================================

集成項目結構:

 

 ===================================================================================

正文

1、spring boot 集成ES基本操做的步驟

1.pom.xml引入jar包

<!-- spring-boot-starter-data-elasticsearch -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

2.ES鏈接信息,配置在application.properties中

#elasticsearch相關配置
#es的cluster集羣名稱能夠查看服務器安裝的集羣名稱 curl http://192.168.92.130:9200 獲取到集羣名稱
spring.data.elasticsearch.cluster-name=docker-cluster
#注意端口爲9300  9300 是 Java 客戶端的端口,支持集羣之間的通訊。9200 是支持 Restful HTTP 的接口
spring.data.elasticsearch.cluster-nodes=192.168.92.130:9300

3.測試實體Builder

package com.sxd.swapping.domain;


import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import org.springframework.data.elasticsearch.annotations.*;

import javax.persistence.Id;
import java.util.Date;


/**
 * es的index的settings 和 mapping 設置,是最初的第一次設置。後續即便更改,也不起做用。
 * 可是mapping中的屬性名稱以及屬性個數若是更改了,會更新到ES中。這樣會致使數據的丟失。須要注意。
 */


@Setter
@Getter

//ES的三個註解
//指定index索引名稱爲項目名   指定type類型名稱爲實體名
@Document(indexName = "swapping",type = "builder")
//至關於ES中的mapping    注意對比文件中的json和原生json  最外層的key是沒有的
@Mapping(mappingPath = "/esConfig/builder-mapping.json")
//至關於ES中的settings   注意對比文件中的json和原生json  最外層的key是沒有的
@Setting(settingPath = "/esConfig/builder-setting.json")
public class Builder {
    
    //id  測試長整型數據   注意與es中索引自己id區分開
    @Id
    private Long id;


    //在建立初始化索引開始   就要去查看mapping是否ik分詞建立成功   不然 須要進行索引數據的遷移操做


    //指定查詢分詞器 爲ik分詞器     存儲分詞器爲 ik分詞器
    //在@Field中指定的ik分詞器沒起做用,所以採用上面的兩個註解 能夠徹底自定義類型Field的各個屬性
    //@Field(searchAnalyzer = "ik_max_word",analyzer = "ik_max_word")


    //類型定義爲text 可測試ik分詞  繁簡體轉化   pinyin分詞 查詢效果
    //名稱     測試字符串類型
    private String buildName;

    //類型定義爲text  可測試大文本
    private String remark;

    //類型定義爲keyword 可測試是否分詞 以及查詢效果
    private String email;

    //數量  測試整型數據
    private int buildNum;

    //時間也能夠進行範圍查詢,可是查詢傳入參數,應該爲mapping中定義的時間字段的 格式化字符串  或 時間戳 不然,ES沒法解析格式會報錯
    //時間    測試時間類型
    private Date buildDate;

    //積分比率  測試浮點型數據
    private Double integral;


    //分頁大小
    private Integer pageNum = 0;
    //分頁數量
    private Integer pageSize = 10;





}
View Code

註釋1:

  @Document註解,註明index名字是 swapping 項目名;type名字是 builder 實體名。【都是能夠自定義的,若是能夠,在settings中設置也是能夠的】

註釋2:

  @Document @Field註解詳解

註釋3:

  自定義mapping

  @Field註解中指定分詞器無效的問題,是經過設置自定義mapping解決的。

  一樣,自定義mapping也解決了同一個字段指定多種分詞器的問題

註釋4:

  自定義settings

  自定義settings的設置目的是爲了,建立分詞器規則以及對於index的自定義設置。由於@Document註解中設置分區和備份 可能無效的問題。經過自定義setting也能夠解決。

註釋5:

  對於實體的mapping和settings的設置,在初始化啓動的第一次,就建立成功了。

  以後即便程序中自定義的mapping更改了,對於ES中index的mapping的設置也不會發生改動。

  這也就意味着,若是在第一次建立index的時候,若是屬性類型指定錯誤,或者分詞器未設置,或者@Field中設置ik分詞器無效等這些問題。那這些問題就一直存在,由於ES中的index的mapping在建立成功後就不能更改了。

  所以,若是須要解決上述的這些問題,

  要麼就是在初次建立的時候,就使用[註釋3]中的方式,自定義mapping。

  要麼就是在出現問題以後,使用 elasticsearch 提供的 reindex api 來遷移數據,建立新的索引。這樣能夠實現不影響線上的訪問,須要無縫切換到新的索引上。

  

 

4.自定義index的settings

{
  "index": {
    "number_of_shards": "2",
    "number_of_replicas": "0",
    "analysis": {

      "filter": {
        "edge_ngram_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 50
        },
        "pinyin_simple_filter": {
          "type": "pinyin",
          "first_letter": "prefix",
          "padding_char": " ",
          "limit_first_letter_length": 50,
          "lowercase": true
        }
      },

      "char_filter": {
        "tsconvert": {
          "type": "stconvert",
          "convert_type": "t2s"
        }
      },

      "analyzer": {
        "ikSearchAnalyzer": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "char_filter": [
            "tsconvert"
          ]
        },
        "pinyinSimpleIndexAnalyzer": {
          "tokenizer": "keyword",
          "filter": [
            "pinyin_simple_filter",
            "edge_ngram_filter",
            "lowercase"
          ]
        }
      }

    }
  }
}
View Code

註釋1:

  對比原生的settings,能夠發現最外層的key是沒有的。這裏附上一份原生的settings,只作參考

{
  "settings": {
    "index": {
      "refresh_interval": "1s",
      "number_of_shards": "5",
      "provided_name": "swapping",
      "creation_date": "1550472518470",
      "store": {
        "type": "fs"
      },
      "number_of_replicas": "1",
      "uuid": "e0UG1DH8RLG9_UYOzijSvw",
      "version": {
        "created": "6050499"
      }
    }
  },
  "defaults": {
    "index": {
      "max_inner_result_window": "100",
      "unassigned": {
        "node_left": {
          "delayed_timeout": "1m"
        }
      },
      "max_terms_count": "65536",
      "routing_partition_size": "1",
      "max_docvalue_fields_search": "100",
      "merge": {
        "scheduler": {
          "max_thread_count": "1",
          "auto_throttle": "true",
          "max_merge_count": "6"
        },
        "policy": {
          "reclaim_deletes_weight": "2.0",
          "floor_segment": "2mb",
          "max_merge_at_once_explicit": "30",
          "max_merge_at_once": "10",
          "max_merged_segment": "5gb",
          "expunge_deletes_allowed": "10.0",
          "segments_per_tier": "10.0",
          "deletes_pct_allowed": "33.0"
        }
      },
      "max_refresh_listeners": "1000",
      "max_regex_length": "1000",
      "load_fixed_bitset_filters_eagerly": "true",
      "number_of_routing_shards": "5",
      "write": {
        "wait_for_active_shards": "1"
      },
      "mapping": {
        "coerce": "false",
        "nested_fields": {
          "limit": "50"
        },
        "depth": {
          "limit": "20"
        },
        "ignore_malformed": "false",
        "total_fields": {
          "limit": "1000"
        }
      },
      "source_only": "false",
      "soft_deletes": {
        "enabled": "false",
        "retention": {
          "operations": "0"
        }
      },
      "max_script_fields": "32",
      "query": {
        "default_field": [
          "*"
        ],
        "parse": {
          "allow_unmapped_fields": "true"
        }
      },
      "format": "0",
      "sort": {
        "missing": [],
        "mode": [],
        "field": [],
        "order": []
      },
      "priority": "1",
      "codec": "default",
      "max_rescore_window": "10000",
      "max_adjacency_matrix_filters": "100",
      "gc_deletes": "60s",
      "optimize_auto_generated_id": "true",
      "max_ngram_diff": "1",
      "translog": {
        "generation_threshold_size": "64mb",
        "flush_threshold_size": "512mb",
        "sync_interval": "5s",
        "retention": {
          "size": "512mb",
          "age": "12h"
        },
        "durability": "REQUEST"
      },
      "auto_expand_replicas": "false",
      "mapper": {
        "dynamic": "true"
      },
      "requests": {
        "cache": {
          "enable": "true"
        }
      },
      "data_path": "",
      "highlight": {
        "max_analyzed_offset": "-1"
      },
      "routing": {
        "rebalance": {
          "enable": "all"
        },
        "allocation": {
          "enable": "all",
          "total_shards_per_node": "-1"
        }
      },
      "search": {
        "slowlog": {
          "level": "TRACE",
          "threshold": {
            "fetch": {
              "warn": "-1",
              "trace": "-1",
              "debug": "-1",
              "info": "-1"
            },
            "query": {
              "warn": "-1",
              "trace": "-1",
              "debug": "-1",
              "info": "-1"
            }
          }
        },
        "throttled": "false"
      },
      "fielddata": {
        "cache": "node"
      },
      "default_pipeline": "_none",
      "max_slices_per_scroll": "1024",
      "shard": {
        "check_on_startup": "false"
      },
      "xpack": {
        "watcher": {
          "template": {
            "version": ""
          }
        },
        "version": "",
        "ccr": {
          "following_index": "false"
        }
      },
      "percolator": {
        "map_unmapped_fields_as_text": "false",
        "map_unmapped_fields_as_string": "false"
      },
      "allocation": {
        "max_retries": "5"
      },
      "indexing": {
        "slowlog": {
          "reformat": "true",
          "threshold": {
            "index": {
              "warn": "-1",
              "trace": "-1",
              "debug": "-1",
              "info": "-1"
            }
          },
          "source": "1000",
          "level": "TRACE"
        }
      },
      "compound_format": "0.1",
      "blocks": {
        "metadata": "false",
        "read": "false",
        "read_only_allow_delete": "false",
        "read_only": "false",
        "write": "false"
      },
      "max_result_window": "10000",
      "store": {
        "stats_refresh_interval": "10s",
        "fs": {
          "fs_lock": "native"
        },
        "preload": []
      },
      "queries": {
        "cache": {
          "enabled": "true"
        }
      },
      "ttl": {
        "disable_purge": "false"
      },
      "warmer": {
        "enabled": "true"
      },
      "max_shingle_diff": "3",
      "query_string": {
        "lenient": "false"
      }
    }
  }
}
View Code

註釋2:

  自定義settings中,設置分片是2,備份是0。若是是備份設置爲1,則表明每一個分片都有一個備份,則總共是4分。

  由於ES只啓動了一個node節點,因此會致使index的狀態爲yellow。由於一個節點,而分片又要建立備份的緣故,會致使備份建立無效。最後的index的state狀態會顯示爲total=4,而success=2。雖然這樣並不影響使用。

  所以建議ES啓動爲多個nodes節點,啓動爲集羣。

  關於index設置分區和備份數量分別爲多少,須要慎重!

註釋3:

  自定義setting中,JSON做用是建立兩個分析器名爲ikSearchAnalyzer,pinyinSimpleIndexAnalyzer,前者使用ik中文分詞器繁體轉簡體char_filter過濾,使得引用此分詞器的字段在設置時,將會自動對中文進行分詞和繁簡體轉換。

  pinyinSimpleIndexAnalyzer 使用pinyin分詞器,並進行edge_ngram 過濾,大寫轉小寫過濾。

註釋4:

  經過自定義setting,實現了對同一字段設置多種分詞器

註釋5:

  關於ES的內置分詞器,能夠詳細看看。

註釋6:

  ES的分詞器,其實就是插件,是工具。而對於分詞的使用,其實能夠分爲讀時分詞寫時分詞

  讀時分詞,發生在用戶查詢時,ES 會即時地對用戶輸入的關鍵詞進行分詞,分詞結果只存在內存中,當查詢結束時,分詞結果也會隨即消失。

  寫時分詞,發生在文檔寫入時,ES 會對文檔進行分詞後,將結果存入倒排索引,該部分最終會以文件的形式存儲於磁盤上,不會因查詢結束或者 ES 重啓而丟失。

  看到這裏,其實就明白了。寫時分詞,是在自定義mapping中指定的,並且一經指定就不能再修改,若要修改必須新建索引。

  因此,查詢的時候,咱們能夠自定義按照哪一種想要的分詞效果進行查詢。沒有指定,就是mapping中指定的查詢分詞。

  寫的時候就是按照mapping中指定的分詞進行存儲,若是沒有指定,則按照ES默認的分詞器進行分詞存儲!

註釋7:

  analyzer中設置的自定義的分詞器的名字在mapping中會被引用

 

5.自定義Index的mapping

{
  "builder": {
    "properties": {
      "id": {
        "type": "long"
      },
      "buildName": {
        "type": "text",
        "analyzer": "ikSearchAnalyzer",
        "search_analyzer": "ikSearchAnalyzer",
        "fields": {
          "pinyin": {
            "type": "text",
            "analyzer": "pinyinSimpleIndexAnalyzer",
            "search_analyzer": "pinyinSimpleIndexAnalyzer"
          }
        }
      },
      "remark": {
        "type": "text",
        "analyzer": "ikSearchAnalyzer",
        "search_analyzer": "ikSearchAnalyzer",
        "fields": {
          "pinyin": {
            "type": "text",
            "analyzer": "pinyinSimpleIndexAnalyzer",
            "search_analyzer": "pinyinSimpleIndexAnalyzer"
          }
        }
      },
      "email": {
        "type": "keyword",
        "ignore_above": 50
      },
      "buildNum": {
        "type": "long"
      },
      "buildDate": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      },
      "integral": {
        "type": "float"
      },
      "pageNum": {
        "type": "long"
      },
      "pageSize": {
        "type": "long"
      },
      "query": {
        "properties": {
          "match_all": {
            "type": "object"
          }
        }
      }
    }
  }
}
View Code

註釋1:

  mapping中只要是對實體的各個屬性對應的類型,以及分詞器進行指定。

註釋2:

  尤爲是時間字段,類型須要設置爲date,時間格式須要設置爲文件中指定的。

  時間類型設置爲date的目的,是對時間進行範圍查詢是可操做的。若是類型設置爲text類型,則時間範圍查詢就沒法實現。

  時間格式的指定,是須要解析ES對接java程序,進行查詢時候,傳入參數能夠是"yyyy-MM-dd"的時間字符串,也能夠是時間戳。

  須要注意的是:時間字段在ES中存儲是時間戳,所以想要進行精準的時間查詢以及時間範圍查詢,其實能夠經過傳入時間戳進行查詢。後面的controller中有具體的方法。

  若是ES查詢時間報錯:Caused by: ElasticsearchParseException[failed to parse date field [Sun Dec 31 16:00:00 UTC 2017] with format [yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis]];

註釋3:

  這裏須要注意的是字符串類型的兩種數據類型text和keyword

  text類型:支持分詞、全文檢索,不支持聚合、排序操做。適合大字段存儲,如:文章詳情、content字段等.

  keyword類型:支持精確匹配,支持聚合、排序操做。適合精準字段匹配,如:url、name、email、title等字段.

  

  keyword支持的最大長度爲32766個UTF-8字符,且若是超過了ignore_above設置的字符串最大長度後,數據將不會被索引,沒法經過term精確匹配查詢.

  text則不受長度限制

 

  本點相關聯的問題:

  問題1:設置爲keyword類型的字段,插入很長的大段內容後,報字符超出異常,沒法插入。

  問題2:檢索超過ignore_above設定長度的字段後,沒法返回結果

註釋4:

  在自定義mapping中實現了對同一字段設置多個分詞器

註釋5:

  對於上面字段中fields的設置,例如 pinyin,是自定義的,會在controller中查詢時候,指定按照mapping中設置好的分詞器查詢時候,用到。

QueryBuilder ikSTQuery = QueryBuilders.matchQuery("buildName",pinyinStr).boost(1f);
QueryBuilder pinyinQuery = QueryBuilders.matchQuery("buildName.pinyin",pinyinStr);

  固然,除了能夠用mapping中預先設定好的強大的分詞器以外,也能夠本身指定分詞器進行查詢。[前提是你的ES中默認有或者你本身安裝了的分詞器]

QueryBuilder matchBuilder =   QueryBuilders.matchQuery( "buildName" ,str).analyzer("ik_max_word");

  這裏的分詞器,能夠參考4中註釋5的 ES中默認的分詞器 進行賦值。甚至更多。

註釋6:

  這裏的分詞器名稱,採用自定義settings中預先設置的分詞器名稱。

 

6.繼承ElasticsearchRepository的BuilderDao

package com.sxd.swapping.esDao;

import com.sxd.swapping.domain.Builder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface BuilderDao extends ElasticsearchRepository<Builder,Long>{

}
View Code

 

7.controller層CRUD

package com.sxd.swapping.controller;

import com.sxd.swapping.base.UniVerResponse;
import com.sxd.swapping.domain.Builder;
import com.sxd.swapping.esDao.BuilderDao;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 使用方式有兩種:
 * 1.一種是通過 SpringData 封裝過的,直接在 dao 接口繼承 ElasticsearchRepository 便可
 * 2.一種是通過 Spring 封裝過的,直接在 Service/Controller 中引入該 bean 便可 ElasticsearchTemplate
 */
@RestController
@RequestMapping("/es")
public class ESBuilderController {

    @Autowired
    BuilderDao builderDao;

    /**
     * 方式1
     *
     * 單個保存索引
     * @return
     */
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public UniVerResponse<Builder> save(@RequestBody Builder builder){
        builder = builder == null ? new Builder() : builder;
        UniVerResponse<Builder> res = new UniVerResponse<>();
        Builder builder2 = builderDao.save(builder);
        res.beTrue(builder2);
        return res;
    }


    /**
     * 方式1
     *
     * 根據ID獲取單個索引
     * @param id
     * @return
     */
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public UniVerResponse<Builder> get(Long id){
        UniVerResponse<Builder> res = new UniVerResponse<>();
        Optional<Builder> get = builderDao.findById(id);
        res.beTrue(get.isPresent() == false ? null : get.get());
        return res;
    }


    /**
     * ============================單條件查詢==================================
     */

    /**
     * 方式1
     *
     * 經過match進行模糊查詢
     * 根據傳入屬性值,檢索指定屬性下是否有匹配
     *
     * 例如:
     * name:中國人
     * 那麼查詢會將 中國人 進行分詞, 中國  人  國人 等。以後再進行查詢匹配
     *
     * @param name
     * @return
     */
    @RequestMapping(value = "/searchNameByMatch", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchNameByMatch(String name){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();
        MatchQueryBuilder matchBuilder = QueryBuilders.matchQuery("buildName",name);
        Iterable<Builder> search = builderDao.search(matchBuilder);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }


    /**
     *  方式1
     *
     *  經過term進行全量徹底匹配查詢
     *  根據傳入屬性值,檢索指定屬性下是否有屬性值徹底匹配的
     *
     *  例如:
     *  name:中國人
     *  那麼查詢不會進行分詞,就是按照  包含完整的  中國人  進行查詢匹配
     *
     * 此時ik中文分詞 並無起做用【此時是在@Field註解 指定的ik分詞器】
     * 例如存入  張衛健  三個字,以ik_max_word 分詞存入,查詢也指定以ik查詢,可是  以張衛健  查詢 沒有結果
     * 以   【張】   或   【衛】   或   【健】  查詢 纔有結果,說明分詞是以默認分詞器 進行分詞 ,也就是一箇中文漢字 進行一個分詞的效果。
     *
     *
     *
     * @param name
     * @return
     */
    @RequestMapping(value = "/searchNameByTerm", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchNameByTerm(String name){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();
        TermQueryBuilder termBuilder = QueryBuilders.termQuery("buildName",name);
        Iterable<Builder> search = builderDao.search(termBuilder);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }


    /**
     * 方式1
     *
     * 根據range進行範圍查詢
     *
     * 時間也能夠進行範圍查詢,但時間傳入值應該爲yyyy-MM-dd HH:mm:ss 格式的時間字符串或時間戳 或其餘定義的時間格式
     * 只有在mapping中定義的時間格式,才能被ES查詢解析成功
     *
     * @param num
     * @return
     */
    @RequestMapping(value = "/searchNumByRange", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchNumByRange(Integer num){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();
        RangeQueryBuilder rangeBuilder = QueryBuilders.rangeQuery("buildNum").gt(0).lt(num);
        Iterable<Builder> search = builderDao.search(rangeBuilder);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }


    //處理GET請求的時間轉化
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }


    /**
     * ============================複合條件查詢==================================
     */

    /**
     * 方式1
     *
     * 使用bool進行復合查詢,使用filter比must query性能好
     *
     * filter是過濾,1.文檔是否包含於結果 2.不涉及評分 3.更快
     * query是查詢,1.文檔是否匹配於結果 2.計算文檔匹配評分 3.速度慢
     *
     *
     * @param builder
     * @return
     */
    @RequestMapping(value = "/searchByBool", method = RequestMethod.GET)
    public UniVerResponse<Page<Builder>>  searchByBool(Builder builder){
        UniVerResponse<Page<Builder>> res = new UniVerResponse<>();
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();

        //多個字段匹配 屬性值 must query
        MultiMatchQueryBuilder matchQueryBuilder =
                QueryBuilders.multiMatchQuery(builder.getBuildName(),"buildName","buildName2");
        boolBuilder.must(matchQueryBuilder);

        //filter 分別過濾不一樣字段,縮小篩選範圍
        TermQueryBuilder numQuery = QueryBuilders.termQuery("buildNum",builder.getBuildNum());
        boolBuilder.filter(numQuery);

        RangeQueryBuilder dateQuery = QueryBuilders.rangeQuery("buildDate").lt(builder.getBuildDate().getTime());
        boolBuilder.filter(dateQuery);

        //排序 + 分頁
        Sort sort = Sort.by(Sort.Direction.DESC,"buildNum");
        PageRequest pageRequest = PageRequest.of(builder.getPageNum()-1,builder.getPageSize(),sort);

        Page<Builder> search = builderDao.search(boolBuilder, pageRequest);
        res.beTrue(search);
        return res;
    }

    /**
     * 方式1
     * 時間範圍查詢
     * ES中時間字段須要設置爲 date類型,才能查詢時間範圍
     * 時間範圍要想準確查詢,須要將時間轉化爲時間戳進行查詢
     *
     * ES中date字段存儲是 時間戳存儲
     *
     *
     * from[包含]  -   to[包含]
     * gt - lt
     * gte - lte
     *
     *
     *
     * @return
     */
    @RequestMapping(value = "/searchByTimeRange", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchByTimeRange(Builder builder){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();

        QueryBuilder queryBuilder = QueryBuilders.rangeQuery("buildDate").from(builder.getBuildDate().getTime());
        Iterable<Builder> search = builderDao.search(queryBuilder);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }


    /**
     *  方式1
     *
     *  檢索全部索引
     * @return
     */
    @RequestMapping(value = "/searchAll", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchAll(){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();
        QueryBuilder queryBuilder = QueryBuilders.boolQuery();
        Iterable<Builder> search = builderDao.search(queryBuilder);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }

    /**
     * 方式1
     *
     * 根據傳入屬性值  全文檢索全部屬性
     * 關於QueryStringQueryBuilder的使用,若是不指定分詞器,那麼查詢的時候,會使用ES默認的分詞器進行查詢。
     * 結果就是 會查詢出與查詢內容絲絕不相干的結果。
     *
     *
     * 關於ES內置分詞器:
     * https://blog.csdn.net/u013795975/article/details/81102010
     *
     *
     *
     * @return
     */
    @RequestMapping(value = "/findByStr", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> findByStr(String paramStr){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();
        QueryStringQueryBuilder qsqb = new QueryStringQueryBuilder(paramStr).analyzer("standard");
        Iterable<Builder> search = builderDao.search(qsqb);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;

    }

    /**
     * 方式1
     *
     * 選擇用term或match方式查詢
     * 查詢字段buildName或者buildName2
     * 指定以分詞器 ik_max_word 或  ik_smart 或 standard[es默認分詞器] 或 english 或 whitespace 分詞器進行分詞查詢
     *
     *
     *
     * @param analyzer  分詞器
     * @param str       查詢屬性值
     * @param param     指定是參數1[buildName] 仍是 參數2[remark]
     * @return
     */
    @RequestMapping(value = "/searchByIK", method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchByIK(String analyzer,String str,Integer param){
        UniVerResponse<List<Builder>> res = new UniVerResponse<>();
        QueryBuilder matchBuilder =   QueryBuilders.matchQuery(param ==1 ? "buildName" : "remark",str).analyzer(analyzer);


        Iterable<Builder> search = builderDao.search(matchBuilder);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }


    /**
     * 方式1
     *
     * 繁簡體轉化查詢、拼音查詢,而且加入評分查詢
     * 評分規則詳情:https://blog.csdn.net/paditang/article/details/79098830
     * @param pinyinStr
     * @return
     */
    @RequestMapping(value = "/searchByPinYin",method = RequestMethod.GET)
    public UniVerResponse<List<Builder>> searchByPinYin(String pinyinStr){
        UniVerResponse<List<Builder>> res =  new UniVerResponse<>();
        DisMaxQueryBuilder disMaxQuery = QueryBuilders.disMaxQuery();
        QueryBuilder ikSTQuery = QueryBuilders.matchQuery("buildName",pinyinStr).boost(1f);
        QueryBuilder pinyinQuery = QueryBuilders.matchQuery("buildName.pinyin",pinyinStr);

        disMaxQuery.add(ikSTQuery);
        disMaxQuery.add(pinyinQuery);

        Iterable<Builder> search = builderDao.search(disMaxQuery);
        Iterator<Builder> iterator = search.iterator();
        List<Builder> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        res.beTrue(list);
        return res;
    }







    /**
     *
     * @param builder
     * @return
     */
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public UniVerResponse<Builder> delete(@RequestBody Builder builder){
        UniVerResponse<Builder> res = new UniVerResponse<>();

        builderDao.deleteById(builder.getId());
        res.beTrue(builder);
        return res;
    }




}
View Code

註釋1:

  注意GET請求接受時間轉化

註釋2:

  filter和query的區別 

  filter是過濾,1.文檔是否匹配 2.不涉及評分 3.更快 4.會自動緩存,下次查詢速度會更快
  query是查詢,1.文檔是否匹配查詢,相關度高不高 2.計算文檔匹配評分 3.速度慢 4.查詢的結果要比filter可能更多一些,由於涉及評分,因此更精確

註釋3:

  term和match的區別

* 經過match進行模糊查詢
     * 根據傳入屬性值進行分詞,檢索指定屬性下是否有匹配
     *
     * 例如:
     * name:中國人
     * 那麼查詢會將 中國人 進行分詞, 中國  人  國人 等。以後再進行查詢匹配
*  經過term進行全量徹底匹配查詢
     *  根據傳入屬性值,檢索指定屬性下是否有屬性值徹底匹配的
     *
     *  例如:
     *  name:中國人
     *  那麼查詢不會進行分詞,就是按照  完整的  中國人  進行查詢匹配

 

 

 

============================================================================

2、kibana管理index

1.Discover下查詢

1.1輸入上面操做的type名builder,能夠看到ES中已經存入的document

 

1.2點擊每一條Document左側的三角,能夠下拉查看document詳情

 

2.Monitoring下管理

 

3.Management管理

 

 

=================================告一段落================================================

相關文章
相關標籤/搜索