0

0

使用Spring Data Elasticsearch按最高得分百分比过滤结果

霞舞

霞舞

发布时间:2025-10-26 12:50:10

|

795人浏览过

|

来源于php中文网

原创

使用Spring Data Elasticsearch按最高得分百分比过滤结果

本文探讨了在spring data elasticsearch中,如何根据搜索结果的最高得分(maxscore)的百分比来动态过滤文档。由于elasticsearch原生查询难以直接实现这种基于动态阈值的过滤,文章提出并详细阐述了一种实用的客户端分页与过滤策略,通过逐步获取搜索结果并计算阈值,高效地筛选出高相关性文档,并提供了具体的实现思路和注意事项。

在Elasticsearch的搜索实践中,我们经常会遇到这样的场景:查询返回了大量的文档,但其中许多文档的相关性得分(score)极低,例如,最高得分可能为5,而许多文档的得分却低至0.0001。这些得分极低的文档往往不具备实际价值,我们希望能够根据一个动态的、相对于最高得分的百分比阈值来过滤结果,例如,只保留得分在最高得分80%以上的文档。本文将深入探讨如何在Spring Data Elasticsearch中实现这一需求。

理解Elasticsearch的得分与过滤挑战

Elasticsearch通过相关性算法(如BM25)为每个匹配的文档计算一个得分,得分越高表示文档与查询越相关。然而,直接在Elasticsearch查询层面实现“过滤掉得分低于最高得分X%的文档”这一逻辑是具有挑战性的。主要原因在于:

  1. 动态阈值: 最高得分是在查询执行后才能确定的,而不是一个固定的值。Elasticsearch的查询DSL(Domain Specific Language)主要用于在查询执行前定义匹配规则和过滤条件。
  2. 查询阶段: 得分计算通常发生在查询的“匹配”阶段,而过滤(如post_filter或bool查询中的filter子句)通常在匹配之后或与匹配并行执行,但它们需要一个预先确定的条件。
  3. 脚本复杂性: 理论上可以通过复杂的script_score或script查询来实现,但这通常会带来显著的性能开销,并且脚本逻辑的维护也更为复杂。

因此,对于这种动态的、基于结果集最高得分的过滤需求,更推荐采用客户端(应用层)的策略。

客户端分页与过滤策略

一种高效且实用的方法是利用Spring Data Elasticsearch提供的分页功能,结合客户端逻辑进行逐步过滤。核心思想是:先获取少量结果以确定最高得分,然后根据该得分计算出过滤阈值,再逐步获取并过滤后续结果。

1. 确定初始最高得分与阈值

首先,执行一个带分页的搜索请求,获取第一页结果。Spring Data Elasticsearch的SearchHits对象中包含了当前结果集的maxScore。利用这个maxScore,我们可以计算出我们所需的得分阈值。

例如,如果第一页的maxScore是5.0,而我们希望保留80%以上的文档,那么阈值就是 5.0 * 0.8 = 4.0。

酷兔AI论文
酷兔AI论文

专业原创高质量、低查重,免费论文大纲,在线AI生成原创论文,AI辅助生成论文的神器!

下载

2. 逐步获取与过滤结果

在确定阈值之后,我们就可以开始遍历当前页的文档,将得分高于或等于阈值的文档添加到最终结果列表中。如果当前页的文档全部满足条件,或者我们还没有达到预期的结果数量,并且Elasticsearch还有更多结果(searchHits.hasMore()),那么就继续请求下一页。在后续页的遍历中,我们只需根据之前计算的阈值进行过滤,直到遇到第一个得分低于阈值的文档,或者所有满足条件的文档都被获取,或者没有更多结果为止。

3. 实现示例

下面是一个使用Spring Data Elasticsearch实现此策略的示例代码:

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
public class DocumentSearchService {

    private final ElasticsearchOperations elasticsearchOperations;

    public DocumentSearchService(ElasticsearchOperations elasticsearchOperations) {
        this.elasticsearchOperations = elasticsearchOperations;
    }

    /**
     * 根据最高得分的百分比过滤Elasticsearch文档。
     *
     * @param queryText 查询字符串
     * @param scorePercentageThreshold 得分百分比阈值 (例如 0.8 代表 80%)
     * @param maxResults 期望返回的最大结果数
     * @param  文档类型
     * @return 过滤后的文档列表
     */
    public  List searchAndFilterByMaxScorePercentage(
            String queryText, double scorePercentageThreshold, int maxResults, Class entityClass) {

        List filteredDocuments = new ArrayList<>();
        int pageSize = Math.min(maxResults, 20); // 初始页面大小,可根据实际情况调整
        int pageNumber = 0;
        double maxScore = -1.0;
        double scoreThreshold = -1.0;
        boolean stopFetching = false;

        while (!stopFetching && filteredDocuments.size() < maxResults) {
            Pageable pageable = PageRequest.of(pageNumber, pageSize);
            CriteriaQuery query = new CriteriaQuery(new Criteria("content").contains(queryText), pageable); // 示例查询

            SearchHits searchHits = elasticsearchOperations.search(query, entityClass);

            if (searchHits.getTotalHits() == 0) {
                break; // 没有结果
            }

            if (maxScore == -1.0) { // 首次获取,确定 maxScore 和阈值
                maxScore = searchHits.getMaxScore();
                if (maxScore <= 0) {
                    // 如果maxScore为0或负数,意味着所有文档得分都非常低,
                    // 此时可以根据业务逻辑决定是返回所有匹配文档,还是返回空列表,
                    // 或者设置一个非常小的正阈值。这里我们直接停止。
                    System.out.println("Max score is zero or negative, stopping filtering.");
                    break;
                }
                scoreThreshold = maxScore * scorePercentageThreshold;
                // 确保阈值不会过低导致不必要的过滤,或过高导致无结果
                if (scoreThreshold < 0.0001 && maxScore > 0) { // 避免因浮点数精度导致阈值过低
                    scoreThreshold = 0.0001;
                }
            }

            for (SearchHit hit : searchHits) {
                if (hit.getScore() >= scoreThreshold) {
                    filteredDocuments.add(hit.getContent());
                    if (filteredDocuments.size() >= maxResults) {
                        stopFetching = true; // 达到最大结果数
                        break;
                    }
                } else {
                    // 遇到第一个得分低于阈值的文档,停止继续获取
                    stopFetching = true;
                    break;
                }
            }

            if (searchHits.hasMore() && !stopFetching) {
                pageNumber++;
            } else {
                stopFetching = true; // 没有更多页或已决定停止
            }
        }
        return filteredDocuments;
    }
}

使用示例:

// 假设 MyDocument 是你的 Elasticsearch 实体类
// @Document(indexName = "my_documents")
// public class MyDocument { ... }

@Autowired
private DocumentSearchService documentSearchService;

public List getTopRelevantDocuments(String query) {
    // 获取得分在最高得分80%以上,且最多100个结果的文档
    return documentSearchService.searchAndFilterByMaxScorePercentage(query, 0.8, 100, MyDocument.class);
}

注意事项与优化

  1. 页面大小(pageSize)选择:
    • 初始页面大小的选择很重要。如果设置过小,可能需要多次往返Elasticsearch,增加延迟。如果设置过大,可能会获取大量不必要的文档。
    • 一个折衷的方案是根据预期返回的最大结果数或经验值来设置,例如20-50。
  2. 性能考量:
    • 这种方法涉及多次Elasticsearch请求,相比单次大请求可能带来更高的网络延迟。然而,它避免了在客户端处理大量低相关性数据,也避免了Elasticsearch服务器端复杂脚本的性能开销。
    • 如果需要处理的文档数量非常庞大,并且对性能有极高的要求,可能需要重新评估。
  3. maxScore为零或负数的情况:
    • 在某些极端情况下,如果所有匹配文档的得分都为零或负数(尽管不常见),maxScore可能为0。此时,根据业务需求决定如何处理,例如,可以返回所有匹配文档,或者直接返回空列表。示例代码中加入了相应的处理逻辑。
  4. 阈值精度:
    • 浮点数计算可能存在精度问题。在比较得分时,可以考虑使用一个小的容差值,或者确保阈值不会因为精度问题而变得过低。
  5. 查询类型:
    • 示例代码使用了简单的CriteriaQuery,实际应用中可以根据需要使用更复杂的NativeSearchQuery来构建查询。
  6. 替代方案(高级):
    • 如果绝对需要在Elasticsearch服务器端完成此过滤,可以考虑使用script_score查询结合painless脚本。脚本可以在查询执行时计算每个文档的得分,并与一个动态值进行比较。但这通常需要更深入的Elasticsearch知识,并且对性能有一定影响,因为脚本会在每个文档上执行。对于大多数场景,客户端分页过滤是更简单和高效的选择。

总结

在Spring Data Elasticsearch中,根据最高得分的百分比动态过滤搜索结果,虽然无法通过简单的原生查询直接实现,但通过结合客户端分页与过滤策略,可以高效且灵活地达到目的。这种方法通过逐步获取数据、计算动态阈值并在应用层进行筛选,平衡了性能与实现的复杂性,为处理大量相关性不同的搜索结果提供了一个实用的解决方案。在实际应用中,需要根据具体业务场景和性能要求,合理选择页面大小并处理各种边界情况。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

105

2025.08.06

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

403

2023.08.14

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

1

2026.01.21

windows激活码分享 windows一键激活教程指南
windows激活码分享 windows一键激活教程指南

Windows 10/11一键激活可以通过PowerShell脚本或KMS工具实现永久或长期激活。最推荐的简便方法是打开PowerShell(管理员),运行 irm https://get.activated.win | iex 脚本,按提示选择数字激活(选项1)。其他方法包括使用HEU KMS Activator工具进行智能激活。

0

2026.01.21

excel表格操作技巧大全 表格制作excel教程
excel表格操作技巧大全 表格制作excel教程

Excel表格操作的核心技巧在于 熟练使用快捷键、数据处理函数及视图工具,如Ctrl+C/V(复制粘贴)、Alt+=(自动求和)、条件格式、数据验证及数据透视表。掌握这些可大幅提升数据分析与办公效率,实现快速录入、查找、筛选和汇总。

1

2026.01.21

毒蘑菇显卡测试网站入口 毒蘑菇测试官网volumeshader_bm
毒蘑菇显卡测试网站入口 毒蘑菇测试官网volumeshader_bm

毒蘑菇VOLUMESHADER_BM测试网站网址为https://toolwa.com/vsbm/,该平台基于WebGL技术通过渲染高复杂度三维分形图形评估设备图形处理能力,用户可通过拖动彩色物体观察画面流畅度判断GPU与CPU协同性能;测试兼容多种设备,但中低端手机易卡顿或崩溃,高端机型可能因发热降频影响表现,桌面端需启用独立显卡并使用支持WebGL的主流浏览器以确保准确结果

3

2026.01.21

github中文官网入口 github中文版官网网页进入
github中文官网入口 github中文版官网网页进入

github中文官网入口https://docs.github.com/zh/get-started,GitHub 是一种基于云的平台,可在其中存储、共享并与他人一起编写代码。 通过将代码存储在GitHub 上的“存储库”中,你可以: “展示或共享”你的工作。 持续“跟踪和管理”对代码的更改。

2

2026.01.21

windows安全中心怎么关闭打开_windows安全中心操作指南
windows安全中心怎么关闭打开_windows安全中心操作指南

Windows安全中心可以通过系统设置轻松开关。 暂时关闭:打开“设置” -> “隐私和安全性” -> “Windows安全中心” -> “病毒和威胁防护” -> “管理设置”,将“实时保护”关闭。打开:同样路径将开关开启即可。如需彻底关闭,需在组策略(gpedit.msc)或注册表中禁用Windows Defender。

1

2026.01.21

C++游戏开发Unreal Engine_C++怎么用Unreal Engine开发游戏
C++游戏开发Unreal Engine_C++怎么用Unreal Engine开发游戏

虚幻引擎(Unreal Engine, 简称UE)是由Epic Games开发的一款功能强大的工业级3D游戏引擎,以高品质实时渲染(如Nanite和Lumen)闻名 。它基于C++语言,为开发者提供高效率的框架、强大的可视化脚本系统(蓝图)、以及针对PC、主机和移动端的完整开发工具,广泛用于游戏、电影制片等领域。

0

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.7万人学习

C# 教程
C# 教程

共94课时 | 7.1万人学习

Java 教程
Java 教程

共578课时 | 48.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号