双数据源混排

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();
    }
}
posted @ 2024-11-08 14:25  穷凶极恶哆啦A梦  阅读(3)  评论(0)    收藏  举报