@Override public Explanation explain(LeafReaderContext context, int doc) throws IOException { Explanation expl = subQueryWeight.explain(context, doc); if (!expl.isMatch()) { return expl; } // First: Gather explanations for all filters List<Explanation> filterExplanations = new ArrayList<>(); for (int i = 0; i < filterFunctions.length; ++i) { Bits docSet = Lucene.asSequentialAccessBits( context.reader().maxDoc(), filterWeights[i].scorer(context)); if (docSet.get(doc)) { FilterFunction filterFunction = filterFunctions[i]; Explanation functionExplanation = filterFunction.function.getLeafScoreFunction(context).explainScore(doc, expl); double factor = functionExplanation.getValue(); float sc = CombineFunction.toFloat(factor); Explanation filterExplanation = Explanation.match( sc, "function score, product of:", Explanation.match(1.0f, "match filter: " + filterFunction.filter.toString()), functionExplanation); filterExplanations.add(filterExplanation); } } if (filterExplanations.size() > 0) { FiltersFunctionFactorScorer scorer = functionScorer(context); int actualDoc = scorer.iterator().advance(doc); assert (actualDoc == doc); double score = scorer.computeScore(doc, expl.getValue()); Explanation factorExplanation = Explanation.match( CombineFunction.toFloat(score), "function score, score mode [" + scoreMode.toString().toLowerCase(Locale.ROOT) + "]", filterExplanations); expl = combineFunction.explain(expl, factorExplanation, maxBoost); } if (minScore != null && minScore > expl.getValue()) { expl = Explanation.noMatch( "Score value is too low, expected at least " + minScore + " but got " + expl.getValue(), expl); } return expl; }
@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); // Add detail about payload Explanation payloadExpl = function.explain(doc, payloadsSeen, payloadScore); result.addDetail(payloadExpl); result.setValue(nonPayloadExpl.getValue() * payloadExpl.getValue()); result.setDescription("PayloadNearQuery, product of:"); return result; }
/* */ public Explanation customExplain( int doc, Explanation subQueryExpl, Explanation valSrcExpl) /* */ throws IOException /* */ { /* 154 */ float valSrcScore = 1.0F; /* 155 */ if (valSrcExpl != null) { /* 156 */ valSrcScore *= valSrcExpl.getValue(); /* */ } /* 158 */ Explanation exp = new Explanation(valSrcScore * subQueryExpl.getValue(), "custom score: product of:"); /* 159 */ exp.addDetail(subQueryExpl); /* 160 */ exp.addDetail(valSrcExpl); /* 161 */ return exp; /* */ }
@Override public Explanation explain(AtomicReaderContext context, int doc) throws IOException { Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs()); if (scorer != null) { int newDoc = scorer.advance(doc); if (newDoc == doc) { float freq = scorer.freq(); SloppySimScorer docScorer = similarity.sloppySimScorer(stats, context); ComplexExplanation result = new ComplexExplanation(); result.setDescription( "weight(" + getQuery() + " in " + doc + ") [" + similarity.getClass().getSimpleName() + "], result of:"); Explanation scoreExplanation = docScorer.explain(doc, new Explanation(freq, "phraseFreq=" + freq)); result.addDetail(scoreExplanation); result.setValue(scoreExplanation.getValue()); result.setMatch(true); return result; } } return new ComplexExplanation(false, 0.0f, "no matching term"); }
@Override public Explanation explain(int docId, Explanation subQueryExpl) { float score = score(docId, subQueryExpl.getValue()); Explanation exp = new Explanation(score, "script score function: product of:"); exp.addDetail(subQueryExpl); return exp; }
@Override public Explanation explain(int doc, Explanation freq) { Explanation expl = new Explanation(score(doc, freq.getValue()), "sum of:"); for (SimScorer subScorer : subScorers) { expl.addDetail(subScorer.explain(doc, freq)); } return expl; }
boolean findPayloadBoostInExplanation(Explanation expl) { if (expl.getDescription().startsWith("payloadBoost=") && expl.getValue() != 1f) { return true; } else { boolean found = false; for (Explanation sub : expl.getDetails()) { found |= findPayloadBoostInExplanation(sub); } return found; } }
@Override protected Explanation explain(final int doc) throws IOException { ComplexExplanation result = new ComplexExplanation(); Explanation nonPayloadExpl = super.explain(doc); result.addDetail(nonPayloadExpl); // QUESTION: Is there a way to avoid this skipTo call? We need to know // whether to load the payload or not Explanation payloadBoost = new Explanation(); result.addDetail(payloadBoost); float payloadScore = getPayloadScore(); payloadBoost.setValue(payloadScore); // GSI: I suppose we could toString the payload, but I don't think that // would be a good idea payloadBoost.setDescription("scorePayload(...)"); result.setValue(nonPayloadExpl.getValue() * payloadScore); result.setDescription("btq, product of:"); result.setMatch( nonPayloadExpl.getValue() == 0 ? Boolean.FALSE : Boolean.TRUE); // LUCENE-1303 return result; }
public Explanation explain(int doc) throws IOException { Explanation subQueryExpl = weight.qWeight.explain(reader, doc); if (!subQueryExpl.isMatch()) { return subQueryExpl; } float sc = subQueryExpl.getValue() * vals.floatVal(doc); Explanation res = new ComplexExplanation(true, sc, BoostedQuery.this.toString() + ", product of:"); res.addDetail(subQueryExpl); res.addDetail(vals.explain(doc)); return res; }
public Explanation explain(IndexReader reader, int doc) throws IOException { ComplexExplanation result = new ComplexExplanation(); result.setDescription("weight(" + getQuery() + " in " + doc + "), product of:"); Explanation idfExpl = new Explanation(idf, "idf(docFreq=" + reader.docFreq(term) + ")"); // explain query weight Explanation queryExpl = new Explanation(); queryExpl.setDescription("queryWeight(" + getQuery() + "), product of:"); Explanation boostExpl = new Explanation(getBoost(), "boost"); if (getBoost() != 1.0f) queryExpl.addDetail(boostExpl); queryExpl.addDetail(idfExpl); Explanation queryNormExpl = new Explanation(queryNorm, "queryNorm"); queryExpl.addDetail(queryNormExpl); queryExpl.setValue(boostExpl.getValue() * idfExpl.getValue() * queryNormExpl.getValue()); result.addDetail(queryExpl); // explain field weight String field = term.field(); ComplexExplanation fieldExpl = new ComplexExplanation(); fieldExpl.setDescription("fieldWeight(" + term + " in " + doc + "), product of:"); Explanation tfExpl = scorer(reader).explain(doc); fieldExpl.addDetail(tfExpl); fieldExpl.addDetail(idfExpl); Explanation fieldNormExpl = new Explanation(); byte[] fieldNorms = reader.norms(field); float fieldNorm = fieldNorms != null ? Similarity.decodeNorm(fieldNorms[doc]) : 0.0f; fieldNormExpl.setValue(fieldNorm); fieldNormExpl.setDescription("fieldNorm(field=" + field + ", doc=" + doc + ")"); fieldExpl.addDetail(fieldNormExpl); fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch())); fieldExpl.setValue(tfExpl.getValue() * idfExpl.getValue() * fieldNormExpl.getValue()); result.addDetail(fieldExpl); result.setMatch(fieldExpl.getMatch()); // combine them result.setValue(queryExpl.getValue() * fieldExpl.getValue()); if (queryExpl.getValue() == 1.0f) return fieldExpl; return result; }
public static void writeExplanation(StreamOutput out, Explanation explanation) throws IOException { out.writeFloat(explanation.getValue()); out.writeString(explanation.getDescription()); Explanation[] subExplanations = explanation.getDetails(); if (subExplanations == null) { out.writeBoolean(false); } else { out.writeBoolean(true); out.writeVInt(subExplanations.length); for (Explanation subExp : subExplanations) { writeExplanation(out, subExp); } } }
private void buildExplanation(XContentBuilder builder, Explanation explanation) throws IOException { builder.startObject(); builder.field(Fields.VALUE, explanation.getValue()); builder.field(Fields.DESCRIPTION, explanation.getDescription()); Explanation[] innerExps = explanation.getDetails(); if (innerExps != null) { builder.startArray(Fields.DETAILS); for (Explanation exp : innerExps) { buildExplanation(builder, exp); } builder.endArray(); } builder.endObject(); }
@Override protected Explanation createExplain(Explanation innerExplain, IndexReader reader, int doc) throws IOException { if (reader instanceof BoboIndexReader) { Explanation finalExpl = new Explanation(); finalExpl.addDetail(innerExplain); _func.initializeReader((BoboIndexReader) reader, _jsonParam); float innerValue = innerExplain.getValue(); float value = 0; if (_func.useInnerScore()) value = _func.newScore(innerValue, doc); else value = _func.newScore(doc); finalExpl.setValue(value); finalExpl.setDescription("Custom score: " + _func.getExplainString(innerValue, doc)); return finalExpl; } else { throw new IllegalStateException("reader not instance of " + BoboIndexReader.class); } }
@Override public Explanation explain(IndexReader reader, int doc) throws IOException { SolrIndexReader topReader = (SolrIndexReader) reader; SolrIndexReader[] subReaders = topReader.getLeafReaders(); int[] offsets = topReader.getLeafOffsets(); int readerPos = SolrIndexReader.readerIndex(doc, offsets); int readerBase = offsets[readerPos]; Explanation subQueryExpl = qWeight.explain(reader, doc); if (!subQueryExpl.isMatch()) { return subQueryExpl; } DocValues vals = boostVal.getValues(context, subReaders[readerPos]); float sc = subQueryExpl.getValue() * vals.floatVal(doc - readerBase); Explanation res = new ComplexExplanation(true, sc, BoostedQuery.this.toString() + ", product of:"); res.addDetail(subQueryExpl); res.addDetail(vals.explain(doc - readerBase)); return res; }
/* */ public Explanation customExplain( int doc, Explanation subQueryExpl, Explanation[] valSrcExpls) /* */ throws IOException /* */ { /* 124 */ if (valSrcExpls.length == 1) { /* 125 */ return customExplain(doc, subQueryExpl, valSrcExpls[0]); /* */ } /* 127 */ if (valSrcExpls.length == 0) { /* 128 */ return subQueryExpl; /* */ } /* 130 */ float valSrcScore = 1.0F; /* 131 */ for (int i = 0; i < valSrcExpls.length; i++) { /* 132 */ valSrcScore *= valSrcExpls[i].getValue(); /* */ } /* 134 */ Explanation exp = new Explanation(valSrcScore * subQueryExpl.getValue(), "custom score: product of:"); /* 135 */ exp.addDetail(subQueryExpl); /* 136 */ for (int i = 0; i < valSrcExpls.length; i++) { /* 137 */ exp.addDetail(valSrcExpls[i]); /* */ } /* 139 */ return exp; /* */ }
private Explanation doExplain(IndexReader reader, int doc) throws IOException { Explanation subQueryExpl = subQueryWeight.explain(reader, doc); if (!subQueryExpl.isMatch()) { return subQueryExpl; } // match Explanation[] valSrcExpls = new Explanation[valSrcWeights.length]; for (int i = 0; i < valSrcWeights.length; i++) { valSrcExpls[i] = valSrcWeights[i].explain(reader, doc); } Explanation customExp = CustomScoreQuery.this .getCustomScoreProvider(reader) .customExplain(doc, subQueryExpl, valSrcExpls); float sc = getValue() * customExp.getValue(); Explanation res = new ComplexExplanation(true, sc, CustomScoreQuery.this.toString() + ", product of:"); res.addDetail(customExp); res.addDetail( new Explanation( getValue(), "queryBoost")); // actually using the q boost as q weight (== weight value) return res; }
/** not a direct test of NearSpans, but a demonstration of how/when this causes problems */ public void testSpanNearScorerExplain() throws Exception { SpanNearQuery q = makeQuery(); Explanation e = q.weight(searcher).explain(searcher.getIndexReader(), 1); assertTrue( "Scorer explanation value for doc#1 isn't positive: " + e.toString(), 0.0f < e.getValue()); }
private void executeRandomJoin( boolean multipleValuesPerDocument, int maxIndexIter, int maxSearchIter, int numberOfDocumentsToIndex) throws Exception { for (int indexIter = 1; indexIter <= maxIndexIter; indexIter++) { if (VERBOSE) { System.out.println("indexIter=" + indexIter); } Directory dir = newDirectory(); RandomIndexWriter w = new RandomIndexWriter( random(), dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random(), MockTokenizer.KEYWORD, false)) .setMergePolicy(newLogMergePolicy())); final boolean scoreDocsInOrder = TestJoinUtil.random().nextBoolean(); IndexIterationContext context = createContext(numberOfDocumentsToIndex, w, multipleValuesPerDocument, scoreDocsInOrder); IndexReader topLevelReader = w.getReader(); w.close(); for (int searchIter = 1; searchIter <= maxSearchIter; searchIter++) { if (VERBOSE) { System.out.println("searchIter=" + searchIter); } IndexSearcher indexSearcher = newSearcher(topLevelReader); int r = random().nextInt(context.randomUniqueValues.length); boolean from = context.randomFrom[r]; String randomValue = context.randomUniqueValues[r]; FixedBitSet expectedResult = createExpectedResult(randomValue, from, indexSearcher.getIndexReader(), context); final Query actualQuery = new TermQuery(new Term("value", randomValue)); if (VERBOSE) { System.out.println("actualQuery=" + actualQuery); } final ScoreMode scoreMode = ScoreMode.values()[random().nextInt(ScoreMode.values().length)]; if (VERBOSE) { System.out.println("scoreMode=" + scoreMode); } final Query joinQuery; if (from) { joinQuery = JoinUtil.createJoinQuery( "from", multipleValuesPerDocument, "to", actualQuery, indexSearcher, scoreMode); } else { joinQuery = JoinUtil.createJoinQuery( "to", multipleValuesPerDocument, "from", actualQuery, indexSearcher, scoreMode); } if (VERBOSE) { System.out.println("joinQuery=" + joinQuery); } // Need to know all documents that have matches. TopDocs doesn't give me that and then I'd // be also testing TopDocsCollector... final FixedBitSet actualResult = new FixedBitSet(indexSearcher.getIndexReader().maxDoc()); final TopScoreDocCollector topScoreDocCollector = TopScoreDocCollector.create(10, false); indexSearcher.search( joinQuery, new Collector() { int docBase; @Override public void collect(int doc) throws IOException { actualResult.set(doc + docBase); topScoreDocCollector.collect(doc); } @Override public void setNextReader(AtomicReaderContext context) { docBase = context.docBase; topScoreDocCollector.setNextReader(context); } @Override public void setScorer(Scorer scorer) throws IOException { topScoreDocCollector.setScorer(scorer); } @Override public boolean acceptsDocsOutOfOrder() { return scoreDocsInOrder; } }); // Asserting bit set... if (VERBOSE) { System.out.println("expected cardinality:" + expectedResult.cardinality()); DocIdSetIterator iterator = expectedResult.iterator(); for (int doc = iterator.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = iterator.nextDoc()) { System.out.println( String.format( Locale.ROOT, "Expected doc[%d] with id value %s", doc, indexSearcher.doc(doc).get("id"))); } System.out.println("actual cardinality:" + actualResult.cardinality()); iterator = actualResult.iterator(); for (int doc = iterator.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = iterator.nextDoc()) { System.out.println( String.format( Locale.ROOT, "Actual doc[%d] with id value %s", doc, indexSearcher.doc(doc).get("id"))); } } assertEquals(expectedResult, actualResult); // Asserting TopDocs... TopDocs expectedTopDocs = createExpectedTopDocs(randomValue, from, scoreMode, context); TopDocs actualTopDocs = topScoreDocCollector.topDocs(); assertEquals(expectedTopDocs.totalHits, actualTopDocs.totalHits); assertEquals(expectedTopDocs.scoreDocs.length, actualTopDocs.scoreDocs.length); if (scoreMode == ScoreMode.None) { continue; } assertEquals(expectedTopDocs.getMaxScore(), actualTopDocs.getMaxScore(), 0.0f); for (int i = 0; i < expectedTopDocs.scoreDocs.length; i++) { if (VERBOSE) { System.out.printf( Locale.ENGLISH, "Expected doc: %d | Actual doc: %d\n", expectedTopDocs.scoreDocs[i].doc, actualTopDocs.scoreDocs[i].doc); System.out.printf( Locale.ENGLISH, "Expected score: %f | Actual score: %f\n", expectedTopDocs.scoreDocs[i].score, actualTopDocs.scoreDocs[i].score); } assertEquals(expectedTopDocs.scoreDocs[i].doc, actualTopDocs.scoreDocs[i].doc); assertEquals(expectedTopDocs.scoreDocs[i].score, actualTopDocs.scoreDocs[i].score, 0.0f); Explanation explanation = indexSearcher.explain(joinQuery, expectedTopDocs.scoreDocs[i].doc); assertEquals(expectedTopDocs.scoreDocs[i].score, explanation.getValue(), 0.0f); } } topLevelReader.close(); dir.close(); } }