ElasticSearch学习笔记(三):CRUD之简单查询

city-event-processing——索引名

一、数据体

索引结构

PUT city-event-processing
{
  "settings": {
    "number_of_shards": 10,
    "number_of_replicas": 1,
    "refresh_interval": "1s"
    
  },
  "mappings": {
    "properties": {
      "address": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "caseNumber": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "causeId": {
        "type": "keyword"
      },
      "createTime": {
        "type": "date",
        "format": "epoch_millis"
      },
      "updateTime": {
        "type": "date",
        "format": "epoch_millis"
      },
      "deviceId": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "deviceName": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "autoCheckPass": {
        "type": "boolean"
      },
      "location": {
        "type": "geo_point"
      },
      "state": {
        "type": "integer"
      },
      "type": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "acceptType": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "videoId": {
        "type": "keyword"
      }
    }
  }
}

数据体

/**
 * @param
 * @Author zhangwei
 * @Date 2021/3/23
 * @Description 告警事件数据库结构
 */
@AllArgsConstructor
@Data
public class AlarmEventPO {
    /**
     * 目标Id
     */
    private String causeId;
    /**
     * 案件编号
     */
    private String caseNumber;
    /**
     * 视频Id
     */
    private String videoId;
    /**
     * 告警地点
     */
    private String address;
    /**
     * 告警地点坐标
     */
    private Coordinate location;
    /**
     * 告警类型
     */
    private String type;
    /**
     * 接受的告警类型
     */
    private String acceptType;
    /**
     * 告警时间
     */
    private Long createTime;
    /**
     * 自动核查是否通过
     */
    private Boolean autoCheckPass;
    /**
     * 案件状态
     */
    private Integer state;
    /**
     * 数据更新时间
     */
    private Long updateTime;
    /**
    * 设备Id
    */
    private String deviceId;
}


二、简单查询

2.1、精准查询某单个案件编号的案件
注:caseNumber为text类型,caseNumber.keyword为keyword类型

2.1.1、DSL语句

GET city-event-processing/_search
{
  "query": {
    "term": {
      "caseNumber.keyword": {
        "value": "2104231001042"
      }
    }
  }
}

2.1.2、RestHighLevelClient实现

    /**
     * @description: 根据案件号查询事件信息
     * @param:
     * @return:
     * @date: 2020/2/25
     */
    @Override
    public Optional<AlarmEventPO> selectAlarmEventByCaseNumber(final String caseNumber) {
        /** 构造查询Request **/
        final QueryBuilder queryBuilder = QueryBuilders.termQuery(“caseNumber.keyword”, caseNumber);
        final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(queryBuilder).size(1);
        final SearchRequest request = Requests.searchRequest(“city-event-processing”).source(searchSourceBuilder);

        try {
            /** 查询 **/
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            RestStatus restStatus = response.status();
            if (restStatus != RestStatus.OK) {
                throw CommonError.ES_ERROR.exception();
            }
            if (response.getHits().getHits().length == 0L) {
                return Optional.empty();
            } else {
                return Arrays.stream(response.getHits().getHits())
                    .map(hit -> JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class)).findFirst();
            }
        } catch (IOException e) {
            log.error("elastic search failed to search event, caseNumber:[{}]", caseNumber);
            throw CommonError.ES_ERROR.exception(e);
        }
    }

2.2、精准查询某些案件编号的案件
注:caseNumber为text类型,caseNumber.keyword为keyword类型

2.1.1、DSL语句

GET city-event-processing/_search
{
  "query": {
    "terms": {
      "caseNumber.keyword": [
        "2104231001042", "2104231001056"
        ]
    }
  }
}

2.1.2、RestHighLevelClient实现

    /**
     * @description: 根据案件号查询事件信息
     * @param:
     * @return:
     * @date: 2020/2/25
     */
    @Override
    public List<AlarmEventPO> selectAlarmEventByCaseNumber(final String[] caseNumbers) {
        /** 构造查询Request **/
        final QueryBuilder queryBuilder = QueryBuilders.termsQuery(“caseNumber.keyword”, caseNumbers);
        final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(queryBuilder).size(1);
        final SearchRequest request = Requests.searchRequest(“city-event-processing”).source(searchSourceBuilder);

        try {
            /** 查询 **/
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            RestStatus restStatus = response.status();
            if (restStatus != RestStatus.OK) {
                throw CommonError.ES_ERROR.exception();
            }
            if (response.getHits().getHits().length == 0L) {
                return Collections.emptyList();
            } else {
                return Arrays.stream(response.getHits().getHits())
                    .map(hit -> JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class)).collect(Collectors.toList());
            }
        } catch (IOException e) {
            log.error("elastic search failed to search event, caseNumber:[{}]", caseNumber);
            throw CommonError.ES_ERROR.exception(e);
        }
    }

2.3、查询state为特定值或者state为某些值的案件
注:state为Integer类型

2.3.1、DSL

state为特定值

GET city-event-processing/_search
{
  "query": {
    "terms": {
      "state": [1]
    }
  }
}

state为某些值

GET city-event-processing/_search
{
  "query": {
    "terms": {
      "state": [1,2]
    }
  }
}

2.3.2、RestHighLevelClient实现

state为特定值

	/**
     * @description: 根据state状态查询事件信息
     * @param:
     * @return:
     * @date: 2020/2/25
     */
    @Override
    public List<AlarmEventPO> selectAlarmEventByState(final Integer state) {
        /** 构造查询Request **/
        final QueryBuilder queryBuilder = QueryBuilders.termQuery("state", state);
        final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(queryBuilder);
        final SearchRequest request = Requests.searchRequest(“city-event-processing”).source(searchSourceBuilder);

        try {
            /** 查询 **/
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            if (response.getHits().getHits().length == 0L) {
                return Optional.empty();
            } else {
                return Arrays.stream(response.getHits().getHits())
                    .map(hit -> JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class)).collect(Collectors.toList());
            }
        } catch (IOException e) {
            log.error("elastic search failed to search event, caseNumber:[{}]", caseNumber);
            throw CommonError.ES_ERROR.exception(e);
        }
    }

state为某些值

	/**
     * @description: 根据state状态查询事件信息
     * @param:
     * @return:
     * @date: 2020/2/25
     */
    @Override
    public List<AlarmEventPO> selectAlarmEventByState(final Integer[] states) {
        /** 构造查询Request **/
        final QueryBuilder queryBuilder = QueryBuilders.termsQuery("state", states);
        final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(queryBuilder);
        final SearchRequest request = Requests.searchRequest(“city-event-processing”).source(searchSourceBuilder);

        try {
            /** 查询 **/
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            if (response.getHits().getHits().length == 0L) {
                return Optional.empty();
            } else {
                return Arrays.stream(response.getHits().getHits())
                    .map(hit -> JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class)).collect(Collectors.toList());
            }
        } catch (IOException e) {
            log.error("elastic search failed to search event, caseNumber:[{}]", caseNumber);
            throw CommonError.ES_ERROR.exception(e);
        }
    }

2.4、范围查询,这里以createTime字段实验

2.4.1、DSL

GET city-event-processing/_search
{
  "query": {
    "range": {
      "createTime": {
        "gte": 1617762286042,
        "lte": 1617762286042
      }
    }
  }
}

gte:大于等于
lte:小于等于
gt:大于
lt:小于

2.4.2、RestHighLevelClient实现

	/**
     * @description: 根据createTime范围查询事件信息
     * @param:startTime 起始时间 endTime  结束时间
     * @return:
     * @date: 2020/2/25
     */
    @Override
    public List<AlarmEventPO> selectAlarmEventByTime(final Long startTime, final Long endTime) {
        /** 构造查询Request **/
        final QueryBuilder queryBuilder = QueryBuilders.rangeQuery(CREATE_TIME_KEY).gte(startTime).lte(endTime);
        final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(queryBuilder);
        final SearchRequest request = Requests.searchRequest(“city-event-processing”).source(searchSourceBuilder);

        try {
            /** 查询 **/
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            if (response.getHits().getHits().length == 0L) {
                return Optional.empty();
            } else {
                return Arrays.stream(response.getHits().getHits())
                    .map(hit -> JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class)).collect(Collectors.toList());
            }
        } catch (IOException e) {
            log.error("elastic search failed to search event, caseNumber:[{}]", caseNumber);
            throw CommonError.ES_ERROR.exception(e);
        }
    }

2.5、多查询条件——与/且关系

时间范围查询(createTime)与(&&)案件号(caseNumber)前缀模糊查询

2.5.1、DSL

GET city-event-processing/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "prefix": {
            "caseNumber.keyword": {
              "value": "210508"
            }
          }
        },
        {
          "range": {
            "createTime": {
              "gte": 1619798400000,
              "lte": 1651115440000
            }
          }
        }
      ]
    }
  }
}

2.5.2、RestHighLevelClient实现

package com.elasticsearch.elasticsearchlearning.dao.impl;

import com.alibaba.fastjson.JSON;
import com.elasticsearch.elasticsearchlearning.constant.CommonConstant;
import com.elasticsearch.elasticsearchlearning.constant.CommonError;
import com.elasticsearch.elasticsearchlearning.dao.BaseElasticSearchDao;
import com.elasticsearch.elasticsearchlearning.dao.IAlarmEventDao;
import com.elasticsearch.elasticsearchlearning.modle.po.AlarmEventPO;
import com.elasticsearch.elasticsearchlearning.query.BaseEventPageQuery;
import com.elasticsearch.elasticsearchlearning.query.BaseEventQuery;
import com.elasticsearch.elasticsearchlearning.utils.Page;
import com.elasticsearch.elasticsearchlearning.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.megvii.framework.util.StringUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @param
 * @Author zhangwei
 * @Date 2021/4/29
 * @Description  案件dao
 */
@Slf4j
@Repository
public class AlarmEventDao extends BaseElasticSearchDao implements IAlarmEventDao {
    private static final String CASE_NUMBER_KEY = "caseNumber.keyword";
    private static final String CREATE_TIME_KEY = "createTime";
    //索引名
    private static final String INDEX_NAME = "city-event-processing";


    /**
     * 根据基础query查询案件
     * @param pageQuery
     * @return
     */
    @Override
    public List<AlarmEventPO> selectAlarmEventPOByBasePageQuery(BaseEventQuery pageQuery) {
        BoolQueryBuilder boolQueryBuilder = buildQueryBuilderByBaseEventQuery(pageQuery);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(boolQueryBuilder);
        final SearchRequest request = Requests.searchRequest(INDEX_NAME).source(sourceBuilder);

        final List<AlarmEventPO> results = new ArrayList<>();
        try {
            // 处理结果
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            RestStatus restStatus = response.status();
            if (restStatus != RestStatus.OK) {
                throw CommonError.ES_ERROR.exception();
            }

            for (SearchHit hit : response.getHits().getHits()) {
                final AlarmEventPO result = JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class);
                results.add(result);
            }
        } catch (IOException e) {
            throw CommonError.ES_ERROR.exception(e);
        }
        
        return results;

    }

    private BoolQueryBuilder buildQueryBuilderByBaseEventQuery(BaseEventQuery query) {
        BoolQueryBuilder result = QueryBuilders.boolQuery();
        //根据案件号前模糊过滤
        String caseNumber = query.getCaseNumber();
        if (StringUtil.isNotEmpty(caseNumber)) {
            QueryBuilder builder = QueryBuilders.prefixQuery(CASE_NUMBER_KEY, caseNumber);
            result.must(builder);
        }
        Long startTime = query.getStartTime();
        //根据产生时间进行筛选
        if (null != startTime) {
            QueryBuilder builder = QueryBuilders.rangeQuery(CREATE_TIME_KEY).gte(startTime);
            result.must(builder);
        }
        Long endTime = query.getStartTime();
        if (null != endTime) {
            QueryBuilder builder = QueryBuilders.rangeQuery(CREATE_TIME_KEY).lte(endTime);
            result.must(builder);
        }
        return result;
    }
}

2.6、多查询条件并分页——或关系

**案件号(caseNumber)前缀模糊查询(记为A),时间范围查询(createTime)(记为B),地址全模糊查询(记为C) **
实现:A && (B || C)——A必须满足,B、C至少满足一个且实现分页
2.6.1、DSL实现

GET city-event-processing/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "prefix": {
            "caseNumber.keyword": {
              "value": "210517"
            }
          }
        }
      ],
      "should": [
        {
          "range": {
            "createTime": {
              "gte": 1621224018000,
              "lte": 1621245618000
            }
          }
        },
        {
          "wildcard": {
            "address.keyword": {
              "value": "*"
            }
          }
        }
        
      ],
      "minimum_should_match": 1
    }
  },
  "from": 100,
  "size": 20
}

2.6.2、RestHighLevelClient实现

    public Page<AlarmEventPO> selectAlarmShouldTest(BaseEventQuery pageQuery) {
        String caseNumber = pageQuery.getCaseNumber();
        Long startTime = pageQuery.getStartTime();
        Long endTime = pageQuery.getEndTime();
        Integer pageNo = pageQuery.getPageNo();
        Integer pageSize = pageQuery.getPageSize();
        int from = pageSize * (pageNo - 1);
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        /*根据案件号前模糊查询*/
        queryBuilder.must(QueryBuilders.prefixQuery(CASE_NUMBER_KEY, caseNumber));
        /*根据时间进行范围查询*/
        queryBuilder.should(QueryBuilders.rangeQuery(CREATE_TIME_KEY).gte(startTime).lte(endTime));
        /*根据地址全模糊查询*/
        queryBuilder.should(QueryBuilders.wildcardQuery(ADDRESS_KEY, "*友谊路*"));
        /*should条件至少满足一个*/
        queryBuilder.minimumShouldMatch(1);

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().from(from).size(pageSize).query(queryBuilder);
        final SearchRequest request = Requests.searchRequest(INDEX_NAME).source(sourceBuilder);

        final List<AlarmEventPO> results = new ArrayList<>();
        /*构造分页builder*/
        PageUtil.PageBuilder pageBuilder = PageUtil.of(pageNo, pageSize, CommonConstant.MAX_RESULT_WINDOW);
        long totalCounts;
        try {
            // 处理结果
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            RestStatus restStatus = response.status();
            if (restStatus != RestStatus.OK) {
                throw CommonError.ES_ERROR.exception();
            }

            for (SearchHit hit : response.getHits().getHits()) {
                final AlarmEventPO result = JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class);
                results.add(result);
            }
            totalCounts = getDocTotalCount(response.getHits().getTotalHits() , queryBuilder, INDEX_NAME);
        } catch (IOException e) {
            throw CommonError.ES_ERROR.exception(e);
        }

        return pageBuilder.build(totalCounts, results);

    }

    /**
     * @Author zhangwei
     * @Date 2021/3/26 18:59
     * @Description 根据查询条件算出总记录数
     */
    @SneakyThrows
    protected Long getDocTotalCount(QueryBuilder boolQueryBuilder, String... indices) {
        CountRequest countRequest = new CountRequest(indices);
        countRequest.query(boolQueryBuilder);
        CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
        return response.getCount();
    }
    /**
     * TotalHits的relation为EQUAL_TO时,totalHits.value数量即为总数量
     */
    public long getDocTotalCount(TotalHits totalHits, QueryBuilder boolQueryBuilder, String... index){
        if (TotalHits.Relation.EQUAL_TO == totalHits.relation) {
            return totalHits.value;
        } else {
            return getDocTotalCount(boolQueryBuilder, index);
        }
    }

2.7、某查询条件下数量查询

2.7.1、DSL实现

GET city-event-processing/_count
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "state": {
              "value": 1
            }
          }
        }
      ]
    }
  }
}

2.7.2、RestHighLevelClient实现

    /**
     * @Author zhangwei
     * @Date 2021/3/26 18:59
     * @Description 根据查询条件算出总记录数
     */
    @SneakyThrows
    protected Long getDocTotalCount(QueryBuilder boolQueryBuilder, String... indices) {
        CountRequest countRequest = new CountRequest(indices);
        countRequest.query(boolQueryBuilder);
        CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
        return response.getCount();
    }

2.8、searchAfter分页查询

2.8.1、searchAfter的运用场景
1、查询时会根据某个或某些字段进行排序。
2、排序后,查询排序字段某单边范围的数据。例如:按号码进行升序排序,查询大于等于1000的数据。
2.8.2、DSL实现

GET city-event-processing/_search
{
  "query": {
    "match_all": {}
  },
  "search_after":[
    "2105211000799"
  ],
  "from": 5, 
  "size": 10,
  "sort": [
    {
      "caseNumber.keyword": {
        "order": "asc"
      }
    }
  ]
}

2.8.3、RestHighLevelClient实现

/**
     * searchAfter分页查询测试
     * @param pageQuery
     * @return
     */
    public Page<AlarmEventPO> selectAlarmSearchAfterTest(BaseEventQuery pageQuery) {
        Integer pageNo = pageQuery.getPageNo();
        Integer pageSize = pageQuery.getPageSize();
        int from = pageSize * (pageNo - 1);
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().from(from).size(pageSize).query(queryBuilder);
        //按案件号进行排序
        sourceBuilder.sort(CASE_NUMBER_KEY, SortOrder.ASC);
        //大于某案件号分页查询
        sourceBuilder.searchAfter(new String[]{pageQuery.getCaseNumber()});
        final SearchRequest request = Requests.searchRequest(INDEX_NAME).source(sourceBuilder);

        final List<AlarmEventPO> results = new ArrayList<>();
        /*构造分页builder*/
        PageUtil.PageBuilder pageBuilder = PageUtil.of(pageNo, pageSize, CommonConstant.MAX_RESULT_WINDOW);
        long totalCounts;
        try {
            // 处理结果
            final SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            RestStatus restStatus = response.status();
            if (restStatus != RestStatus.OK) {
                throw CommonError.ES_ERROR.exception();
            }

            for (SearchHit hit : response.getHits().getHits()) {
                final AlarmEventPO result = JSON.parseObject(hit.getSourceAsString(), AlarmEventPO.class);
                results.add(result);
            }
            totalCounts = getDocTotalCount(response.getHits().getTotalHits() , queryBuilder, INDEX_NAME);
        } catch (IOException e) {
            throw CommonError.ES_ERROR.exception(e);
        }

        return pageBuilder.build(totalCounts, results);

    }
/**
     * @Author zhangwei
     * @Date 2021/3/26 18:59
     * @Description 根据查询条件算出总记录数
     */
    @SneakyThrows
    protected Long getDocTotalCount(QueryBuilder boolQueryBuilder, String... indices) {
        CountRequest countRequest = new CountRequest(indices);
        countRequest.query(boolQueryBuilder);
        CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
        return response.getCount();
    }
    /**
     * 在totalHits.relation为EQUAL_TO时,此时的value就是查询的总数
     * 查询统计数量
     */
    public long getDocTotalCount(TotalHits totalHits, QueryBuilder boolQueryBuilder, String... index){
        if (TotalHits.Relation.EQUAL_TO == totalHits.relation) {
            return totalHits.value;
        } else {
            return getDocTotalCount(boolQueryBuilder, index);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值