Elastic-JavaApi SpringBoot布爾查詢

最近在寫Es的Java查詢Api,發現從部署到Api沒有很好的資料,所以整理記錄一下,配置就不發了,直接分析代碼和需求。

Es複雜查詢

基於 SpringBoot + EsTransportClient

需求分析

這個查詢的需求是,滿足中文分詞 與查詢 標題和描述(同時滿足),也可以 或查詢 多組條件拼出來的 查詢條件(滿足一個或多個) 。比如:查詢:同時滿足黃色和好吃和產地爲南京的水果同時滿足藍色和難吃和產地爲北京 這樣的組查詢。 這些查詢條件大概是這樣的 Json :

[
    {"BULLETIN_TYPE_NAME":"xxx公示",
    "NOTICE_INDUSTRIES_NAME": "其他",
    "REGION_PROVINCE": "江蘇xx"
    },
    {"BULLETIN_TYPE_NAME":"xxxx",
    "NOTICE_INDUSTRIES_NAME": "市政xx",
    "REGION_PROVINCE": "江蘇xx",
    "REGION_NAME": "江蘇省xxx"
    }
]

還要滿足默認用某個字段進行排序、支持分頁,返回所有和限制條件下的搜索數目,這裏用布爾查詢可以解決。

查詢代碼


    // 查詢
    @RequestMapping(value = "Query", method = RequestMethod.POST)
    public ResponseEntity query(
            @RequestParam(name = "NOTICE_CONTENT", required = false) String NOTICE_CONTENT,
            @RequestParam(name = "NOTICE_NAME", required = false) String NOTICE_NAME,
            @RequestParam(name = "Index", required = true) String index,
            @RequestParam(name = "PageSize", required = false, defaultValue = "20") int PageSize,
            @RequestParam(name = "CurrentPage", required = false, defaultValue = "1") int CurrentPage,
            @RequestParam(name = "Type", required = true) String type,
            @RequestParam(name = "BoolQueryJson", required = true) String BoolQueryJson) {


        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        if (NOTICE_NAME != null) {
            boolQuery.must(QueryBuilders.matchQuery("NOTICE_NAME", NOTICE_NAME));
        }

        if (NOTICE_CONTENT != null) {
            boolQuery.must(QueryBuilders.matchQuery("NOTICE_CONTENT", NOTICE_CONTENT));
        }


        JSONArray jsonArray = JSONArray.fromObject(BoolQueryJson);

        if (jsonArray != null && jsonArray.size() > 0) {
            for (int i = 0; i < jsonArray.size(); i++) {
                int j = 0;
                BoolQueryBuilder boolQueryParam = QueryBuilders.boolQuery();

                if (jsonArray.getJSONObject(i).has("BULLETIN_TYPE_NAME")) {
                    String BULLETIN_TYPE_NAMEI = jsonArray.getJSONObject(i).getString("BULLETIN_TYPE_NAME");
                    MatchPhraseQueryBuilder BULLETIN_TYPE_NAMEMP = QueryBuilders.matchPhraseQuery("BULLETIN_TYPE_NAME", BULLETIN_TYPE_NAMEI);
                    boolQueryParam.should(BULLETIN_TYPE_NAMEMP);
                    j++;
                }

                if (jsonArray.getJSONObject(i).has("NOTICE_INDUSTRIES_NAME")) {
                    String NOTICE_INDUSTRIES_NAMEI = jsonArray.getJSONObject(i).getString("NOTICE_INDUSTRIES_NAME");
                    MatchPhraseQueryBuilder NOTICE_INDUSTRIES_NAMEMP = QueryBuilders.matchPhraseQuery("NOTICE_INDUSTRIES_NAME", NOTICE_INDUSTRIES_NAMEI);
                    boolQueryParam.should(NOTICE_INDUSTRIES_NAMEMP);
                    j++;
                }

                if (jsonArray.getJSONObject(i).has("REGION_PROVINCE")) {
                    String REGION_PROVINCEI = jsonArray.getJSONObject(i).getString("REGION_PROVINCE");
                    MatchPhraseQueryBuilder REGION_PROVINCEMP = QueryBuilders.matchPhraseQuery("REGION_PROVINCE", REGION_PROVINCEI);
                    boolQueryParam.should(REGION_PROVINCEMP);
                    j++;
                }

                if (jsonArray.getJSONObject(i).has("REGION_NAME")) {
                    String REGION_NAMEI = jsonArray.getJSONObject(i).getString("REGION_NAME");
                    MatchPhraseQueryBuilder REGION_NAMEMP = QueryBuilders.matchPhraseQuery("REGION_NAME", REGION_NAMEI);
                    boolQueryParam.should(REGION_NAMEMP);
                    j++;
                }

                if (jsonArray.getJSONObject(i).has("TRADE_PLAT")) {
                    String TRADE_PLATI = jsonArray.getJSONObject(i).getString("TRADE_PLAT");
                    MatchPhraseQueryBuilder TRADE_PLATMP = QueryBuilders.matchPhraseQuery("TRADE_PLAT", TRADE_PLATI);
                    boolQueryParam.should(TRADE_PLATMP);
                    j++;
                }
                boolQueryParam.minimumShouldMatch(j);
                boolQuery.should(boolQueryParam);
            }
            boolQuery.minimumShouldMatch(1);
        }

        SearchRequestBuilder builder = this.client.prepareSearch(index)
                .setTypes(type)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                 .setFetchSource(null,new String[]{"NOTICE_CONTENT"})
                .setQuery(boolQuery)
                .addSort("NOTICE_SEND_TIME", SortOrder.DESC)
                .setFrom((CurrentPage - 1) * PageSize)
                .setSize(PageSize);

        SearchResponse response = builder.get();
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

        int sum = 0;
        for (SearchHit hit : response.getHits()) {
            result.add(hit.getSource());
            // 限制條件內的總數
            sum++;
        }

        // 所有文檔查詢的總數
        SearchHits searchHits = response.getHits();
        long AllSum = searchHits.getTotalHits();

        return new ResponseEntity(result, HttpStatus.OK);

    }

DSL 結構

生成的 ES DSL 查詢 結構

{
  "from" : 0,
  "size" : 10,
  "query" : {
    "bool" : {
      "must" : [
        {
          "match" : {
            "NOTICE_NAME" : {
              "query" : "中國石油",
              "operator" : "OR",
              "prefix_length" : 0,
              "max_expansions" : 50,
              "fuzzy_transpositions" : true,
              "lenient" : false,
              "zero_terms_query" : "NONE",
              "boost" : 1.0
            }
          }
        },
        {
          "match" : {
            "NOTICE_CONTENT" : {
              "query" : "中國石油",
              "operator" : "OR",
              "prefix_length" : 0,
              "max_expansions" : 50,
              "fuzzy_transpositions" : true,
              "lenient" : false,
              "zero_terms_query" : "NONE",
              "boost" : 1.0
            }
          }
        }
      ],
      "should" : [
        {
          "bool" : {
            "should" : [
              {
                "match_phrase" : {
                  "BULLETIN_TYPE_NAME" : {
                    "query" : "中標候選人公示",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              },
              {
                "match_phrase" : {
                  "NOTICE_INDUSTRIES_NAME" : {
                    "query" : "其他",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              },
              {
                "match_phrase" : {
                  "REGION_PROVINCE" : {
                    "query" : "江蘇",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              },
              {
                "match_phrase" : {
                  "REGION_NAME" : {
                    "query" : "江蘇省",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              }
            ],
            "disable_coord" : false,
            "adjust_pure_negative" : true,
            "minimum_should_match" : "4",
            "boost" : 1.0
          }
        },
        {
          "bool" : {
            "should" : [
              {
                "match_phrase" : {
                  "BULLETIN_TYPE_NAME" : {
                    "query" : "招標公告",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              },
              {
                "match_phrase" : {
                  "NOTICE_INDUSTRIES_NAME" : {
                    "query" : "市政",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              },
              {
                "match_phrase" : {
                  "REGION_PROVINCE" : {
                    "query" : "江蘇",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              },
              {
                "match_phrase" : {
                  "REGION_NAME" : {
                    "query" : "江蘇省,南京市",
                    "slop" : 0,
                    "boost" : 1.0
                  }
                }
              }
            ],
            "disable_coord" : false,
            "adjust_pure_negative" : true,
            "minimum_should_match" : "4",
            "boost" : 1.0
          }
        }
      ],
      "disable_coord" : false,
      "adjust_pure_negative" : true,
      "minimum_should_match" : "1",
      "boost" : 1.0
    }
  },
  "stored_fields" : "NOTICE_SEND_TIME",
  "sort" : [
    {
      "NOTICE_SEND_TIME" : {
        "order" : "desc"
      }
    }
  ]
}

Es客戶端Bean Config


@Configuration
public class MyConfig {

    @Bean
    public TransportClient client() throws UnknownHostException {
        InetSocketTransportAddress node = new InetSocketTransportAddress(
                InetAddress.getByName("192.168.1.21"),
                9300
        );

        Settings settings = Settings.builder()
                .put("cluster.name","leon")
                .build();

        TransportClient client = new PreBuiltTransportClient(settings);
        client.addTransportAddress(node);
        return client;
    }
}

查詢結果

這裏寫圖片描述

總結

  1. 注意引入的 jar 包版本一致性
  2. 多個條件一起拼湊組成了 一個 should ,嵌套構成了 bool 查詢,要注意循環的順序,即:添加到外層的時機。
    比如 if 中的
boolQueryParam.should(REGION_NAMEMP);
boolQuery.should(boolQueryParam);

如果我合併寫成

boolQuery.should(boolQueryParam.should(REGION_NAMEMP));

構造的 builder 就會出現8個should ,顯然不是我要的結果
3. 如果熟悉 Es ,以前用過 Kirbna 查詢結果和預期不一致,可以打印 builder 通過查看 Es 生成的 Json 查詢體來查錯。
4. searchHits.getTotalHits(); 並不等於 response.getHits() 的 數目
5. .setFetchSource(null,new String[]{"NOTICE_CONTENT"}) 設置不反回某個字段,當然inclunde部分可以設置返回,具體參考 SearchRequestBuilder 方法,可以指定 inclundeexclude

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