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分詞器、繁簡體轉化分詞器[安裝步驟]
=======================================================================================
下文中紅色字體部分,即爲集成過程當中解決的問題。
=======================================================================================
===================================================================================
<!-- spring-boot-starter-data-elasticsearch --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
#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
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; }
註釋1:
@Document註解,註明index名字是 swapping 項目名;type名字是 builder 實體名。【都是能夠自定義的,若是能夠,在settings中設置也是能夠的】
註釋2:
註釋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 來遷移數據,建立新的索引。這樣能夠實現不影響線上的訪問,須要無縫切換到新的索引上。
{ "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" ] } } } } }
註釋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" } } } }
註釋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中會被引用
{ "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" } } } } } }
註釋1:
mapping中只要是對實體的各個屬性對應的類型,以及分詞器進行指定。
註釋2:
尤爲是時間字段,類型須要設置爲date,時間格式須要設置爲文件中指定的。
時間類型設置爲date的目的,是對時間進行範圍查詢是可操做的。若是類型設置爲text類型,則時間範圍查詢就沒法實現。
時間格式的指定,是須要解析ES對接java程序,進行查詢時候,傳入參數能夠是"yyyy-MM-dd"的時間字符串,也能夠是時間戳。
須要注意的是:時間字段在ES中存儲是時間戳,所以想要進行精準的時間查詢以及時間範圍查詢,其實能夠經過傳入時間戳進行查詢。後面的controller中有具體的方法。
註釋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中預先設置的分詞器名稱。
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>{ }
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; } }
註釋1:
注意GET請求接受時間轉化
註釋2:
filter和query的區別
filter是過濾,1.文檔是否匹配 2.不涉及評分 3.更快 4.會自動緩存,下次查詢速度會更快
query是查詢,1.文檔是否匹配查詢,相關度高不高 2.計算文檔匹配評分 3.速度慢 4.查詢的結果要比filter可能更多一些,由於涉及評分,因此更精確
註釋3:
term和match的區別
* 經過match進行模糊查詢 * 根據傳入屬性值進行分詞,檢索指定屬性下是否有匹配 * * 例如: * name:中國人 * 那麼查詢會將 中國人 進行分詞, 中國 人 國人 等。以後再進行查詢匹配
* 經過term進行全量徹底匹配查詢 * 根據傳入屬性值,檢索指定屬性下是否有屬性值徹底匹配的 * * 例如: * name:中國人 * 那麼查詢不會進行分詞,就是按照 完整的 中國人 進行查詢匹配
============================================================================
=================================告一段落================================================