elasticsearch之十四springboot測試文檔基本查詢

個人專題目錄](http://www.itdecent.cn/p/140e2a59db2c)


1. elasticsearch文檔基本查詢

1.1 ids查詢

ids查詢是一類簡單的查詢,它過濾返回的文檔只包含其中指定標識符的文檔,該查詢默認指定作用在“_id”上面。

POST /book-index/_search
{
  "from": 0,
  "size": 100,
  "query": {
    "ids": {
      "values": [
        "1145177",
        "1170772"
      ],
      "boost": 1
    }
  }
}
@Override
public void idsQuery(String index, String... ids) throws Exception {
    builder(index, QueryBuilders.idsQuery().addIds(ids));
}
@Test
public void testIdsQuery() throws Exception {
    baseQuery.idsQuery(Constants.INDEX_NAME, "1145177", "1170772");
}

1.2 prefix查詢

前綴查詢,可以使我們找到某個字段以給定前綴開頭的文檔。

最典型的使用場景,一般是在文本框錄入的時候的聯(lián)想功能。

例如想查到公司名以“中國”開頭的文檔:

#prefix 查詢
POST /book-index/_search
{
  "from": 0,
  "size": 100,
  "query": {
    "prefix": {
      "title": {
        "value": "三",
        "boost": 1
      }
    }
  }
}
/**
 * 模糊查詢:prefixQuery
 */
@Override
public void prefixQuery(String index, String field, String prefixValue) throws Exception {
    builder(index, QueryBuilders.prefixQuery(field, prefixValue));
}
@Test
public void testPrefixQuery() throws Exception {
    baseQuery.prefixQuery(Constants.INDEX_NAME, "title", "三");
}

1.3 fuzzy查詢

fuzzy才是實現(xiàn)真正的模糊查詢,我們輸入的字符可以是個大概,他可以根據(jù)我們輸入的文字大概進行匹配查詢。返回包含與搜索詞類似的詞的文檔,該詞由Levenshtein編輯距離度量。

包括以下幾種情況:

  • 更改角色(box→fox)

  • 刪除字符(aple→apple)

  • 插入字符(sick→sic)

  • 調(diào)換兩個相鄰字符(ACT→CAT)

參數(shù)說明:

prefix_length

不能被 “模糊化” 的初始字符數(shù)。 大部分的拼寫錯誤發(fā)生在詞的結(jié)尾,而不是詞的開始。 例如通過將 prefix_length 設置為 3 ,你可能夠顯著降低匹配的詞項數(shù)量。

例如用戶在查詢過程中把“中國移動”輸為了“中國聯(lián)動”:

POST /book-index/_search
{
  "from": 0,
  "size": 100,
  "query": {
    "fuzzy": {
      "categoryName": {
        "value": "平電視",
        "fuzziness": "AUTO",
        "prefix_length": 0,
        "max_expansions": 50,#控制查詢的數(shù)量,注意不是返回結(jié)果的最大值,而是在查詢過程中最多去匹配到多少條,比如此處 50 條,即便有 1 萬條能匹配到的,當查詢過程中匹配到 50 條的時候就不再繼續(xù)查詢直接返回結(jié)果
        "transpositions": true,
        "boost": 1
      }
    }
  }
}
@Override
    public void fuzzyQuery(String index, String field, String keyword) throws Exception {
        builder(index, QueryBuilders.fuzzyQuery(field, keyword));
    }

把prefixLength(2)參數(shù)改為prefixLength(3)就查不到結(jié)果了,不能被 “模糊化” 的初始字符變成了中國聯(lián)。

@Test
public void testFuzzyQuery() throws Exception {
    baseQuery.fuzzyQuery(Constants.INDEX_NAME, "title", "平電視");
}

1.4 wildcard查詢

wildcard查詢允許我們在要查詢的內(nèi)容中使用通配符*和?,和SQL語句。

<font color=red>注意:wildcard查詢不注意查詢性能,應盡可能避免使用。</font>

例如用戶在查詢公司名以“中國”開頭的文檔:

#wildcard 查詢。查詢條件分詞,模糊查詢
POST /book-index/_search
{
  "from": 0,
  "size": 100,
  "query": {
    "wildcard": {
      "title": {
        "wildcard": "三*",
        "boost": 1
      }
    }
  }
}
/**
 * 模糊查詢:WildcardQuery
 */
@Override
public void wildCardQuery(String index, String field, String wildcardValue) throws Exception {
    builder(index, QueryBuilders.wildcardQuery(field, wildcardValue));
}
@Test
public void testWildCardQuery() throws Exception {
    baseQuery.wildCardQuery(Constants.INDEX_NAME, "title", "三*");
}

1.5 range查詢

本章到目前為止,對于數(shù)字,只介紹如何處理精確值查詢。 實際上,對數(shù)字范圍進行過濾有時會更有用。例如,我們可能想要查找所有價格大于 20 且小于40 美元的產(chǎn)品。

在 SQL 中,范圍查詢可以表示為:

SELECT document
FROM   products
WHERE  price BETWEEN 20 AND 40

Elasticsearch 有 range 查詢, 不出所料地,可以用它來查找處于某個范圍內(nèi)的文檔:

"range" : {
    "price" : {
        "gte" : 20,
        "lte" : 40
    }
}

range 查詢可同時提供包含(inclusive)和不包含(exclusive)這兩種范圍表達式,可供組合的選項如下:

  • gt: > 大于(greater than)

  • lt: < 小于(less than)

  • gte: >= 大于或等于(greater than or equal to)

  • lte: <= 小于或等于(less than or equal to)

例如要查詢出短信狀態(tài)報告響應時間在1-10秒之內(nèi)的文檔:

#范圍查詢
POST /book-index/_search
{
  "from": 0,
  "size": 100,
  "query": {
    "range": {
      "price": {
        "from": 2000,
        "to": 3000,
        "include_lower": true,
        "include_upper": true,
        "boost": 1
      }
    }
  }
}
/**
 * 范圍查詢:rangeQuery
 */
@Override
public void rangeQuery(String index, String field, int from, int to) throws Exception {
    //builder(index, QueryBuilders.rangeQuery(field).from(from).to(to));
    builder(index, QueryBuilders.rangeQuery(field).gte(2000).lte(3000));
}
@Test
public void testRangeQuery() throws Exception {
    baseQuery.rangeQuery(Constants.INDEX_NAME, "price", 2000, 3000);
}

1.6 regexp查詢

正則表達式查詢,wildcard和regexp查詢的工作方式和prefix查詢完全一樣。它們也需要遍歷倒排索引中的詞條列表來找到所有的匹配詞條,然后逐個詞條地收集對應的文檔ID。它們和prefix查詢的唯一區(qū)別在于它們能夠支持更加復雜的模式。

這也意味著使用它們存在相同的風險。對一個含有很多不同詞條的字段運行這類查詢是非常消耗資源的。避免使用一個以通配符開頭的模式(比如,*foo)。

盡管對于前綴匹配,可以在索引期間準備你的數(shù)據(jù)讓它更加高效,通配符和正則表達式匹配只能在查詢期間被完成。雖然使用場景有限,但是這些查詢也有它們的用武之地。

<font color=red>注意:prefix,wildcard以及regexp查詢基于詞條進行操作。如果你在一個analyzed字段上使用了它們,它們會檢查字段中的每個詞條,而不是整個字段。</font>

例如查找長號碼(longCode)以1069開頭后面是數(shù)字的文檔:

#模糊查詢:regexpQuery
POST /book-index/_search
{
  "from": 0,
  "size": 100,
  "query": {
    "regexp": {
      "title": {
        "value": """\\w+(.)*""",
        "flags_value": 65535,
        "max_determinized_states": 10000,
        "boost": 1
      }
    }
  }
}
/**
 * 模糊查詢:regexpQuery
 */
@Override
public void regexQuery(String index, String field, String regex) throws Exception {
    QueryBuilders.regexpQuery("title", "\\w+(.)*");
    builder(index, QueryBuilders.regexpQuery(field, regex));
}
@Test
public void testRegexQuery() throws Exception {
    baseQuery.regexQuery(Constants.INDEX_NAME, "title", "\\\\w+(.)*");
}

1.7 scroll查詢

ES對于from+size的個數(shù)是有限制的,二者之和不能超過1w。當所請求的數(shù)據(jù)總量大于1w時,可用scroll來代替from+size。

原理

ES的搜索是分2個階段進行的,即Query階段和Fetch階段。 Query階段比較輕量級,通過查詢倒排索引,獲取滿足查詢結(jié)果的文檔ID列表。 而Fetch階段比較重,需要將每個shard的結(jié)果取回,在協(xié)調(diào)結(jié)點進行全局排序。 通過From+size這種方式分批獲取數(shù)據(jù)的時候,隨著from加大,需要全局排序并丟棄的結(jié)果數(shù)量隨之上升,性能越來越差。

而Scroll查詢,先做輕量級的Query階段以后,免去了繁重的全局排序過程。 它只是將查詢結(jié)果集,也就是doc id列表保留在一個上下文里, 之后每次分批取回的時候,只需根據(jù)設置的size,在每個shard內(nèi)部按照一定順序(默認doc_id續(xù)), 取回這個size數(shù)量的文檔即可。

使用場景

由此也可以看出scroll不適合支持那種實時的和用戶交互的前端分頁工作,其主要用途用于從ES集群分批拉取大量結(jié)果集的情況,一般都是offline的應用場景。 比如需要將非常大的結(jié)果集拉取出來,存放到其他系統(tǒng)處理,或者需要做大索引的reindex等等。
不要把 scroll 用于實時請求,它主要用于大數(shù)據(jù)量的場景。例如:將一個索引的內(nèi)容索引到另一個不同配置的新索引中。

代碼示例

例如滾動查詢所有文檔:

POST  /book-index/_search?scroll=1m
{
    "query": {
        "match_all" : {}
    },
    "sort": [
        "_doc"
        ]
}

POST /_search/scroll #繼續(xù)使用滾動查詢
{
   "scroll": "1m", #設置時間
   "scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAAAdFkEwRENOVTdnUUJPWVZUd1p2WE5hV2cAAAAAAAAAHhZBMERDTlU3Z1FCT1lWVHdadlhOYVdnAAAAAAAAAB8WQTBEQ05VN2dRQk9ZVlR3WnZYTmFXZw==" #指定在哪個scroll的基礎上繼續(xù)查詢
}

清除scroll

雖然我們在設置開啟scroll時,設置了一個scroll的存活時間,但是如果能夠在使用完順手關閉,可以提早釋放資源,降低ES的負擔.

DELETE /_search/scroll
{
    "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB"
}
public void scrollQuery(String index) throws Exception {
    SearchRequest searchRequest = new SearchRequest(index);
    //值不需要足夠長來處理所有數(shù)據(jù)—它只需要足夠長來處理前一批結(jié)果。每個滾動請求(帶有滾動參數(shù))設置一個新的過期時間。
    //創(chuàng)建一個有效期為 1 分鐘的滾動
    Scroll scroll = new Scroll(TimeValue.timeValueMillis(1L));
    //設置滾動查詢
    searchRequest.scroll(scroll);
    //創(chuàng)建查詢條件的封裝對象
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //一般情況下我們的滾動查詢需要查詢所有數(shù)據(jù)
    searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    //設定每次返回多少條數(shù)據(jù)
    searchSourceBuilder.size(5);
    //對我們的請求指定查詢條件
    searchRequest.source(searchSourceBuilder);
    log.info("string:" + searchRequest.source());
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

    String scrollId = searchResponse.getScrollId();
    SearchHit[] searchHits = searchResponse.getHits().getHits();
    log.info("-----首頁-----");
    for (SearchHit searchHit : searchHits) {
        log.info(searchHit.getSourceAsString());
    }
    //遍歷搜索命中的數(shù)據(jù),直到?jīng)]有數(shù)據(jù)
    while (searchHits != null && searchHits.length > 0) {
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        scrollRequest.scroll(scroll);
        log.info("string:" + scrollRequest.toString());
        try {
            searchResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        scrollId = searchResponse.getScrollId();
        searchHits = searchResponse.getHits().getHits();
        if (searchHits != null && searchHits.length > 0) {
            log.info("-----下一頁-----");
            for (SearchHit searchHit : searchHits) {
                log.info(searchHit.getSourceAsString());
            }
        }

    }

    //清除滾屏
    ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
    //也可以選擇setScrollIds()將多個scrollId一起使用
    clearScrollRequest.addScrollId(scrollId);
    ClearScrollResponse clearScrollResponse = null;
    try {
        clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
        e.printStackTrace();
    }
    boolean succeeded = clearScrollResponse != null && clearScrollResponse.isSucceeded();
    log.info("succeeded:" + succeeded);
}
@Test
public void testScrollQuery() throws Exception {
    baseQuery.scrollQuery(indexName,type);
}

2. delete-by-query

刪除查詢的文檔,由于每個文檔都需要單獨被刪除,查詢大量文檔可能需要很長的時間。

注意:

不要使用delete-by-query來刪除一個索引下的全部或者大部分文檔,確實需要的話,可以創(chuàng)建一個新的索引,然后將需要保留的文檔重新索引到新的索引中去,這樣你就可以直接刪掉舊索引。
POST /book-index/_delete_by_query
{
   "query": { #此處使用之前的查詢條件
    "match_all": {}
  }
}
@Override
public void deleteByQuery(String index, QueryBuilder queryBuilder) throws Exception {
    DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(index).setQuery(queryBuilder);

    BulkByScrollResponse response = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
    long deleted = response.getDeleted();
    log.info("刪除的數(shù)量是{}", deleted);
    log.info(response);
}
@Test
public void testDeleteByQuery() throws Exception {
    docService.deleteByQuery(Constants.INDEX_NAME, QueryBuilders.termQuery("id", "0"));
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容