Index: lucene/core/src/java/org/apache/lucene/search/TopTermsRewrite.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TopTermsRewrite.java	(revision a09f3facfc5d9d096661a2c45458fc5b55a07819)
+++ lucene/core/src/java/org/apache/lucene/search/TopTermsRewrite.java	(date 1530827344000)
@@ -25,8 +25,8 @@
 
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.index.TermStates;
 import org.apache.lucene.index.TermState;
+import org.apache.lucene.index.TermStates;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.BytesRef;
@@ -156,10 +156,13 @@
     
     final B b = getTopLevelBuilder();
     final ScoreTerm[] scoreTerms = stQueue.toArray(new ScoreTerm[stQueue.size()]);
+    //nocommit why sort?  should addClause document an expected order?
     ArrayUtil.timSort(scoreTerms, scoreTermSortByTermComp);
 
     for (final ScoreTerm st : scoreTerms) {
-      final Term term = new Term(query.field, st.bytes.toBytesRef());
+      final BytesRef bytesRef = st.bytes.get(); // a reference
+      final Term term = new Term(query.field, bytesRef);
+      assert term.bytes() != bytesRef : "TermQuery should create a new BytesRef";
       // We allow negative term scores (fuzzy query does this, for example) while collecting the terms,
       // but truncate such boosts to 0.0f when building the query:
       addClause(b, term, st.termState.docFreq(), Math.max(0.0f, st.boost), st.termState); // add to query
@@ -182,13 +185,7 @@
     return true;
   }
   
-  private static final Comparator<ScoreTerm> scoreTermSortByTermComp = 
-    new Comparator<ScoreTerm>() {
-      @Override
-      public int compare(ScoreTerm st1, ScoreTerm st2) {
-        return st1.bytes.get().compareTo(st2.bytes.get());
-      }
-    };
+  private static final Comparator<ScoreTerm> scoreTermSortByTermComp = Comparator.comparing(st -> st.bytes.get());
 
   static final class ScoreTerm implements Comparable<ScoreTerm> {
     public final BytesRefBuilder bytes = new BytesRefBuilder();
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java	(revision a09f3facfc5d9d096661a2c45458fc5b55a07819)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java	(date 1530824656000)
@@ -51,23 +51,36 @@
  */
 public class SpanMultiTermQueryWrapper<Q extends MultiTermQuery> extends SpanQuery {
 
+  public static final int MAX_TERMS = -1;
   protected final Q query;
-  private SpanRewriteMethod rewriteMethod;
+  private MultiTermQuery.RewriteMethod rewriteMethod;
 
   /**
-   * Create a new SpanMultiTermQueryWrapper. 
-   * 
+   * Create a new SpanMultiTermQueryWrapper.
+   *
    * @param query Query to wrap.
+   * @param maxTerms max number of terms
    */
   @SuppressWarnings({"rawtypes","unchecked"})
+  public SpanMultiTermQueryWrapper(Q query, int maxTerms) {
+    this.query = Objects.requireNonNull(query);
+    this.rewriteMethod = selectRewriteMethod(query, maxTerms);
+  }
+
+  /**
+   * Create a new SpanMultiTermQueryWrapper.
+   *
+   * @param query Query to wrap.
+   */
   public SpanMultiTermQueryWrapper(Q query) {
-    this.query = Objects.requireNonNull(query);
-    this.rewriteMethod = selectRewriteMethod(query);
+    this(query, MAX_TERMS);
   }
 
-  private static SpanRewriteMethod selectRewriteMethod(MultiTermQuery query) {
+  private static MultiTermQuery.RewriteMethod selectRewriteMethod(MultiTermQuery query, int maxTerms) {
     MultiTermQuery.RewriteMethod method = query.getRewriteMethod();
-    if (method instanceof TopTermsRewrite) {
+    if (maxTerms != MAX_TERMS) {
+      return new SpanQueryFrequentTermsScoringRewrite(maxTerms, FrequentTermsScoringRewrite.DF_ORDER);
+    } else if (method instanceof TopTermsRewrite) {
       final int pqsize = ((TopTermsRewrite) method).getSize();
       return new TopTermsSpanBooleanQueryRewrite(pqsize);
     } else {
@@ -78,7 +91,8 @@
   /**
    * Expert: returns the rewriteMethod
    */
-  public final SpanRewriteMethod getRewriteMethod() {
+  //nocommit is this change acceptable in a minor release?  It's "expert" so I think so?
+  public final MultiTermQuery.RewriteMethod getRewriteMethod() {
     return rewriteMethod;
   }
 
@@ -86,6 +100,7 @@
    * Expert: sets the rewrite method. This only makes sense
    * to be a span rewrite method.
    */
+  //nocommit deprecate and overload with one of type MultiTermQuery.RewriteMethod ?
   public final void setRewriteMethod(SpanRewriteMethod rewriteMethod) {
     this.rewriteMethod = rewriteMethod;
   }
@@ -127,6 +142,7 @@
     return classHash() * 31 + query.hashCode();
   }
 
+  //nocommit shouldn't the rewrite method be a part of equals()?
   @Override
   public boolean equals(Object other) {
     return sameClassAs(other) &&
@@ -134,6 +150,7 @@
   }
 
   /** Abstract class that defines how the query is rewritten. */
+  //nocommit deprecate this?
   public static abstract class SpanRewriteMethod extends MultiTermQuery.RewriteMethod {
     @Override
     public abstract SpanQuery rewrite(IndexReader reader, MultiTermQuery query) throws IOException;
@@ -202,7 +219,7 @@
     
         @Override
         protected List<SpanQuery> getTopLevelBuilder() {
-          return new ArrayList<SpanQuery>();
+          return new ArrayList<>();
         }
 
         @Override
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanQueryFrequentTermsScoringRewrite.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanQueryFrequentTermsScoringRewrite.java	(date 1530824656000)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanQueryFrequentTermsScoringRewrite.java	(date 1530824656000)
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.search.spans;
+
+import java.util.function.BiPredicate;
+
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.PriorityQueue;
+
+/**
+ * FrequentTermsScoringRewrite implementation to build a <code>SpanOrQuery</code>
+ * from terms collected for rewrite.
+ */
+//nocommit maybe this should be an inner class of SpanMultiTermQueryWrapper?
+public class SpanQueryFrequentTermsScoringRewrite extends FrequentTermsScoringRewrite {
+
+  public SpanQueryFrequentTermsScoringRewrite(int maxSize, BiPredicate<ScoreTerm, ScoreTerm> orderingFunction) {
+    super(maxSize, orderingFunction);
+  }
+
+  @Override
+  protected Query build(PriorityQueue<FrequentTermsScoringRewrite.ScoreTerm> builder) {
+    SpanQuery[] result = new SpanTermQuery[builder.size()];
+//nocommit do we even need to "pop" in this order or instead lexicographic (like TopTermsRewrite) or just don't sort?
+    for (int pos = 0; pos < result.length; pos++) {
+      ScoreTerm st = builder.pop();
+      result[pos] = new SpanTermQuery(st.term, st.termState);
+    }
+    return new SpanOrQuery(result);
+  }
+}
Index: lucene/core/src/test/org/apache/lucene/search/TestSpanQueryFrequentTermsScoringRewrite.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestSpanQueryFrequentTermsScoringRewrite.java	(date 1530824656000)
+++ lucene/core/src/test/org/apache/lucene/search/TestSpanQueryFrequentTermsScoringRewrite.java	(date 1530824656000)
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.search;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.spans.FrequentTermsScoringRewrite;
+import org.apache.lucene.search.spans.SpanOrQuery;
+import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.SpanQueryFrequentTermsScoringRewrite;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestSpanQueryFrequentTermsScoringRewrite extends LuceneTestCase {
+
+  private static Directory directory;
+  private static IndexReader reader;
+
+  /**
+   * Test is inspired from patch submitted in LUCENE-6513
+   */
+  @BeforeClass
+  public static void setupIndex() throws IOException {
+
+    directory = newDirectory();
+
+    final RandomIndexWriter writer = new RandomIndexWriter(random(), directory, new MockAnalyzer(random()));
+    StringBuilder terms = new StringBuilder(" term1");
+
+    // odds - build up
+    // evens - single only
+    // 5 & 6 - add to all
+    for (int i = 0; i < 300; i++) {
+      Document doc = new Document();
+      String value = "term8 term54";
+      if (i % 2 == 0) {
+        value += " term" + i;
+      } else {
+        terms.append(" term").append(i);
+        value += terms.toString();
+      }
+      doc.add(newTextField("field", value, Field.Store.NO));
+      writer.addDocument(doc);
+      //writer.flush();//new segment; RandomIndexWriter will do this occasionally
+    }
+
+    writer.close();
+    reader = DirectoryReader.open(directory);
+  }
+
+  @AfterClass
+  public static void teardownIndex() throws IOException {
+    reader.close();
+    directory.close();
+  }
+
+  @Test
+  public void testFrequentTermScoringRewriteForSpanQuery() throws IOException {
+    final int size = 8;
+    final MultiTermQuery wildcardQuery = new WildcardQuery(new Term("field", "term*"));
+
+    FrequentTermsScoringRewrite frequentTermsScoringRewrite = new SpanQueryFrequentTermsScoringRewrite(size, FrequentTermsScoringRewrite.DF_THEN_TTF_ORDER);
+    Query query = frequentTermsScoringRewrite.rewrite(reader, wildcardQuery);
+
+    List<SpanQuery> clauses = Arrays.asList(((SpanOrQuery) query).getClauses());
+
+    //check if the number of expanded terms is as expected
+    assertEquals(size, clauses.size());
+
+    List<String> expectedTerms = Arrays.asList("term8", "term54", "term1", "term3", "term5", "term7", "term9", "term11");
+
+    //check if every term in the query rewrite is as expected
+    assertEquals(expectedTerms.size(),
+        clauses.stream().filter(clause -> expectedTerms.contains(clause.toString("field"))).count());
+  }
+}
\ No newline at end of file
Index: lucene/core/src/java/org/apache/lucene/search/spans/FrequentTermsScoringRewrite.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/FrequentTermsScoringRewrite.java	(date 1530824656000)
+++ lucene/core/src/java/org/apache/lucene/search/spans/FrequentTermsScoringRewrite.java	(date 1530824656000)
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.search.spans;
+
+import java.io.IOException;
+import java.util.function.BiPredicate;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermStates;
+import org.apache.lucene.search.ScoringRewrite;
+import org.apache.lucene.util.PriorityQueue;
+
+/**
+ * A query rewrite method for MultiTermQuery which collects frequent terms in
+ * index limited by <code>maxNumTerms</code>. Rank of frequent term is calculated based
+ * on ordering specified in <code>orderingFunction</code>.
+ *
+ * @lucene.internal because ScoringRewrite is also
+ */
+public abstract class FrequentTermsScoringRewrite extends ScoringRewrite<PriorityQueue<FrequentTermsScoringRewrite.ScoreTerm>> {
+
+  public static final BiPredicate<ScoreTerm, ScoreTerm> DF_ORDER = (a, b) -> a.termState.docFreq() < b.termState.docFreq();
+  public static final BiPredicate<ScoreTerm, ScoreTerm> DF_THEN_TTF_ORDER = (a, b) -> {
+    if (a.termState.docFreq() == b.termState.docFreq()) {
+      return a.termState.totalTermFreq() < b.termState.totalTermFreq();
+    } else {
+      return a.termState.docFreq() < b.termState.docFreq();
+    }
+  };
+  private final int maxNumTerms;
+  private final BiPredicate<ScoreTerm, ScoreTerm> orderingFunction;
+
+  public FrequentTermsScoringRewrite(int maxNumTerms) {
+    this.maxNumTerms = maxNumTerms;
+    this.orderingFunction = DF_ORDER;
+  }
+
+  public FrequentTermsScoringRewrite(int maxNumTerms, BiPredicate<ScoreTerm, ScoreTerm> orderingFunction) {
+    this.maxNumTerms = maxNumTerms;
+    this.orderingFunction = orderingFunction;
+  }
+
+  //nocommit equals & hashcode?
+
+  @Override
+  protected PriorityQueue getTopLevelBuilder() throws IOException {
+    return new PriorityQueue<ScoreTerm>(maxNumTerms) {
+      @Override
+      protected boolean lessThan(ScoreTerm a, ScoreTerm b) {
+        return orderingFunction.test(a, b);
+      }
+    };
+  }
+
+  @Override
+  protected void checkMaxClauseCount(int count) throws IOException {
+  }
+
+  @Override
+  protected void addClause(PriorityQueue topLevel, Term term, int docCount, float boost, TermStates states) throws IOException {
+    topLevel.insertWithOverflow(new ScoreTerm(term, states));
+  }
+
+  //nocommit this looks remarkably like SpanTermQuery; what if we add a SpanTermQuery.getTermState()?
+  public static final class ScoreTerm {
+    public final Term term;
+    public final TermStates termState;
+
+    public ScoreTerm(Term term, TermStates termState) {
+      this.term = term;
+      this.termState = termState;
+    }
+  }
+}
\ No newline at end of file
