0

我构建了一个查询,它本质上是其他查询的加权和:

val query = new BooleanQuery
for ((subQuery, weight) <- ...) {
  subQuery.setBoost(weight)
  query.add(subQuery, BooleanClause.Occur.MUST)
}

当我查询索引时,我会返回包含总分的文档。这很好,但我还需要知道每个子查询的子分数是多少。我怎样才能得到那些?这就是我现在正在做的事情:

for (scoreDoc <- searcher.search(query, nHits).scoreDocs) {
  val score = scoreDoc.score
  val subScores = subQueries.map { subQuery =>
    val weight = searcher.createNormalizedWeight(subQuery)
    val scorer = weight.scorer(reader, true, true)
    scorer.advance(scoreDoc.doc)
    scorer.score
  }
}

我认为这给了我正确的分数,但是当我知道文档已经作为总分的一部分进行评分时,前进并重新评分似乎很浪费。

有没有更有效的方法来获得这些子分数?

[我的代码在 Scala 中,但如果更容易,请随时用 Java 响应。]

编辑:这是遵循罗伯特·缪尔的建议后的样子。

查询:

val query = new BooleanQuery
for ((subQuery, weight) <- ...) {
  val weightedQuery = new BoostedQuery(subQuery, new ConstValueSource(weight))
  query.add(weightedQuery, BooleanClause.Occur.MUST)
}

搜索:

val collector = new DocScoresCollector(nHits)
searcher.search(query, collector)
for (docScores <- collector.getDocSubScores) {
  ...
}

收藏者:

class DocScoresCollector(maxSize: Int) extends Collector {
  var scorer: Scorer = null
  var subScorers: Seq[Scorer] = null
  val priorityQueue = new DocScoresPriorityQueue(maxSize)

  override def setScorer(scorer: Scorer): Unit = {
    this.scorer = scorer
    // a little reflection hackery is required here because of a bug in
    // BoostedQuery's scorer's getChildren method
    // https://issues.apache.org/jira/browse/LUCENE-4261
    this.subScorers = scorer.getChildren.asScala.map(childScorer =>
      childScorer.child ...some hackery... ).toList
  }

  override def acceptsDocsOutOfOrder: Boolean = false

  override def collect(doc: Int): Unit = {
    this.scorer.advance(doc)
    val score = this.scorer.score
    val subScores = this.subScorers.map(_.score)
    priorityQueue.insertWithOverflow(DocScores(doc, score, subScores))
  }

  override def setNextReader(context: AtomicReaderContext): Unit = {}

  def getDocSubScores: Seq[DocScores] = {
    val buffer = Buffer.empty[DocScores]
    while (this.priorityQueue.size > 0) {
      buffer += this.priorityQueue.pop
    }
    buffer
  }
}

case class DocScores(doc: Int, score: Float, subScores: Seq[Float])

class DocScoresPriorityQueue(maxSize: Int) extends PriorityQueue[DocScores](maxSize) {
  def lessThan(a: DocScores, b: DocScores) = a.score < b.score
}
4

1 回答 1

2

有一个 scorer 导航 API:基本思想是您编写一个收集器,并在其 setScorer 方法中,通常您会将对该 Scorer 的引用保存到以后的 score() 每次命中,您现在可以遍历该 Scorer 的 subscorer 的树并且很快。

请注意,记分员有指向创建它们的权重的指针,以及返回到查询的权重。

使用所有这些,您可以隐藏对您在 setScorer 方法中关心的子计分器的引用,例如从 TermQueries 创建的所有子计分器。然后,在对命中进行评分时,您可以调查收集器中这些节点的 freq() 和 score() 之类的内容。

在 3.x 系列中,这是一个仅限于布尔关系的访问者 API,在 4.x 系列中(目前只有一个 alpha 版本),您可以获取每个子评分者的子级+关系,因此它可以与任意查询(包括您编写的自定义查询或其他)。

注意事项:

测试:

于 2012-07-26T00:58:33.527 回答