You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2015/04/21 15:23:04 UTC

svn commit: r1675114 [1/2] - in /lucene/dev/branches/branch_5x: ./ lucene/ lucene/core/ lucene/core/src/java/org/apache/lucene/search/ lucene/core/src/java/org/apache/lucene/search/payloads/ lucene/core/src/java/org/apache/lucene/search/similarities/ l...

Author: jpountz
Date: Tue Apr 21 13:23:02 2015
New Revision: 1675114

URL: http://svn.apache.org/r1675114
Log:
LUCENE-6446: Simplified Explanation API.

Removed:
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ComplexExplanation.java
Modified:
    lucene/dev/branches/branch_5x/   (props changed)
    lucene/dev/branches/branch_5x/lucene/   (props changed)
    lucene/dev/branches/branch_5x/lucene/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_5x/lucene/core/   (props changed)
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Explanation.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Filter.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/SortRescorer.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadFunction.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffect.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Distribution.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/MultiSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/index/TestOmitTf.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/TestDocValuesScoring.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/TestSimilarity.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/TestSortRescorer.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/payloads/TestPayloadNearQuery.java
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/search/similarities/TestSimilarityBase.java
    lucene/dev/branches/branch_5x/lucene/expressions/   (props changed)
    lucene/dev/branches/branch_5x/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java
    lucene/dev/branches/branch_5x/lucene/join/   (props changed)
    lucene/dev/branches/branch_5x/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java
    lucene/dev/branches/branch_5x/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java
    lucene/dev/branches/branch_5x/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
    lucene/dev/branches/branch_5x/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
    lucene/dev/branches/branch_5x/lucene/misc/   (props changed)
    lucene/dev/branches/branch_5x/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java
    lucene/dev/branches/branch_5x/lucene/queries/   (props changed)
    lucene/dev/branches/branch_5x/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreProvider.java
    lucene/dev/branches/branch_5x/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
    lucene/dev/branches/branch_5x/lucene/queries/src/java/org/apache/lucene/queries/TermsQuery.java
    lucene/dev/branches/branch_5x/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
    lucene/dev/branches/branch_5x/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
    lucene/dev/branches/branch_5x/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionValues.java
    lucene/dev/branches/branch_5x/lucene/queries/src/test/org/apache/lucene/queries/TestCustomScoreQuery.java
    lucene/dev/branches/branch_5x/lucene/spatial/   (props changed)
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/   (props changed)
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxValueSource.java
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/util/DistanceToShapeValueSource.java
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java   (contents, props changed)
    lucene/dev/branches/branch_5x/lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapePredicateValueSource.java
    lucene/dev/branches/branch_5x/lucene/test-framework/   (props changed)
    lucene/dev/branches/branch_5x/lucene/test-framework/src/java/org/apache/lucene/search/CheckHits.java
    lucene/dev/branches/branch_5x/solr/   (props changed)
    lucene/dev/branches/branch_5x/solr/core/   (props changed)
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/schema/LatLonType.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SolrPluginUtils.java

Modified: lucene/dev/branches/branch_5x/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/CHANGES.txt?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/CHANGES.txt (original)
+++ lucene/dev/branches/branch_5x/lucene/CHANGES.txt Tue Apr 21 13:23:02 2015
@@ -94,6 +94,8 @@ API Changes
 * LUCENE-6425: Replaced Query.extractTerms with Weight.extractTerms.
   (Adrien Grand)
 
+* LUCENE-6446: Simplified Explanation API. (Adrien Grand)
+
 Other
 
 * LUCENE-6413: Test runner should report the number of suites completed/ 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java Tue Apr 21 13:23:02 2015
@@ -135,8 +135,7 @@ public class BooleanWeight extends Weigh
   @Override
   public Explanation explain(LeafReaderContext context, int doc) throws IOException {
     final int minShouldMatch = query.getMinimumNumberShouldMatch();
-    ComplexExplanation sumExpl = new ComplexExplanation();
-    sumExpl.setDescription("sum of:");
+    List<Explanation> subs = new ArrayList<>();
     int coord = 0;
     float sum = 0.0f;
     boolean fail = false;
@@ -146,30 +145,17 @@ public class BooleanWeight extends Weigh
     for (Iterator<Weight> wIter = weights.iterator(); wIter.hasNext();) {
       Weight w = wIter.next();
       BooleanClause c = cIter.next();
-      if (w.scorer(context, context.reader().getLiveDocs()) == null) {
-        if (c.isRequired()) {
-          fail = true;
-          Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")");
-          sumExpl.addDetail(r);
-        }
-        continue;
-      }
       Explanation e = w.explain(context, doc);
       if (e.isMatch()) {
         if (c.isScoring()) {
-          sumExpl.addDetail(e);
+          subs.add(e);
           sum += e.getValue();
           coord++;
         } else if (c.isRequired()) {
-          Explanation r = new Explanation(0f, "match on required clause, product of:");
-          r.addDetail(new Explanation(0f, Occur.FILTER + " clause"));
-          r.addDetail(e);
-          sumExpl.addDetail(r);
+          subs.add(Explanation.match(0f, "match on required clause, product of:",
+              Explanation.match(0f, Occur.FILTER + " clause"), e));
         } else if (c.isProhibited()) {
-          Explanation r =
-            new Explanation(0.0f, "match on prohibited clause (" + c.getQuery().toString() + ")");
-          r.addDetail(e);
-          sumExpl.addDetail(r);
+          subs.add(Explanation.noMatch("match on prohibited clause (" + c.getQuery().toString() + ")", e));
           fail = true;
         }
         if (!c.isProhibited()) {
@@ -179,39 +165,24 @@ public class BooleanWeight extends Weigh
           shouldMatchCount++;
         }
       } else if (c.isRequired()) {
-        Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")");
-        r.addDetail(e);
-        sumExpl.addDetail(r);
+        subs.add(Explanation.noMatch("no match on required clause (" + c.getQuery().toString() + ")", e));
         fail = true;
       }
     }
     if (fail) {
-      sumExpl.setMatch(Boolean.FALSE);
-      sumExpl.setValue(0.0f);
-      sumExpl.setDescription
-        ("Failure to meet condition(s) of required/prohibited clause(s)");
-      return sumExpl;
+      return Explanation.noMatch("Failure to meet condition(s) of required/prohibited clause(s)", subs);
+    } else if (matchCount == 0) {
+      return Explanation.noMatch("No matching clauses", subs);
     } else if (shouldMatchCount < minShouldMatch) {
-      sumExpl.setMatch(Boolean.FALSE);
-      sumExpl.setValue(0.0f);
-      sumExpl.setDescription("Failure to match minimum number "+
-                             "of optional clauses: " + minShouldMatch);
-      return sumExpl;
-    }
-    
-    sumExpl.setMatch(0 < matchCount);
-    sumExpl.setValue(sum);
-    
-    final float coordFactor = disableCoord ? 1.0f : coord(coord, maxCoord);
-    if (coordFactor == 1.0f) {
-      return sumExpl;                             // eliminate wrapper
+      return Explanation.noMatch("Failure to match minimum number of optional clauses: " + minShouldMatch, subs);
     } else {
-      ComplexExplanation result = new ComplexExplanation(sumExpl.isMatch(),
-                                                         sum*coordFactor,
-                                                         "product of:");
-      result.addDetail(sumExpl);
-      result.addDetail(new Explanation(coordFactor,
-                                       "coord("+coord+"/"+maxCoord+")"));
+      // we have a match
+      Explanation result = Explanation.match(sum, "sum of:", subs);
+      final float coordFactor = disableCoord ? 1.0f : coord(coord, maxCoord);
+      if (coordFactor != 1f) {
+        result = Explanation.match(sum * coordFactor, "product of:",
+            result, Explanation.match(coordFactor, "coord("+coord+"/"+maxCoord+")"));
+      }
       return result;
     }
   }

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java Tue Apr 21 13:23:02 2015
@@ -117,19 +117,13 @@ public class ConstantScoreQuery extends
       final Scorer cs = scorer(context, context.reader().getLiveDocs());
       final boolean exists = (cs != null && cs.advance(doc) == doc);
 
-      final ComplexExplanation result = new ComplexExplanation();
       if (exists) {
-        result.setDescription(ConstantScoreQuery.this.toString() + ", product of:");
-        result.setValue(queryWeight);
-        result.setMatch(Boolean.TRUE);
-        result.addDetail(new Explanation(getBoost(), "boost"));
-        result.addDetail(new Explanation(queryNorm, "queryNorm"));
+        return Explanation.match(
+            queryWeight, ConstantScoreQuery.this.toString() + ", product of:",
+            Explanation.match(getBoost(), "boost"), Explanation.match(queryNorm, "queryNorm"));
       } else {
-        result.setDescription(ConstantScoreQuery.this.toString() + " doesn't match id " + doc);
-        result.setValue(0);
-        result.setMatch(Boolean.FALSE);
+        return Explanation.noMatch(ConstantScoreQuery.this.toString() + " doesn't match id " + doc);
       }
-      return result;
     }
   }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java Tue Apr 21 13:23:02 2015
@@ -62,19 +62,13 @@ public abstract class ConstantScoreWeigh
     final Scorer s = scorer(context, context.reader().getLiveDocs());
     final boolean exists = (s != null && s.advance(doc) == doc);
 
-    final ComplexExplanation result = new ComplexExplanation();
     if (exists) {
-      result.setDescription(getQuery().toString() + ", product of:");
-      result.setValue(queryWeight);
-      result.setMatch(Boolean.TRUE);
-      result.addDetail(new Explanation(getQuery().getBoost(), "boost"));
-      result.addDetail(new Explanation(queryNorm, "queryNorm"));
+      return Explanation.match(
+          queryWeight, getQuery().toString() + ", product of:",
+          Explanation.match(getQuery().getBoost(), "boost"), Explanation.match(queryNorm, "queryNorm"));
     } else {
-      result.setDescription(getQuery().toString() + " doesn't match id " + doc);
-      result.setValue(0);
-      result.setMatch(Boolean.FALSE);
+      return Explanation.noMatch(getQuery().toString() + " doesn't match id " + doc);
     }
-    return result;
   }
 
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java Tue Apr 21 13:23:02 2015
@@ -184,21 +184,25 @@ public class DisjunctionMaxQuery extends
     /** Explain the score we computed for doc */
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      if (disjuncts.size() == 1) return weights.get(0).explain(context,doc);
-      ComplexExplanation result = new ComplexExplanation();
+      boolean match = false;
       float max = 0.0f, sum = 0.0f;
-      result.setDescription(tieBreakerMultiplier == 0.0f ? "max of:" : "max plus " + tieBreakerMultiplier + " times others of:");
+      List<Explanation> subs = new ArrayList<>();
       for (Weight wt : weights) {
         Explanation e = wt.explain(context, doc);
         if (e.isMatch()) {
-          result.setMatch(Boolean.TRUE);
-          result.addDetail(e);
+          match = true;
+          subs.add(e);
           sum += e.getValue();
           max = Math.max(max, e.getValue());
         }
       }
-      result.setValue(max + (sum - max) * tieBreakerMultiplier);
-      return result;
+      if (match) {
+        final float score = max + (sum - max) * tieBreakerMultiplier;
+        final String desc = tieBreakerMultiplier == 0.0f ? "max of:" : "max plus " + tieBreakerMultiplier + " times others of:";
+        return Explanation.match(score, desc, subs);
+      } else {
+        return Explanation.noMatch("No matching clause");
+      }
     }
     
   }  // end of DisjunctionMaxWeight inner class

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Explanation.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Explanation.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Explanation.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Explanation.java Tue Apr 21 13:23:02 2015
@@ -18,45 +18,74 @@ package org.apache.lucene.search;
  */
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 
 /** Expert: Describes the score computation for document and query. */
 public class Explanation {
-  private float value;                            // the value of this node
-  private String description;                     // what it represents
-  private ArrayList<Explanation> details;                      // sub-explanations
 
-  public Explanation() {}
+  /**
+   * Create a new explanation for a match.
+   * @param value       the contribution to the score of the document
+   * @param description how {@code value} was computed
+   * @param details     sub explanations that contributed to this explanation
+   */
+  public static Explanation match(float value, String description, Collection<Explanation> details) {
+    return new Explanation(true, value, description, details);
+  }
 
-  public Explanation(float value, String description) {
-    this.value = value;
-    this.description = description;
+  /**
+   * Create a new explanation for a match.
+   * @param value       the contribution to the score of the document
+   * @param description how {@code value} was computed
+   * @param details     sub explanations that contributed to this explanation
+   */
+  public static Explanation match(float value, String description, Explanation... details) {
+    return new Explanation(true, value, description, Arrays.asList(details));
   }
 
   /**
-   * Indicates whether or not this Explanation models a good match.
-   *
-   * <p>
-   * By default, an Explanation represents a "match" if the value is positive.
-   * </p>
-   * @see #getValue
+   * Create a new explanation for a document which does not match.
    */
-  public boolean isMatch() {
-    return (0.0f < getValue());
+  public static Explanation noMatch(String description, Collection<Explanation> details) {
+    return new Explanation(false, 0f, description, Collections.<Explanation>emptyList());
+  }
+
+  /**
+   * Create a new explanation for a document which does not match.
+   */
+  public static Explanation noMatch(String description, Explanation... details) {
+    return new Explanation(false, 0f, description, Collections.<Explanation>emptyList());
   }
 
+  private final boolean match;                          // whether the document matched
+  private final float value;                            // the value of this node
+  private final String description;                     // what it represents
+  private final List<Explanation> details;              // sub-explanations
+
+  /** Create a new explanation  */
+  private Explanation(boolean match, float value, String description, Collection<Explanation> details) {
+    this.match = match;
+    this.value = value;
+    this.description = Objects.requireNonNull(description);
+    this.details = Collections.unmodifiableList(new ArrayList<>(details));
+  }
 
+  /**
+   * Indicates whether or not this Explanation models a match.
+   */
+  public boolean isMatch() {
+    return match;
+  }
   
   /** The value assigned to this explanation node. */
   public float getValue() { return value; }
-  /** Sets the value assigned to this explanation node. */
-  public void setValue(float value) { this.value = value; }
 
   /** A description of this explanation node. */
   public String getDescription() { return description; }
-  /** Sets the description of this explanation node. */
-  public void setDescription(String description) {
-    this.description = description;
-  }
 
   /**
    * A short one line summary which should contain all high level
@@ -68,18 +97,9 @@ public class Explanation {
   
   /** The sub-nodes of this explanation node. */
   public Explanation[] getDetails() {
-    if (details == null)
-      return null;
     return details.toArray(new Explanation[0]);
   }
 
-  /** Adds a sub-node to this explanation node. */
-  public void addDetail(Explanation detail) {
-    if (details == null)
-      details = new ArrayList<>();
-    details.add(detail);
-  }
-
   /** Render an explanation as text. */
   @Override
   public String toString() {
@@ -94,10 +114,8 @@ public class Explanation {
     buffer.append("\n");
 
     Explanation[] details = getDetails();
-    if (details != null) {
-      for (int i = 0 ; i < details.length; i++) {
-        buffer.append(details[i].toString(depth+1));
-      }
+    for (int i = 0 ; i < details.length; i++) {
+      buffer.append(details[i].toString(depth+1));
     }
 
     return buffer.toString();
@@ -114,10 +132,8 @@ public class Explanation {
     buffer.append("<br />\n");
 
     Explanation[] details = getDetails();
-    if (details != null) {
-      for (int i = 0 ; i < details.length; i++) {
-        buffer.append(details[i].toHtml());
-      }
+    for (int i = 0 ; i < details.length; i++) {
+      buffer.append(details[i].toHtml());
     }
 
     buffer.append("</li>\n");

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Filter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Filter.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Filter.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/Filter.java Tue Apr 21 13:23:02 2015
@@ -95,14 +95,12 @@ public abstract class Filter extends Que
       public Explanation explain(LeafReaderContext context, int doc) throws IOException {
         final Scorer scorer = scorer(context, context.reader().getLiveDocs());
         final boolean match = (scorer != null && scorer.advance(doc) == doc);
-        final String desc;
         if (match) {
           assert scorer.score() == 0f;
-          desc = "Match on id " + doc;
+          return Explanation.match(0f, "Match on id " + doc);
         } else {
-          desc = "No match on id " + doc;
+          return Explanation.match(0f, "No match on id " + doc);
         }
-        return new ComplexExplanation(match, 0f, desc);
       }
 
       @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java Tue Apr 21 13:23:02 2015
@@ -108,10 +108,7 @@ public class FilteredQuery extends Query
         if (docIdSetIterator.advance(i) == i) {
           return inner;
         } else {
-          Explanation result = new Explanation
-            (0.0f, "failure to match filter: " + f.toString());
-          result.addDetail(inner);
-          return result;
+          return Explanation.noMatch("failure to match filter: " + f.toString(), inner);
         }
       }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java Tue Apr 21 13:23:02 2015
@@ -18,6 +18,8 @@ package org.apache.lucene.search;
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.lucene.index.IndexReader;
@@ -119,15 +121,12 @@ public final class MatchAllDocsQuery ext
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) {
-      // explain query weight
-      Explanation queryExpl = new ComplexExplanation
-        (true, queryWeight, "MatchAllDocsQuery, product of:");
+      List<Explanation> subs = new ArrayList<>();
       if (getBoost() != 1.0f) {
-        queryExpl.addDetail(new Explanation(getBoost(),"boost"));
+        subs.add(Explanation.match(getBoost(),"boost"));
       }
-      queryExpl.addDetail(new Explanation(queryNorm,"queryNorm"));
-
-      return queryExpl;
+      subs.add(Explanation.match(queryNorm, "queryNorm"));
+      return Explanation.match(queryWeight, "MatchAllDocsQuery, product of:", subs);
     }
   }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java Tue Apr 21 13:23:02 2015
@@ -244,17 +244,16 @@ public class MultiPhraseQuery extends Qu
         if (newDoc == doc) {
           float freq = slop == 0 ? scorer.freq() : ((SloppyPhraseScorer)scorer).sloppyFreq();
           SimScorer docScorer = similarity.simScorer(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;
+          Explanation freqExplanation = Explanation.match(freq, "phraseFreq=" + freq);
+          Explanation scoreExplanation = docScorer.explain(doc, freqExplanation);
+          return Explanation.match(
+              scoreExplanation.getValue(),
+              "weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:",
+              scoreExplanation);
         }
       }
       
-      return new ComplexExplanation(false, 0.0f, "no matching term");
+      return Explanation.noMatch("no matching term");
     }
   }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java Tue Apr 21 13:23:02 2015
@@ -341,17 +341,16 @@ public class PhraseQuery extends Query {
         if (newDoc == doc) {
           float freq = slop == 0 ? scorer.freq() : ((SloppyPhraseScorer)scorer).sloppyFreq();
           SimScorer docScorer = similarity.simScorer(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;
+          Explanation freqExplanation = Explanation.match(freq, "phraseFreq=" + freq);
+          Explanation scoreExplanation = docScorer.explain(doc, freqExplanation);
+          return Explanation.match(
+              scoreExplanation.getValue(),
+              "weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:",
+              scoreExplanation);
         }
       }
       
-      return new ComplexExplanation(false, 0.0f, "no matching term");
+      return Explanation.noMatch("no matching term");
     }
   }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java Tue Apr 21 13:23:02 2015
@@ -17,14 +17,13 @@ package org.apache.lucene.search;
  * limitations under the License.
  */
 
-import org.apache.lucene.index.PostingsEnum;
-import org.apache.lucene.index.LeafReaderContext;
-
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
 
+import org.apache.lucene.index.LeafReaderContext;
+
 /** A {@link Rescorer} that uses a provided Query to assign
  *  scores to the first-pass hits.
  *
@@ -151,22 +150,16 @@ public abstract class QueryRescorer exte
       score = combine(firstPassExplanation.getValue(), true,  secondPassScore.floatValue());
     }
 
-    Explanation result = new Explanation(score, "combined first and second pass score using " + getClass());
-
-    Explanation first = new Explanation(firstPassExplanation.getValue(), "first pass score");
-    first.addDetail(firstPassExplanation);
-    result.addDetail(first);
+    Explanation first = Explanation.match(firstPassExplanation.getValue(), "first pass score", firstPassExplanation);
 
     Explanation second;
     if (secondPassScore == null) {
-      second = new Explanation(0.0f, "no second pass score");
+      second = Explanation.noMatch("no second pass score");
     } else {
-      second = new Explanation(secondPassScore, "second pass score");
+      second = Explanation.match(secondPassScore, "second pass score", secondPassExplanation);
     }
-    second.addDetail(secondPassExplanation);
-    result.addDetail(second);
 
-    return result;
+    return Explanation.match(score, "combined first and second pass score using " + getClass(), first, second);
   }
 
   /** Sugar API, calling {#rescore} using a simple linear

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/SortRescorer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/SortRescorer.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/SortRescorer.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/SortRescorer.java Tue Apr 21 13:23:02 2015
@@ -18,6 +18,7 @@ package org.apache.lucene.search;
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
@@ -98,23 +99,22 @@ public class SortRescorer extends Rescor
     TopDocs hits = rescore(searcher, oneHit, 1);
     assert hits.totalHits == 1;
 
-    // TODO: if we could ask the Sort to explain itself then
-    // we wouldn't need the separate ExpressionRescorer...
-    Explanation result = new Explanation(0.0f, "sort field values for sort=" + sort.toString());
+    List<Explanation> subs = new ArrayList<>();
 
     // Add first pass:
-    Explanation first = new Explanation(firstPassExplanation.getValue(), "first pass score");
-    first.addDetail(firstPassExplanation);
-    result.addDetail(first);
+    Explanation first = Explanation.match(firstPassExplanation.getValue(), "first pass score", firstPassExplanation);
+    subs.add(first);
 
     FieldDoc fieldDoc = (FieldDoc) hits.scoreDocs[0];
 
     // Add sort values:
     SortField[] sortFields = sort.getSort();
     for(int i=0;i<sortFields.length;i++) {
-      result.addDetail(new Explanation(0.0f, "sort field " + sortFields[i].toString() + " value=" + fieldDoc.fields[i]));
+      subs.add(Explanation.match(0.0f, "sort field " + sortFields[i].toString() + " value=" + fieldDoc.fields[i]));
     }
 
-    return result;
+    // TODO: if we could ask the Sort to explain itself then
+    // we wouldn't need the separate ExpressionRescorer...
+    return Explanation.match(0.0f, "sort field values for sort=" + sort.toString(), subs);
   }
 }

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/TermQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/TermQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/TermQuery.java Tue Apr 21 13:23:02 2015
@@ -126,18 +126,16 @@ public class TermQuery extends Query {
         if (newDoc == doc) {
           float freq = scorer.freq();
           SimScorer docScorer = similarity.simScorer(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, "termFreq=" + freq));
-          result.addDetail(scoreExplanation);
-          result.setValue(scoreExplanation.getValue());
-          result.setMatch(true);
-          return result;
+          Explanation freqExplanation = Explanation.match(freq, "termFreq=" + freq);
+          Explanation scoreExplanation = docScorer.explain(doc, freqExplanation);
+          return Explanation.match(
+              scoreExplanation.getValue(),
+              "weight(" + getQuery() + " in " + doc + ") ["
+                  + similarity.getClass().getSimpleName() + "], result of:",
+              scoreExplanation);
         }
       }
-      return new ComplexExplanation(false, 0.0f, "no matching term");
+      return Explanation.noMatch("no matching term");
     }
   }
   

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadFunction.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadFunction.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadFunction.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadFunction.java Tue Apr 21 13:23:02 2015
@@ -56,10 +56,9 @@ public abstract class PayloadFunction {
   public abstract float docScore(int docId, String field, int numPayloadsSeen, float payloadScore);
   
   public Explanation explain(int docId, String field, int numPayloadsSeen, float payloadScore){
-    Explanation result = new Explanation();
-    result.setDescription(getClass().getSimpleName() + ".docScore()");
-    result.setValue(docScore(docId, field, numPayloadsSeen, payloadScore));
-    return result;
+    return Explanation.match(
+        docScore(docId, field, numPayloadsSeen, payloadScore),
+        getClass().getSimpleName() + ".docScore()");
   };
   
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java Tue Apr 21 13:23:02 2015
@@ -22,7 +22,6 @@ import java.util.Collection;
 import java.util.Iterator;
 
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Scorer;
@@ -161,26 +160,25 @@ public class PayloadNearQuery extends Sp
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
           float freq = scorer.freq();
+          Explanation freqExplanation = Explanation.match(freq, "phraseFreq=" + 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());
+          Explanation scoreExplanation = docScorer.explain(doc, freqExplanation);
+          Explanation expl = Explanation.match(
+              scoreExplanation.getValue(),
+              "weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:",
+              scoreExplanation);
           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 Explanation.match(
+              expl.getValue() * payloadExpl.getValue(),
+              "PayloadNearQuery, product of:",
+              expl, payloadExpl);
         }
       }
       
-      return new ComplexExplanation(false, 0.0f, "no matching term");
+      return Explanation.noMatch("no matching term");
     }
   }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java Tue Apr 21 13:23:02 2015
@@ -23,18 +23,16 @@ import java.util.Objects;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.PostingsEnum;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity.SimScorer;
-import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.SpanScorer;
 import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.search.spans.SpanWeight;
+import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.search.spans.TermSpans;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
@@ -179,12 +177,13 @@ public class PayloadTermQuery extends Sp
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
           float freq = scorer.sloppyFreq();
+          Explanation freqExplanation = Explanation.match(freq, "phraseFreq=" + 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());
+          Explanation scoreExplanation = docScorer.explain(doc, freqExplanation);
+          Explanation expl = Explanation.match(
+              scoreExplanation.getValue(),
+              "weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:",
+              scoreExplanation);
           // 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
@@ -192,25 +191,18 @@ public class PayloadTermQuery extends Sp
           // 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:");
+            return Explanation.match(
+                expl.getValue() * payloadExpl.getValue(),
+                "btq, product of:", expl, payloadExpl);
           } else {
-            result.addDetail(payloadExpl);
-            result.setValue(payloadExpl.getValue());
-            result.setDescription("btq(includeSpanScore=false), result of:");
+            return Explanation.match(payloadExpl.getValue(), "btq(includeSpanScore=false), result of:", payloadExpl);
           }
-          result.setMatch(true); // LUCENE-1303
-          return result;
         }
       }
       
-      return new ComplexExplanation(false, 0.0f, "no matching term");
+      return Explanation.noMatch("no matching term");
     }
   }
 

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffect.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffect.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffect.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffect.java Tue Apr 21 13:23:02 2015
@@ -56,7 +56,7 @@ public abstract class AfterEffect {
 
     @Override
     public final Explanation explain(BasicStats stats, float tfn) {
-      return new Explanation(1, "no aftereffect");
+      return Explanation.match(1, "no aftereffect");
     }
     
     @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java Tue Apr 21 13:23:02 2015
@@ -37,13 +37,12 @@ public class AfterEffectB extends AfterE
   
   @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;
+    return Explanation.match(
+        score(stats, tfn),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(tfn, "tfn"),
+        Explanation.match(stats.getTotalTermFreq(), "totalTermFreq"),
+        Explanation.match(stats.getDocFreq(), "docFreq"));
   }
 
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java Tue Apr 21 13:23:02 2015
@@ -35,11 +35,10 @@ public class AfterEffectL extends AfterE
   
   @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;
+    return Explanation.match(
+        score(stats, tfn),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(tfn, "tfn"));
   }
   
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java Tue Apr 21 13:23:02 2015
@@ -18,9 +18,11 @@ package org.apache.lucene.search.similar
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.Explanation;
@@ -166,7 +168,7 @@ public class BM25Similarity extends Simi
     final long df = termStats.docFreq();
     final long max = collectionStats.maxDoc();
     final float idf = idf(df, max);
-    return new Explanation(idf, "idf(docFreq=" + df + ", maxDocs=" + max + ")");
+    return Explanation.match(idf, "idf(docFreq=" + df + ", maxDocs=" + max + ")");
   }
 
   /**
@@ -185,16 +187,14 @@ public class BM25Similarity extends Simi
   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:");
+    List<Explanation> details = new ArrayList<>();
     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 + ")"));
+      details.add(Explanation.match(termIdf, "idf(docFreq=" + df + ", maxDocs=" + max + ")"));
       idf += termIdf;
     }
-    exp.setValue(idf);
-    return exp;
+    return Explanation.match(idf, "idf(), sum of:", details);
   }
 
   @Override
@@ -292,34 +292,38 @@ public class BM25Similarity extends Simi
       this.weight = idf.getValue() * queryBoost * topLevelBoost;
     } 
   }
-  
-  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"));
+  private Explanation explainTFNorm(int doc, Explanation freq, BM25Stats stats, NumericDocValues norms) {
+    List<Explanation> subs = new ArrayList<>();
+    subs.add(freq);
+    subs.add(Explanation.match(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));
+      subs.add(Explanation.match(0, "parameter b (norms omitted for field)"));
+      return Explanation.match(
+          (freq.getValue() * (k1 + 1)) / (freq.getValue() + k1),
+          "parameter b (norms omitted for field)", subs);
     } 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;
+      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);
+    }
+  }
+
+  private Explanation explainScore(int doc, Explanation freq, BM25Stats stats, NumericDocValues norms) {
+    Explanation boostExpl = Explanation.match(stats.queryBoost * stats.topLevelBoost, "boost");
+    List<Explanation> subs = new ArrayList<>();
+    if (boostExpl.getValue() != 1.0f)
+      subs.add(boostExpl);
+    subs.add(stats.idf);
+    Explanation tfNormExpl = explainTFNorm(doc, freq, stats, norms);
+    subs.add(tfNormExpl);
+    return Explanation.match(
+        boostExpl.getValue() * stats.idf.getValue() * tfNormExpl.getValue(),
+        "score(doc="+doc+",freq="+freq+"), product of:", subs);
   }
 
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java Tue Apr 21 13:23:02 2015
@@ -47,15 +47,11 @@ public abstract class BasicModel {
    * 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;
+    return Explanation.match(
+        score(stats, tfn),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"),
+        Explanation.match(stats.getTotalTermFreq(), "totalTermFreq"));
   }
   
   /**

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java Tue Apr 21 13:23:02 2015
@@ -38,15 +38,11 @@ public class BasicModelIn extends BasicM
   
   @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;
+    return Explanation.match(
+        score(stats, tfn),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"),
+        Explanation.match(stats.getDocFreq(), "docFreq"));
   }
 
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java Tue Apr 21 13:23:02 2015
@@ -17,6 +17,8 @@ package org.apache.lucene.search.similar
  * limitations under the License.
  */
 
+import java.util.List;
+
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.similarities.AfterEffect.NoAfterEffect;
 import org.apache.lucene.search.similarities.Normalization.NoNormalization;
@@ -112,17 +114,17 @@ public class DFRSimilarity extends Simil
   }
   
   @Override
-  protected void explain(Explanation expl,
+  protected void explain(List<Explanation> subs,
       BasicStats stats, int doc, float freq, float docLen) {
     if (stats.getTotalBoost() != 1.0f) {
-      expl.addDetail(new Explanation(stats.getTotalBoost(), "boost"));
+      subs.add(Explanation.match(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));
+    subs.add(normExpl);
+    subs.add(basicModel.explain(stats, tfn));
+    subs.add(afterEffect.explain(stats, tfn));
   }
 
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Distribution.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Distribution.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Distribution.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Distribution.java Tue Apr 21 13:23:02 2015
@@ -39,7 +39,7 @@ public abstract class Distribution {
   /** Explains the score. Returns the name of the model only, since
    * both {@code tfn} and {@code lambda} are explained elsewhere. */
   public Explanation explain(BasicStats stats, float tfn, float lambda) {
-    return new Explanation(
+    return Explanation.match(
         score(stats, tfn, lambda), getClass().getSimpleName());
   }
   

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java Tue Apr 21 13:23:02 2015
@@ -17,6 +17,8 @@ package org.apache.lucene.search.similar
  * limitations under the License.
  */
 
+import java.util.List;
+
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.similarities.Normalization.NoNormalization;
 
@@ -103,16 +105,15 @@ public class IBSimilarity extends Simila
 
   @Override
   protected void explain(
-      Explanation expl, BasicStats stats, int doc, float freq, float docLen) {
+      List<Explanation> subs, BasicStats stats, int doc, float freq, float docLen) {
     if (stats.getTotalBoost() != 1.0f) {
-      expl.addDetail(new Explanation(stats.getTotalBoost(), "boost"));
+      subs.add(Explanation.match(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()));
+    subs.add(normExpl);
+    subs.add(lambdaExpl);
+    subs.add(distribution.explain(stats, normExpl.getValue(), lambdaExpl.getValue()));
   }
   
   /**

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java Tue Apr 21 13:23:02 2015
@@ -17,6 +17,7 @@ package org.apache.lucene.search.similar
  * limitations under the License.
  */
 
+import java.util.List;
 import java.util.Locale;
 
 import org.apache.lucene.search.Explanation;
@@ -70,21 +71,21 @@ public class LMDirichletSimilarity exten
   }
   
   @Override
-  protected void explain(Explanation expl, BasicStats stats, int doc,
+  protected void explain(List<Explanation> subs, BasicStats stats, int doc,
       float freq, float docLen) {
     if (stats.getTotalBoost() != 1.0f) {
-      expl.addDetail(new Explanation(stats.getTotalBoost(), "boost"));
+      subs.add(Explanation.match(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(
+    subs.add(Explanation.match(mu, "mu"));
+    Explanation weightExpl = Explanation.match(
+        (float)Math.log(1 + freq /
+        (mu * ((LMStats)stats).getCollectionProbability())),
+        "term weight");
+    subs.add(weightExpl);
+    subs.add(Explanation.match(
         (float)Math.log(mu / (docLen + mu)), "document norm"));
-    super.explain(expl, stats, doc, freq, docLen);
+    super.explain(subs, stats, doc, freq, docLen);
   }
 
   /** Returns the &mu; parameter. */

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java Tue Apr 21 13:23:02 2015
@@ -17,6 +17,7 @@ package org.apache.lucene.search.similar
  * limitations under the License.
  */
 
+import java.util.List;
 import java.util.Locale;
 
 import org.apache.lucene.search.Explanation;
@@ -58,13 +59,13 @@ public class LMJelinekMercerSimilarity e
   }
   
   @Override
-  protected void explain(Explanation expl, BasicStats stats, int doc,
+  protected void explain(List<Explanation> subs, BasicStats stats, int doc,
       float freq, float docLen) {
     if (stats.getTotalBoost() != 1.0f) {
-      expl.addDetail(new Explanation(stats.getTotalBoost(), "boost"));
+      subs.add(Explanation.match(stats.getTotalBoost(), "boost"));
     }
-    expl.addDetail(new Explanation(lambda, "lambda"));
-    super.explain(expl, stats, doc, freq, docLen);
+    subs.add(Explanation.match(lambda, "lambda"));
+    super.explain(subs, stats, doc, freq, docLen);
   }
 
   /** Returns the &lambda; parameter. */

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LMSimilarity.java Tue Apr 21 13:23:02 2015
@@ -17,6 +17,7 @@ package org.apache.lucene.search.similar
  * limitations under the License.
  */
 
+import java.util.List;
 import java.util.Locale;
 
 import org.apache.lucene.search.CollectionStatistics;
@@ -69,9 +70,9 @@ public abstract class LMSimilarity exten
   }
 
   @Override
-  protected void explain(Explanation expl, BasicStats stats, int doc,
+  protected void explain(List<Explanation> subExpls, BasicStats stats, int doc,
       float freq, float docLen) {
-    expl.addDetail(new Explanation(collectionModel.computeProbability(stats),
+    subExpls.add(Explanation.match(collectionModel.computeProbability(stats),
                                    "collection probability"));
   }
   

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java Tue Apr 21 13:23:02 2015
@@ -35,14 +35,11 @@ public class LambdaDF extends Lambda {
   
   @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;
+    return Explanation.match(
+        lambda(stats),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(stats.getDocFreq(), "docFreq"),
+        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"));
   }
   
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java Tue Apr 21 13:23:02 2015
@@ -35,14 +35,11 @@ public class LambdaTTF extends Lambda {
 
   @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;
+    return Explanation.match(
+        lambda(stats),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(stats.getTotalTermFreq(), "totalTermFreq"),
+        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"));
   }
   
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/MultiSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/MultiSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/MultiSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/MultiSimilarity.java Tue Apr 21 13:23:02 2015
@@ -18,9 +18,11 @@ package org.apache.lucene.search.similar
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.TermStatistics;
@@ -83,11 +85,11 @@ public class MultiSimilarity extends Sim
 
     @Override
     public Explanation explain(int doc, Explanation freq) {
-      Explanation expl = new Explanation(score(doc, freq.getValue()), "sum of:");
+      List<Explanation> subs = new ArrayList<>();
       for (SimScorer subScorer : subScorers) {
-        expl.addDetail(subScorer.explain(doc, freq));
+        subs.add(subScorer.explain(doc, freq));
       }
-      return expl;
+      return Explanation.match(score(doc, freq.getValue()), "sum of:", subs);
     }
 
     @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java Tue Apr 21 13:23:02 2015
@@ -45,14 +45,12 @@ public abstract class Normalization {
    * 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;
+    return Explanation.match(
+        tfn(stats, tf, len),
+        getClass().getSimpleName() + ", computed from: ",
+        Explanation.match(tf, "tf"),
+        Explanation.match(stats.getAvgFieldLength(), "avgFieldLength"),
+        Explanation.match(len, "len"));
   }
 
   /** Implementation used when there is no normalization. */
@@ -68,7 +66,7 @@ public abstract class Normalization {
 
     @Override
     public final Explanation explain(BasicStats stats, float tf, float len) {
-      return new Explanation(1, "no normalization");
+      return Explanation.match(1, "no normalization");
     }
     
     @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java Tue Apr 21 13:23:02 2015
@@ -31,6 +31,7 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.SmallFloat;
 
 import java.io.IOException;
+import java.util.Collections;
 
 /** 
  * Similarity defines the components of Lucene scoring.
@@ -210,10 +211,10 @@ public abstract class Similarity {
      * @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;
+      return Explanation.match(
+          score(doc, freq.getValue()),
+          "score(doc=" + doc + ",freq=" + freq.getValue() +"), with freq of:",
+          Collections.singleton(freq));
     }
   }
   

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java Tue Apr 21 13:23:02 2015
@@ -18,9 +18,11 @@ package org.apache.lucene.search.similar
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.Explanation;
@@ -32,7 +34,7 @@ import org.apache.lucene.util.SmallFloat
  * A subclass of {@code Similarity} that provides a simplified API for its
  * descendants. Subclasses are only required to implement the {@link #score}
  * and {@link #toString()} methods. Implementing
- * {@link #explain(Explanation, BasicStats, int, float, float)} is optional,
+ * {@link #explain(List, BasicStats, int, float, float)} is optional,
  * inasmuch as SimilarityBase already provides a basic explanation of the score
  * and the term frequency. However, implementers of a subclass are encouraged to
  * include as much detail about the scoring method as possible.
@@ -152,14 +154,14 @@ public abstract class SimilarityBase ext
    * clauses to explain details of their scoring formulae.
    * <p>The default implementation does nothing.</p>
    * 
-   * @param expl the explanation to extend with details.
+   * @param subExpls the list of details of the explanation to extend
    * @param stats the corpus level statistics.
    * @param doc the document id.
    * @param freq the term frequency.
    * @param docLen the document length.
    */
   protected void explain(
-      Explanation expl, BasicStats stats, int doc, float freq, float docLen) {}
+      List<Explanation> subExpls, BasicStats stats, int doc, float freq, float docLen) {}
   
   /**
    * Explains the score. The implementation here provides a basic explanation
@@ -168,7 +170,7 @@ public abstract class SimilarityBase ext
    * attaches the score (computed via the {@link #score(BasicStats, float, float)}
    * method) and the explanation for the term frequency. Subclasses content with
    * this format may add additional details in
-   * {@link #explain(Explanation, BasicStats, int, float, float)}.
+   * {@link #explain(List, BasicStats, int, float, float)}.
    *  
    * @param stats the corpus level statistics.
    * @param doc the document id.
@@ -178,15 +180,13 @@ public abstract class SimilarityBase ext
    */
   protected Explanation explain(
       BasicStats stats, int doc, Explanation freq, float docLen) {
-    Explanation result = new Explanation(); 
-    result.setValue(score(stats, freq.getValue(), docLen));
-    result.setDescription("score(" + getClass().getSimpleName() +
-        ", doc=" + doc + ", freq=" + freq.getValue() +"), computed from:");
-    result.addDetail(freq);
-    
-    explain(result, stats, doc, freq.getValue(), docLen);
+    List<Explanation> subs = new ArrayList<>();
+    explain(subs, stats, doc, freq.getValue(), docLen);
     
-    return result;
+    return Explanation.match(
+        score(stats, freq.getValue(), docLen),
+        "score(" + getClass().getSimpleName() + ", doc=" + doc + ", freq=" + freq.getValue() +"), computed from:",
+        subs);
   }
   
   @Override

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java Tue Apr 21 13:23:02 2015
@@ -18,9 +18,11 @@ package org.apache.lucene.search.similar
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.Explanation;
@@ -582,7 +584,7 @@ public abstract class TFIDFSimilarity ex
     final long df = termStats.docFreq();
     final long max = collectionStats.maxDoc();
     final float idf = idf(df, max);
-    return new Explanation(idf, "idf(docFreq=" + df + ", maxDocs=" + max + ")");
+    return Explanation.match(idf, "idf(docFreq=" + df + ", maxDocs=" + max + ")");
   }
 
   /**
@@ -601,16 +603,14 @@ public abstract class TFIDFSimilarity ex
   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:");
+    List<Explanation> subs = new ArrayList<>();
     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 + ")"));
+      subs.add(Explanation.match(termIdf, "idf(docFreq=" + df + ", maxDocs=" + max + ")"));
       idf += termIdf;
     }
-    exp.setValue(idf);
-    return exp;
+    return Explanation.match(idf, "idf(), sum of:", subs);
   }
 
   /** Computes a score factor based on a term's document frequency (the number
@@ -764,58 +764,43 @@ public abstract class TFIDFSimilarity ex
     }
   }  
 
-  private Explanation explainScore(int doc, Explanation freq, IDFStats stats, NumericDocValues norms) {
-    Explanation result = new Explanation();
-    result.setDescription("score(doc="+doc+",freq="+freq.getValue()+"), product of:");
-
-    // explain query weight
-    Explanation queryExpl = new Explanation();
-    queryExpl.setDescription("queryWeight, product of:");
+  private Explanation explainQuery(IDFStats stats) {
+    List<Explanation> subs = new ArrayList<>();
 
-    Explanation boostExpl = new Explanation(stats.queryBoost, "boost");
+    Explanation boostExpl = Explanation.match(stats.queryBoost, "boost");
     if (stats.queryBoost != 1.0f)
-      queryExpl.addDetail(boostExpl);
-    queryExpl.addDetail(stats.idf);
+      subs.add(boostExpl);
+    subs.add(stats.idf);
 
-    Explanation queryNormExpl = new Explanation(stats.queryNorm,"queryNorm");
-    queryExpl.addDetail(queryNormExpl);
+    Explanation queryNormExpl = Explanation.match(stats.queryNorm,"queryNorm");
+    subs.add(queryNormExpl);
 
-    queryExpl.setValue(boostExpl.getValue() *
-                       stats.idf.getValue() *
-                       queryNormExpl.getValue());
-
-    result.addDetail(queryExpl);
-
-    // explain field weight
-    Explanation fieldExpl = new Explanation();
-    fieldExpl.setDescription("fieldWeight in "+doc+
-                             ", product of:");
-
-    Explanation tfExplanation = new Explanation();
-    tfExplanation.setValue(tf(freq.getValue()));
-    tfExplanation.setDescription("tf(freq="+freq.getValue()+"), with freq of:");
-    tfExplanation.addDetail(freq);
-    fieldExpl.addDetail(tfExplanation);
-    fieldExpl.addDetail(stats.idf);
-
-    Explanation fieldNormExpl = new Explanation();
-    float fieldNorm = norms != null ? decodeNormValue(norms.get(doc)) : 1.0f;
-    fieldNormExpl.setValue(fieldNorm);
-    fieldNormExpl.setDescription("fieldNorm(doc="+doc+")");
-    fieldExpl.addDetail(fieldNormExpl);
-    
-    fieldExpl.setValue(tfExplanation.getValue() *
-                       stats.idf.getValue() *
-                       fieldNormExpl.getValue());
+    return Explanation.match(
+        boostExpl.getValue() * stats.idf.getValue() * queryNormExpl.getValue(),
+        "queryWeight, product of:", subs);
+  }
 
-    result.addDetail(fieldExpl);
-    
-    // combine them
-    result.setValue(queryExpl.getValue() * fieldExpl.getValue());
+  private Explanation explainField(int doc, Explanation freq, IDFStats stats, NumericDocValues norms) {
+    Explanation tfExplanation = Explanation.match(tf(freq.getValue()), "tf(freq="+freq.getValue()+"), with freq of:", freq);
+    Explanation fieldNormExpl = Explanation.match(
+        norms != null ? decodeNormValue(norms.get(doc)) : 1.0f,
+        "fieldNorm(doc=" + doc + ")");
+
+    return Explanation.match(
+        tfExplanation.getValue() * stats.idf.getValue() * fieldNormExpl.getValue(),
+        "fieldWeight in " + doc + ", product of:",
+        tfExplanation, stats.idf, fieldNormExpl);
+  }
 
-    if (queryExpl.getValue() == 1.0f)
+  private Explanation explainScore(int doc, Explanation freq, IDFStats stats, NumericDocValues norms) {
+    Explanation queryExpl = explainQuery(stats);
+    Explanation fieldExpl = explainField(doc, freq, stats, norms);
+    if (queryExpl.getValue() == 1f) {
       return fieldExpl;
-
-    return result;
+    }
+    return Explanation.match(
+        queryExpl.getValue() * fieldExpl.getValue(),
+        "score(doc="+doc+",freq="+freq.getValue()+"), product of:",
+        queryExpl, fieldExpl);
   }
 }

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java?rev=1675114&r1=1675113&r2=1675114&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java Tue Apr 21 13:23:02 2015
@@ -28,7 +28,6 @@ import org.apache.lucene.index.LeafReade
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.index.Terms;
-import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Scorer;
@@ -110,16 +109,14 @@ public class SpanWeight extends Weight {
       if (newDoc == doc) {
         float freq = scorer.sloppyFreq();
         SimScorer docScorer = similarity.simScorer(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;
+        Explanation freqExplanation = Explanation.match(freq, "phraseFreq=" + freq);
+        Explanation scoreExplanation = docScorer.explain(doc, freqExplanation);
+        return Explanation.match(scoreExplanation.getValue(),
+            "weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:",
+            scoreExplanation);
       }
     }
 
-    return new ComplexExplanation(false, 0.0f, "no matching term");
+    return Explanation.noMatch("no matching term");
   }
 }