merge up to trunk

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene4456@1393245 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index d4f83cd..eea7e73 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -38,6 +38,11 @@
   facet.method=fcs concurrently. PivotFacetHelper now extends SimpleFacet and
   the getFacetImplementation() extension hook was removed. (dsmiley)
 
+* SOLR-3897: A highlighter parameter "hl.preserveMulti" to return all of the
+  values of a multiValued field in their original order when highlighting.
+  (Joel Bernstein via yonik)
+
+
 Optimizations
 ----------------------
 
diff --git a/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java b/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java
index 8f57ad9..9855f8b 100644
--- a/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java
+++ b/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java
@@ -446,6 +446,8 @@
       listFields.add(field.stringValue());
     }
 
+    boolean preserveMulti = params.getFieldBool(fieldName, HighlightParams.PRESERVE_MULTI, false);
+
     String[] docTexts = (String[]) listFields.toArray(new String[listFields.size()]);
    
     // according to Document javadoc, doc.getValues() never returns null. check empty instead of null
@@ -511,8 +513,14 @@
       try {
         TextFragment[] bestTextFragments = highlighter.getBestTextFragments(tstream, docTexts[j], mergeContiguousFragments, numFragments);
         for (int k = 0; k < bestTextFragments.length; k++) {
-          if ((bestTextFragments[k] != null) && (bestTextFragments[k].getScore() > 0)) {
-            frags.add(bestTextFragments[k]);
+          if (preserveMulti) {
+            if (bestTextFragments[k] != null) {
+              frags.add(bestTextFragments[k]);
+            }
+          } else {
+            if ((bestTextFragments[k] != null) && (bestTextFragments[k].getScore() > 0)) {
+              frags.add(bestTextFragments[k]);
+            }
           }
         }
       } catch (InvalidTokenOffsetsException e) {
@@ -520,20 +528,29 @@
       }
     }
     // sort such that the fragments with the highest score come first
-    Collections.sort(frags, new Comparator<TextFragment>() {
-      public int compare(TextFragment arg0, TextFragment arg1) {
-        return Math.round(arg1.getScore() - arg0.getScore());
-      }
-    });
-    
+     if(!preserveMulti){
+        Collections.sort(frags, new Comparator<TextFragment>() {
+                public int compare(TextFragment arg0, TextFragment arg1) {
+                 return Math.round(arg1.getScore() - arg0.getScore());
+        }
+        });
+     }
+
      // convert fragments back into text
      // TODO: we can include score and position information in output as snippet attributes
     if (frags.size() > 0) {
       ArrayList<String> fragTexts = new ArrayList<String>();
       for (TextFragment fragment: frags) {
-        if ((fragment != null) && (fragment.getScore() > 0)) {
-          fragTexts.add(fragment.toString());
+        if (preserveMulti) {
+          if (fragment != null) {
+            fragTexts.add(fragment.toString());
+          }
+        } else {
+          if ((fragment != null) && (fragment.getScore() > 0)) {
+            fragTexts.add(fragment.toString());
+          }
         }
+
         if (fragTexts.size() >= numFragments) break;
       }
       summaries = fragTexts.toArray(new String[0]);
diff --git a/solr/core/src/test/org/apache/solr/highlight/HighlighterTest.java b/solr/core/src/test/org/apache/solr/highlight/HighlighterTest.java
index 391a1fc..2db08fa 100755
--- a/solr/core/src/test/org/apache/solr/highlight/HighlighterTest.java
+++ b/solr/core/src/test/org/apache/solr/highlight/HighlighterTest.java
@@ -299,6 +299,31 @@
     );
   }
 
+ @Test
+  public void testPreserveMulti() {
+    HashMap<String,String> args = new HashMap<String,String>();
+    args.put("hl", "true");
+    args.put("hl.fl", "cat");
+    args.put("hl.snippets", "2");
+    args.put("f.cat.hl.preserveMulti", "true");
+    TestHarness.LocalRequestFactory sumLRF = h.getRequestFactory(
+        "standard", 0, 200, args);
+
+    assertU(adoc("cat", "electronics",
+        "cat", "monitor",
+        "id", "1"));
+    assertU(commit());
+    assertU(optimize());
+    assertQ("Preserve multi",
+        sumLRF.makeRequest("cat:monitor"),
+        "//lst[@name='highlighting']/lst[@name='1']",
+        "//lst[@name='1']/arr[@name='cat']/str[.=\'electronics\']",
+        "//lst[@name='1']/arr[@name='cat']/str[.=\'<em>monitor</em>\']"
+    );
+  }
+
+
+
   @Test
   public void testDefaultFieldHighlight() {
 
diff --git a/solr/solrj/src/java/org/apache/solr/common/params/HighlightParams.java b/solr/solrj/src/java/org/apache/solr/common/params/HighlightParams.java
index d2d7146..287060e 100644
--- a/solr/solrj/src/java/org/apache/solr/common/params/HighlightParams.java
+++ b/solr/solrj/src/java/org/apache/solr/common/params/HighlightParams.java
@@ -32,6 +32,7 @@
   public static final String FORMATTER   = HIGHLIGHT+".formatter";
   public static final String ENCODER     = HIGHLIGHT+".encoder";
   public static final String FRAGMENTER  = HIGHLIGHT+".fragmenter";
+  public static final String PRESERVE_MULTI = HIGHLIGHT+".preserveMulti";
   public static final String FRAG_LIST_BUILDER = HIGHLIGHT+".fragListBuilder";
   public static final String FRAGMENTS_BUILDER = HIGHLIGHT+".fragmentsBuilder";
   public static final String BOUNDARY_SCANNER = HIGHLIGHT+".boundaryScanner";