我们真的需要链式查询吗?
Published on 2023-07-22 16:29 in 暂未分类 with 是奉壹呀

我们真的需要链式查询吗?

    多年后,面对Quicksql的时候,我将会想起刚参加工作面对mongodb复杂聚合查询的那个遥远下午。

    那是一种淡淡的忧伤,这么多年来,这种忧伤仅次于Neo4j那独树一帜的语法给我带来的惊()喜()。

    warning!
    warning!
    warning!






    以下是一个基于java api的管道聚合查询,满屏溢出的忧伤,逆流成河。

    List<AggregationOperation> operations = new ArrayList<>();
    
           Criteria criteriaGroup = new Criteria();
           criteriaGroup.andOperator(
                   Criteria.where("_id").is(userQuery.getId()),
                   Criteria.where("deleteFlag").is("N"));
           MatchOperation matchGroup = new MatchOperation(criteriaGroup);
    
           UnwindOperation unwindUser = new UnwindOperation(Fields.field("userIds"), true);
    
           Field fromEntityType = Fields.field("user");
           Field localFieldEntityType = Fields.field("userIds");
           Field foreignFieldEntityType = Fields.field("_id");
           Field asEntityType = Fields.field("user");
           LookupOperation lookUpUser = new LookupOperation(fromEntityType, localFieldEntityType,
                   foreignFieldEntityType, asEntityType);
    
           Fields projectUserFields = Fields.fields("user");
           ProjectionOperation projectGroup = new ProjectionOperation(projectUserFields);
           projectGroup.andExclude("_id");
    
           AggregationExpression userAggregationExpression = ArrayOperators.ArrayElemAt.arrayOf("user").elementAt(0);
           ReplaceRootOperation replaceRootUser = new ReplaceRootOperation(userAggregationExpression);
    
           SetOperation set = new SetOperation("deleteFlag", "");
    
           Criteria criteriaUser = new Criteria();
           criteriaUser.orOperator(
                   Criteria.where("userName").regex(".*" + userQuery.getKeyword() + ".*"),
                   Criteria.where("userDes").regex(".*" + userQuery.getKeyword() + ".*"));
           MatchOperation matchUser = new MatchOperation(criteriaUser);
    
           List<AggregationOperation> dataOperations = new ArrayList<>();
           SkipOperation skipOperation = new SkipOperation((userQuery.getPage() - 1) * userQuery.getSize());
           LimitOperation limitOperation = new LimitOperation(userQuery.getSize());
           dataOperations.add(skipOperation);
           dataOperations.add(limitOperation);
    
           CountOperation countOperation = new CountOperation("total");
    
           FacetOperation facet = new FacetOperation().and(countOperation).as("metadata")
                   .and(dataOperations.toArray(new AggregationOperation[dataOperations.size()])).as("records");
    
           AggregationExpression totalArray = ArrayOperators.ArrayElemAt.arrayOf("metadata").elementAt(0);
           ObjectOperators.MergeObjects om = ObjectOperators.valueOf(totalArray).mergeWith(ROOT);
           ReplaceRootOperation replaceRootTotal = new ReplaceRootOperation(om);
    
           operations.add(matchGroup);
           operations.add(unwindUser);
           operations.add(lookUpUser);
           operations.add(projectGroup);
           operations.add(replaceRootUser);
           operations.add(set);
           operations.add(matchUser);
           operations.add(facet);
           operations.add(replaceRootTotal);
    
           Aggregation aggregation = Aggregation.newAggregation(operations);
           AggregationResults<UserPageVO> results = mongoTemplate.aggregate(aggregation, "user_group",
                   UserPageVO.class);
           UserPageVO vo = results.getUniqueMappedResult();   
    

    或者ElasticSearch 的一个不算复杂的常规聚合查询

    SearchRequestBuilder searchRequestBuilder=getClient(deviceIndexName,esclient);
    BoolQueryBuilder query = QueryBuilders.boolQuery();
    		query.filter(QueryBuilders.rangeQuery("time").gte(startTimeMills).lte(endTimeMills));
    query.should(QueryBuilders.termsQuery("deviceId", deviceIdLst));
     
    TermsBuilder deviceAgg = AggregationBuilders.terms("deviceAgg").field("deviceId").size(1000);
    Map<String, Object> aggs = new HashMap<>();
    String aggStr = "{\"rated\":{\"top_hits\":{\"sort\":[{\"ip\":{\"order\":\"desc\"}},{\"time\":{\"order\":\"asc\"}}],\"size\":1}}}";
    aggs = JSON.parseObject(aggStr, new TypeReference<Map<String, Object>>(){});
    deviceAgg.subAggregation(aggs);
    CardinalityBuilder deviceIdCard = AggregationBuilders.cardinality("deviceCard").field("deviceId").precisionThreshold(100);
     
    SearchResponse response = searchRequestBuilder.setQuery(query).addAggregation(deviceIdCard).addAggregation(deviceAgg).setSize(1).execute().actionGet();
    Terms deviceAggRes = response.getAggregations().get("deviceAgg");
    List<Map<String, Object>> dataList = new ArrayList<>();
    for (Terms.Bucket deviceBuck : deviceAggRes.getBuckets()){
    	InternalTopHits rated = deviceBuck.getAggregations().get("rated");
    	SearchHit[] searchHits = rated.getHits().hits();
    	List<Map<String, Object>> hitDataList = Lambda.extract(searchHits, Lambda.on(SearchHit.class).getSource());
    	dataList.addAll(hitDataList);
    }
    

    那时我想的是,我会java,我还会sql,但为什么我不会es和mongodb数据库查询。

    这个(java),再加上这个(sql),能不能站着把钱挣了?

    这些复杂的API,我需要花时间去熟悉,好不容易用熟悉了常用的一些,然后,除非我一直用,不然过一个月,铁定又给忘了。

    ElasticSearch,mongodb不就是数据库吗?
    甭管你造些什么名词出来忽悠我,关系型数据库,非关系型数据库,nosql,new sql,它不都是数据库吗?

    这些年过去了,

    好消息是,mybatis本身已经支持mongodb,neo4j这些非传统关系型数据库了。
    有些个人开发者或者公司也在这些非关系型数据库的基础上,开发包装了一层通用的查询语言,大大降低了学习曲线和上手难度。

    坏消息是,mybatis-plus好像又走上了monogodb-java-driver各种API点点点的复读机老路了。

    我能理解SQL是Structured,而es,mongo已经不是Structured的数据库了,但面对传统关系型数据库,mybatis-plus为什么要反其道而行之呢?

    企业微信截图_1690009257199.png

    我需要去熟悉这些API,一个稍微复杂的查询点点点出来一串链式的代码堆在那里。

    企业微信截图_16900098594906.png

    我并不觉得这是一件优雅的事情。

    同样的,在大数据领域里,面对的数据库挑战并不比web方向更少。
    相关的数据库更加的繁多。

    但是spark/flink保证了统一的操作方式。

    比如,要实现一个经典的topN,使用flink DataStream API的方式,非常复杂,需要熟悉事件驱动和诸多API。

    以前写过demo,但没找到,网上随便找了一个,大家随意感受一下代码量,以及上手难度。

    https://blog.csdn.net/zhungcan/article/details/116271588

    而使用table api 即flink sql呢?
    刚好手边有个以前写的demo,不涉及业务。

    一个简单的全局topN场景

     String sql = " select * from ( " +
                    "select *, ROW_NUMBER() over ( partition by key order by price desc  ) as rownum " +
                    "from test " +
                    ") where rownum <= 3";
    

    这种一般用得少,多作用于下面这种实时窗口上

     String sqlWindowTopN =
                    "select * from (" +
                    "  select *, " +
                    "   ROW_NUMBER() over (partition by window_start, window_end order by total desc ) as rownum " +
                    "     from (" +
                    "       select key,window_start,window_end,count(key) as `count`,sum(price) total from table (" +
                    "           HOP(TABLE test, DESCRIPTOR(`time`), interval '1' minute, interval '3' minutes)" +
                    "        ) group by window_start, window_end, key" +
                    "   )" +
                    ") where rownum <= 3";
    
            st.executeSql(sqlWindowTopN).print();
    

    哪怕你不是java程序员,哪怕你对flink乃至于大数据一窍不通,但只要你懂sql,你基本就能看懂上面代码的意图,上手难度大大降低。

    大数据的趋势是最大程度对新手甚至外行友好。力求零成本上手。
    数据是流动的,没有流和批的区别,更没有高低贵贱的鄙视链,只区分有界与无界。
    数据从一个地方流到另一个地方,各种分析处理,都能通过sql来直接解决。

    企业微信截图_16900126613156.png

    https://juejin.cn/post/7231567262448746551

    不管大数据还是web方向,我觉得不管什么数据库,甭造些名词来唬我,只要是存数据的地方,就应该用通用的查询语言来操作,不要搞些生僻的API在那里点点点,什么都要点点点只会害了你。

    本文无过激字眼,不引战。彼之砒霜,吾之蜜糖。彼之蜜糖,吾之砒霜。理性交流。

    posted @   是奉壹呀  阅读(305)  评论(3编辑  收藏  举报
    相关博文:
    阅读排行:
    · 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
    · 单线程的Redis速度为什么快?
    · 展开说说关于C#中ORM框架的用法!
    · SQL Server 2025 AI相关能力初探
    · Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
    点击右上角即可分享
    微信分享提示