lucene3.0.3中的PayloadTermQuery、PayloadFunction

上一篇寫了SpanTermQuery,他沒有任何意思,但是他有個子類,叫做PayloadTermQuery,這個類倒是可以實現一些功能。他也是根據term進行召回,但是對於得分的計算可以加入payload的信息,這樣可以使得某個term和某個doc產生單獨的關係,比如我們在電商搜索中,對於某個商品想做推廣,使得他在搜索a詞的時候得分特別大,但是其他的搜索的得分不會變大,顯然使用boost是不正確的,因爲他會使得所有的詞的搜索得分都會變大。這個時候就可以使用payload了,這個類和SpanTermQuery的不同之處在於得分上,我們看一下這個類的得分的實現,在PayloadTermQuery最終生成的PayloadTermSpanScorer中,在調用nextDoc的時候會調用setFreqCurrentDoc方法,即計算當前doc的所有的位置的得分,源碼如下:

/**
 * 根據term在doc中的每一次出現計算得分,計算當前doc的freq值,相比spanTermQuery他的區別是添加了payload的處理
 */
@Override
protected boolean setFreqCurrentDoc() throws IOException {
	if (!more) {
		return false;
	}
	doc = spans.doc();
	freq = 0.0f;
	payloadScore = 0;
	payloadsSeen = 0;
	Similarity similarity1 = getSimilarity();
	while (more && doc == spans.doc()) {
		int matchLength = spans.end() - spans.start();
		freq += similarity1.sloppyFreq(matchLength);//計算出現頻率的得分
		processPayload(similarity1);//計算payload的得分,比TermSpanQuery就是多了這個方法
		more = spans.next();// this moves positions to the next match in this document
	}
	return more || (freq != 0);
}

 最關鍵的就是多了一個processPayload的方法,他會讀取當前位置的payload信息,然後根據payload計算一個得分,我們看一下這個方法的源碼:

protected void processPayload(Similarity similarity) throws IOException {
	if (positions.isPayloadAvailable()) {
		payload = positions.getPayload(payload, 0);//讀取payload
		payloadScore = function.currentScore(doc, term.field(), spans.start(), spans.end(), payloadsSeen,
				payloadScore, similarity.scorePayload(doc, term.field(), spans.start(), spans.end(),
						payload, 0, positions.getPayloadLength()));//計算payload
		payloadsSeen++;
	} else {
		// zero out the payload?
	}
}

 對於payload的計算會先根據similarity的scorePayload進行得分,默認的Similarity是直接返回1所以我們必須修改這個方法纔可以,在計算完這個值之後還要調用一個function.currentScore方法,funtion是穿入的一個屬性,用來計算當前的doc上所有的payload的得分,這個方法包括很多的參數,比如payloadSeen(當前的payload是第幾個),paylaodScore(沒有處理當前的payload時的得分),當前的得分。可以發現他是綜合考量了所有的出現位置後計算的得分,但是這個得分並不是最終的得分,最終的得分是在score方法計算的。

@Override
public float score() throws IOException {
	return includeSpanScore ? getSpanScore() * getPayloadScore() : getPayloadScore();
}

 這裏涉及到一個includeSpanScore,也就是不包括paylaod的得分(也就是和SpanTermQuery一樣的得分),如果包括就兩者相乘,不然就只計算PayLoad的得分,getPayloadScore的公式爲:

protected float getPayloadScore() {
	return function.docScore(doc, term.field(), payloadsSeen, payloadScore);
}

 可以發現他是對之前計算的payloadScore又進行了一次計算,纔算是最終的payload的得分。

 

通過上面的分析就能根據payload進行最終的得分的計算了,剩下的就是看PayloadFunction具體的實現了。

 

PayloadFunction是個抽象類,他有兩個重要的方法,一個是 public abstract float currentScore(int docId, String field, int start, int end, int numPayloadsSeen, float currentScore, float currentPayloadScore);用來計算當前位置的payload的得分,一個是public abstract float docScore(int docId, String field, int numPayloadsSeen, float payloadScore);用於計算當前doc的所有的payLoad的得分,我們看一下他的具體實現類。

 

1、AveragePayloadFunction:對所有位置的payload的得分去平均數。

  @Override
  public float currentScore(int docId, String field, int start, int end, int numPayloadsSeen, float currentScore, float currentPayloadScore) {
    return currentPayloadScore + currentScore;//將當前的位置的得分和之前的得分加起來,也就是計算所有位置的paylaod的和
  }

  @Override
  public float docScore(int docId, String field, int numPayloadsSeen, float payloadScore) {
    return numPayloadsSeen > 0 ? (payloadScore / numPayloadsSeen) : 1;//將和除以次數,計算平均分。
  }

 

2、MaxPayloadFunction:對所有位置的payload取最大值

@Override
public float currentScore(int docId, String field, int start, int end, int numPayloadsSeen, float currentScore,	float currentPayloadScore) {
	if (numPayloadsSeen == 0) {
			return currentPayloadScore;//第一次返回當前值
	} else {
		return Math.max(currentPayloadScore, currentScore);//返回所有的最大值
	}
}

@Override
public float docScore(int docId, String field, int numPayloadsSeen, float payloadScore) {
	return numPayloadsSeen > 0 ? payloadScore : 1;//返回最大值
}

 

3、MinPayloadFunction:對所有位置的payload取最小值

  @Override
  public float currentScore(int docId, String field, int start, int end, int numPayloadsSeen, float currentScore, float currentPayloadScore) {
    if (numPayloadsSeen == 0) {
      return currentPayloadScore;
    } else {
      return Math.min(currentPayloadScore, currentScore);
    }
  }

  @Override
  public float docScore(int docId, String field, int numPayloadsSeen, float payloadScore) {
    return numPayloadsSeen > 0 ? payloadScore : 1;
  }

 

當然我們也可以自己實現function。payload的功能寫到這就算是完了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章