需求

匹配分、热度分归一化
排序:匹配分 * 0.8 + 热度分 * 0.2

实体

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class ProductNewSearchInfo {

    /**
     * 产品唯一CODE
     */
    private String productCode;

    /**
     * ES分(匹配分)
     */
    @JSONField(serialize = false)
    private Float esScore;

    /**
     * 产品分(热门分)
     */
    private Float productScore;

    /**
     * 归一化排序分
     */
    @JSONField(serialize = false)
    private Double sigmoidSortScore;

    /**
     * 归一化排序分详情
     */
    @JSONField(serialize = false)
    private String sigmoidScoreDetail;

}

归一化排序

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 线性函数归一化
 * x = x - min / max - min
 *
 * @author jason
 */
@Slf4j
public class EsInfoSort {

    public static void main(String[] args) {
        List<ProductNewSearchInfo> eSearchInfoList = CollectionUtil.newArrayList(
                new ProductNewSearchInfo().setProductCode("4352").setEsScore(31.5223345F).setProductScore(5.54F),
                new ProductNewSearchInfo().setProductCode("4353").setEsScore(33.2587443F).setProductScore(8.24F),
                new ProductNewSearchInfo().setProductCode("4354").setEsScore(32.2387447F).setProductScore(2.34F),
                new ProductNewSearchInfo().setProductCode("4355").setEsScore(35.2323348F).setProductScore(6.54F),
                new ProductNewSearchInfo().setProductCode("4356").setEsScore(80.8578587F).setProductScore(3.74F),
                new ProductNewSearchInfo().setProductCode("4358").setEsScore(70.8578587F),
                new ProductNewSearchInfo().setProductCode("4359").setProductScore(9.2345335F),

                new ProductNewSearchInfo().setProductCode("4360"),
                new ProductNewSearchInfo().setProductCode("4361"),
                new ProductNewSearchInfo().setProductCode("4362")
        );

        eSearchInfoList = new EsInfoSort().sortBySigmoidScore(eSearchInfoList);

        log.info("归一化排序后:{}", JSONUtil.formatJsonStr(JSONUtil.toJsonStr(eSearchInfoList)));
    }

    /**
     * 归一化排序
     */
    private List<ProductNewSearchInfo> sortBySigmoidScore(List<ProductNewSearchInfo> eSearchInfoList) {
        // 匹配分
        Float esMax = eSearchInfoList.stream()
                .filter(o -> ObjectUtil.isNotNull(o.getEsScore()))
                .max(Comparator.comparing(ProductNewSearchInfo::getEsScore))
                .orElse(new ProductNewSearchInfo().setEsScore(0F))
                .getEsScore();
        Float esMin = eSearchInfoList.stream()
                .filter(o -> ObjectUtil.isNotNull(o.getEsScore()))
                .min(Comparator.comparing(ProductNewSearchInfo::getEsScore))
                .orElse(new ProductNewSearchInfo().setEsScore(0F))
                .getEsScore();
        float esMaxSubMin = esMax - esMin;

        // 热度分
        Float productMax = eSearchInfoList.stream()
                .filter(o -> ObjectUtil.isNotNull(o.getProductScore()))
                .max(Comparator.comparing(ProductNewSearchInfo::getProductScore))
                .orElse(new ProductNewSearchInfo().setProductScore(0F))
                .getProductScore();
        Float productMin = eSearchInfoList.stream()
                .filter(o -> ObjectUtil.isNotNull(o.getProductScore()))
                .min(Comparator.comparing(ProductNewSearchInfo::getProductScore))
                .orElse(new ProductNewSearchInfo().setProductScore(0F))
                .getProductScore();
        float productMaxSubMin = productMax - productMin;

        // 排序分
        eSearchInfoList
                .forEach(item -> {
                    Float esScore = Optional.ofNullable(item.getEsScore()).orElse(0F);
                    Float productScore = Optional.ofNullable(item.getProductScore()).orElse(0F);

                    float esSigmoidScore = (esScore - esMin) / esMaxSubMin;
                    float productSigmoidScore = (productScore - productMin) / productMaxSubMin;
                    if (Float.isNaN(esSigmoidScore)) {
                        esSigmoidScore = 0F;
                    }
                    if (Float.isNaN(productSigmoidScore)) {
                        productSigmoidScore = 0F;
                    }

                    item.setSigmoidScoreDetail(StrUtil.format("匹配分: {}, 热度分: {}", esSigmoidScore, productSigmoidScore));

                    item.setSigmoidSortScore((esSigmoidScore * 0.8) + (productSigmoidScore * 0.2));
                });

        return eSearchInfoList.stream()
                .filter(o -> ObjectUtil.isNotNull(o.getSigmoidSortScore()))
                .sorted(Comparator.comparing(ProductNewSearchInfo::getSigmoidSortScore).reversed())
                .collect(Collectors.toList());
    }

}

排序结果

[
    {
        "productCode": "4356",
        "esScore": 80.85786,
        "productScore": 3.74,
        "sigmoidSortScore": 0.8406118899583817,
        "sigmoidScoreDetail": "匹配分: 1.0, 热度分: 0.20305945"
    },
    {
        "productCode": "4358",
        "esScore": 70.85786,
        "sigmoidSortScore": 0.5699651718139649,
        "sigmoidScoreDetail": "匹配分: 0.7973063, 热度分: -0.33939934"
    },
    {
        "productCode": "4353",
        "esScore": 33.258743,
        "productScore": 8.24,
        "sigmoidSortScore": 0.19930680990219116,
        "sigmoidScoreDetail": "匹配分: 0.035195902, 热度分: 0.85575044"
    },
    {
        "productCode": "4355",
        "esScore": 35.232334,
        "productScore": 6.54,
        "sigmoidSortScore": 0.18199512958526612,
        "sigmoidScoreDetail": "匹配分: 0.07519935, 热度分: 0.60917825"
    },
    {
        "productCode": "4352",
        "esScore": 31.522335,
        "productScore": 5.54,
        "sigmoidSortScore": 0.09282717108726501,
        "sigmoidScoreDetail": "匹配分: 0.0, 热度分: 0.46413586"
    },
    {
        "productCode": "4354",
        "esScore": 32.238743,
        "productScore": 2.34,
        "sigmoidSortScore": 0.011616908013820648,
        "sigmoidScoreDetail": "匹配分: 0.014521135, 热度分: 0.0"
    },
    {
        "productCode": "4359",
        "productScore": 9.234533,
        "sigmoidSortScore": -0.3111503124237061,
        "sigmoidScoreDetail": "匹配分: -0.6389379, 热度分: 1.0"
    },
    {
        "productCode": "4360",
        "sigmoidSortScore": -0.579030179977417,
        "sigmoidScoreDetail": "匹配分: -0.6389379, 热度分: -0.33939934"
    },
    {
        "productCode": "4361",
        "sigmoidSortScore": -0.579030179977417,
        "sigmoidScoreDetail": "匹配分: -0.6389379, 热度分: -0.33939934"
    },
    {
        "productCode": "4362",
        "sigmoidSortScore": -0.579030179977417,
        "sigmoidScoreDetail": "匹配分: -0.6389379, 热度分: -0.33939934"
    }
]
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐