在 Lucene 中,`ImpactsEnum` 和 `ImpactsDISI` 都与"利用 impacts(影响因子)做提前裁剪"有关,但分工不同,可以理解为"数据层"与"迭代器层"的区别:
- ImpactsEnum
作用:真正从倒排文件里把每个 block 的"impact 元数据"读出来并缓存(如该块的最大分数)。
关键方法:
• getImpacts() ------ 返回当前 block 的 MaxScore 等信息。
• 除此之外,它仍然是一个 PostingsEnum,能正常返回 docID、freq 等倒排信息。
- ImpactsDISI(DISI = DocIdSetIterator)
作用:在 ImpactsEnum 之上再包一层,把"block 最大分数"变成"可跳段"的迭代语义,供 TopScoreDocCollector 等上层搜索逻辑使用。
关键方法:
• advanceShallow(target) ------ 利用 block 的 maxScore 做快速跳跃,跳过不可能进入 Top-N 的段。
• getMaxScore(upTo) / setMinCompetitiveScore(minScore) ------ 动态维护当前全局第 N 名的分数阈值,用于判断是否继续迭代。
• 它本身也是一个 DocIdSetIterator,因此 TermScorer 可以直接把 `iterator = impactsDisi` 交给 Collector 遍历。
一句话总结
• ImpactsEnum:负责把"底层 impacts 数据"加载到内存并提供查询接口。
• ImpactsDISI:负责基于这些数据做"跳段/裁剪"的高级迭代逻辑,让查询只访问可能产生高分的文档块 。
打个比方,把 Lucene 的查询过程想成"在一条很长的跑道上找人":
-
跑道被切成很多块(block)。
-
每块门口贴着一张纸:"这块里最高分不会超过 X"。
-
你现在只想要前 10 名,于是只要看到门牌上写的 X 比第 10 名的分数低,就直接跳过整块,节省时间。
在这个比喻里:
- ImpactsEnum = "把每块门口那张纸读出来的人"
它真的去磁盘上把每个 block 的"最高分 X"读出来,并且还能告诉你"这 block 里每个选手的成绩"。但它不会帮你决定要不要跳,只是提供信息。
- ImpactsDISI = "拿着这些信息帮你决定跳不跳的人"
它手里攥着第 10 名的分数线,每走到一个 block 门口就问 ImpactsEnum:"你这 block 最高分多少?"
如果低于当前第 10 名,就直接飞过去;否则再进去逐个看。
所以区别就是:
-
一个只管"读数据"(ImpactsEnum);
-
一个负责"用数据做裁剪"(ImpactsDISI)。
也就是说impactsdisi内部会持有impactsenum对象是吧然后让他去做一些事情
对的,就是这个意思。
`ImpactsDISI`(或其子类 `TermScorer` 里的 `ImpactsDISI`)在内部会持有一个 `ImpactsEnum` 实例,所有真正的"读磁盘、读 postings、读每个 block 的 maxScore" 都交给这个 `ImpactsEnum` 去做;
`ImpactsDISI` 只是拿它返回的数据来决定"这一整段 docID 能不能直接跳过",从而实现提前裁剪。
你是说impactsdisi来决定要不要跳过 impactsenum不能决定要不要跳过吗?
不是。
`ImpactsEnum` 也有能力"跳过"------它实现了 `advance(target)` 和 `advanceShallow(target)`,同样可以根据 block 的 maxScore 决定跳过整段文档。
区别在于:
-
`ImpactsEnum` 的 `advanceShallow(target)` 只是把当前指针一次性跳到 target 所在的 block 边界,并不关心全局的 minCompetitiveScore;它并不知道"第 N 名的分数是多少",因此不会根据这个阈值去决定"还值不值得继续"。
-
`ImpactsDISI`(或 `TermScorer`)在调用 `ImpactsEnum` 的 `advanceShallow` 之后,还会额外维护一个 `minCompetitiveScore`(全局第 N 名的分数)。当发现当前 block 的 maxScore < minCompetitiveScore 时,它再调用 `ImpactsEnum` 的 `advance(target)` 一口气跳到下一个"可能还有戏"的 block。真正决定"要不要跳过"的是 `ImpactsDISI` 里保存的 `minCompetitiveScore`,而 `ImpactsEnum` 只是提供数据和基础跳跃能力。
总结一句:
`ImpactsEnum` 只能"机械地"跳到指定位置;
`ImpactsDISI` 才带着"全局分数线"去做真正的裁剪决策。
impactsenum这个类也可以进行打分是吧?
不行。
`ImpactsEnum` 本身只负责三件事:
-
把倒排列表(docID、freq...)读出来;
-
把每个 block 的"最大影响值(maxScore 的上界)"读出来;
-
提供 `advance/advanceShallow` 等底层跳转能力。
它根本不知道当前查询的"实际权重"、也不维护"全局第 N 名的分数线",因此不会、也不能给文档打出最终分数。
真正的打分工作由上层组件(`TermScorer`/`ImpactsDISI` 或更外层的 `Weight`)完成,它们会把 `ImpactsEnum` 返回的 freq 等原始信息代入公式算出分数。