当前位置:   article > 正文

【java】仿级联查询 | Java通过DSL字符串查询ES (es8 dsl java)_java dsl

java dsl

一、需求

1、实现一个搜索框,可以实现所有的字段的查询,并且每个字段可以支持不同的查询方式
如下图所示
在这里插入图片描述
2、点击任何一个字段,弹出该字段支持的查询方式
在这里插入图片描述

二、设计

1、背景

由于项目是前后端分离项目,这里就需要前端的配合
由于我们的数据库选择是 mysql+es
今天这篇文章主要是使用es进行业务查询

2、思路
  • 如何获取该表的所有列字段?
  • 如果根据字段类型给与对应的查询条件?

这里我是使用 Java开发的,所以第一点直接想到的就是使用反射

实现思路

  • 首先需要将所有列表,创建对应的实体类(即:每一列对应实体每个字段)
  • 然后前端传入具体为哪一个类的类型,通过反射获取其所有字段名
  • 然后我们这里会根据字段的数据类型,决定使用的判断方式(即:string:使用 “=”, “!=”, “:”, “!:” )
  • 最后组装数据返回前端
  • 前端使用 DSL 表达式 ,拼接 DSL字符串回传java进行查询
  • Java根据DSL表达式查询 并返回结果

三、Java代码实现

这里定义了2种判断条件

  • STRING = {“=”, “!=”, “:”, “!:”};
  • OTHER = {“=”, “>”, “<”, “!=”};
@Slf4j
@Service("AwsScanService")
public class AwsScanServiceImpl implements AwsScanService {
    // string 类型下
    private static final String[] STRING = {"=", "!=", ":", "!:"};
    // 非 string 类型下
    private static final String[] OTHER = {"=", ">", "<", "!="};

    @Override
    public R getScanSearchFields(ScanBasicVo index) {
        Class cls = getObject(index.getServiceId());
        log.info("获取当前对象的类型为:" + cls);
        Field[] fields = cls.getDeclaredFields();
        Map<String, Object> res = new HashMap<>();
        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            f.setAccessible(true);
            try {
                //f.getName()得到对应字段的属性名,f.get(o)得到对应字段属性值,f.getGenericType()得到对应字段的类型
                log.info("属性名:" + f.getName() + ";字段类型:" + f.getGenericType());
                if ("class java.lang.String".equals(f.getGenericType())) {
                    res.put(f.getName(), STRING);
                }
                res.put(f.getName(), OTHER);

            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                log.info("ReflectUtil error:" + e.toString());
            }
        }

        return R.ok().put("data", res);
    }


    private Class getObject(int serviceId) {
        switch (serviceId) {
            case 1:
                return AwsDynamodbVo.class;
            case 2:
                return AwsEc2CompareVo.class;
            case 3:
                return AwsEc2Vo.class;
            case 4:
                return AwsElasticacheVo.class;
            case 5:
                return AwsIAMVo.class;
            case 6:
                return AwsOpensearchVo.class;
            case 7:
                return AwsRdsVo.class;
            case 8:
                return AwsRiVo.class;
            case 9:
                return AwsS3Vo.class;
            case 10:
                return AwsVpcVo.class;
        }
        return null;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

具体的对应的每个vo就不在这里一一展示了
下面来看一下最后的执行返回结果

{
    "msg": "success",
    "code": 20000,
    "data": {
        "dyTableName": [
            "=",
            ">",
            "<",
            "!="
        ],
        "tableStatus": [
            "=",
            ">",
            "<",
            "!="
        ],
        "tableSizeBytes": [
            "=",
            ">",
            "<",
            "!="
        ],
        "itemCount": [
            "=",
            ">",
            "<",
            "!="
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

四、DSL表达式

# 等于
GET /aws_1_1_ec2/_search
{
  "query": {
    "match": {
      "instanceName": "unipus-ucont-apiworker102"
    }
  }
}

# 不等于
GET /aws_1_1_ec2/_search
{
  "query": {
    "bool": {
      "must_not": {
        "term": {
          "instanceName": "unipus-ucont-apiworker102"
        }
      }
    }
  }
}

# 模糊查询
GET /aws_1_1_ec2/_search
{
  "query": {
    "wildcard": {
      "instanceName":{
				"value":"*ucont*"
			}
    }
  }
}

# 不包含
GET aws_1_1_ec2/_search
{
   "query":{ 
     "bool" : {
       "must_not" : {
         "wildcard" : {
           "type" : "*c4*"
         }
       }
     }
   } 
}

# 范围内查询
GET /aws_1_1_ec2/_search
{
  "query": {
    "bool": {
      "must": {
        "range": {
          "cpuMax": {
            "gte": 15,
            "lte": 18
          }
        }
      }
    }
  }
}

# 多关系查询
GET aws_1_1_ec2/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "wildcard": {
            "type": "*c5*"
          }
        },
        {
          "bool": {
            "must_not": [
              {
                "wildcard": {
                  "vpcName": {
                    "value": "*Test*"
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

五、前端拼接 DSL 字符串

这里需要注意,java接收的dsl字符串不能包含最外层的 「query」关键字
比如上面的DSL查询转化为字符串
"{ “match”:{ “platform”:“windows” } } "

这里目前还没有给前端找到更好的插件,也欢迎在前端拼接过DSL的朋友留言!!!

六、Java使用ES查询DSL字符串

查询dsl字符串查询es的方法

 @PostMapping("/dsl/query")
    public PageUtils queryDslToEs(@RequestBody ScanCommonVo scanCommonVo) throws IOException {
        String dsl = scanCommonVo.getDsl();
        String index = scanCommonVo.getIndex();
        //分页
        long curPage = scanCommonVo.getPage() == 0 ? 1 : scanCommonVo.getPage();
        long limit = scanCommonVo.getLimit() == 0 ? 10 : scanCommonVo.getLimit();
        // 返回结果
        List<Object> arrayRes = new ArrayList<>();
//        String dsl = "{ \"match\":{ \"platform\":\"windows\" } } ";

        // 判断索引是否存在
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        try {
            // 如果不存在则返回
            if (!restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT)) {
                return new PageUtils(arrayRes, 0, (int) limit, (int) curPage);
            }

            // 如果存在则继续查询 dsl
            WrapperQueryBuilder queryBuilder = new WrapperQueryBuilder(dsl);
            SearchRequest searchRequest = new SearchRequest();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            // 设置分页
            searchSourceBuilder.query(queryBuilder).from((int)curPage).size((int)limit);
            searchRequest.source(searchSourceBuilder).indices(index);
            SearchResponse searchResp = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 计算返回的条数
            CountRequest countRequest = new CountRequest(index);
            countRequest.query(queryBuilder);
            long count = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT).getCount();

            SearchHit[] searchHitArr = searchResp.getHits().getHits();
            for (SearchHit searchHit : searchHitArr) {
                Map<String, Object> temp = searchHit.getSourceAsMap();
                // es自动生成的主键,然后插入现有的结果集中
//                temp.put("id", searchHit.getId());
                arrayRes.add(temp);
            }
            return new PageUtils(arrayRes, (int) count, (int) limit, (int) curPage);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RRException(BizCodeEnum.SEARCH_OPENSEARCH_INDEX_ERROR.getMessage(), BizCodeEnum.SEARCH_OPENSEARCH_INDEX_ERROR.getCode());
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

传入的vo

@Data
public class ScanCommonVo extends BasicVo  {
    @NotBlank(message = "dsl 表达式不能为空")
    private String dsl;

    @NotBlank(message = "索引")
    private String index;
}

@Data
@ApiModel(description = " aws 基类")
public class BasicVo {
    /**
     * page 当前页索引
     */
    @ApiModelProperty(value = "当前页索引")
    private Long page;

    /**
     * 每页大小
     */
    @ApiModelProperty(value = "每页大小")
    private Long limit;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

七、通过 Apifox 测试

关键词查询与分页查询都ok
在这里插入图片描述

八、前端展示效果

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/68160
推荐阅读
相关标签
  

闽ICP备14008679号