Elasticsearch 操作手册

一、Java 通用连接池准备

@Configuration
@Order(-1)
public class EsConfiguration {
    @Value("${spring.elasticsearch.rest.username}")
    private String username;

    @Value("${spring.elasticsearch.rest.password}")
    private String password;

    @Value("${spring.elasticsearch.rest.uris}")
    private String url;

    @Bean(name = "esHighLevelClient")
    @Order(-1)
    public RestHighLevelClient client(){
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(username,password));
        RestClientBuilder restClientBuilder = getRestClientBuilder(url)
                .setHttpClientConfigCallback(httpAsyncClientBuilder -> {
                    httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
                            .setKeepAliveStrategy(((response, context) -> 
                            // 注意: 这块需要根据系统的所需的连接时长来自己调试,如果处理不当可能在查询时导致ES关闭造成异常
                            TimeUnit.MINUTES.toMicros(3)));
                });
        return new RestHighLevelClient(restClientBuilder);
    }

    public static RestClientBuilder getRestClientBuilder(String esUrl){
        return RestClient.builder(createHttpHost(URI.create(esUrl)));
    }

    public static HttpHost createHttpHost(URI uri){
        if(StrUtil.isEmpty(uri.getUserInfo())){
            return HttpHost.create(uri.toString());
        }
        try {
            return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
                    uri.getQuery(), uri.getFragment()).toString());
        } catch (URISyntaxException e) {
           throw new IllegalStateException(e);
        }
    }

}

二、自定义查询条件注解@ConditionIn @ConditionLike @ConditionIs 解析

// 自定义注解
// 定义枚举类:查询方式
public enum Condition implements Serializable {
    Gt,
    Gte,
    In,
    Is,
    Lt,
    Lte,
    Ne,
    Like
}
// 1.base 注解
/**
 * @Author 
 * @Date 2021/6/4 17:19
 * @Version 1.0
 */
@Target({ElementType.TYPE})
@Retention(RUNTIME)
public @interface BaseCondition {

    Class makeCondition();
}

// 2. 注解创建
/**
 * 模糊查询,应用于AbsMongoQuery的子类
 * @Author 
 * @Date 2021/4/13 16:51
 * @Version 1.0
 */
@Target({ElementType.FIELD})
@Retention(RUNTIME)
@BaseCondition(makeCondition = ConditionLikeImpl.class)
public @interface ConditionLike {
    String key() default "";
}

// 3. 实现base
public interface ICondition {

    /**
     * 获取key的值
     * @param an
     * @return
     */
    String getKey(Annotation an, Field field);

    /**
     * 获取 条件
     * @param an
     * @return
     */
    Condition getCondtion(Annotation an);


    /**
     * 获取条件对应的值
     * @param an
     * @param field
     * @param obj
     * @return
     */
    default Object getCondtionValue(Annotation an, Field field, Object obj) {
        // 获取值
        field.setAccessible(true);
        Object value = null;
        try {
            value = field.get(obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        if (null == value) {
            return null;
        }
        if (value instanceof String) {
            if (StringUtils.isEmpty(value)) {
                return null;
            }
        } else if (value instanceof List){
            if (CollectionUtils.isEmpty((List)value)) {
                return null;
            }
        } else if (value.getClass().isArray()) {
            Object[] array = (Object[]) value;
            if (array.length == 0) {
                return null;
            }
        }
        return value;
    }

}

// 4. key value 获取
public class ConditionLikeImpl implements ICondition {

    @Override
    public String getKey(Annotation an, Field field) {
        if (an instanceof ConditionLike) {
            return ((ConditionLike) an).key();
        }
        return null;
    }

    @Override
    public Condition getCondtion(Annotation an) {
        return Condition.Like;
    }
}


@Data
public class MongoCondition implements Serializable {

    public MongoCondition(String key, MongoFieldCondition fieldCondition) {
        this.key = key;
        this.fieldConditionList = new ArrayList<MongoFieldCondition>();
        this.fieldConditionList.add(fieldCondition);

    }

    /**
     * key
     */
    private String key;

    /**
     * 多条件
     */
    private List<MongoFieldCondition> fieldConditionList;
}

 /**
 * 生成搜索条件: 排序,分页,条件查询
 * @param queryBo
 * @return
 */
private MongoQueryDto convertQueryDto(AbsMongoQuery queryBo) {
    MongoQueryDto mongoQueryDto = new MongoQueryDto();
    if (null != queryBo) {
        // 排序
        mongoQueryDto.setOrderByList(queryBo.getOrderByList());
        // 分页
        mongoQueryDto.setPageNumber(queryBo.getPageNumber());
        mongoQueryDto.setPageSize(queryBo.getPageSize());
        // 条件
        mongoQueryDto.setCondtionList(convertQueryCondition(queryBo));
    }
    return mongoQueryDto;
}

 /**
     * 生成条件查询集合
     * @param queryBo
     * @return
     */
    private List<MongoCondition> convertQueryCondition(AbsMongoQuery queryBo) {
        List<MongoCondition> list = new ArrayList<>();
        Arrays.stream(queryBo.getClass().getDeclaredFields()).forEach(
                field -> {
                    // 获取注解
                    Annotation[] annnos = field.getDeclaredAnnotations();
                    // 获取条件key
                    String key = queryConditionKey(annnos, field);
                    // 配置条件以及对应的条件值
                    MongoFieldCondition mongoCondition = makeCondition(annnos, field, queryBo);
                    // 配置可以以及条件
                    configConition(list, key, mongoCondition);
                }
        );
        return list;
    }
    
     /**
     * 获取查询的key
     * @param annnos
     * @param field
     * @return
     */
    private String queryConditionKey(Annotation[] annnos, Field field) {
        for (Annotation an : annnos) {
            // 反射取出 属性名称
            ICondition condition = getICondition(an, field);
            if (null != condition) {
                return condition.getKey(an, field);
            }
        }
        return null;
    }
    
     /**
     * 生成单条件
     * @param annnos
     * @param field
     * @param queryBo
     * @return
     */
    private MongoFieldCondition makeCondition(Annotation[] annnos, Field field, AbsMongoQuery queryBo) {
        MongoFieldCondition mongoFieldCondition = new MongoFieldCondition();
        for (Annotation an : annnos) {
            // 反射取出 属性名称
            ICondition condition = getICondition(an, field);
            if (null != condition) {
                mongoFieldCondition.setCondition(condition.getCondtion(an));
                mongoFieldCondition.setValue(condition.getCondtionValue(an, field, queryBo));
                return mongoFieldCondition;
            }
        }
        return null;
    }
    
    /**
     * 配置条件
     * @param list
     * @param key
     * @param mongoFieldCondition
     */
    private void configConition(List<MongoCondition> list, String key, MongoFieldCondition mongoFieldCondition) {
        // 判断条件
        if (StringUtils.isEmpty(key)
                || null == mongoFieldCondition
                || Objects.isNull(mongoFieldCondition.getCondition())
                || Objects.isNull(mongoFieldCondition.getValue())) {
            return;
        }
        // 存在对应key则添加字段条件
        for (MongoCondition cd : list) {
            if (key.equals(cd.getKey())) {
                cd.getFieldConditionList().add(mongoFieldCondition);
                return;
            }
        }
        // 不存在则添加条件
        MongoCondition mongoCondition = new MongoCondition(key, mongoFieldCondition);
        list.add(mongoCondition);
    }
    
    
    protected ICondition getICondition(Annotation an, Field field) {
        String annoName = an.annotationType().getName();
        if (annoName.startsWith("com.wti.mongodb.dto.query.annotation")) {
            AnnotationAttributes annotationAttributes
                    = AnnotatedElementUtils.getMergedAnnotationAttributes(field, BaseCondition.class);
            if (null != annotationAttributes
                    && annotationAttributes.containsKey("makeCondition")) {
                Object makeConditionValue = annotationAttributes.get("makeCondition");
                if (makeConditionValue instanceof Class) {
                    Class clazz = (Class)makeConditionValue;
                    try {
                        Object makeContionInstance = clazz.newInstance();
                        if (makeContionInstance instanceof ICondition) {
                            return ((ICondition)makeContionInstance);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        return null;
                    }
                }
            }
        }
        return null;
    }
    

三、ES 操作

3.1 查询全部

@Resource
@Qualifier("esHighLevelClient")
private RestHighLevelClient client;
    /**
     * es 默认未节点
     */
    private static final String  INVENTORY =  "_inventory";
    /**
     * 使用.keyword 查询
     */
    private static final String KEYWORD = ".keyword";
    
    public void testSearchAll() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("address");
        // 指定类型
        searchRequest.types("_doc");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 搜索方式
        // matchAllQuery搜索全部
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        // 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","address","location","timestamp"},new String[]{});
        // 向搜索请求对象中设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索,向ES发起http请求
        SearchResponse searchResponse = client.search(searchRequest);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        long totalHits = hits.getTotalHits();
        // 得到匹配度高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit hit:searchHits){
            // 文档的主键
            String id = hit.getId();
            // 源文档内容
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            // 日期
            Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
            System.out.println(name);
            // 方式二:
            String sourceAsString = hit.getSourceAsString();
            T t = JSON.parseObject(sourceAsString,tClass );
        }
    }
    
}

3.2 精准匹配

TermQuery为精确查询,在搜索时会整体匹配关键字,不再将关键字分词

// dsl
{
    "query": {
    	"term": { // 查询的方式为 term 精确查询
    		"esName": "spring框架" // 查询的字段为 name 关键字是 spring
    	}
    }
}
// java API
// 搜索方式
// termQuery 精确查询
searchSourceBuilder.query(QueryBuilders.termQuery("esName", "spring框架"));
queryBuilder.termsQuery("key", obj1, obj2..)   //一次匹配多个值

3.3 全文检索 MatchQuery

MatchQuery 即全文检索,会对关键字进行分词后匹配词条。

query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用
逗号分隔也可以不用

operator:设置查询的结果取交集还是并集,并集用 or, 交集用 and

//DSL
{
    "query": {
        "match": {
            "description": {
                "query": "spring框架",
                "operator": "or"
            }
        }
    }
}
// JAVA API
// matchQuery全文检索
searchSourceBuilder.query(QueryBuilders.matchQuery("esName", "Spring框架"));
QueryBuilders.multiMatchQuery(value, key1, key2, key3);

3.4 多字段联合搜索 MultiQuery

MultiQuery可以通过 fields 属性来设置多个域联合查找。

// DLS
/**{
    "query": {
        "multi_match": {
            "query": "spring框架",
            "minimum_should_match": "80%",
            "fields": ["esName", "description"]
        }
    }
}*/
// JAVA API
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("spring框架", "esName", "address").minimumShouldMatch("80%"));
//注:“spring开发框架”会被分为三个词:spring、开发、框架
//设置"minimum_should_match": "80%"表示,三个词在文档的匹配占比为80%,即3*0.8=2.4,向下取整得2,表示至少有两个词在文档中要匹///配成功。
//提升boost:
//在多域联合查询的时候,可以通过 boost //来设置某个域在计算得分时候的比重,比重越高的域当他符合条件时计算的得分越高,相应的该记录也更靠前。通过在 fields //中给相应的字段用 ^权重倍数来实现。

// DSL
"fields": ["name^10", "address"]
// JAVA API
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring框架", "esName", "address").field("esName", 10)); // 设置 name 10倍权重

3.5 布尔查询 BoolQuery

布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来,有三个可选的参数:
must: 文档必须匹配must所包括的查询条件,相当于 “AND”
should: 文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
must_not: 文档不能匹配must_not所包括的该查询条件,相当于“NOT”


// DSL
/**{
    "query": {
        "bool": { // 布尔查询
            "must": [ // 查询条件 must 表示数组中的查询方式所规定的条件都必须满足
                {
                    "multi_match": {
                        "query": "spring框架",
                        "minimum_should_match": "50%",
                        "fields": [
                            "esName^10",
                            "description"
                        ]
                    }
                },
                {
                    "term": {
                        "type": "餐饮"
                    }
                }
            ]
        }
    }
}*/
// JAVA API
// 首先构造多关键字查询条件
MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架", "esName", "address").field("esName", 10);
// 然后构造精确匹配查询条件
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("type", "201002");
// 组合两个条件,组合方式为 must 全满足
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(matchQueryBuilder);
boolQueryBuilder.must(termQueryBuilder);
// 将查询条件封装给查询对象
searchSourceBuilder.query(boolQueryBuilder);

3.6 模糊匹配WildcardQuery

对于es关键字或单词的查询我们可以借助QueryBuilders.wildcardQuery方法来操作,只需要指定es中对应的列和要查询的内容即可:

boolQueryBuilder.must(QueryBuilders.wildcardQuery("name", "spring"+"*"));

3.7 范围匹配RangeQuery

boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));

3.8 分页查询

// 设置查询的起始位置,默认是0
sourceBuilder.from(0); 
// 设置查询结果的页大小,默认是10
sourceBuilder.size(10);

3.9 过滤

定义过滤器查询,是在原本查询结果的基础上对数据进行筛选,因此省略了重新计算的分的步骤,效率更高。并且方便缓存。推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用,过滤器在布尔查询中使用。

boolQueryBuilder.filter(QueryBuilders.termQuery("type", "餐饮"));

3.10 排序

SearchSourceBuilder允许增加一或多个排序参数SortBuilder,有四个具体实现FieldSortBuilder, ScoreSortBuilder, GeoDistanceSortBuilder 和 ScriptSortBuilder。
注:支持对 keyword、date、float 等类型添加排序,text类型的字段不允许排序

// 默认排序。根据_score倒序
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); 
// 根据_id升序
sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.ASC)); 
searchBuilder.sort("type", SortOrder.DESC);

3.11 高亮

 // 高亮查询
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.preTags(""); // 高亮前缀
    highlightBuilder.postTags(""); // 高亮后缀
    highlightBuilder.fields().add(new HighlightBuilder.Field("esName")); // 高亮字段
    // 添加高亮查询条件到搜索源
    searchSourceBuilder.highlighter(highlightBuilder);

3.12 扩展 boolQury 嵌套查询

BoolQueryBuilder bu= QueryBuilders.boolQuery();
BoolQueryBuilder builder = QueryBuilders.boolQuery();
for (Map<String, String> sitfTrack : sitfTracks) {
    builder.should(QueryBuilders.matchQuery("sourceCode",sitfTrack.get("interfaceCode")));
}
//注意这里
bu.must(builder);
bu.must(QueryBuilders.termQuery("batchNum.keyword", traceLog.getBatchNum()));

你可能感兴趣的:(数据库,elasticsearch,java)