private void duelRun(PercolateQuery.QueryStore queryStore, MemoryIndex memoryIndex, IndexSearcher shardSearcher) throws IOException { boolean requireScore = randomBoolean(); IndexSearcher percolateSearcher = memoryIndex.createSearcher(); Query percolateQuery = fieldType.percolateQuery("type", queryStore, new BytesArray("{}"), percolateSearcher); Query query = requireScore ? percolateQuery : new ConstantScoreQuery(percolateQuery); TopDocs topDocs = shardSearcher.search(query, 10); Query controlQuery = new ControlQuery(memoryIndex, queryStore); controlQuery = requireScore ? controlQuery : new ConstantScoreQuery(controlQuery); TopDocs controlTopDocs = shardSearcher.search(controlQuery, 10); assertThat(topDocs.totalHits, equalTo(controlTopDocs.totalHits)); assertThat(topDocs.scoreDocs.length, equalTo(controlTopDocs.scoreDocs.length)); for (int j = 0; j < topDocs.scoreDocs.length; j++) { assertThat(topDocs.scoreDocs[j].doc, equalTo(controlTopDocs.scoreDocs[j].doc)); assertThat(topDocs.scoreDocs[j].score, equalTo(controlTopDocs.scoreDocs[j].score)); if (requireScore) { Explanation explain1 = shardSearcher.explain(query, topDocs.scoreDocs[j].doc); Explanation explain2 = shardSearcher.explain(controlQuery, controlTopDocs.scoreDocs[j].doc); assertThat(explain1.isMatch(), equalTo(explain2.isMatch())); assertThat(explain1.getValue(), equalTo(explain2.getValue())); } } }
@Override public void hitExecute(SearchContext context, HitContext hitContext) { if (context.explain() == false) { return; } try { final int topLevelDocId = hitContext.hit().docId(); Explanation explanation = context.searcher().explain(context.query(), topLevelDocId); for (RescoreSearchContext rescore : context.rescore()) { explanation = rescore.rescorer().explain(topLevelDocId, context, rescore, explanation); } // we use the top level doc id, since we work with the top level searcher hitContext.hit().explanation(explanation); } catch (IOException e) { throw new FetchPhaseExecutionException(context, "Failed to explain doc [" + hitContext.hit().getType() + "#" + hitContext.hit().getId() + "]", e); } finally { context.clearReleasables(SearchContext.Lifetime.COLLECTION); } }
@Override public Explanation explain(LeafReaderContext context, int doc, float finalScore, List<Explanation> featureExplanations) { String modelDescription = ""; for (int layer = 0; layer < weightMatrices.size(); layer++) { float[][] weightMatrix = weightMatrices.get(layer); int numRows = weightMatrix.length; int numCols = weightMatrix[0].length; if (layer == 0) { modelDescription += String.format("Input has %1$d features.", numCols - 1); } else { modelDescription += String.format("%nHidden layer #%1$d has %2$d units.", layer, numCols); } } return Explanation.match(finalScore, modelDescription); }
void assertAllSearchWorks(String indexName) { logger.info("--> testing _all search"); SearchResponse searchRsp = client().prepareSearch(indexName).get(); ElasticsearchAssertions.assertNoFailures(searchRsp); assertThat(searchRsp.getHits().getTotalHits(), greaterThanOrEqualTo(1L)); SearchHit bestHit = searchRsp.getHits().getAt(0); // Make sure there are payloads and they are taken into account for the score // the 'string' field has a boost of 4 in the mappings so it should get a payload boost String stringValue = (String) bestHit.getSourceAsMap().get("string"); assertNotNull(stringValue); Explanation explanation = client().prepareExplain(indexName, bestHit.getType(), bestHit.getId()) .setQuery(QueryBuilders.matchQuery("_all", stringValue)).get().getExplanation(); assertTrue("Could not find payload boost in explanation\n" + explanation, findPayloadBoostInExplanation(explanation)); // Make sure the query can run on the whole index searchRsp = client().prepareSearch(indexName).setQuery(QueryBuilders.matchQuery("_all", stringValue)).setExplain(true).get(); ElasticsearchAssertions.assertNoFailures(searchRsp); assertThat(searchRsp.getHits().getTotalHits(), greaterThanOrEqualTo(1L)); }
private Explanation explainTFNorm(int doc, Explanation freq, BM25StatsFixed stats, NumericDocValues norms) { List<Explanation> subs = new ArrayList<>(); subs.add(freq); subs.add(Explanation.match(k1, "parameter k1")); if (norms == null) { subs.add(Explanation.match(0, "parameter b (norms omitted for field)")); return Explanation.match( (freq.getValue() * (k1 + 1)) / (freq.getValue() + k1), "tfNorm, computed from:", subs); } else { float doclen = norms.get(doc); subs.add(Explanation.match(b, "parameter b")); subs.add(Explanation.match(stats.avgdl, "avgFieldLength")); subs.add(Explanation.match(doclen, "fieldLength")); return Explanation.match( (freq.getValue() * (k1 + 1)) / (freq.getValue() + k1 * (1 - b + b * doclen/stats.avgdl)), "tfNorm, computed from:", subs); } }
@Override protected void explain(Explanation expl, BasicStats stats, int doc, float freq, float docLen) { if (stats.getTotalBoost() != 1.0f) { expl.addDetail(new Explanation(stats.getTotalBoost(), "boost")); } expl.addDetail(new Explanation(mu, "mu")); Explanation weightExpl = new Explanation(); weightExpl.setValue((float)Math.log(1 + freq / (mu * ((LMStats)stats).getCollectionProbability()))); weightExpl.setDescription("term weight"); expl.addDetail(weightExpl); expl.addDetail(new Explanation( (float)Math.log(mu / (docLen + mu)), "document norm")); super.explain(expl, stats, doc, freq, docLen); }
@Override public void hitExecute(SearchContext context, HitContext hitContext) { try { final int topLevelDocId = hitContext.hit().docId(); Explanation explanation = context.searcher().explain(context.query(), topLevelDocId); for (RescoreSearchContext rescore : context.rescore()) { explanation = rescore.rescorer().explain(topLevelDocId, context, rescore, explanation); } // we use the top level doc id, since we work with the top level searcher hitContext.hit().explanation(explanation); } catch (IOException e) { throw new FetchPhaseExecutionException(context, "Failed to explain doc [" + hitContext.hit().type() + "#" + hitContext.hit().id() + "]", e); } finally { context.clearReleasables(SearchContext.Lifetime.COLLECTION); } }
@Override public Explanation explain(LeafReaderContext context, int doc) throws IOException { Explanation subQueryExpl = subQueryWeight.explain(context, doc); if (!subQueryExpl.isMatch()) { return subQueryExpl; } Explanation expl; if (function != null) { Explanation functionExplanation = function.getLeafScoreFunction(context).explainScore(doc, subQueryExpl); expl = combineFunction.explain(subQueryExpl, functionExplanation, maxBoost); } else { expl = subQueryExpl; } 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 public Explanation explain(Query query, int doc) throws IOException { if (aggregatedDfs != null) { // dfs data is needed to explain the score return super.explain(createNormalizedWeight(query, true), doc); } return in.explain(query, doc); }
@Override public Explanation explain(int topLevelDocId, SearchContext context, RescoreSearchContext rescoreContext, Explanation sourceExplanation) throws IOException { QueryRescoreContext rescore = (QueryRescoreContext) rescoreContext; ContextIndexSearcher searcher = context.searcher(); if (sourceExplanation == null) { // this should not happen but just in case return Explanation.noMatch("nothing matched"); } // TODO: this isn't right? I.e., we are incorrectly pretending all first pass hits were rescored? If the requested docID was // beyond the top rescoreContext.window() in the first pass hits, we don't rescore it now? Explanation rescoreExplain = searcher.explain(rescore.query(), topLevelDocId); float primaryWeight = rescore.queryWeight(); Explanation prim; if (sourceExplanation.isMatch()) { prim = Explanation.match( sourceExplanation.getValue() * primaryWeight, "product of:", sourceExplanation, Explanation.match(primaryWeight, "primaryWeight")); } else { prim = Explanation.noMatch("First pass did not match", sourceExplanation); } // NOTE: we don't use Lucene's Rescorer.explain because we want to insert our own description with which ScoreMode was used. Maybe // we should add QueryRescorer.explainCombine to Lucene? if (rescoreExplain != null && rescoreExplain.isMatch()) { float secondaryWeight = rescore.rescoreQueryWeight(); Explanation sec = Explanation.match( rescoreExplain.getValue() * secondaryWeight, "product of:", rescoreExplain, Explanation.match(secondaryWeight, "secondaryWeight")); QueryRescoreMode scoreMode = rescore.scoreMode(); return Explanation.match( scoreMode.combine(prim.getValue(), sec.getValue()), scoreMode + " of:", prim, sec); } else { return prim; } }
private static Explanation parseExplanation(XContentParser parser) throws IOException { ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation); XContentParser.Token token; Float value = null; String description = null; List<Explanation> details = new ArrayList<>(); while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, () -> parser.getTokenLocation()); String currentFieldName = parser.currentName(); token = parser.nextToken(); if (Fields.VALUE.equals(currentFieldName)) { value = parser.floatValue(); } else if (Fields.DESCRIPTION.equals(currentFieldName)) { description = parser.textOrNull(); } else if (Fields.DETAILS.equals(currentFieldName)) { ensureExpectedToken(XContentParser.Token.START_ARRAY, token, () -> parser.getTokenLocation()); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { details.add(parseExplanation(parser)); } } else { throwUnknownField(currentFieldName, parser.getTokenLocation()); } } if (value == null) { throw new ParsingException(parser.getTokenLocation(), "missing explanation value"); } if (description == null) { throw new ParsingException(parser.getTokenLocation(), "missing explanation description"); } return Explanation.match(value, description, details); }
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(); }
public static Explanation readExplanation(StreamInput in) throws IOException { boolean match = in.readBoolean(); String description = in.readString(); final Explanation[] subExplanations = new Explanation[in.readVInt()]; for (int i = 0; i < subExplanations.length; ++i) { subExplanations[i] = readExplanation(in); } if (match) { return Explanation.match(in.readFloat(), description, subExplanations); } else { return Explanation.noMatch(description, subExplanations); } }
public static void writeExplanation(StreamOutput out, Explanation explanation) throws IOException { out.writeBoolean(explanation.isMatch()); out.writeString(explanation.getDescription()); Explanation[] subExplanations = explanation.getDetails(); out.writeVInt(subExplanations.length); for (Explanation subExp : subExplanations) { writeExplanation(out, subExp); } if (explanation.isMatch()) { out.writeFloat(explanation.getValue()); } }
@Override protected ExplainResponse shardOperation(ExplainRequest request, ShardId shardId) throws IOException { ShardSearchLocalRequest shardSearchLocalRequest = new ShardSearchLocalRequest(shardId, new String[]{request.type()}, request.nowInMillis, request.filteringAlias()); SearchContext context = searchService.createSearchContext(shardSearchLocalRequest, SearchService.NO_TIMEOUT, null); Term uidTerm = new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(request.type(), request.id())); Engine.GetResult result = null; try { result = context.indexShard().get(new Engine.Get(false, uidTerm)); if (!result.exists()) { return new ExplainResponse(shardId.getIndexName(), request.type(), request.id(), false); } context.parsedQuery(context.getQueryShardContext().toQuery(request.query())); context.preProcess(true); int topLevelDocId = result.docIdAndVersion().docId + result.docIdAndVersion().context.docBase; Explanation explanation = context.searcher().explain(context.query(), topLevelDocId); for (RescoreSearchContext ctx : context.rescore()) { Rescorer rescorer = ctx.rescorer(); explanation = rescorer.explain(topLevelDocId, context, ctx, explanation); } if (request.storedFields() != null || (request.fetchSourceContext() != null && request.fetchSourceContext().fetchSource())) { // Advantage is that we're not opening a second searcher to retrieve the _source. Also // because we are working in the same searcher in engineGetResult we can be sure that a // doc isn't deleted between the initial get and this call. GetResult getResult = context.indexShard().getService().get(result, request.id(), request.type(), request.storedFields(), request.fetchSourceContext()); return new ExplainResponse(shardId.getIndexName(), request.type(), request.id(), true, explanation, getResult); } else { return new ExplainResponse(shardId.getIndexName(), request.type(), request.id(), true, explanation); } } catch (IOException e) { throw new ElasticsearchException("Could not explain", e); } finally { Releasables.close(result, context); } }
private static Explanation createExplanation(int depth) { String description = randomAsciiOfLengthBetween(5, 20); float value = randomFloat(); List<Explanation> details = new ArrayList<>(); if (depth > 0) { int numberOfDetails = randomIntBetween(1, 3); for (int i = 0; i < numberOfDetails; i++) { details.add(createExplanation(depth - 1)); } } return Explanation.match(value, description, details); }
public void testExplain() throws Exception { assertAcked(prepareCreate("test") .addMapping("type1", jsonBuilder().startObject().startObject("type1").startObject("properties") .startObject("nested1") .field("type", "nested") .endObject() .endObject().endObject().endObject())); ensureGreen(); client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject() .field("field1", "value1") .startArray("nested1") .startObject() .field("n_field1", "n_value1") .endObject() .startObject() .field("n_field1", "n_value1") .endObject() .endArray() .endObject()) .setRefreshPolicy(IMMEDIATE) .execute().actionGet(); SearchResponse searchResponse = client().prepareSearch("test") .setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1"), ScoreMode.Total)) .setExplain(true) .execute().actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); Explanation explanation = searchResponse.getHits().getHits()[0].getExplanation(); assertThat(explanation.getValue(), equalTo(searchResponse.getHits().getHits()[0].getScore())); assertThat(explanation.toString(), startsWith("0.36464313 = Score based on 2 child docs in range from 0 to 1")); }
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; } }
BM25StatsFixed(String field, float k1, float b, Explanation idf, float avgdl) { this.field = field; this.idf = idf; this.avgdl = avgdl; this.multK1minusB = k1 * (1 - b); // Normally avgdl should be >= 1, but let's use Math.max to avoid division by zero just in case this.multK1_b_InvAvgdl = k1 * b / Math.max(1e-10f, avgdl); normalize(1f, 1f); }
public void checkFiltersFunctionScoreExplanation(Explanation randomExplanation, String functionExpl, int whichFunction) { assertThat(randomExplanation.getDescription(), equalTo("min of:")); Explanation explanation = randomExplanation.getDetails()[0]; assertThat(explanation.getDescription(), equalTo("function score, score mode [avg]")); Explanation functionExplanation = randomExplanation.getDetails()[0].getDetails()[whichFunction]; assertThat(functionExplanation.getDescription(), equalTo("function score, product of:")); assertThat(functionExplanation.getDetails()[0].getDescription(), equalTo("match filter: " + FIELD + ":" + TERM.text())); assertThat(functionExplanation.getDetails()[1].getDescription(), equalTo(functionExpl)); }
@Override public Explanation explain(AtomicReaderContext context, int doc) throws IOException { PayloadNearSpanScorer scorer = (PayloadNearSpanScorer) scorer(context, context.reader().getLiveDocs()); if (scorer != null) { int newDoc = scorer.advance(doc); if (newDoc == doc) { float freq = scorer.freq(); SimScorer docScorer = similarity.simScorer(stats, context); Explanation expl = new Explanation(); expl.setDescription("weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:"); Explanation scoreExplanation = docScorer.explain(doc, new Explanation(freq, "phraseFreq=" + freq)); expl.addDetail(scoreExplanation); expl.setValue(scoreExplanation.getValue()); String field = ((SpanQuery)getQuery()).getField(); // now the payloads part Explanation payloadExpl = function.explain(doc, field, scorer.payloadsSeen, scorer.payloadScore); // combined ComplexExplanation result = new ComplexExplanation(); result.addDetail(expl); result.addDetail(payloadExpl); result.setValue(expl.getValue() * payloadExpl.getValue()); result.setDescription("PayloadNearQuery, product of:"); return result; } } return new ComplexExplanation(false, 0.0f, "no matching term"); }
@Override public Explanation explain(AtomicReaderContext context, int doc) throws IOException { PayloadTermSpanScorer scorer = (PayloadTermSpanScorer) scorer(context, context.reader().getLiveDocs()); if (scorer != null) { int newDoc = scorer.advance(doc); if (newDoc == doc) { float freq = scorer.sloppyFreq(); SimScorer docScorer = similarity.simScorer(stats, context); Explanation expl = new Explanation(); expl.setDescription("weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:"); Explanation scoreExplanation = docScorer.explain(doc, new Explanation(freq, "phraseFreq=" + freq)); expl.addDetail(scoreExplanation); expl.setValue(scoreExplanation.getValue()); // now the payloads part // QUESTION: Is there a way to avoid this skipTo call? We need to know // whether to load the payload or not // GSI: I suppose we could toString the payload, but I don't think that // would be a good idea String field = ((SpanQuery)getQuery()).getField(); Explanation payloadExpl = function.explain(doc, field, scorer.payloadsSeen, scorer.payloadScore); payloadExpl.setValue(scorer.getPayloadScore()); // combined ComplexExplanation result = new ComplexExplanation(); if (includeSpanScore) { result.addDetail(expl); result.addDetail(payloadExpl); result.setValue(expl.getValue() * payloadExpl.getValue()); result.setDescription("btq, product of:"); } else { result.addDetail(payloadExpl); result.setValue(payloadExpl.getValue()); result.setDescription("btq(includeSpanScore=false), result of:"); } result.setMatch(true); // LUCENE-1303 return result; } } return new ComplexExplanation(false, 0.0f, "no matching term"); }
/** * Returns an explanation for the score. * <p>Most basic models use the number of documents and the total term * frequency to compute Inf<sub>1</sub>. This method provides a generic * explanation for such models. Subclasses that use other statistics must * override this method.</p> */ public Explanation explain(BasicStats stats, float tfn) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(score(stats, tfn)); result.addDetail(new Explanation(tfn, "tfn")); result.addDetail( new Explanation(stats.getNumberOfDocuments(), "numberOfDocuments")); result.addDetail( new Explanation(stats.getTotalTermFreq(), "totalTermFreq")); return result; }
@Override protected void explain( Explanation expl, BasicStats stats, int doc, float freq, float docLen) { if (stats.getTotalBoost() != 1.0f) { expl.addDetail(new Explanation(stats.getTotalBoost(), "boost")); } Explanation normExpl = normalization.explain(stats, freq, docLen); Explanation lambdaExpl = lambda.explain(stats); expl.addDetail(normExpl); expl.addDetail(lambdaExpl); expl.addDetail(distribution.explain( stats, normExpl.getValue(), lambdaExpl.getValue())); }
@Override public final Explanation explain(BasicStats stats, float tfn) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(score(stats, tfn)); result.addDetail(new Explanation(tfn, "tfn")); result.addDetail( new Explanation(stats.getNumberOfDocuments(), "numberOfDocuments")); result.addDetail( new Explanation(stats.getDocFreq(), "docFreq")); return result; }
@Override public final Explanation explain(BasicStats stats, float tfn) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(score(stats, tfn)); result.addDetail(new Explanation(tfn, "tfn")); result.addDetail(new Explanation(stats.getTotalTermFreq(), "totalTermFreq")); result.addDetail(new Explanation(stats.getDocFreq(), "docFreq")); return result; }
/** * Computes a score factor for a phrase. * * <p> * The default implementation sums the idf factor for * each term in the phrase. * * @param collectionStats collection-level statistics * @param termStats term-level statistics for the terms in the phrase * @return an Explain object that includes both an idf * score factor for the phrase and an explanation * for each term. */ public Explanation idfExplain(CollectionStatistics collectionStats, TermStatistics termStats[]) { final long max = collectionStats.maxDoc(); float idf = 0.0f; final Explanation exp = new Explanation(); exp.setDescription("idf(), sum of:"); for (final TermStatistics stat : termStats ) { final long df = stat.docFreq(); final float termIdf = idf(df, max); exp.addDetail(new Explanation(termIdf, "idf(docFreq=" + df + ", maxDocs=" + max + ")")); idf += termIdf; } exp.setValue(idf); return exp; }
@Override public final SimWeight computeWeight(float queryBoost, CollectionStatistics collectionStats, TermStatistics... termStats) { Explanation idf = termStats.length == 1 ? idfExplain(collectionStats, termStats[0]) : idfExplain(collectionStats, termStats); float avgdl = avgFieldLength(collectionStats); // compute freq-independent part of bm25 equation across all norm values float cache[] = new float[256]; for (int i = 0; i < cache.length; i++) { cache[i] = k1 * ((1 - b) + b * decodeNormValue((byte)i) / avgdl); } return new BM25Stats(collectionStats.field(), idf, queryBoost, avgdl, cache); }
BM25Stats(String field, Explanation idf, float queryBoost, float avgdl, float cache[]) { this.field = field; this.idf = idf; this.queryBoost = queryBoost; this.avgdl = avgdl; this.cache = cache; }
private Explanation explainScore(int doc, Explanation freq, BM25Stats stats, NumericDocValues norms) { Explanation result = new Explanation(); result.setDescription("score(doc="+doc+",freq="+freq+"), product of:"); Explanation boostExpl = new Explanation(stats.queryBoost * stats.topLevelBoost, "boost"); if (boostExpl.getValue() != 1.0f) result.addDetail(boostExpl); result.addDetail(stats.idf); Explanation tfNormExpl = new Explanation(); tfNormExpl.setDescription("tfNorm, computed from:"); tfNormExpl.addDetail(freq); tfNormExpl.addDetail(new Explanation(k1, "parameter k1")); if (norms == null) { tfNormExpl.addDetail(new Explanation(0, "parameter b (norms omitted for field)")); tfNormExpl.setValue((freq.getValue() * (k1 + 1)) / (freq.getValue() + k1)); } else { float doclen = decodeNormValue((byte)norms.get(doc)); tfNormExpl.addDetail(new Explanation(b, "parameter b")); tfNormExpl.addDetail(new Explanation(stats.avgdl, "avgFieldLength")); tfNormExpl.addDetail(new Explanation(doclen, "fieldLength")); tfNormExpl.setValue((freq.getValue() * (k1 + 1)) / (freq.getValue() + k1 * (1 - b + b * doclen/stats.avgdl))); } result.addDetail(tfNormExpl); result.setValue(boostExpl.getValue() * stats.idf.getValue() * tfNormExpl.getValue()); return result; }
@Override protected void explain(Explanation expl, BasicStats stats, int doc, float freq, float docLen) { if (stats.getTotalBoost() != 1.0f) { expl.addDetail(new Explanation(stats.getTotalBoost(), "boost")); } expl.addDetail(new Explanation(lambda, "lambda")); super.explain(expl, stats, doc, freq, docLen); }
@Override public final Explanation explain(BasicStats stats, float tfn) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(score(stats, tfn)); result.addDetail(new Explanation(tfn, "tfn")); return result; }
@Override public final Explanation explain(BasicStats stats) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(lambda(stats)); result.addDetail( new Explanation(stats.getDocFreq(), "docFreq")); result.addDetail( new Explanation(stats.getNumberOfDocuments(), "numberOfDocuments")); return result; }
/** * Explain the score for a single document * @param doc document id within the inverted index segment * @param freq Explanation of how the sloppy term frequency was computed * @return document's score */ public Explanation explain(int doc, Explanation freq) { Explanation result = new Explanation(score(doc, freq.getValue()), "score(doc=" + doc + ",freq=" + freq.getValue() +"), with freq of:"); result.addDetail(freq); return result; }
/** Returns an explanation for the normalized term frequency. * <p>The default normalization methods use the field length of the document * and the average field length to compute the normalized term frequency. * This method provides a generic explanation for such methods. * Subclasses that use other statistics must override this method.</p> */ public Explanation explain(BasicStats stats, float tf, float len) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(tfn(stats, tf, len)); result.addDetail(new Explanation(tf, "tf")); result.addDetail( new Explanation(stats.getAvgFieldLength(), "avgFieldLength")); result.addDetail(new Explanation(len, "len")); return result; }
@Override protected void explain(Explanation expl, BasicStats stats, int doc, float freq, float docLen) { if (stats.getTotalBoost() != 1.0f) { expl.addDetail(new Explanation(stats.getTotalBoost(), "boost")); } Explanation normExpl = normalization.explain(stats, freq, docLen); float tfn = normExpl.getValue(); expl.addDetail(normExpl); expl.addDetail(basicModel.explain(stats, tfn)); expl.addDetail(afterEffect.explain(stats, tfn)); }
@Override public final Explanation explain(BasicStats stats) { Explanation result = new Explanation(); result.setDescription(getClass().getSimpleName() + ", computed from: "); result.setValue(lambda(stats)); result.addDetail( new Explanation(stats.getTotalTermFreq(), "totalTermFreq")); result.addDetail( new Explanation(stats.getNumberOfDocuments(), "numberOfDocuments")); return result; }
@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; }
@Override public final SimWeight computeWeight(CollectionStatistics collectionStats, TermStatistics... termStats) { Explanation idf = termStats.length == 1 ? idfExplain(collectionStats, termStats[0]) : idfExplain(collectionStats, termStats); float avgdl = avgFieldLength(collectionStats); return new BM25StatsFixed(collectionStats.field(), k1, b, idf, avgdl); }
@Override public final SimWeight computeWeight(float queryBoost, CollectionStatistics collectionStats, TermStatistics... termStats) { final Explanation idf = termStats.length == 1 ? idfExplain(collectionStats, termStats[0]) : idfExplain(collectionStats, termStats); return new IDFStats(collectionStats.field(), idf, queryBoost); }