首页 > 其他分享 >双数据源混排

双数据源混排

时间:2024-11-08 14:22:26浏览次数:1  
标签:return pageSize 数据源 param pageParam 混排 final result

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;

/**
 * @author lanan
 */
public class PageQueryHelper <R, U> {

    private static final int MAX_QUERY_TIMES = 10;

    private static final int MAX_SORT_DATA_SIZE = 120;

    private final Function<PageQueryParam, Pair<Integer, List<R>>> aQuery;

    private final Function<PageQueryParam, Pair<Integer, List<R>>> bQuery;

    private final Function<R, U> compareFunction;

    private final Comparator<U> comparator;

    public PageQueryHelper(
            Function<PageQueryParam, Pair<Integer, List<R>>> aQuery,
            Function<PageQueryParam, Pair<Integer, List<R>>> bQuery,
            Function<R, U> compareFunction,
            final Comparator<U> comparator) {
        this.aQuery = aQuery;
        this.bQuery = bQuery;
        this.compareFunction = compareFunction;
        this.comparator = comparator;
    }


    /**
     * 双数据源混合查询
     * @param globalPageParam
     * @return
     */
    @SuppressWarnings({"PMD.NcssCount", "java:S3776"})
    public Pair<Integer, List<R>> dualDataSourcePageQuery(PageQueryParam globalPageParam) {
        if (globalPageParam == null) {
            return Pair.of(0, Collections.emptyList());
        }

        //分别取第一页数据,并将总页数一起取到
        final PageQueryParam aPageParam = initPageParam(globalPageParam);
        final PageQueryParam bPageParam = initPageParam(globalPageParam);
        final Pair<Integer, List<R>> aStartPage = aQuery.apply(aPageParam);
        final Pair<Integer, List<R>> bStartPage = bQuery.apply(bPageParam);
        final Integer aTotal = aStartPage.getLeft();
        final Integer bTotal = bStartPage.getLeft();

        final Integer globalStart = globalPageParam.getStart();
        //两个数据源的总数据量小于start
        final int total = aTotal + bTotal;
        if (total <= globalStart) {
            return Pair.of(total, Collections.emptyList());
        }

        //定义双数据源的取值范围(globalStart可能存在的数据范围)
        int aRight = Math.min(Math.max(0, aTotal - 1), globalStart);
        int bRight = Math.min(Math.max(0, bTotal - 1 ), globalStart);
        int aLeft = Math.max(globalStart - bTotal, 0);
        int bLeft = Math.max(globalStart - aTotal, 0);
        final Integer pageSize = globalPageParam.getNum();

        final List<R> aLeftPage = aLeft == 0 ? aStartPage.getRight() : queryA(aPageParam, aLeft, Math.min(pageSize, aTotal - aLeft));
        final List<R> bLeftPage = bLeft == 0 ? bStartPage.getRight() : queryB(bPageParam, bLeft, Math.min(pageSize, bTotal - bLeft));
        //优先处理这种情况:globalStart之前全是数据源a的数据或全是数据源b的数据
        final List<R> rs = returnIfSorted(aLeftPage, bLeftPage, aRight, bRight, aPageParam, bPageParam, globalPageParam);
        if (CollectionUtils.isNotEmpty(rs)) {
            return Pair.of(total, rs);
        }
        int times = 0;
        while (aRight > aLeft && ++times < MAX_QUERY_TIMES) {
            int aMid = (aRight - aLeft) / 2 + aLeft;
            int bMid = globalStart - aMid;
            // 如果 aRight - aLeft <= pageSize,则page页数据的取值访问为:
            // A[aLeft - pageSize, aRight + pageSize] + B[bMid - pageSize, bMid + 2 * pageSize]
            // 直接取到这个区间的数据,进行排序后,返回globalStart后的数据即可
            if (aRight - aLeft <= pageSize || bRight - bLeft <= pageSize) {
                if (aRight - aLeft <= pageSize) {
                    final List<R> result = new ArrayList<>(MAX_SORT_DATA_SIZE);
                    result.addAll(queryA(aPageParam, aLeft - pageSize, aRight - aLeft + 2 * pageSize));
                    result.addAll(queryB(bPageParam, bMid - pageSize,  3 * pageSize));
                    sort(result);
                    int skip = globalStart - Math.max(aLeft - pageSize, 0) - Math.max(bMid - pageSize, 0);
                    return Pair.of(total, result.subList(skip, Math.min(skip + pageSize, result.size())));
                } else {
                    final List<R> result = new ArrayList<>(MAX_SORT_DATA_SIZE);
                    result.addAll(queryB(bPageParam, bLeft - pageSize, bRight - bLeft + 2 * pageSize));
                    result.addAll(queryA(aPageParam, aMid - pageSize,   3 * pageSize));
                    sort(result);
                    int skip = globalStart - Math.max(bLeft - pageSize, 0) - Math.max(aMid - pageSize, 0);
                    return Pair.of(total, result.subList(skip, Math.min(skip + pageSize, result.size())));
                }
            }
            final List<R> aPage = queryA(aPageParam, aMid, pageSize);
            final List<R> bPage = queryB(bPageParam, bMid, pageSize);
            R aMin = aPage.get(0);
            R aMax = aPage.get(aPage.size() - 1);
            R bMin = bPage.get(0);
            R bMax = bPage.get(bPage.size() - 1);
            if (compare(aMin, bMax) > 0) {
                aRight = aMid - 1;
                bLeft = bMid;
            } else if (compare(bMin, aMax) > 0) {
                aLeft = aMid + 1;
                bRight = bMid;
            } else {
                //定位到了全局排序值大概位置
                //此时第page页的数据范围:
                //A[aMid - pageSize, aMid + pageSize]
                //B[bMid - pageSize, bMid + pageSize]
                //全部取回后排序,返回globalStart之后的数据即可
                final List<R> result = new ArrayList<>(MAX_SORT_DATA_SIZE);
                result.addAll(aPage);
                result.addAll(bPage);
                result.addAll(queryA(aPageParam, Math.max(aMid - pageSize, 0), Math.min(aMid, pageSize)));
                result.addAll(queryB(bPageParam, Math.max(bMid - pageSize, 0), Math.min(bMid, pageSize)));
                sort(result);
                int skip = globalStart - Math.max(aMid - pageSize, 0) - (Math.max(bMid - pageSize, 0));
                return Pair.of(total, result.subList(skip, pageSize + skip));
            }
        }

        LOG.warn("双数据混合查询异常");
        return Pair.of(total, Collections.emptyList());
    }

    /**
     * 特殊情况的处理,page页之前全是A或者全是B的数据,那么此时可直接返回需要的数据
     * aLeft + b
     * @param aLeftPage
     * @param bLeftPage
     * @param aRight
     * @param bRight
     * @param aPageParam
     * @param bPageParam
     * @param globalPageParam
     * @return
     */
    private List<R> returnIfSorted(List<R> aLeftPage, List<R> bLeftPage,
                                   int aRight, int bRight, PageQueryParam aPageParam, PageQueryParam bPageParam,
                                   PageQueryParam globalPageParam) {
        int pageSize = globalPageParam.getNum();
        //优先处理这种情况:globalStart之前全是数据源a的数据或全是数据源b的数据
        final List<R> bRightPage = queryB(bPageParam, bRight, pageSize);
        if (CollectionUtils.isEmpty(aLeftPage)) {
            return bRightPage;
        }
        int globalStart = globalPageParam.getStart();
        if (CollectionUtils.isEmpty(bRightPage) || compare(aLeftPage.get(0), bRightPage.get(0)) >= 0) {
            //bRight < globalStart,说明此时 bRightPage取的是 B数据源的最后一条数据,且该数据小于等于aLeftPage[0]
            if (bRight < globalStart) {
                return aLeftPage;
            } else {
                //bRightPage[0]之前的数据全小于aLeftPage[0],此时第page页的数据在bRightPage+aLeftPage中
                final List<R> result = new ArrayList<>(aLeftPage.size() + bRightPage.size());
                result.addAll(aLeftPage);
                result.addAll(bRightPage);
                sort(result);
                return result.subList(0, Math.min(result.size(), pageSize));
            }
        }
        final List<R> aRightPage = queryA(aPageParam, aRight, pageSize);
        if (CollectionUtils.isEmpty(bLeftPage)) {
            return aRightPage;
        }
        if (CollectionUtils.isEmpty(aRightPage) || compare(bLeftPage.get(0), aRightPage.get(0)) >= 0) {
            if (aRight <  globalStart) {
                return bLeftPage;
            } else {
                final List<R> result = new ArrayList<>(aRightPage.size() + bLeftPage.size());
                result.addAll(aRightPage);
                result.addAll(bLeftPage);
                sort(result);
                return result.subList(0, Math.min(result.size(), pageSize));
            }
        }
        return Collections.emptyList();
    }

    /**
     * 比较两个数据
     * @param o1
     * @param o2
     * @return
     */
    private int compare(R o1, R o2) {
        final U aCompareValue = compareFunction.apply(o1);
        final U bCompareValue = compareFunction.apply(o2);
        return comparator.compare(aCompareValue, bCompareValue);
    }

    /**
     * 根据指定字段进行排序
     * */
    private void sort(List<R> list) {
        Collections.sort(list, this::compare);
    }

    /**
     * 生成查询条件
     * @param globalPageParam
     * @return
     */
    private PageQueryParam initPageParam(PageQueryParam globalPageParam) {
        final PageQueryParam pageParam = new PageQueryParam();
        pageParam.setUserId(globalPageParam.getUserId());
        pageParam.setKeyword(globalPageParam.getKeyword());
        pageParam.setStart(0);
        pageParam.setNum(globalPageParam.getNum());
        return pageParam;
    }

    /**
     * 从A数据源查询数据
     * @param pageParam 减少PageQueryParam对象创建数
     * @param start 起始范围
     * @param num 获取数量
     * @return
     */
    private List<R> queryA(PageQueryParam pageParam, int start, int num) {
        pageParam.setStart(start);
        pageParam.setNum(num);
        return queryList(aQuery, pageParam);
    }

    /**
     * 从B数据源查询数据
     * @param pageParam 减少PageQueryParam对象创建数
     * @param start 起始范围
     * @param num 获取数量
     * @return
     */
    private List<R> queryB(PageQueryParam pageParam, int start, int num) {
        pageParam.setStart(start);
        pageParam.setNum(num);
        return queryList(bQuery, pageParam);
    }

    /**
     * 从数据源获取数据
     * @param query
     * @param pageParam
     * @return
     */
    private List<R> queryList(Function<PageQueryParam, Pair<Integer, List<R>>> query, PageQueryParam pageParam) {
        if (pageParam.getStart() < 0) {
            pageParam.setStart(0);
        }
        final Pair<Integer, List<R>> pair = query.apply(pageParam);
        if (pair == null || CollectionUtils.isEmpty(pair.getRight())) {
            return Collections.emptyList();
        }
        return pair.getRight();
    }
}

标签:return,pageSize,数据源,param,pageParam,混排,final,result
From: https://www.cnblogs.com/lanan-blog/p/18534978

相关文章

  • Spring Boot 多数据源配置实战指南:从入门到精通
    引言在现代企业级应用开发中,往往需要同时访问多个数据库来满足不同的业务需求。SpringBoot作为一个快速开发的框架,提供了非常便捷的方式来配置和管理多数据源。本文将详细介绍如何在SpringBoot项目中配置多数据源,并通过实际代码示例展示其应用场景。一、为什么要配置多......
  • 如何在DataGrip上使用hive的数据源编写Spark代码
    Spark是当今大数据领域最活跃、最热门、最高效的大数据通用计算平台之一。Spark作为一个计算平台并不是作为一个数据库不像hive以及mysql一样可以直接远程连接DataGrip使用,本篇就展示如何使用DataGrip使用hive作为数据源编写Spark代码查询hive数据库首先确保你的hive以及Spa......
  • 实时数仓及olap可视化构建(基于mysql,将maxwell改成seatunnel可以快速达成异构数据源实
    1.OLAP可视化实现(需要提前整合版本)Linux121Linux122Linux123jupyter✔spark✔✔✔python3+SuperSet3.0✔hive✔ClinckHouse✔Kafka✔✔✔Phoenix✔DataX✔maxwell✔Hadoop✔✔✔MySQL✔......
  • @DS注解方式springboot多数据源配置及失效场景解决
    1.使用教程导入依赖<!--多数据源--> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.0</version> </dependency>123456配置数据源......
  • 以外部表 (External Table) 的形式,接入其他数据源
    外部表|StarRockshttps://docs.starrocks.io/zh/docs/data_source/External_table/外部表StarRocks支持以外部表(ExternalTable)的形式,接入其他数据源。外部表指的是保存在其他数据源中的数据表,而StartRocks只保存表对应的元数据,并直接向外部表所在数据源发起查询。目......
  • SpringBoot3.0整合Mybatis-plus实现多数据源(重构类方式)
    背景前段时间在做一个数据中台的项目,系统用到了不同数据库中的数据。自己又不想手写JDBC连接,既然我有这个需求,那功能应该有人实现了,于是开始了网上搜了,搜索后发现基本都是讲读写分离、主备切换的,后面也查略了Mybatis-plus的官网,里面有这个功能,但好像是我组件的版本之间不兼容,导致......
  • FPGA图像处理仿真:生成数据源的方法
    免责声明:本文所提供的信息和内容仅供参考。作者对本文内容的准确性、完整性、及时性或适用性不作任何明示或暗示的保证。在任何情况下,作者不对因使用本文内容而导致的任何直接或间接损失承担责任,包括但不限于数据丢失、业务中断或其他经济损失。读者在使用本文信息时,应自行验......
  • 04 springboot-工程搭建案例(多环境部署,数据源, Swagger, 国际化,工具类)
    项目搭建模板(多环境切换)springboot系列,最近持续更新中,如需要请关注如果你觉得我分享的内容或者我的努力对你有帮助,或者你只是想表达对我的支持和鼓励,请考虑给我点赞、评论、收藏。您的鼓励是我前进的动力,让我感到非常感激。文章目录1项目截图2pom.xml3多环境配......
  • egret图文混排
    classTextImageextendseui.Group{publictemplet:egret.TextFieldconstructor(protected_TextImageData:Array<any>=[]){super();this.init()}setTextImageData(val:Array<any>){this._Te......
  • spring整合整合Druid数据源连接池
    1.普通的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接......