[SpringBoot] + [Elasticsearch——->DSL classification query]

1. DSL category query

Elasticsearch provides a JSON-based DSL (Domain Specific Language) to define queries. Common query types include:

  • Query all: query all data, for general testing. For example: match_all

  • Full text search: use the word segmenter to segment the user input content, and then match it in the inverted index database. For example:

    • match_query
    • multi_match_query
  • Precise query: Search for data based on precise entry values, generally for keyword, numeric, date, boolean and other types of fields. For example:

    • ids
    • range
    • term
  • Geographic (geo) query: query based on latitude and longitude. For example:

    • geo_distance
    • geo_bounding_box
  • Compound query: compound query can combine the above-mentioned various query conditions and combine query conditions. For example:

    • bool
    • function_score

2. Deep paging problem

For deep paging, ES provides two solutions, official documents:

  • search after: sorting is required when paging, the principle is to query the next page of data starting from the last sorting value. The official recommended way to use.
  • scroll: The principle is to form a snapshot of the sorted document ids and store them in memory. It is officially deprecated.

Common implementation schemes and advantages and disadvantages of pagination query:

  • from + size:

    • Advantages: Support random page turning
    • Disadvantages: deep paging problem, the default query upper limit (from + size) is 10000
    • Scenario: Random page-turning searches such as Baidu, JD.com, Google, and Taobao
  • search after:

    • Advantages: no query upper limit (the size of a single query does not exceed 10000)
    • Disadvantage: can only query backward page by page, does not support random page turning
    • Scenario: Search without random page turning requirements, such as mobile phone scrolling down to turn pages
  • scroll:

    • Advantages: no query upper limit (the size of a single query does not exceed 10000)
    • Disadvantages: There will be additional memory consumption, and the search results are not real-time
    • Scenario: Acquisition and migration of massive data. It is not recommended starting from ES7.1. It is recommended to use the search after solution.

3. DSL classification query code implementation

  • Array traversal uses stream
List<HotelDoc> hotelDocList = Stream.of(hits).map(hit -> {<!-- -->
            //Get the json string corresponding to each record
            String json = hit. getSourceAsString();
            HotelDoc hotelDoc = JSON. parseObject(json, HotelDoc. class);
            System.out.println("hotelDoc = " + hotelDoc);
            return hotelDoc;
        }).collect(Collectors.toList());
  • DSL classification query code implementation
@Autowired
    private RestHighLevelClient client;

    /**
     * Query all
     * match_all
     *
     * @throws Exception
     */
    @Test
    public void matchAllTest() throws Exception {
        //1. Create a request object
        SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
        //2.. Set conditions, prepare DSL
        searchRequest.source().query(
                QueryBuilders.matchAllQuery()
        );
        //3. Execute query, search
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. Parse the result
        //4.1 The total number of records obtained
        Long count = searchResponse.getHits().getTotalHits().value;
        System.out.println("count = " + count);
        //4.2 Get the hits array (record list)
        SearchHit[] hits = searchResponse. getHits(). getHits();
        List<HotelDoc> hotelDocList = Stream.of(hits).map(hit -> {<!-- -->
            //Get the json string corresponding to each record
            String json = hit. getSourceAsString();
            HotelDoc hotelDoc = JSON. parseObject(json, HotelDoc. class);
            System.out.println("hotelDoc = " + hotelDoc);
            return hotelDoc;
        }).collect(Collectors.toList());
        /*for (SearchHit hit : hits) {
            //Get the json string corresponding to each record
            String json = hit. getSourceAsString();
            HotelDoc hotelDoc = JSON. parseObject(json, HotelDoc. class);
            System.out.println("hotelDoc = " + hotelDoc);
        }*/
    }

    /**
     * match
     * multi_match
     *
     * @throws Exception
     */
    @Test
    public void matchTest() throws Exception {
        //1. Create a request object
        SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
        //2. Set request conditions, DSL
        searchRequest.source().query(
                //QueryBuilders.matchQuery("all", "Home on the Bund")
                QueryBuilders.multiMatchQuery("Home on the Bund", "name", "brand", "business")
        );
        //3. Execute query
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. Parse the result
        Long count = response.getHits().getTotalHits().value;
        System.out.println("count = " + count);
        SearchHit[] hits = response. getHits(). getHits();
        for (SearchHit hit : hits) {
            String json = hit. getSourceAsString();
            System.out.println("json = " + json);
        }
    }

    /**
     * term precise query
     *
     * @throws Exception
     */
    @Test
    public void termTest() throws Exception {
        //1. Create a request object
        SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
        //2. Set request conditions, DSL
        searchRequest. source()
                .query(QueryBuilders.termQuery("city", "Shanghai"));
        //3. Execute query
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. Parse the result
        Long count = response.getHits().getTotalHits().value;
        System.out.println("count = " + count);
        SearchHit[] hits = response. getHits(). getHits();
        for (SearchHit hit : hits) {
            String json = hit. getSourceAsString();
            System.out.println("json = " + json);
        }
    }

    /**
     * range
     * range query
     *
     * @throws Exception
     */
    @Test
    public void rangeTest() throws Exception {
        //1. Create a request object
        SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
        //2. Set request conditions, DSL
        searchRequest.source().query(QueryBuilders.rangeQuery("price").gte(1000).lt(3000));
        //3. Execute query
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. Parse the result
        Long count = response.getHits().getTotalHits().value;
        System.out.println("count = " + count);
        SearchHit[] hits = response. getHits(). getHits();
        for (SearchHit hit : hits) {
            String json = hit. getSourceAsString();
            System.out.println("json = " + json);
        }
    }

    /**
     * Longitude and latitude query
     *
     * @throws Exception
     */
    @Test
    public void geoDistanceTest() throws Exception {
        //1. Create a request object
        SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
        //2. Set request conditions, DSL
        searchRequest. source()
                .query(QueryBuilders. geoDistanceQuery("location")
                        //.point(new GeoPoint(31.242201,121.509106))
                        .point(31.242201, 121.509106)
                        .distance(15, DistanceUnit.KILOMETERS));
        //3. Execute query
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. Parse the result
        Long count = response.getHits().getTotalHits().value;
        System.out.println("count = " + count);
        SearchHit[] hits = response. getHits(). getHits();
        for (SearchHit hit : hits) {
            String json = hit. getSourceAsString();
            System.out.println("json = " + json);
        }
    }

    /**
     * bool query
     *
     * @throws Exception
     */
    @Test
    public void Test() throws Exception {
        //1. Create a request object
        SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
        //2. Set request conditions, DSL
        searchRequest. source()
                .query(QueryBuilders. boolQuery()
                        .must(QueryBuilders.matchQuery("name", "Home Inn"))
                        .mustNot(QueryBuilders. rangeQuery("price").gt(400))
                        .filter(QueryBuilders. geoDistanceQuery("location")
                                .point(31.21, 121.5)
                                .distance(15, DistanceUnit.KILOMETERS)));
        //3. Execute query
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. Parse the result
        Long count = response.getHits().getTotalHits().value;
        System.out.println("count = " + count);
        SearchHit[] hits = response. getHits(). getHits();
        for (SearchHit hit : hits) {
            String json = hit. getSourceAsString();
            System.out.println("json = " + json);
        }
    }

Filter + score + sort + distance sort + highlight + pagination

The obtained distance data and highlight data need special processing

 @Autowired
    private RestHighLevelClient client;

    @Override
    public PageResult<HotelDoc> searchHotel(SearchParam searchParam) {<!-- -->
        //1. Verify parameters
        //The first few data
        Integer page = searchParam. getPage();
        //page size
        Integer size = searchParam. getSize();
        //Search content
        String key = searchParam. getKey();
        //Create a wrapper result object
        PageResult<HotelDoc> hotelDocPageResultge = new PageResult<>();
        //Create collection
        List<HotelDoc> hotelDocList = new ArrayList<>();
        try {<!-- -->
            if (page == null || size == null) {<!-- -->
                throw new RuntimeException("Illegal parameter");
            }
            //2. Business processing
            //2.1 Create request object
            SearchRequest searchRequest = new SearchRequest(ESConstant.HOTEL_INDEX);
            //2.2 Prepare query conditions
            //Create query condition object---->Use bool query
            BoolQueryBuilder boolQuery = QueryBuilders. boolQuery();
            //------> search box judges null and empty string
            if (StringUtils.isBlank(key)) {<!-- -->
                //2.2.1 If it is empty, query all
                boolQuery. must(QueryBuilders. matchAllQuery());
            } else {<!-- -->
                //2.2.2 If it is not empty, query according to the condition
                boolQuery. must(QueryBuilders. matchQuery("all", key));
            }
            //------> city
            String city = searchParam. getCity();
            if (StringUtils.isNoneBlank(city)) {<!-- -->
                boolQuery. filter(QueryBuilders. termQuery("city", city));
            }
            //------------>Brand
            String brand = searchParam. getBrand();
            if (StringUtils.isNoneBlank(brand)) {<!-- -->
                boolQuery. filter(QueryBuilders. termQuery("brand", brand));
            }
            //------------> star rating
            String starName = searchParam. getStarName();
            if (StringUtils.isNoneBlank(starName)) {<!-- -->
                boolQuery. filter(QueryBuilders. termQuery("starName", starName));
            }
            //---------> price
            Integer minPrice = searchParam. getMinPrice();
            Integer maxPrice = searchParam. getMaxPrice();
            if (null != minPrice & amp; & amp; null != maxPrice) {<!-- -->
                boolQuery. filter(QueryBuilders
                        .rangeQuery("price")
                        .gte(minPrice)
                        .lte(maxPrice));
            }
            //-----------> score isAD=true add points for advertising
            FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(boolQuery,
                    new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{<!-- -->
                            new FunctionScoreQueryBuilder. FilterFunctionBuilder(
                                    QueryBuilders. termQuery("isAD", true),
                                    ScoreFunctionBuilders. weightFactorFunction(100F)
                            )}).boostMode(CombineFunction.SUM);

            //searchRequest.source().query(boolQuery);
            //=================> Sort before pagination, after condition
            String sortBy = searchParam. getSortBy();
            if ("score".equals(sortBy)) {<!-- -->
                searchRequest.source().sort("score", SortOrder.DESC);
            } else if ("price".equals(sortBy)) {<!-- -->
                searchRequest.source().sort("price", SortOrder.DESC);
            }

            //================> Sort according to distance --> Need to process after getting the query distance
            String location = searchParam. getLocation();
            if (StringUtils.isNoneBlank(location)) {<!-- -->
                searchRequest.source().sort(
                        SortBuilders. geoDistanceSort("location", new GeoPoint(location))
                                .unit(DistanceUnit.KILOMETERS));
            }
            //2.3 Prepare paging parameters
            //============================>highlight --> highlight data also needs to be processed and returned to the front end
            //functionScoreQueryBuilder encapsulates boolQuery
            searchRequest. source()
                    .query(functionScoreQueryBuilder).highlighter(
                            new HighlightBuilder()
                                    .field("name")
                                    .requireFieldMatch(false)
                                    .preTags("<font style='color:red'>")
                                    .postTags("</font>")
                    )
                    .from((page - 1) * size)
                    .size(size);
            //2.4 execute query
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            //2.5 parse the result and encapsulate the data
            //2.5.1 Get the total number of records that meet the conditions
            long total = response.getHits().getTotalHits().value;
            //2.5.2 Get the number of records that meet the conditions
            for (SearchHit hit : response. getHits(). getHits()) {<!-- -->
                String hotelDocJson = hit. getSourceAsString();
                HotelDoc hotelDoc = JSON.parseObject(hotelDocJson, HotelDoc.class);
                //Process the distance data obtained
                Object[] sortValues = hit. getSortValues();
                if (sortValues != null & amp; & amp; sortValues. length > 0) {<!-- -->
                    //Get the distance between the current position and location
                    Object distance = sortValues[0];
                    hotelDoc. setDistance(distance);
                }
                // process the highlighted data
                Map<String, HighlightField> highlightFields = hit. getHighlightFields();
                //Judge whether the highlighted data obtained is empty
                if (highlightFields!=null & amp; & amp; highlightFields. size()>0) {<!-- -->
                    HighlightField highlightField = highlightFields. get("name");
                    Text[] fragments = highlightField. getFragments();
                    String name = fragments[0].toString();
                    hotelDoc. setName(name);
                }
                hotelDocList.add(hotelDoc);
            }
            //2.5.3 Encapsulation data
            hotelDocPageResultge.setTotal(total);
            hotelDocPageResultge.setHotels(hotelDocList);
        } catch (IOException e) {<!-- -->
            e.printStackTrace();
        }
        return hotelDocPageResult;
    }