Elasticsearch使用小结

前言

ES大家可能都不陌生,它是一个高效的搜索引擎,可以帮助我们快速存储、搜索大量数据。

我工作中负责的模块之一,是一个核心存储服务,我们业务的数据通过这个服务存储到MySQL中,并向内部系统提供查询能力。我们主要数据被拆分成了两张表,因此面对各种各样的查询需求,如果只依赖MySQL本身的查询能力查询,会遇到几个问题:
1.如果需要查询的字段或者查询条件分别在两张表中,那么就需要联表查询。我们一般是不推荐join查询的。因为join的效率较低,如果数据量大的话,查询速度很难保证。

2.如果为了加快查询速度,最好对需要查询的字段建索引。那么作为底层存储服务,面对的查询需求是各种各样的,极端点的可能就是每个字段都可能需要被查询,如果对每个字段建立索引,会增加索引变更的维护成本,并且更多的索引需要占用的空间也更多,因此一般是不推荐表中索引列太多的。即时是只对高频查询字段建立索引,索引列的量仍然不少。

因此我们团队的解决方案是在ES中维护一份与数据库中一致的数据,存储的内容就是各种查询需求需要的字段和我们的唯一业务ID,即订单号。查询时先在ES中查出所有符合条件的订单号,再根据订单号从数据库的两张表中分别查出需要的数据返回。

实例

下面总结一些工作中遇到的比较棘手的查询需求

例1

有两个时间字段,order_datetime、order_create_time,以yyyy-MM-dd HH:mm:ss格式存储,需要查出所有符合order_datetime比order_create_time大30min的订单

解决方案:用脚本查询

Java实现如下:

        BoolQueryBuilder queryRequest = new BoolQueryBuilder();
        String scriptStr = "(doc['order_datetime'].value.getMillis() - doc['order_create_time'].value.getMillis())/(3600000.0/60) > 30"
        Map<String, Object> params = new HashMap<>();
        Script script = new Script(ScriptType.INLINE, "painless", scriptStr, params);
        ScriptQueryBuilder scriptQueryBuilder = new ScriptQueryBuilder(script);
        queryRequest.must(scriptQueryBuilder);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(queryRequest);
        searchSourceBuilder.fetchSource(false);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.source(searchSourceBuilder);
        searchRequest.indices(Constants.ES_ORDER_INDEX);

        searchResponse = restHighLevelClient.search(searchRequest);

DSL:

{
    "from": 0,
    "size": 10,
    "query": {
        "bool": {
            "must": {
                "script": {
                    "script": {
                        "source": "(doc['order_datetime'].value.getMillis() - doc['create_time'].value.getMillis())/(3600000.0/60) >= 30",
                        "lang": "painless"
                    },
                    "boost": 1.0
                }
            }
        }
    },
    "_source": false
}

例2

某个字段price_plan有几种可能的值:1,2,3,4或null,为null的含义与为1相同。查询时的入参为多个值,如[2,3]表示查询出所有price_plan = 2或price_plan=3的数据,[1,4]表示查出所有price_plan = 1或price_plan=4或price_plan为空的数据。null的含义与为1相同是带来麻烦的核心点。

Java:

        BoolQueryBuilder queryRequest = new BoolQueryBuilder();
        if (!CollectionUtils.isEmpty(request.getPricePlan())) {
            BoolQueryBuilder queryShould = new BoolQueryBuilder();
            queryShould.should(new TermsQueryBuilder("price_plan", request.getPricePlan()));

            // 如果查询1,则应该把null也查出来
            if (request.getPricePlan().contains(1)){
                BoolQueryBuilder queryExist = new BoolQueryBuilder();
                ExistsQueryBuilder existsQueryBuilder = QueryBuilders.existsQuery("price_plan");
                queryExist.mustNot(existsQueryBuilder);
                queryShould.should(queryExist);
            }

            queryRequest.must(queryShould);
        }

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(queryRequest);
        searchSourceBuilder.fetchSource(false);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.source(searchSourceBuilder);
        searchRequest.indices(Constants.ES_ORDER_INDEX);

        searchResponse = restHighLevelClient.search(searchRequest);

DSL:

{
    "from": 0,
    "size": 10,
    "query": {
        "bool": {
            "must": [{
                "terms": {
                    "order_status": [0],
                    "boost": 1.0
                }
            }, {
                "bool": {
                    "should": [{
                        "terms": {
                            "price_plan": [1, 3, 2],
                            "boost": 1.0
                        }
                    }, {
                        "bool": {
                            "must_not": [{
                                "exists": {
                                    "field": "price_plan",
                                    "boost": 1.0
                                }
                            }],
                            "adjust_pure_negative": true,
                            "boost": 1.0
                        }
                    }]
                }
            }]
        }
    },
    "_source": false
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 这一篇最主要是记录下命令,方便以后查找 使用Mysql 创建数据库 create database mysql_t...
    Treehl阅读 604评论 0 0
  • # MongoDB ## 数据库分类 ### 关系型数据库 * 具备ACID特性 * Atomic原子性,也就...
    奥利奥_4e9e阅读 608评论 0 0
  • MySQL的基本操作可以包括两个方面:MySQL常用语句如高频率使用的增删改查(CRUD)语句和MySQL高级功能...
    wwmin_阅读 1,138评论 0 54
  • 1、dede_addonarticle:附加文章表 索引:PRIMARY KEY('aid'),KEY'typei...
    渝娃阅读 2,219评论 0 1
  • SQL学习 法则1:col table表/columns列/rows行 问题:movies表有100万数据? 法则...
    jessica涯阅读 561评论 0 1