Index: src/java/org/apache/lucene/search/payloads/AveragePayloadFunction.java =================================================================== --- src/java/org/apache/lucene/search/payloads/AveragePayloadFunction.java (revision 911598) +++ src/java/org/apache/lucene/search/payloads/AveragePayloadFunction.java (working copy) @@ -1,5 +1,7 @@ package org.apache.lucene.search.payloads; +import org.apache.lucene.search.Explanation; + /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -25,6 +27,8 @@ * **/ public class AveragePayloadFunction extends PayloadFunction{ + int numPayloadsSeen=0; + float payloadScore=0; @Override public float currentScore(int docId, String field, int start, int end, int numPayloadsSeen, float currentScore, float currentPayloadScore) { @@ -33,8 +37,19 @@ @Override public float docScore(int docId, String field, int numPayloadsSeen, float payloadScore) { + this.numPayloadsSeen = numPayloadsSeen; + this.payloadScore = payloadScore; return numPayloadsSeen > 0 ? (payloadScore / numPayloadsSeen) : 1; } + + @Override + public Explanation explain(int doc) { + Explanation payloadBoost = new Explanation(); + float avgPayloadScore = (numPayloadsSeen > 0 ? (payloadScore / numPayloadsSeen) : 1); + payloadBoost.setValue(avgPayloadScore); + payloadBoost.setDescription("AveragePayloadFunction(...)"); + return payloadBoost; + } @Override public int hashCode() { Index: src/java/org/apache/lucene/search/payloads/MaxPayloadFunction.java =================================================================== --- src/java/org/apache/lucene/search/payloads/MaxPayloadFunction.java (revision 911598) +++ src/java/org/apache/lucene/search/payloads/MaxPayloadFunction.java (working copy) @@ -1,5 +1,7 @@ package org.apache.lucene.search.payloads; +import org.apache.lucene.search.Explanation; + /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -25,21 +27,33 @@ * **/ public class MaxPayloadFunction extends PayloadFunction { + int numPayloadsSeen=0; + float payloadScore=0; + @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); + 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 + @Override public float docScore(int docId, String field, int numPayloadsSeen, float payloadScore) { - return numPayloadsSeen > 0 ? payloadScore : 1; + this.numPayloadsSeen = numPayloadsSeen; + this.payloadScore = payloadScore; + return numPayloadsSeen > 0 ? payloadScore : 1; + } + @Override + public Explanation explain(int doc) { + Explanation payloadBoost = new Explanation(); + float maxPayloadScore = (numPayloadsSeen > 0 ? payloadScore : 1); + payloadBoost.setValue(maxPayloadScore); + payloadBoost.setDescription("MaxPayloadFunction(...)"); + return payloadBoost; } - - @Override + @Override public int hashCode() { final int prime = 31; int result = 1; @@ -46,7 +60,6 @@ result = prime * result + this.getClass().hashCode(); return result; } - @Override public boolean equals(Object obj) { if (this == obj) Index: src/java/org/apache/lucene/search/payloads/MinPayloadFunction.java =================================================================== --- src/java/org/apache/lucene/search/payloads/MinPayloadFunction.java (revision 911598) +++ src/java/org/apache/lucene/search/payloads/MinPayloadFunction.java (working copy) @@ -1,5 +1,7 @@ package org.apache.lucene.search.payloads; +import org.apache.lucene.search.Explanation; + /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -22,22 +24,32 @@ * **/ public class MinPayloadFunction extends PayloadFunction { + int numPayloadsSeen=0; + float payloadScore=0; @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); + 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) { + this.numPayloadsSeen = numPayloadsSeen; + this.payloadScore = payloadScore; return numPayloadsSeen > 0 ? payloadScore : 1; } - @Override + public Explanation explain(int doc) { + Explanation payloadBoost = new Explanation(); + float maxPayloadScore = (numPayloadsSeen > 0 ? payloadScore : 1); + payloadBoost.setValue(maxPayloadScore); + payloadBoost.setDescription("MinPayloadFunction(...)"); + return payloadBoost; + } public int hashCode() { final int prime = 31; int result = 1; Index: src/java/org/apache/lucene/search/payloads/PayloadFunction.java =================================================================== --- src/java/org/apache/lucene/search/payloads/PayloadFunction.java (revision 911598) +++ src/java/org/apache/lucene/search/payloads/PayloadFunction.java (working copy) @@ -17,6 +17,7 @@ */ import java.io.Serializable; +import org.apache.lucene.search.Explanation; /** * An abstract class that defines a way for Payload*Query instances to transform @@ -21,12 +22,12 @@ /** * An abstract class that defines a way for Payload*Query instances to transform * the cumulative effects of payload scores for a document. - * + * * @see org.apache.lucene.search.payloads.PayloadTermQuery for more information - * + * * @lucene.experimental This class and its derivations are experimental and subject to * change - * + * **/ public abstract class PayloadFunction implements Serializable { @@ -55,6 +56,8 @@ */ public abstract float docScore(int docId, String field, int numPayloadsSeen, float payloadScore); + public abstract Explanation explain(int docId); + @Override public abstract int hashCode(); Index: src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java =================================================================== --- src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java (revision 911598) +++ src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java (working copy) @@ -79,7 +79,7 @@ newClauses[i] = (SpanQuery) clauses.get(i).clone(); } PayloadNearQuery boostingNearQuery = new PayloadNearQuery(newClauses, slop, - inOrder); + inOrder, function); boostingNearQuery.setBoost(getBoost()); return boostingNearQuery; } @@ -84,7 +84,6 @@ return boostingNearQuery; } - @Override public String toString(String field) { StringBuilder buffer = new StringBuilder(); buffer.append("payloadNear(["); @@ -152,7 +151,6 @@ public class PayloadNearSpanScorer extends SpanScorer { Spans spans; - protected float payloadScore; private int payloadsSeen; Similarity similarity = getSimilarity(); @@ -225,16 +223,14 @@ @Override protected Explanation explain(int doc) throws IOException { Explanation result = new Explanation(); + // Add detail about tf/idf... Explanation nonPayloadExpl = super.explain(doc); result.addDetail(nonPayloadExpl); - Explanation payloadBoost = new Explanation(); - result.addDetail(payloadBoost); - float avgPayloadScore = (payloadsSeen > 0 ? (payloadScore / payloadsSeen) - : 1); - payloadBoost.setValue(avgPayloadScore); - payloadBoost.setDescription("scorePayload(...)"); - result.setValue(nonPayloadExpl.getValue() * avgPayloadScore); - result.setDescription("bnq, product of:"); + // Add detail about payload + Explanation payloadExpl = function.explain(doc); + result.addDetail(payloadExpl); + result.setValue(nonPayloadExpl.getValue() * payloadExpl.getValue()); + result.setDescription("PayloadNearQuery, product of:"); return result; } } Index: src/test/org/apache/lucene/search/payloads/TestPayloadNearQuery.java =================================================================== --- src/test/org/apache/lucene/search/payloads/TestPayloadNearQuery.java (revision 911598) +++ src/test/org/apache/lucene/search/payloads/TestPayloadNearQuery.java (working copy) @@ -30,7 +30,9 @@ import org.apache.lucene.index.Payload; import org.apache.lucene.index.Term; import org.apache.lucene.search.DefaultSimilarity; +import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryUtils; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Searcher; @@ -64,6 +66,7 @@ } private class PayloadFilter extends TokenFilter { + String fieldName; int numSeen = 0; protected PayloadAttribute payAtt; @@ -69,10 +72,10 @@ public PayloadFilter(TokenStream input, String fieldName) { super(input); - payAtt = addAttribute(PayloadAttribute.class); + this.fieldName = fieldName; + payAtt = (PayloadAttribute) addAttribute(PayloadAttribute.class); } - @Override public boolean incrementToken() throws IOException { boolean result = false; if (input.incrementToken() == true){ @@ -88,7 +91,7 @@ } } - private PayloadNearQuery newPhraseQuery (String fieldName, String phrase, boolean inOrder) { + private PayloadNearQuery newPhraseQuery (String fieldName, String phrase, boolean inOrder, PayloadFunction function ) { String[] words = phrase.split("[\\s]+"); SpanQuery clauses[] = new SpanQuery[words.length]; for (int i=0;i -1); + assertTrue(hits.scoreDocs[j].score + " explain value does not equal: " + 3, explain.getValue() == 3f); + } + } + public void testMaxFunction() throws IOException { + PayloadNearQuery query; + TopDocs hits; + query = newPhraseQuery("field", "twenty two", true, new MaxPayloadFunction()); + QueryUtils.check(query); + // all 10 hits should have score = 4 (max payload value) + hits = searcher.search(query, null, 100); + assertTrue("hits is null and it shouldn't be", hits != null); + assertTrue("should be 10 hits", hits.totalHits == 10); + for (int j = 0; j < hits.scoreDocs.length; j++) { + ScoreDoc doc = hits.scoreDocs[j]; + assertTrue(doc.score + " does not equal: " + 4, doc.score == 4); + Explanation explain = searcher.explain(query, hits.scoreDocs[j].doc); + String exp = explain.toString(); + assertTrue(exp, exp.indexOf("MaxPayloadFunction") > -1); + assertTrue(hits.scoreDocs[j].score + " explain value does not equal: " + 4, explain.getValue() == 4f); + } + } + public void testMinFunction() throws IOException { + PayloadNearQuery query; + TopDocs hits; + + query = newPhraseQuery("field", "twenty two", true, new MinPayloadFunction()); + QueryUtils.check(query); + // all 10 hits should have score = 2 (min payload value) + hits = searcher.search(query, null, 100); + assertTrue("hits is null and it shouldn't be", hits != null); + assertTrue("should be 10 hits", hits.totalHits == 10); + for (int j = 0; j < hits.scoreDocs.length; j++) { + ScoreDoc doc = hits.scoreDocs[j]; + assertTrue(doc.score + " does not equal: " + 2, doc.score == 2); + Explanation explain = searcher.explain(query, hits.scoreDocs[j].doc); + String exp = explain.toString(); + assertTrue(exp, exp.indexOf("MinPayloadFunction") > -1); + assertTrue(hits.scoreDocs[j].score + " explain value does not equal: " + 2, explain.getValue() == 2f); + } + } + private SpanQuery[] getClauses() { + SpanNearQuery q1, q2; + q1 = spanNearQuery("field2", "twenty two"); + q2 = spanNearQuery("field2", "twenty three"); + SpanQuery[] clauses = new SpanQuery[2]; + clauses[0] = q1; + clauses[1] = q2; + return clauses; + } private SpanNearQuery spanNearQuery(String fieldName, String words) { String[] wordList = words.split("[\\s]+"); SpanQuery clauses[] = new SpanQuery[wordList.length]; @@ -186,7 +256,7 @@ public void testLongerSpan() throws IOException { PayloadNearQuery query; TopDocs hits; - query = newPhraseQuery("field", "nine hundred ninety nine", true); + query = newPhraseQuery("field", "nine hundred ninety nine", true, new AveragePayloadFunction()); hits = searcher.search(query, null, 100); ScoreDoc doc = hits.scoreDocs[0]; // System.out.println("Doc: " + doc.toString()); @@ -203,10 +273,10 @@ // combine ordered and unordered spans with some nesting to make sure all payloads are counted - SpanQuery q1 = newPhraseQuery("field", "nine hundred", true); - SpanQuery q2 = newPhraseQuery("field", "ninety nine", true); - SpanQuery q3 = newPhraseQuery("field", "nine ninety", false); - SpanQuery q4 = newPhraseQuery("field", "hundred nine", false); + SpanQuery q1 = newPhraseQuery("field", "nine hundred", true, new AveragePayloadFunction()); + SpanQuery q2 = newPhraseQuery("field", "ninety nine", true, new AveragePayloadFunction()); + SpanQuery q3 = newPhraseQuery("field", "nine ninety", false, new AveragePayloadFunction()); + SpanQuery q4 = newPhraseQuery("field", "hundred nine", false, new AveragePayloadFunction()); SpanQuery[]clauses = new SpanQuery[] {new PayloadNearQuery(new SpanQuery[] {q1,q2}, 0, true), new PayloadNearQuery(new SpanQuery[] {q3,q4}, 0, false)}; query = new PayloadNearQuery(clauses, 0, false); hits = searcher.search(query, null, 100); @@ -227,7 +297,6 @@ //we know it is size 4 here, so ignore the offset/length return payload[0]; } - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //Make everything else 1 so we see the effect of the payload //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -249,7 +318,6 @@ @Override public float tf(float freq) { return 1.0f; } - // idf used for phrase queries @Override public IDFExplanation idfExplain(Collection terms, Searcher searcher) throws IOException { return new IDFExplanation() {