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 2016/03/04 14:22:58 UTC

[2/2] lucene-solr git commit: LUCENE-7064: Split MultiPhraseQuery into an immutable class and a Builder

LUCENE-7064: Split MultiPhraseQuery into an immutable class and a Builder

This closes #19


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/14070770
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/14070770
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/14070770

Branch: refs/heads/branch_6_0
Commit: 1407077092f3b6d50fbecf07eda3f739f135455d
Parents: d0de8cf
Author: Luc Vanlerberghe <lu...@bvdinfo.com>
Authored: Thu Mar 3 11:52:14 2016 +0100
Committer: Adrien Grand <jp...@gmail.com>
Committed: Fri Mar 4 14:22:25 2016 +0100

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   3 +
 lucene/MIGRATE.txt                              |   8 +-
 .../apache/lucene/search/MultiPhraseQuery.java  | 273 +++++++++++--------
 .../org/apache/lucene/util/QueryBuilder.java    |  18 +-
 .../lucene/search/TestComplexExplanations.java  |  10 +-
 .../lucene/search/TestMultiPhraseQuery.java     | 190 +++++++------
 .../lucene/search/TestPhrasePrefixQuery.java    |  16 +-
 .../lucene/search/TestPositionIncrement.java    |   6 +-
 .../lucene/search/TestSimpleExplanations.java   |  52 ++--
 .../search/TestSimpleSearchEquivalence.java     |  16 +-
 .../lucene/search/TestSloppyPhraseQuery2.java   |  10 +-
 .../apache/lucene/util/TestQueryBuilder.java    |  20 +-
 .../highlight/WeightedSpanTermExtractor.java    |   6 +-
 .../search/highlight/HighlighterTest.java       |  16 +-
 .../classic/MultiFieldQueryParser.java          |   6 +-
 .../queryparser/classic/QueryParserBase.java    |   9 +-
 .../builders/MultiPhraseQueryNodeBuilder.java   |   6 +-
 .../standard/builders/SlopQueryNodeBuilder.java |   8 +-
 .../classic/TestMultiPhraseQueryParsing.java    |  10 +-
 .../queryparser/classic/TestQueryParser.java    |  28 +-
 .../apache/lucene/payloads/PayloadSpanUtil.java |   6 +-
 .../lucene/search/TestTermAutomatonQuery.java   |   8 +-
 .../apache/solr/parser/SolrQueryParserBase.java |  11 +-
 .../solr/search/ExtendedDismaxQParser.java      |   8 +-
 24 files changed, 415 insertions(+), 329 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 37473d5..684b63e 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -116,6 +116,9 @@ API Changes
 * LUCENE-7058: Add getters to various Query implementations (Guillaume Smet via
   Alan Woodward)
 
+* LUCENE-7064: MultiPhraseQuery is now immutable and should be constructed
+  with MultiPhraseQuery.Builder. (Luc Vanlerberghe via Adrien Grand)
+
 Optimizations
 
 * LUCENE-6891: Use prefix coding when writing points in 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/MIGRATE.txt
----------------------------------------------------------------------
diff --git a/lucene/MIGRATE.txt b/lucene/MIGRATE.txt
index b8d12ac..b48226d 100644
--- a/lucene/MIGRATE.txt
+++ b/lucene/MIGRATE.txt
@@ -31,11 +31,11 @@ of filters as they have been optimized for the filtering case. And you can
 construct a BooleanQuery with one MUST clause for the query, and one FILTER
 clause for the filter in order to have similar behaviour to FilteredQuery.
 
-## PhraseQuery and BooleanQuery made immutable (LUCENE-6531 LUCENE-6570)
+## PhraseQuery, MultiPhraseQuery, and BooleanQuery made immutable (LUCENE-6531 LUCENE-7064 LUCENE-6570)
 
-PhraseQuery and BooleanQuery are now immutable and have a builder API to help
-construct them. For instance a BooleanQuery that used to be constructed like
-this:
+PhraseQuery, MultiPhraseQuery, and BooleanQuery are now immutable and have a
+builder API to help construct them. For instance a BooleanQuery that used to
+be constructed like this:
 
   BooleanQuery bq = new BooleanQuery();
   bq.add(q1, Occur.SHOULD);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
index b665388..d703ebd 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
@@ -37,91 +37,147 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.PriorityQueue;
 
 /**
- * A generalized version of {@link PhraseQuery}, with an added
- * method {@link #add(Term[])} for adding more than one term at the same position
- * that are treated as a disjunction (OR).
- * To use this class to search for the phrase "Microsoft app*" first use
- * {@link #add(Term)} on the term "microsoft" (assuming lowercase analysis), then
+ * A generalized version of {@link PhraseQuery}, with the possibility of
+ * adding more than one term at the same position that are treated as a disjunction (OR).
+ * To use this class to search for the phrase "Microsoft app*" first create a Builder and use
+ * {@link Builder#add(Term)} on the term "microsoft" (assuming lowercase analysis), then
  * find all terms that have "app" as prefix using {@link LeafReader#terms(String)},
  * seeking to "app" then iterating and collecting terms until there is no longer
- * that prefix, and finally use {@link #add(Term[])} to add them to the query.
+ * that prefix, and finally use {@link Builder#add(Term[])} to add them.
+ * {@link Builder#build()} returns the fully constructed (and immutable) MultiPhraseQuery.
  */
 public class MultiPhraseQuery extends Query {
-  private String field;// becomes non-null on first add() then is unmodified
-  private final ArrayList<Term[]> termArrays = new ArrayList<>();
-  private final ArrayList<Integer> positions = new ArrayList<>();
+  /** A builder for multi-phrase queries */
+  public static class Builder {
+    private String field; // becomes non-null on first add() then is unmodified
+    private final ArrayList<Term[]> termArrays;
+    private final ArrayList<Integer> positions;
+    private int slop;
+
+    /** Default constructor. */
+    public Builder() {
+      this.field = null;
+      this.termArrays = new ArrayList<>();
+      this.positions = new ArrayList<>();
+      this.slop = 0;
+    }
 
-  private int slop = 0;
+    /** Copy constructor: this will create a builder that has the same
+     *  configuration as the provided builder. */
+    public Builder(MultiPhraseQuery multiPhraseQuery) {
+      this.field = multiPhraseQuery.field;
 
-  /** Sets the phrase slop for this query.
-   * @see PhraseQuery#getSlop()
-   */
-  public void setSlop(int s) {
-    if (s < 0) {
-      throw new IllegalArgumentException("slop value cannot be negative");
+      int length = multiPhraseQuery.termArrays.length;
+
+      this.termArrays = new ArrayList<>(length);
+      this.positions = new ArrayList<>(length);
+
+      for (int i = 0 ; i < length ; ++i) {
+        this.termArrays.add(multiPhraseQuery.termArrays[i]);
+        this.positions.add(multiPhraseQuery.positions[i]);
+      }
+
+      this.slop = multiPhraseQuery.slop;
     }
-    slop = s; 
-  }
 
-  /** Sets the phrase slop for this query.
-   * @see PhraseQuery#getSlop()
-   */
-  public int getSlop() { return slop; }
+    /** Sets the phrase slop for this query.
+     * @see PhraseQuery#getSlop()
+     */
+    public Builder setSlop(int s) {
+      if (s < 0) {
+        throw new IllegalArgumentException("slop value cannot be negative");
+      }
+      slop = s;
 
-  /** Add a single term at the next position in the phrase.
-   */
-  public void add(Term term) { add(new Term[]{term}); }
+      return this;
+    }
 
-  /** Add multiple terms at the next position in the phrase.  Any of the terms
-   * may match (a disjunction).
-   * The array is not copied or mutated, the caller should consider it
-   * immutable subsequent to calling this method.
-   */
-  public void add(Term[] terms) {
-    int position = 0;
-    if (positions.size() > 0)
-      position = positions.get(positions.size() - 1) + 1;
+    /** Add a single term at the next position in the phrase.
+     */
+    public Builder add(Term term) { return add(new Term[]{term}); }
 
-    add(terms, position);
-  }
+    /** Add multiple terms at the next position in the phrase.  Any of the terms
+     * may match (a disjunction).
+     * The array is not copied or mutated, the caller should consider it
+     * immutable subsequent to calling this method.
+     */
+    public Builder add(Term[] terms) {
+      int position = 0;
+      if (positions.size() > 0)
+        position = positions.get(positions.size() - 1) + 1;
 
-  /**
-   * Allows to specify the relative position of terms within the phrase.
-   * The array is not copied or mutated, the caller should consider it
-   * immutable subsequent to calling this method.
-   */
-  public void add(Term[] terms, int position) {
-    Objects.requireNonNull(terms, "Term array must not be null");
-    if (termArrays.size() == 0)
-      field = terms[0].field();
-
-    for (Term term : terms) {
-      if (!term.field().equals(field)) {
-        throw new IllegalArgumentException(
-            "All phrase terms must be in the same field (" + field + "): " + term);
+      return add(terms, position);
+    }
+
+    /**
+     * Allows to specify the relative position of terms within the phrase.
+     * The array is not copied or mutated, the caller should consider it
+     * immutable subsequent to calling this method.
+     */
+    public Builder add(Term[] terms, int position) {
+      Objects.requireNonNull(terms, "Term array must not be null");
+      if (termArrays.size() == 0)
+        field = terms[0].field();
+
+      for (Term term : terms) {
+        if (!term.field().equals(field)) {
+          throw new IllegalArgumentException(
+              "All phrase terms must be in the same field (" + field + "): " + term);
+        }
       }
+
+      termArrays.add(terms);
+      positions.add(position);
+
+      return this;
     }
 
-    termArrays.add(terms);
-    positions.add(position);
+    /** Builds a {@link MultiPhraseQuery}. */
+    public MultiPhraseQuery build() {
+      int[] positionsArray = new int[this.positions.size()];
+
+      for (int i = 0; i < this.positions.size(); ++i) {
+        positionsArray[i] = this.positions.get(i);
+      }
+
+      Term[][] termArraysArray = termArrays.toArray(new Term[termArrays.size()][]);
+
+      return new MultiPhraseQuery(field, termArraysArray, positionsArray, slop);
+    }
+  }
+
+  private final String field;
+  private final Term[][] termArrays;
+  private final int[] positions;
+  private final int slop;
+
+  private MultiPhraseQuery(String field, Term[][] termArrays, int[] positions, int slop) {
+    // No argument checks here since they are provided by the MultiPhraseQuery.Builder
+    this.field = field;
+    this.termArrays = termArrays;
+    this.positions = positions;
+    this.slop = slop;
   }
 
+  /** Sets the phrase slop for this query.
+   * @see PhraseQuery#getSlop()
+   */
+  public int getSlop() { return slop; }
+
   /**
-   * Returns a List of the terms in the multi-phrase.
-   * Do not modify the List or its contents.
+   * Returns the arrays of arrays of terms in the multi-phrase.
+   * Do not modify!
    */
-  public List<Term[]> getTermArrays() {
-    return Collections.unmodifiableList(termArrays);
+  public Term[][] getTermArrays() {
+    return termArrays;
   }
 
   /**
    * Returns the relative positions of terms in this phrase.
+   * Do not modify!
    */
   public int[] getPositions() {
-    int[] result = new int[positions.size()];
-    for (int i = 0; i < positions.size(); i++)
-      result[i] = positions.get(i);
-    return result;
+    return positions;
   }
 
 
@@ -137,7 +193,7 @@ public class MultiPhraseQuery extends Query {
       this.needsScores = needsScores;
       this.similarity = searcher.getSimilarity(needsScores);
       final IndexReaderContext context = searcher.getTopReaderContext();
-      
+
       // compute idf
       ArrayList<TermStatistics> allTermStats = new ArrayList<>();
       for(final Term[] terms: termArrays) {
@@ -151,7 +207,7 @@ public class MultiPhraseQuery extends Query {
         }
       }
       stats = similarity.computeWeight(
-          searcher.collectionStatistics(field), 
+          searcher.collectionStatistics(field),
           allTermStats.toArray(new TermStatistics[allTermStats.size()]));
     }
 
@@ -174,10 +230,10 @@ public class MultiPhraseQuery extends Query {
 
     @Override
     public Scorer scorer(LeafReaderContext context) throws IOException {
-      assert !termArrays.isEmpty();
+      assert termArrays.length != 0;
       final LeafReader reader = context.reader();
-      
-      PhraseQuery.PostingsAndFreq[] postingsFreqs = new PhraseQuery.PostingsAndFreq[termArrays.size()];
+
+      PhraseQuery.PostingsAndFreq[] postingsFreqs = new PhraseQuery.PostingsAndFreq[termArrays.length];
 
       final Terms fieldTerms = reader.terms(field);
       if (fieldTerms == null) {
@@ -195,9 +251,9 @@ public class MultiPhraseQuery extends Query {
       float totalMatchCost = 0;
 
       for (int pos=0; pos<postingsFreqs.length; pos++) {
-        Term[] terms = termArrays.get(pos);
+        Term[] terms = termArrays[pos];
         List<PostingsEnum> postings = new ArrayList<>();
-        
+
         for (Term term : terms) {
           TermState termState = termContexts.get(term).get(context.ord);
           if (termState != null) {
@@ -206,11 +262,11 @@ public class MultiPhraseQuery extends Query {
             totalMatchCost += PhraseQuery.termPositionsCost(termsEnum);
           }
         }
-        
+
         if (postings.isEmpty()) {
           return null;
         }
-        
+
         final PostingsEnum postingsEnum;
         if (postings.size() == 1) {
           postingsEnum = postings.get(0);
@@ -218,7 +274,7 @@ public class MultiPhraseQuery extends Query {
           postingsEnum = new UnionPostingsEnum(postings);
         }
 
-        postingsFreqs[pos] = new PhraseQuery.PostingsAndFreq(postingsEnum, positions.get(pos).intValue(), terms);
+        postingsFreqs[pos] = new PhraseQuery.PostingsAndFreq(postingsEnum, positions[pos], terms);
       }
 
       // sort by increasing docFreq order
@@ -253,17 +309,17 @@ public class MultiPhraseQuery extends Query {
               scoreExplanation);
         }
       }
-      
+
       return Explanation.noMatch("no matching term");
     }
   }
 
   @Override
   public Query rewrite(IndexReader reader) throws IOException {
-    if (termArrays.isEmpty()) {
+    if (termArrays.length == 0) {
       return new MatchNoDocsQuery();
-    } else if (termArrays.size() == 1) {                 // optimize one-term case
-      Term[] terms = termArrays.get(0);
+    } else if (termArrays.length == 1) {                 // optimize one-term case
+      Term[] terms = termArrays[0];
       BooleanQuery.Builder builder = new BooleanQuery.Builder();
       builder.setDisableCoord(true);
       for (Term term : terms) {
@@ -290,16 +346,12 @@ public class MultiPhraseQuery extends Query {
     }
 
     buffer.append("\"");
-    int k = 0;
-    Iterator<Term[]> i = termArrays.iterator();
     int lastPos = -1;
-    boolean first = true;
-    while (i.hasNext()) {
-      Term[] terms = i.next();
-      int position = positions.get(k);
-      if (first) {
-        first = false;
-      } else {
+
+    for (int i = 0 ; i < termArrays.length ; ++i) {
+      Term[] terms = termArrays[i];
+      int position = positions[i];
+      if (i != 0) {
         buffer.append(" ");
         for (int j=1; j<(position-lastPos); j++) {
           buffer.append("? ");
@@ -317,7 +369,6 @@ public class MultiPhraseQuery extends Query {
         buffer.append(terms[0].text());
       }
       lastPos = position;
-      ++k;
     }
     buffer.append("\"");
 
@@ -333,12 +384,13 @@ public class MultiPhraseQuery extends Query {
   /** Returns true if <code>o</code> is equal to this. */
   @Override
   public boolean equals(Object o) {
-    if (!(o instanceof MultiPhraseQuery)) return false;
+    if (super.equals(o) == false) {
+      return false;
+    }
     MultiPhraseQuery other = (MultiPhraseQuery)o;
-    return super.equals(o)
-      && this.slop == other.slop
-      && termArraysEquals(this.termArrays, other.termArrays)
-      && this.positions.equals(other.positions);
+    return this.slop == other.slop
+      && termArraysEquals(this.termArrays, other.termArrays) // terms equal implies field equal
+      && Arrays.equals(this.positions, other.positions);
   }
 
   /** Returns a hash code value for this object.*/
@@ -346,10 +398,10 @@ public class MultiPhraseQuery extends Query {
   public int hashCode() {
     return super.hashCode()
       ^ slop
-      ^ termArraysHashCode()
-      ^ positions.hashCode();
+      ^ termArraysHashCode() // terms equal implies field equal
+      ^ Arrays.hashCode(positions);
   }
-  
+
   // Breakout calculation of the termArrays hashcode
   private int termArraysHashCode() {
     int hashCode = 1;
@@ -361,15 +413,14 @@ public class MultiPhraseQuery extends Query {
   }
 
   // Breakout calculation of the termArrays equals
-  private boolean termArraysEquals(List<Term[]> termArrays1, List<Term[]> termArrays2) {
-    if (termArrays1.size() != termArrays2.size()) {
+  private boolean termArraysEquals(Term[][] termArrays1, Term[][] termArrays2) {
+    if (termArrays1.length != termArrays2.length) {
       return false;
     }
-    ListIterator<Term[]> iterator1 = termArrays1.listIterator();
-    ListIterator<Term[]> iterator2 = termArrays2.listIterator();
-    while (iterator1.hasNext()) {
-      Term[] termArray1 = iterator1.next();
-      Term[] termArray2 = iterator2.next();
+
+    for (int i = 0 ; i < termArrays1.length ; ++i) {
+      Term[] termArray1 = termArrays1[i];
+      Term[] termArray2 = termArrays2[i];
       if (!(termArray1 == null ? termArray2 == null : Arrays.equals(termArray1,
           termArray2))) {
         return false;
@@ -377,8 +428,8 @@ public class MultiPhraseQuery extends Query {
     }
     return true;
   }
-  
-  /** 
+
+  /**
    * Takes the logical union of multiple PostingsEnum iterators.
    * <p>
    * Note: positions are merged during freq()
@@ -388,14 +439,14 @@ public class MultiPhraseQuery extends Query {
     final DocsQueue docsQueue;
     /** cost of this enum: sum of its subs */
     final long cost;
-    
+
     /** queue ordered by position for current doc */
     final PositionsQueue posQueue = new PositionsQueue();
     /** current doc posQueue is working */
     int posQueueDoc = -2;
     /** list of subs (unordered) */
     final PostingsEnum[] subs;
-    
+
     UnionPostingsEnum(Collection<PostingsEnum> subs) {
       docsQueue = new DocsQueue(subs.size());
       long cost = 0;
@@ -440,7 +491,7 @@ public class MultiPhraseQuery extends Query {
     public int nextDoc() throws IOException {
       PostingsEnum top = docsQueue.top();
       int doc = top.docID();
-      
+
       do {
         top.nextDoc();
         top = docsQueue.updateTop();
@@ -452,7 +503,7 @@ public class MultiPhraseQuery extends Query {
     @Override
     public int advance(int target) throws IOException {
       PostingsEnum top = docsQueue.top();
-      
+
       do {
         top.advance(target);
         top = docsQueue.updateTop();
@@ -465,7 +516,7 @@ public class MultiPhraseQuery extends Query {
     public long cost() {
       return cost;
     }
-    
+
     @Override
     public int startOffset() throws IOException {
       return -1; // offsets are unsupported
@@ -480,8 +531,8 @@ public class MultiPhraseQuery extends Query {
     public BytesRef getPayload() throws IOException {
       return null; // payloads are unsupported
     }
-    
-    /** 
+
+    /**
      * disjunction of postings ordered by docid.
      */
     static class DocsQueue extends PriorityQueue<PostingsEnum> {
@@ -494,8 +545,8 @@ public class MultiPhraseQuery extends Query {
         return a.docID() < b.docID();
       }
     }
-    
-    /** 
+
+    /**
      * queue of terms for a single document. its a sorted array of
      * all the positions from all the postings
      */
@@ -504,7 +555,7 @@ public class MultiPhraseQuery extends Query {
       private int index = 0;
       private int size = 0;
       private int[] array = new int[arraySize];
-      
+
       void add(int i) {
         if (size == arraySize)
           growArray();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/java/org/apache/lucene/util/QueryBuilder.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/QueryBuilder.java b/lucene/core/src/java/org/apache/lucene/util/QueryBuilder.java
index 7acff49..259335b 100644
--- a/lucene/core/src/java/org/apache/lucene/util/QueryBuilder.java
+++ b/lucene/core/src/java/org/apache/lucene/util/QueryBuilder.java
@@ -354,8 +354,8 @@ public class QueryBuilder {
    * Creates complex phrase query from the cached tokenstream contents 
    */
   private Query analyzeMultiPhrase(String field, TokenStream stream, int slop) throws IOException {
-    MultiPhraseQuery mpq = newMultiPhraseQuery();
-    mpq.setSlop(slop);
+    MultiPhraseQuery.Builder mpqb = newMultiPhraseQueryBuilder();
+    mpqb.setSlop(slop);
     
     TermToBytesRefAttribute termAtt = stream.getAttribute(TermToBytesRefAttribute.class);
 
@@ -369,9 +369,9 @@ public class QueryBuilder {
       
       if (positionIncrement > 0 && multiTerms.size() > 0) {
         if (enablePositionIncrements) {
-          mpq.add(multiTerms.toArray(new Term[0]), position);
+          mpqb.add(multiTerms.toArray(new Term[0]), position);
         } else {
-          mpq.add(multiTerms.toArray(new Term[0]));
+          mpqb.add(multiTerms.toArray(new Term[0]));
         }
         multiTerms.clear();
       }
@@ -380,11 +380,11 @@ public class QueryBuilder {
     }
     
     if (enablePositionIncrements) {
-      mpq.add(multiTerms.toArray(new Term[0]), position);
+      mpqb.add(multiTerms.toArray(new Term[0]), position);
     } else {
-      mpq.add(multiTerms.toArray(new Term[0]));
+      mpqb.add(multiTerms.toArray(new Term[0]));
     }
-    return mpq;
+    return mpqb.build();
   }
   
   /**
@@ -424,7 +424,7 @@ public class QueryBuilder {
    * This is intended for subclasses that wish to customize the generated queries.
    * @return new MultiPhraseQuery instance
    */
-  protected MultiPhraseQuery newMultiPhraseQuery() {
-    return new MultiPhraseQuery();
+  protected MultiPhraseQuery.Builder newMultiPhraseQueryBuilder() {
+    return new MultiPhraseQuery.Builder();
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestComplexExplanations.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestComplexExplanations.java b/lucene/core/src/test/org/apache/lucene/search/TestComplexExplanations.java
index 014566c..1a4591d 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestComplexExplanations.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestComplexExplanations.java
@@ -231,11 +231,11 @@ public class TestComplexExplanations extends BaseExplanationTestCase {
   }
   
   public void testMPQ7() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1"}));
-    q.add(ta(new String[] {"w2"}));
-    q.setSlop(1);
-    bqtest(new BoostQuery(q, 0), new int[] { 0,1,2 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1"}));
+    qb.add(ta(new String[] {"w2"}));
+    qb.setSlop(1);
+    bqtest(new BoostQuery(qb.build(), 0), new int[] { 0,1,2 });
   }
   
   public void testBQ12() throws Exception {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java
index 5c81fed..942ac13 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java
@@ -62,11 +62,11 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     IndexSearcher searcher = newSearcher(reader);
     
     // search for "blueberry pi*":
-    MultiPhraseQuery query1 = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder query1builder = new MultiPhraseQuery.Builder();
     // search for "strawberry pi*":
-    MultiPhraseQuery query2 = new MultiPhraseQuery();
-    query1.add(new Term("body", "blueberry"));
-    query2.add(new Term("body", "strawberry"));
+    MultiPhraseQuery.Builder query2builder = new MultiPhraseQuery.Builder();
+    query1builder.add(new Term("body", "blueberry"));
+    query2builder.add(new Term("body", "strawberry"));
     
     LinkedList<Term> termsWithPrefix = new LinkedList<>();
     
@@ -83,11 +83,13 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
       }
     } while (te.next() != null);
     
-    query1.add(termsWithPrefix.toArray(new Term[0]));
+    query1builder.add(termsWithPrefix.toArray(new Term[0]));
+    MultiPhraseQuery query1 = query1builder.build();
     assertEquals("body:\"blueberry (piccadilly pie pizza)\"", query1.toString());
-    query2.add(termsWithPrefix.toArray(new Term[0]));
-    assertEquals("body:\"strawberry (piccadilly pie pizza)\"", query2
-        .toString());
+    
+    query2builder.add(termsWithPrefix.toArray(new Term[0]));
+    MultiPhraseQuery query2 = query2builder.build();
+    assertEquals("body:\"strawberry (piccadilly pie pizza)\"", query2.toString());
     
     ScoreDoc[] result;
     result = searcher.search(query1, 1000).scoreDocs;
@@ -96,7 +98,7 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     assertEquals(0, result.length);
     
     // search for "blue* pizza":
-    MultiPhraseQuery query3 = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder query3builder = new MultiPhraseQuery.Builder();
     termsWithPrefix.clear();
     prefix = "blue";
     te.seekCeil(new BytesRef(prefix));
@@ -107,15 +109,18 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
       }
     } while (te.next() != null);
     
-    query3.add(termsWithPrefix.toArray(new Term[0]));
-    query3.add(new Term("body", "pizza"));
+    query3builder.add(termsWithPrefix.toArray(new Term[0]));
+    query3builder.add(new Term("body", "pizza"));
+    
+    MultiPhraseQuery query3 = query3builder.build();
     
     result = searcher.search(query3, 1000).scoreDocs;
     assertEquals(2, result.length); // blueberry pizza, bluebird pizza
     assertEquals("body:\"(blueberry bluebird) pizza\"", query3.toString());
     
     // test slop:
-    query3.setSlop(1);
+    query3builder.setSlop(1);
+    query3 = query3builder.build();
     result = searcher.search(query3, 1000).scoreDocs;
     
     // just make sure no exc:
@@ -124,10 +129,10 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     assertEquals(3, result.length); // blueberry pizza, bluebird pizza, bluebird
                                     // foobar pizza
     
-    MultiPhraseQuery query4 = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder query4builder = new MultiPhraseQuery.Builder();
     expectThrows(IllegalArgumentException.class, () -> {
-      query4.add(new Term("field1", "foo"));
-      query4.add(new Term("field2", "foobar"));
+      query4builder.add(new Term("field1", "foo"));
+      query4builder.add(new Term("field2", "foobar"));
     });
     
     writer.close();
@@ -145,11 +150,11 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     writer.close();
 
     IndexSearcher searcher = newSearcher(r);
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(new Term("body", "blueberry"));
-    q.add(new Term("body", "chocolate"));
-    q.add(new Term[] {new Term("body", "pie"), new Term("body", "tart")});
-    assertEquals(2, searcher.search(q, 1).totalHits);
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(new Term("body", "blueberry"));
+    qb.add(new Term("body", "chocolate"));
+    qb.add(new Term[] {new Term("body", "pie"), new Term("body", "tart")});
+    assertEquals(2, searcher.search(qb.build(), 1).totalHits);
     r.close();
     indexStore.close();
   }
@@ -164,12 +169,12 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     
     IndexSearcher searcher = newSearcher(r);
     
-    MultiPhraseQuery q = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
     // this will fail, when the scorer would propagate [a] rather than [a,b],
-    q.add(new Term[] {new Term("body", "a"), new Term("body", "b")});
-    q.add(new Term[] {new Term("body", "a")});
-    q.setSlop(6);
-    assertEquals(1, searcher.search(q, 1).totalHits); // should match on "a b"
+    qb.add(new Term[] {new Term("body", "a"), new Term("body", "b")});
+    qb.add(new Term[] {new Term("body", "a")});
+    qb.setSlop(6);
+    assertEquals(1, searcher.search(qb.build(), 1).totalHits); // should match on "a b"
     
     r.close();
     indexStore.close();
@@ -183,10 +188,10 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     writer.close();
     
     IndexSearcher searcher = newSearcher(r);
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(new Term[] {new Term("body", "a"), new Term("body", "d")}, 0);
-    q.add(new Term[] {new Term("body", "a"), new Term("body", "f")}, 2);
-    assertEquals(1, searcher.search(q, 1).totalHits); // should match on "a b"
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(new Term[] {new Term("body", "a"), new Term("body", "d")}, 0);
+    qb.add(new Term[] {new Term("body", "a"), new Term("body", "f")}, 2);
+    assertEquals(1, searcher.search(qb.build(), 1).totalHits); // should match on "a b"
     r.close();
     indexStore.close();
   }
@@ -215,10 +220,10 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     BooleanQuery.Builder q = new BooleanQuery.Builder();
     q.add(new TermQuery(new Term("body", "pie")), BooleanClause.Occur.MUST);
     
-    MultiPhraseQuery trouble = new MultiPhraseQuery();
-    trouble.add(new Term[] {new Term("body", "blueberry"),
+    MultiPhraseQuery.Builder troubleBuilder = new MultiPhraseQuery.Builder();
+    troubleBuilder.add(new Term[] {new Term("body", "blueberry"),
         new Term("body", "blue")});
-    q.add(trouble, BooleanClause.Occur.MUST);
+    q.add(troubleBuilder.build(), BooleanClause.Occur.MUST);
     
     // exception will be thrown here without fix
     ScoreDoc[] hits = searcher.search(q.build(), 1000).scoreDocs;
@@ -246,11 +251,11 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     BooleanQuery.Builder q = new BooleanQuery.Builder();
     q.add(new TermQuery(new Term("type", "note")), BooleanClause.Occur.MUST);
     
-    MultiPhraseQuery trouble = new MultiPhraseQuery();
-    trouble.add(new Term("body", "a"));
-    trouble
+    MultiPhraseQuery.Builder troubleBuilder = new MultiPhraseQuery.Builder();
+    troubleBuilder.add(new Term("body", "a"));
+    troubleBuilder
         .add(new Term[] {new Term("body", "test"), new Term("body", "this")});
-    q.add(trouble, BooleanClause.Occur.MUST);
+    q.add(troubleBuilder.build(), BooleanClause.Occur.MUST);
     
     // exception will be thrown here without fix for #35626:
     ScoreDoc[] hits = searcher.search(q.build(), 1000).scoreDocs;
@@ -268,9 +273,10 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     IndexReader reader = writer.getReader();
     IndexSearcher searcher = newSearcher(reader);
     
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(new Term("body", "a"));
-    q.add(new Term[] {new Term("body", "nope"), new Term("body", "nope")});
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(new Term("body", "a"));
+    qb.add(new Term[] {new Term("body", "nope"), new Term("body", "nope")});
+    MultiPhraseQuery q = qb.build();
     assertEquals("Wrong number of hits", 0,
         searcher.search(q, 1).totalHits);
     
@@ -283,28 +289,36 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
   }
   
   public void testHashCodeAndEquals() {
-    MultiPhraseQuery query1 = new MultiPhraseQuery();
-    MultiPhraseQuery query2 = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder query1builder = new MultiPhraseQuery.Builder();
+    MultiPhraseQuery query1 = query1builder.build();
+    
+    MultiPhraseQuery.Builder query2builder = new MultiPhraseQuery.Builder();
+    MultiPhraseQuery query2 = query2builder.build();
     
     assertEquals(query1.hashCode(), query2.hashCode());
     assertEquals(query1, query2);
     
     Term term1 = new Term("someField", "someText");
     
-    query1.add(term1);
-    query2.add(term1);
+    query1builder.add(term1);
+    query1 = query1builder.build();
+
+    query2builder.add(term1);
+    query2 = query2builder.build();
     
     assertEquals(query1.hashCode(), query2.hashCode());
     assertEquals(query1, query2);
     
     Term term2 = new Term("someField", "someMoreText");
     
-    query1.add(term2);
+    query1builder.add(term2);
+    query1 = query1builder.build();
     
     assertFalse(query1.hashCode() == query2.hashCode());
     assertFalse(query1.equals(query2));
     
-    query2.add(term2);
+    query2builder.add(term2);
+    query2 = query2builder.build();
     
     assertEquals(query1.hashCode(), query2.hashCode());
     assertEquals(query1, query2);
@@ -320,7 +334,7 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
   
   // LUCENE-2526
   public void testEmptyToString() {
-    new MultiPhraseQuery().toString();
+    new MultiPhraseQuery.Builder().build().toString();
   }
   
   public void testCustomIDF() throws Exception {
@@ -338,10 +352,10 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
       } 
     });
     
-    MultiPhraseQuery query = new MultiPhraseQuery();
-    query.add(new Term[] { new Term("body", "this"), new Term("body", "that") });
-    query.add(new Term("body", "is"));
-    Weight weight = query.createWeight(searcher, true);
+    MultiPhraseQuery.Builder queryBuilder = new MultiPhraseQuery.Builder();
+    queryBuilder.add(new Term[] { new Term("body", "this"), new Term("body", "that") });
+    queryBuilder.add(new Term("body", "is"));
+    Weight weight = queryBuilder.build().createWeight(searcher, true);
     assertEquals(10f * 10f, weight.getValueForNormalization(), 0.001f);
 
     writer.close();
@@ -372,7 +386,7 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     IndexReader r = writer.getReader();
     writer.close();
     IndexSearcher s = newSearcher(r);
-    MultiPhraseQuery mpq = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
     //mpq.setSlop(1);
 
     // NOTE: not great that if we do the else clause here we
@@ -382,13 +396,13 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
     // return the same position more than once (0, in this
     // case):
     if (true) {
-      mpq.add(new Term[] {new Term("field", "b"), new Term("field", "c")}, 0);
-      mpq.add(new Term[] {new Term("field", "a")}, 0);
+      mpqb.add(new Term[] {new Term("field", "b"), new Term("field", "c")}, 0);
+      mpqb.add(new Term[] {new Term("field", "a")}, 0);
     } else {
-      mpq.add(new Term[] {new Term("field", "a")}, 0);
-      mpq.add(new Term[] {new Term("field", "b"), new Term("field", "c")}, 0);
+      mpqb.add(new Term[] {new Term("field", "a")}, 0);
+      mpqb.add(new Term[] {new Term("field", "b"), new Term("field", "c")}, 0);
     }
-    TopDocs hits = s.search(mpq, 2);
+    TopDocs hits = s.search(mpqb.build(), 2);
     assertEquals(2, hits.totalHits);
     assertEquals(hits.scoreDocs[0].score, hits.scoreDocs[1].score, 1e-5);
     /*
@@ -449,15 +463,15 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
    * in each position - one of each position is sufficient (OR logic)
    */
   public void testZeroPosIncrSloppyParsedAnd() throws IOException {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(new Term[]{ new Term("field", "a"), new Term("field", "1") }, -1);
-    q.add(new Term[]{ new Term("field", "b"), new Term("field", "1") }, 0);
-    q.add(new Term[]{ new Term("field", "c") }, 1);
-    doTestZeroPosIncrSloppy(q, 0);
-    q.setSlop(1);
-    doTestZeroPosIncrSloppy(q, 0);
-    q.setSlop(2);
-    doTestZeroPosIncrSloppy(q, 1);
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(new Term[]{ new Term("field", "a"), new Term("field", "1") }, -1);
+    qb.add(new Term[]{ new Term("field", "b"), new Term("field", "1") }, 0);
+    qb.add(new Term[]{ new Term("field", "c") }, 1);
+    doTestZeroPosIncrSloppy(qb.build(), 0);
+    qb.setSlop(1);
+    doTestZeroPosIncrSloppy(qb.build(), 0);
+    qb.setSlop(2);
+    doTestZeroPosIncrSloppy(qb.build(), 1);
   }
   
   private void doTestZeroPosIncrSloppy(Query q, int nExpected) throws IOException {
@@ -511,49 +525,49 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
    * MPQ AND Mode - Manually creating a multiple phrase query
    */
   public void testZeroPosIncrSloppyMpqAnd() throws IOException {
-    final MultiPhraseQuery mpq = new MultiPhraseQuery();
+    final MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
     int pos = -1;
     for (Token tap : INCR_0_QUERY_TOKENS_AND) {
       pos += tap.getPositionIncrement();
-      mpq.add(new Term[]{new Term("field",tap.toString())}, pos); //AND logic
+      mpqb.add(new Term[]{new Term("field",tap.toString())}, pos); //AND logic
     }
-    doTestZeroPosIncrSloppy(mpq, 0);
-    mpq.setSlop(1);
-    doTestZeroPosIncrSloppy(mpq, 0);
-    mpq.setSlop(2);
-    doTestZeroPosIncrSloppy(mpq, 1);
+    doTestZeroPosIncrSloppy(mpqb.build(), 0);
+    mpqb.setSlop(1);
+    doTestZeroPosIncrSloppy(mpqb.build(), 0);
+    mpqb.setSlop(2);
+    doTestZeroPosIncrSloppy(mpqb.build(), 1);
   }
 
   /**
    * MPQ Combined AND OR Mode - Manually creating a multiple phrase query
    */
   public void testZeroPosIncrSloppyMpqAndOrMatch() throws IOException {
-    final MultiPhraseQuery mpq = new MultiPhraseQuery();
+    final MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
     for (Token tap[] : INCR_0_QUERY_TOKENS_AND_OR_MATCH) {
       Term[] terms = tapTerms(tap);
       final int pos = tap[0].getPositionIncrement()-1;
-      mpq.add(terms, pos); //AND logic in pos, OR across lines 
+      mpqb.add(terms, pos); //AND logic in pos, OR across lines 
     }
-    doTestZeroPosIncrSloppy(mpq, 0);
-    mpq.setSlop(1);
-    doTestZeroPosIncrSloppy(mpq, 0);
-    mpq.setSlop(2);
-    doTestZeroPosIncrSloppy(mpq, 1);
+    doTestZeroPosIncrSloppy(mpqb.build(), 0);
+    mpqb.setSlop(1);
+    doTestZeroPosIncrSloppy(mpqb.build(), 0);
+    mpqb.setSlop(2);
+    doTestZeroPosIncrSloppy(mpqb.build(), 1);
   }
 
   /**
    * MPQ Combined AND OR Mode - Manually creating a multiple phrase query - with no match
    */
   public void testZeroPosIncrSloppyMpqAndOrNoMatch() throws IOException {
-    final MultiPhraseQuery mpq = new MultiPhraseQuery();
+    final MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
     for (Token tap[] : INCR_0_QUERY_TOKENS_AND_OR_NO_MATCHN) {
       Term[] terms = tapTerms(tap);
       final int pos = tap[0].getPositionIncrement()-1;
-      mpq.add(terms, pos); //AND logic in pos, OR across lines 
+      mpqb.add(terms, pos); //AND logic in pos, OR across lines 
     }
-    doTestZeroPosIncrSloppy(mpq, 0);
-    mpq.setSlop(2);
-    doTestZeroPosIncrSloppy(mpq, 0);
+    doTestZeroPosIncrSloppy(mpqb.build(), 0);
+    mpqb.setSlop(2);
+    doTestZeroPosIncrSloppy(mpqb.build(), 0);
   }
 
   private Term[] tapTerms(Token[] tap) {
@@ -565,11 +579,11 @@ public class TestMultiPhraseQuery extends LuceneTestCase {
   }
   
   public void testNegativeSlop() throws Exception {
-    MultiPhraseQuery query = new MultiPhraseQuery();
-    query.add(new Term("field", "two"));
-    query.add(new Term("field", "one"));
+    MultiPhraseQuery.Builder queryBuilder = new MultiPhraseQuery.Builder();
+    queryBuilder.add(new Term("field", "two"));
+    queryBuilder.add(new Term("field", "one"));
     expectThrows(IllegalArgumentException.class, () -> {
-      query.setSlop(-2);
+      queryBuilder.setSlop(-2);
     });
   }
   

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
index 3e8f9e1..8151150 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
@@ -63,11 +63,11 @@ public class TestPhrasePrefixQuery extends LuceneTestCase {
     IndexSearcher searcher = newSearcher(reader);
     
     // PhrasePrefixQuery query1 = new PhrasePrefixQuery();
-    MultiPhraseQuery query1 = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder query1builder = new MultiPhraseQuery.Builder();
     // PhrasePrefixQuery query2 = new PhrasePrefixQuery();
-    MultiPhraseQuery query2 = new MultiPhraseQuery();
-    query1.add(new Term("body", "blueberry"));
-    query2.add(new Term("body", "strawberry"));
+    MultiPhraseQuery.Builder query2builder = new MultiPhraseQuery.Builder();
+    query1builder.add(new Term("body", "blueberry"));
+    query2builder.add(new Term("body", "strawberry"));
     
     LinkedList<Term> termsWithPrefix = new LinkedList<>();
     
@@ -84,14 +84,14 @@ public class TestPhrasePrefixQuery extends LuceneTestCase {
       }
     } while (te.next() != null);
     
-    query1.add(termsWithPrefix.toArray(new Term[0]));
-    query2.add(termsWithPrefix.toArray(new Term[0]));
+    query1builder.add(termsWithPrefix.toArray(new Term[0]));
+    query2builder.add(termsWithPrefix.toArray(new Term[0]));
     
     ScoreDoc[] result;
-    result = searcher.search(query1, 1000).scoreDocs;
+    result = searcher.search(query1builder.build(), 1000).scoreDocs;
     assertEquals(2, result.length);
     
-    result = searcher.search(query2, 1000).scoreDocs;
+    result = searcher.search(query2builder.build(), 1000).scoreDocs;
     assertEquals(0, result.length);
     reader.close();
     indexStore.close();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java b/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
index 84468da..b7ae42a 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
@@ -175,9 +175,9 @@ public class TestPositionIncrement extends LuceneTestCase {
 
     // multi-phrase query should succed for non existing searched term
     // because there exist another searched terms in the same searched position. 
-    MultiPhraseQuery mq = new MultiPhraseQuery();
-    mq.add(new Term[]{new Term("field", "3"),new Term("field", "9")},0);
-    hits = searcher.search(mq, 1000).scoreDocs;
+    MultiPhraseQuery.Builder mqb = new MultiPhraseQuery.Builder();
+    mqb.add(new Term[]{new Term("field", "3"),new Term("field", "9")},0);
+    hits = searcher.search(mqb.build(), 1000).scoreDocs;
     assertEquals(1, hits.length);
 
     q = new PhraseQuery("field", "2", "4");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java b/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
index 9274fdf..a388842 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
@@ -200,42 +200,42 @@ public class TestSimpleExplanations extends BaseExplanationTestCase {
   /* MultiPhraseQuery */
   
   public void testMPQ1() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1"}));
-    q.add(ta(new String[] {"w2","w3", "xx"}));
-    qtest(q, new int[] { 0,1,2,3 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1"}));
+    qb.add(ta(new String[] {"w2","w3", "xx"}));
+    qtest(qb.build(), new int[] { 0,1,2,3 });
   }
   public void testMPQ2() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1"}));
-    q.add(ta(new String[] {"w2","w3"}));
-    qtest(q, new int[] { 0,1,3 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1"}));
+    qb.add(ta(new String[] {"w2","w3"}));
+    qtest(qb.build(), new int[] { 0,1,3 });
   }
   public void testMPQ3() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1","xx"}));
-    q.add(ta(new String[] {"w2","w3"}));
-    qtest(q, new int[] { 0,1,2,3 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1","xx"}));
+    qb.add(ta(new String[] {"w2","w3"}));
+    qtest(qb.build(), new int[] { 0,1,2,3 });
   }
   public void testMPQ4() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1"}));
-    q.add(ta(new String[] {"w2"}));
-    qtest(q, new int[] { 0 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1"}));
+    qb.add(ta(new String[] {"w2"}));
+    qtest(qb.build(), new int[] { 0 });
   }
   public void testMPQ5() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1"}));
-    q.add(ta(new String[] {"w2"}));
-    q.setSlop(1);
-    qtest(q, new int[] { 0,1,2 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1"}));
+    qb.add(ta(new String[] {"w2"}));
+    qb.setSlop(1);
+    qtest(qb.build(), new int[] { 0,1,2 });
   }
   public void testMPQ6() throws Exception {
-    MultiPhraseQuery q = new MultiPhraseQuery();
-    q.add(ta(new String[] {"w1","w3"}));
-    q.add(ta(new String[] {"w2"}));
-    q.setSlop(1);
-    qtest(q, new int[] { 0,1,2,3 });
+    MultiPhraseQuery.Builder qb = new MultiPhraseQuery.Builder();
+    qb.add(ta(new String[] {"w1","w3"}));
+    qb.add(ta(new String[] {"w2"}));
+    qb.setSlop(1);
+    qtest(qb.build(), new int[] { 0,1,2,3 });
   }
 
   /* some simple tests of boolean queries containing term queries */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestSimpleSearchEquivalence.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSimpleSearchEquivalence.java b/lucene/core/src/test/org/apache/lucene/search/TestSimpleSearchEquivalence.java
index 33007eb..6386c79 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSimpleSearchEquivalence.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSimpleSearchEquivalence.java
@@ -145,10 +145,10 @@ public class TestSimpleSearchEquivalence extends SearchEquivalenceTestBase {
     Term t2 = randomTerm();
     PhraseQuery q1 = new PhraseQuery(t1.field(), t1.bytes(), t2.bytes());
     Term t3 = randomTerm();
-    MultiPhraseQuery q2 = new MultiPhraseQuery();
-    q2.add(t1);
-    q2.add(new Term[] { t2, t3 });
-    assertSubsetOf(q1, q2);
+    MultiPhraseQuery.Builder q2b = new MultiPhraseQuery.Builder();
+    q2b.add(t1);
+    q2b.add(new Term[] { t2, t3 });
+    assertSubsetOf(q1, q2b.build());
   }
   
   /** same as above, with posincs */
@@ -160,10 +160,10 @@ public class TestSimpleSearchEquivalence extends SearchEquivalenceTestBase {
     builder.add(t2, 2);
     PhraseQuery q1 = builder.build();
     Term t3 = randomTerm();
-    MultiPhraseQuery q2 = new MultiPhraseQuery();
-    q2.add(t1);
-    q2.add(new Term[] { t2, t3 }, 2);
-    assertSubsetOf(q1, q2);
+    MultiPhraseQuery.Builder q2b = new MultiPhraseQuery.Builder();
+    q2b.add(t1);
+    q2b.add(new Term[] { t2, t3 }, 2);
+    assertSubsetOf(q1, q2b.build());
   }
   
   /** "A B"~∞ = +A +B if A != B */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery2.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery2.java b/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery2.java
index 3910fac..1a97a99 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery2.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery2.java
@@ -147,8 +147,8 @@ public class TestSloppyPhraseQuery2 extends SearchEquivalenceTestBase {
     for (int i = 0; i < 10; i++) {
       MultiPhraseQuery q1 = randomPhraseQuery(seed);
       MultiPhraseQuery q2 = randomPhraseQuery(seed);
-      q1.setSlop(i);
-      q2.setSlop(i+1);
+      q1 = new MultiPhraseQuery.Builder(q1).setSlop(i).build();
+      q2 = new MultiPhraseQuery.Builder(q2).setSlop(i+1).build();
       assertSubsetOf(q1, q2);
     }
   }
@@ -156,7 +156,7 @@ public class TestSloppyPhraseQuery2 extends SearchEquivalenceTestBase {
   private MultiPhraseQuery randomPhraseQuery(long seed) {
     Random random = new Random(seed);
     int length = TestUtil.nextInt(random, 2, 5);
-    MultiPhraseQuery pq = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder pqb = new MultiPhraseQuery.Builder();
     int position = 0;
     for (int i = 0; i < length; i++) {
       int depth = TestUtil.nextInt(random, 1, 3);
@@ -164,9 +164,9 @@ public class TestSloppyPhraseQuery2 extends SearchEquivalenceTestBase {
       for (int j = 0; j < depth; j++) {
         terms[j] = new Term("field", "" + (char) TestUtil.nextInt(random, 'a', 'z'));
       }
-      pq.add(terms, position);
+      pqb.add(terms, position);
       position += TestUtil.nextInt(random, 1, 3);
     }
-    return pq;
+    return pqb.build();
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/core/src/test/org/apache/lucene/util/TestQueryBuilder.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/util/TestQueryBuilder.java b/lucene/core/src/test/org/apache/lucene/util/TestQueryBuilder.java
index bd5a49a..205fbab 100644
--- a/lucene/core/src/test/org/apache/lucene/util/TestQueryBuilder.java
+++ b/lucene/core/src/test/org/apache/lucene/util/TestQueryBuilder.java
@@ -173,11 +173,11 @@ public class TestQueryBuilder extends LuceneTestCase {
   
   /** forms multiphrase query */
   public void testSynonymsPhrase() throws Exception {
-    MultiPhraseQuery expected = new MultiPhraseQuery();
-    expected.add(new Term("field", "old"));
-    expected.add(new Term[] { new Term("field", "dogs"), new Term("field", "dog") });
+    MultiPhraseQuery.Builder expectedBuilder = new MultiPhraseQuery.Builder();
+    expectedBuilder.add(new Term("field", "old"));
+    expectedBuilder.add(new Term[] { new Term("field", "dogs"), new Term("field", "dog") });
     QueryBuilder builder = new QueryBuilder(new MockSynonymAnalyzer());
-    assertEquals(expected, builder.createPhraseQuery("field", "old dogs"));
+    assertEquals(expectedBuilder.build(), builder.createPhraseQuery("field", "old dogs"));
   }
 
   protected static class SimpleCJKTokenizer extends Tokenizer {
@@ -331,13 +331,13 @@ public class TestQueryBuilder extends LuceneTestCase {
   
   /** forms multiphrase query */
   public void testCJKSynonymsPhrase() throws Exception {
-    MultiPhraseQuery expected = new MultiPhraseQuery();
-    expected.add(new Term("field", "中"));
-    expected.add(new Term[] { new Term("field", "国"), new Term("field", "國")});
+    MultiPhraseQuery.Builder expectedBuilder = new MultiPhraseQuery.Builder();
+    expectedBuilder.add(new Term("field", "中"));
+    expectedBuilder.add(new Term[] { new Term("field", "国"), new Term("field", "國")});
     QueryBuilder builder = new QueryBuilder(new MockCJKSynonymAnalyzer());
-    assertEquals(expected, builder.createPhraseQuery("field", "中国"));
-    expected.setSlop(3);
-    assertEquals(expected, builder.createPhraseQuery("field", "中国", 3));
+    assertEquals(expectedBuilder.build(), builder.createPhraseQuery("field", "中国"));
+    expectedBuilder.setSlop(3);
+    assertEquals(expectedBuilder.build(), builder.createPhraseQuery("field", "中国", 3));
   }
 
   public void testNoTermAttribute() {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java
----------------------------------------------------------------------
diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java
index a73ebf8..650be87 100644
--- a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java
+++ b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java
@@ -156,7 +156,7 @@ public class WeightedSpanTermExtractor {
       extract(((ToChildBlockJoinQuery) query).getParentQuery(), boost, terms);
     } else if (query instanceof MultiPhraseQuery) {
       final MultiPhraseQuery mpq = (MultiPhraseQuery) query;
-      final List<Term[]> termArrays = mpq.getTermArrays();
+      final Term[][] termArrays = mpq.getTermArrays();
       final int[] positions = mpq.getPositions();
       if (positions.length > 0) {
 
@@ -171,8 +171,8 @@ public class WeightedSpanTermExtractor {
         final List<SpanQuery>[] disjunctLists = new List[maxPosition + 1];
         int distinctPositions = 0;
 
-        for (int i = 0; i < termArrays.size(); ++i) {
-          final Term[] termArray = termArrays.get(i);
+        for (int i = 0; i < termArrays.length; ++i) {
+          final Term[] termArray = termArrays[i];
           List<SpanQuery> disjuncts = disjunctLists[positions[i]];
           if (disjuncts == null) {
             disjuncts = (disjunctLists[positions[i]] = new ArrayList<>(termArray.length));

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java
----------------------------------------------------------------------
diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java
index 187f4a5..936d121 100644
--- a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java
+++ b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java
@@ -793,29 +793,29 @@ public class HighlighterTest extends BaseTokenStreamTestCase implements Formatte
   }
 
   public void testQueryScorerMultiPhraseQueryHighlighting() throws Exception {
-    MultiPhraseQuery mpq = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
 
-    mpq.add(new Term[] { new Term(FIELD_NAME, "wordx"), new Term(FIELD_NAME, "wordb") });
-    mpq.add(new Term(FIELD_NAME, "wordy"));
+    mpqb.add(new Term[] { new Term(FIELD_NAME, "wordx"), new Term(FIELD_NAME, "wordb") });
+    mpqb.add(new Term(FIELD_NAME, "wordy"));
 
-    doSearching(mpq);
+    doSearching(mpqb.build());
 
     final int maxNumFragmentsRequired = 2;
     assertExpectedHighlightCount(maxNumFragmentsRequired, 6);
   }
 
   public void testQueryScorerMultiPhraseQueryHighlightingWithGap() throws Exception {
-    MultiPhraseQuery mpq = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
 
     /*
      * The toString of MultiPhraseQuery doesn't work so well with these
      * out-of-order additions, but the Query itself seems to match accurately.
      */
 
-    mpq.add(new Term[] { new Term(FIELD_NAME, "wordz") }, 2);
-    mpq.add(new Term[] { new Term(FIELD_NAME, "wordx") }, 0);
+    mpqb.add(new Term[] { new Term(FIELD_NAME, "wordz") }, 2);
+    mpqb.add(new Term[] { new Term(FIELD_NAME, "wordx") }, 0);
 
-    doSearching(mpq);
+    doSearching(mpqb.build());
 
     final int maxNumFragmentsRequired = 1;
     final int expectedHighlights = 2;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/MultiFieldQueryParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/MultiFieldQueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/MultiFieldQueryParser.java
index 02d25c4..b9963ec 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/MultiFieldQueryParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/MultiFieldQueryParser.java
@@ -134,7 +134,11 @@ public class MultiFieldQueryParser extends QueryParser
       }
       q = builder.build();
     } else if (q instanceof MultiPhraseQuery) {
-      ((MultiPhraseQuery) q).setSlop(slop);
+      MultiPhraseQuery mpq = (MultiPhraseQuery)q;
+      
+      if (slop != mpq.getSlop()) {
+        q = new MultiPhraseQuery.Builder(mpq).setSlop(slop).build();
+      }
     }
     return q;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParserBase.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParserBase.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParserBase.java
index c988b8c..d0a371c 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParserBase.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParserBase.java
@@ -502,9 +502,12 @@ public abstract class QueryParserBase extends QueryBuilder implements CommonQuer
         builder.add(terms[i], positions[i]);
       }
       query = builder.build();
-    }
-    if (query instanceof MultiPhraseQuery) {
-      ((MultiPhraseQuery) query).setSlop(slop);
+    } else if (query instanceof MultiPhraseQuery) {
+      MultiPhraseQuery mpq = (MultiPhraseQuery)query;
+      
+      if (slop != mpq.getSlop()) {
+        query = new MultiPhraseQuery.Builder(mpq).setSlop(slop).build();
+      }
     }
 
     return query;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MultiPhraseQueryNodeBuilder.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MultiPhraseQueryNodeBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MultiPhraseQueryNodeBuilder.java
index 6976a04..35debe9 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MultiPhraseQueryNodeBuilder.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MultiPhraseQueryNodeBuilder.java
@@ -43,7 +43,7 @@ public class MultiPhraseQueryNodeBuilder implements StandardQueryBuilder {
   public MultiPhraseQuery build(QueryNode queryNode) throws QueryNodeException {
     MultiPhraseQueryNode phraseNode = (MultiPhraseQueryNode) queryNode;
 
-    MultiPhraseQuery phraseQuery = new MultiPhraseQuery();
+    MultiPhraseQuery.Builder phraseQueryBuilder = new MultiPhraseQuery.Builder();
 
     List<QueryNode> children = phraseNode.getChildren();
 
@@ -70,14 +70,14 @@ public class MultiPhraseQueryNodeBuilder implements StandardQueryBuilder {
       for (int positionIncrement : positionTermMap.keySet()) {
         List<Term> termList = positionTermMap.get(positionIncrement);
 
-        phraseQuery.add(termList.toArray(new Term[termList.size()]),
+        phraseQueryBuilder.add(termList.toArray(new Term[termList.size()]),
             positionIncrement);
 
       }
 
     }
 
-    return phraseQuery;
+    return phraseQueryBuilder.build();
 
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/SlopQueryNodeBuilder.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/SlopQueryNodeBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/SlopQueryNodeBuilder.java
index e964346..77667d9 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/SlopQueryNodeBuilder.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/SlopQueryNodeBuilder.java
@@ -55,7 +55,13 @@ public class SlopQueryNodeBuilder implements StandardQueryBuilder {
       query = builder.build();
 
     } else {
-      ((MultiPhraseQuery) query).setSlop(phraseSlopNode.getValue());
+      MultiPhraseQuery mpq = (MultiPhraseQuery)query;
+      
+      int slop = phraseSlopNode.getValue();
+      
+      if (slop != mpq.getSlop()) {
+        query = new MultiPhraseQuery.Builder(mpq).setSlop(slop).build();
+      }
     }
 
     return query;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiPhraseQueryParsing.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiPhraseQueryParsing.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiPhraseQueryParsing.java
index 709f3c3..1651038 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiPhraseQueryParsing.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiPhraseQueryParsing.java
@@ -100,12 +100,12 @@ public class TestMultiPhraseQueryParsing extends LuceneTestCase {
     Query q = qp.parse("\"this text is acually ignored\"");
     assertTrue("wrong query type!", q instanceof MultiPhraseQuery);
 
-    MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery();
-    multiPhraseQuery.add(new Term[]{ new Term("field", "a"), new Term("field", "1") }, -1);
-    multiPhraseQuery.add(new Term[]{ new Term("field", "b"), new Term("field", "1") }, 0);
-    multiPhraseQuery.add(new Term[]{ new Term("field", "c") }, 1);
+    MultiPhraseQuery.Builder multiPhraseQueryBuilder = new MultiPhraseQuery.Builder();
+    multiPhraseQueryBuilder.add(new Term[]{ new Term("field", "a"), new Term("field", "1") }, -1);
+    multiPhraseQueryBuilder.add(new Term[]{ new Term("field", "b"), new Term("field", "1") }, 0);
+    multiPhraseQueryBuilder.add(new Term[]{ new Term("field", "c") }, 1);
 
-    assertEquals(multiPhraseQuery, q);
+    assertEquals(multiPhraseQueryBuilder.build(), q);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestQueryParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestQueryParser.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestQueryParser.java
index 2457d3c..5b4eba8 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestQueryParser.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestQueryParser.java
@@ -338,16 +338,17 @@ public class TestQueryParser extends QueryParserTestBase {
   
   /** forms multiphrase query */
   public void testSynonymsPhrase() throws Exception {
-    MultiPhraseQuery expectedQ = new MultiPhraseQuery();
-    expectedQ.add(new Term("field", "old"));
-    expectedQ.add(new Term[] { new Term("field", "dogs"), new Term("field", "dog") });
+    MultiPhraseQuery.Builder expectedQBuilder = new MultiPhraseQuery.Builder();
+    expectedQBuilder.add(new Term("field", "old"));
+    expectedQBuilder.add(new Term[] { new Term("field", "dogs"), new Term("field", "dog") });
     QueryParser qp = new QueryParser("field", new MockSynonymAnalyzer());
-    assertEquals(expectedQ, qp.parse("\"old dogs\""));
+    assertEquals(expectedQBuilder.build(), qp.parse("\"old dogs\""));
     qp.setDefaultOperator(Operator.AND);
-    assertEquals(expectedQ, qp.parse("\"old dogs\""));
-    BoostQuery expected = new BoostQuery(expectedQ, 2f);
+    assertEquals(expectedQBuilder.build(), qp.parse("\"old dogs\""));
+    BoostQuery expected = new BoostQuery(expectedQBuilder.build(), 2f);
     assertEquals(expected, qp.parse("\"old dogs\"^2"));
-    expectedQ.setSlop(3);
+    expectedQBuilder.setSlop(3);
+    expected = new BoostQuery(expectedQBuilder.build(), 2f);
     assertEquals(expected, qp.parse("\"old dogs\"~3^2"));
   }
   
@@ -461,15 +462,16 @@ public class TestQueryParser extends QueryParserTestBase {
   
   /** forms multiphrase query */
   public void testCJKSynonymsPhrase() throws Exception {
-    MultiPhraseQuery expectedQ = new MultiPhraseQuery();
-    expectedQ.add(new Term("field", "中"));
-    expectedQ.add(new Term[] { new Term("field", "国"), new Term("field", "國")});
+    MultiPhraseQuery.Builder expectedQBuilder = new MultiPhraseQuery.Builder();
+    expectedQBuilder.add(new Term("field", "中"));
+    expectedQBuilder.add(new Term[] { new Term("field", "国"), new Term("field", "國")});
     QueryParser qp = new QueryParser("field", new MockCJKSynonymAnalyzer());
     qp.setDefaultOperator(Operator.AND);
-    assertEquals(expectedQ, qp.parse("\"中国\""));
-    Query expected = new BoostQuery(expectedQ, 2f);
+    assertEquals(expectedQBuilder.build(), qp.parse("\"中国\""));
+    Query expected = new BoostQuery(expectedQBuilder.build(), 2f);
     assertEquals(expected, qp.parse("\"中国\"^2"));
-    expectedQ.setSlop(3);
+    expectedQBuilder.setSlop(3);
+    expected = new BoostQuery(expectedQBuilder.build(), 2f);
     assertEquals(expected, qp.parse("\"中国\"~3^2"));
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/sandbox/src/java/org/apache/lucene/payloads/PayloadSpanUtil.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/payloads/PayloadSpanUtil.java b/lucene/sandbox/src/java/org/apache/lucene/payloads/PayloadSpanUtil.java
index 400e42c..20cd2c0 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/payloads/PayloadSpanUtil.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/payloads/PayloadSpanUtil.java
@@ -114,7 +114,7 @@ public class PayloadSpanUtil {
 
     } else if (query instanceof MultiPhraseQuery) {
       final MultiPhraseQuery mpq = (MultiPhraseQuery) query;
-      final List<Term[]> termArrays = mpq.getTermArrays();
+      final Term[][] termArrays = mpq.getTermArrays();
       final int[] positions = mpq.getPositions();
       if (positions.length > 0) {
 
@@ -129,8 +129,8 @@ public class PayloadSpanUtil {
             new List[maxPosition + 1];
         int distinctPositions = 0;
 
-        for (int i = 0; i < termArrays.size(); ++i) {
-          final Term[] termArray = termArrays.get(i);
+        for (int i = 0; i < termArrays.length; ++i) {
+          final Term[] termArray = termArrays[i];
           List<Query> disjuncts = disjunctLists[positions[i]];
           if (disjuncts == null) {
             disjuncts = (disjunctLists[positions[i]] = new ArrayList<>(

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java
index 392941d..8016b61 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java
@@ -522,15 +522,15 @@ public class TestTermAutomatonQuery extends LuceneTestCase {
           }
         }
         String string = sb.toString();
-        MultiPhraseQuery mpq = new MultiPhraseQuery();
+        MultiPhraseQuery.Builder mpqb = new MultiPhraseQuery.Builder();
         for(int j=0;j<string.length();j++) {
           if (string.charAt(j) == '*') {
-            mpq.add(allTerms);
+            mpqb.add(allTerms);
           } else {
-            mpq.add(new Term("field", ""+string.charAt(j)));
+            mpqb.add(new Term("field", ""+string.charAt(j)));
           }
         }
-        bq.add(mpq, BooleanClause.Occur.SHOULD);
+        bq.add(mpqb.build(), BooleanClause.Occur.SHOULD);
         strings.add(new BytesRef(string));
       }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
index 20fee1e..81c0fd7 100644
--- a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
+++ b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
@@ -387,7 +387,6 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
     // only set slop of the phrase query was a result of this parser
     // and not a sub-parser.
     if (subQParser == null) {
-
       if (query instanceof PhraseQuery) {
         PhraseQuery pq = (PhraseQuery) query;
         Term[] terms = pq.getTerms();
@@ -398,11 +397,13 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
         }
         builder.setSlop(slop);
         query = builder.build();
+      } else if (query instanceof MultiPhraseQuery) {
+        MultiPhraseQuery mpq = (MultiPhraseQuery)query;
+      
+        if (slop != mpq.getSlop()) {
+          query = new MultiPhraseQuery.Builder(mpq).setSlop(slop).build();
+        }
       }
-      if (query instanceof MultiPhraseQuery) {
-        ((MultiPhraseQuery) query).setSlop(slop);
-      }
-
     }
 
     return query;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/14070770/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParser.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParser.java b/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParser.java
index 4671292..8401f3e 100644
--- a/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParser.java
+++ b/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParser.java
@@ -1235,9 +1235,11 @@ public class ExtendedDismaxQParser extends QParser {
               builder.setSlop(slop);
               query = builder.build();
             } else if (query instanceof MultiPhraseQuery) {
-              MultiPhraseQuery pq = (MultiPhraseQuery)query;
-              if (minClauseSize > 1 && pq.getTermArrays().size() < minClauseSize) return null;
-              ((MultiPhraseQuery)query).setSlop(slop);
+              MultiPhraseQuery mpq = (MultiPhraseQuery)query;
+              if (minClauseSize > 1 && mpq.getTermArrays().length < minClauseSize) return null;
+              if (slop != mpq.getSlop()) {
+                query = new MultiPhraseQuery.Builder(mpq).setSlop(slop).build();
+              }
             } else if (minClauseSize > 1) {
               // if it's not a type of phrase query, it doesn't meet the minClauseSize requirements
               return null;