You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mk...@apache.org on 2016/02/09 23:09:20 UTC

lucene-solr git commit: SOLR-8466: facet.method=uif for UnInvertedField faceting, like it was with 'fc' earlier.

Repository: lucene-solr
Updated Branches:
  refs/heads/master a928e4b40 -> eac3bb9b3


SOLR-8466: facet.method=uif for UnInvertedField faceting, like it was with 'fc' earlier.


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

Branch: refs/heads/master
Commit: eac3bb9b32a45e5fc9faa54b372f89e25606a976
Parents: a928e4b
Author: Mikhail Khludnev <mk...@apache.org>
Authored: Wed Feb 10 01:06:56 2016 +0300
Committer: Mikhail Khludnev <mk...@apache.org>
Committed: Wed Feb 10 01:06:56 2016 +0300

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   4 +
 .../org/apache/solr/request/SimpleFacets.java   |  76 +++++++++-
 .../solr/search/facet/FacetProcessor.java       |  22 +++
 .../org/apache/solr/TestDistributedSearch.java  |  15 +-
 .../org/apache/solr/TestRandomDVFaceting.java   |   9 +-
 .../apache/solr/request/SimpleFacetsTest.java   | 138 ++++++++++++-------
 .../org/apache/solr/request/TestFaceting.java   |  89 ++++++++----
 .../apache/solr/common/params/FacetParams.java  |   5 +
 8 files changed, 272 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index d37ce33..c310376 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -146,6 +146,10 @@ New Features
 
 * SOLR-8502: Improve Solr JDBC Driver to support SQL Clients like DBVisualizer (Kevin Risden, Joel Bernstein)
 
+* SOLR-8466: adding facet.method=uif to bring back UnInvertedField faceting which is used to work on
+  facet.method=fc. It's more performant for rarely changing indexes. Note: it ignores prefix and contains yet.
+  (Jamie Johnson via Mikhail Khludnev)
+
 Bug Fixes
 ----------------------
 * SOLR-8386: Add field option in the new admin UI schema page loads up even when no schemaFactory has been

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
index e13cd75..9f409cc 100644
--- a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
+++ b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
@@ -69,6 +69,7 @@ import org.apache.solr.search.QueryParsing;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.search.SortedIntDocSet;
 import org.apache.solr.search.SyntaxError;
+import org.apache.solr.search.facet.FacetProcessor;
 import org.apache.solr.search.grouping.GroupingSpecification;
 import org.apache.solr.util.BoundedTreeSet;
 import org.apache.solr.util.DefaultSolrThreadFactory;
@@ -77,6 +78,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -360,7 +362,7 @@ public class SimpleFacets {
   }
 
   enum FacetMethod {
-    ENUM, FC, FCS;
+    ENUM, FC, FCS, UIF;
   }
 
   /**
@@ -422,6 +424,8 @@ public class SimpleFacets {
       method = FacetMethod.FCS;
     } else if (FacetParams.FACET_METHOD_fc.equals(methodStr)) {
       method = FacetMethod.FC;
+    } else if(FacetParams.FACET_METHOD_uif.equals(methodStr)) {
+      method = FacetMethod.UIF;
     }
 
     if (method == FacetMethod.ENUM && TrieField.getMainValuePrefix(ft) != null) {
@@ -485,6 +489,73 @@ public class SimpleFacets {
             counts = ps.getFacetCounts(executor);
           }
           break;
+        case UIF:
+
+            //Emulate the JSON Faceting structure so we can use the same parsing classes
+            Map<String, Object> jsonFacet = new HashMap<>(13);
+            jsonFacet.put("type", "terms");
+            jsonFacet.put("field", field);
+            jsonFacet.put("offset", offset);
+            jsonFacet.put("limit", limit);
+            jsonFacet.put("mincount", mincount);
+            jsonFacet.put("missing", missing);
+            
+            if (prefix!=null) {
+              // presumably it supports single-value, but at least now returns wrong results on multi-value
+              throw new SolrException (
+                  SolrException.ErrorCode.BAD_REQUEST,
+                  FacetParams.FACET_PREFIX+"="+prefix+
+                  " are not supported by "+FacetParams.FACET_METHOD+"="+FacetParams.FACET_METHOD_uif+
+                  " for field:"+ field
+                  //jsonFacet.put("prefix", prefix);
+              );
+            }
+            jsonFacet.put("numBuckets", params.getFieldBool(field, "numBuckets", false));
+            jsonFacet.put("allBuckets", params.getFieldBool(field, "allBuckets", false));
+            jsonFacet.put("method", "uif");
+            jsonFacet.put("cacheDf", 0);
+            jsonFacet.put("perSeg", false);
+            
+            final String sortVal;
+            switch(sort){
+              case FacetParams.FACET_SORT_COUNT_LEGACY:
+                sortVal = FacetParams.FACET_SORT_COUNT;
+              break;
+              case FacetParams.FACET_SORT_INDEX_LEGACY:
+                sortVal = FacetParams.FACET_SORT_INDEX;
+              break;
+              default:
+                sortVal = sort;
+            }
+            jsonFacet.put("sort", sortVal );
+
+            Map<String, Object> topLevel = new HashMap<>();
+            topLevel.put(field, jsonFacet);
+              
+            topLevel.put("processEmpty", true);
+
+            FacetProcessor fproc = FacetProcessor.createProcessor(rb.req, topLevel, // rb.getResults().docSet
+                                                                    docs );
+            //TODO do we handle debug?  Should probably already be handled by the legacy code
+            fproc.process();
+
+            //Go through the response to build the expected output for SimpleFacets
+            Object res = fproc.getResponse();
+            counts = new NamedList<Integer>();
+            if(res != null) {
+              SimpleOrderedMap<Object> som = (SimpleOrderedMap<Object>)res;
+              SimpleOrderedMap<Object> asdf = (SimpleOrderedMap<Object>) som.get(field);
+
+              List<SimpleOrderedMap<Object>> buckets = (List<SimpleOrderedMap<Object>>)asdf.get("buckets");
+              for(SimpleOrderedMap<Object> b : buckets) {
+                counts.add(b.get("val").toString(), (Integer)b.get("count"));
+              }
+              if(missing) {
+                SimpleOrderedMap<Object> missingCounts = (SimpleOrderedMap<Object>) asdf.get("missing");
+                counts.add(null, (Integer)missingCounts.get("count"));
+              }
+            }
+          break;
         case FC:
           counts = DocValuesFacets.getCounts(searcher, docs, field, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
           break;
@@ -958,5 +1029,4 @@ public class SimpleFacets {
   public ResponseBuilder getResponseBuilder() {
     return rb;
   }
-}
-
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java b/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
index ba1aa44..37013b0 100644
--- a/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
+++ b/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
@@ -33,12 +33,14 @@ import org.apache.lucene.search.Query;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.search.BitDocSet;
 import org.apache.solr.search.DocIterator;
 import org.apache.solr.search.DocSet;
 import org.apache.solr.search.QParser;
+import org.apache.solr.search.QueryContext;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.search.SyntaxError;
 import org.apache.solr.util.RTimer;
@@ -60,6 +62,26 @@ public class FacetProcessor<FacetRequestT extends FacetRequest>  {
   public void process() throws IOException {
     handleDomainChanges();
   }
+  
+  /** factory method for invoking json facet framework as whole */
+  public static FacetProcessor<?> createProcessor(SolrQueryRequest req, 
+      Map<String, Object> params, DocSet docs){
+    FacetParser parser = new FacetTopParser(req);
+    FacetRequest facetRequest = null;
+    try {
+      facetRequest = parser.parse(params);
+    } catch (SyntaxError syntaxError) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError);
+    }
+
+    FacetContext fcontext = new FacetContext();
+    fcontext.base = docs;
+    fcontext.req = req;
+    fcontext.searcher = req.getSearcher();
+    fcontext.qcontext = QueryContext.newContext(fcontext.searcher);
+
+    return facetRequest.createFacetProcessor(fcontext);
+  }
 
   protected void handleDomainChanges() throws IOException {
     if (freq.domain == null) return;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/core/src/test/org/apache/solr/TestDistributedSearch.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/TestDistributedSearch.java b/solr/core/src/test/org/apache/solr/TestDistributedSearch.java
index 7e07514..b6750c2 100644
--- a/solr/core/src/test/org/apache/solr/TestDistributedSearch.java
+++ b/solr/core/src/test/org/apache/solr/TestDistributedSearch.java
@@ -409,14 +409,14 @@ public class TestDistributedSearch extends BaseDistributedSearchTestCase {
     ,"facet.field",t1);
 
     // test filter tagging, facet exclusion, and naming (multi-select facet support)
-    query("q","*:*", "rows",0, "facet","true", "facet.query","{!key=myquick}quick", "facet.query","{!key=myall ex=a}all", "facet.query","*:*"
+    queryAndCompareUIF("q","*:*", "rows",0, "facet","true", "facet.query","{!key=myquick}quick", "facet.query","{!key=myall ex=a}all", "facet.query","*:*"
     ,"facet.field","{!key=mykey ex=a}"+t1
     ,"facet.field","{!key=other ex=b}"+t1
     ,"facet.field","{!key=again ex=a,b}"+t1
     ,"facet.field",t1
     ,"fq","{!tag=a}id:[1 TO 7]", "fq","{!tag=b}id:[3 TO 9]"
     );
-    query("q", "*:*", "facet", "true", "facet.field", "{!ex=t1}SubjectTerms_mfacet", "fq", "{!tag=t1}SubjectTerms_mfacet:(test 1)", "facet.limit", "10", "facet.mincount", "1");
+    queryAndCompareUIF("q", "*:*", "facet", "true", "facet.field", "{!ex=t1}SubjectTerms_mfacet", "fq", "{!tag=t1}SubjectTerms_mfacet:(test 1)", "facet.limit", "10", "facet.mincount", "1");
 
     // test field that is valid in schema but missing in all shards
     query("q","*:*", "rows",100, "facet","true", "facet.field",missingField, "facet.mincount",2);
@@ -1051,6 +1051,17 @@ public class TestDistributedSearch extends BaseDistributedSearchTestCase {
           "stats.facet", fieldName);
   }
 
+  /** comparing results with facet.method=uif */
+  private void queryAndCompareUIF(Object ... params) throws Exception {
+    final QueryResponse expect = query(params);
+    
+    final Object[] newParams = Arrays.copyOf(params, params.length+2);
+    newParams[newParams.length-2] = "facet.method";
+    newParams[newParams.length-1] = "uif";
+    final QueryResponse uifResult = query(newParams);
+    compareResponses(expect, uifResult);
+  }
+
   protected void checkMinCountsField(List<FacetField.Count> counts, Object[] pairs) {
     assertEquals("There should be exactly " + pairs.length / 2 + " returned counts. There were: " + counts.size(), counts.size(), pairs.length / 2);
     assertTrue("Variable len param must be an even number, it was: " + pairs.length, (pairs.length % 2) == 0);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/core/src/test/org/apache/solr/TestRandomDVFaceting.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/TestRandomDVFaceting.java b/solr/core/src/test/org/apache/solr/TestRandomDVFaceting.java
index 8ac0bcc..7decfce 100644
--- a/solr/core/src/test/org/apache/solr/TestRandomDVFaceting.java
+++ b/solr/core/src/test/org/apache/solr/TestRandomDVFaceting.java
@@ -144,8 +144,8 @@ public class TestRandomDVFaceting extends SolrTestCaseJ4 {
 
   // NOTE: dv is not a "real" facet.method. when we see it, we facet on the dv field (*_dv)
   // but alias the result back as if we faceted on the regular indexed field for comparisons.
-  List<String> multiValuedMethods = Arrays.asList(new String[]{"enum","fc","dv"});
-  List<String> singleValuedMethods = Arrays.asList(new String[]{"enum","fc","fcs","dv"});
+  List<String> multiValuedMethods = Arrays.asList(new String[]{"enum","fc","dv","uif"});
+  List<String> singleValuedMethods = Arrays.asList(new String[]{"enum","fc","fcs","dv","uif"});
 
 
   void doFacetTests(FldType ftype) throws Exception {
@@ -215,6 +215,9 @@ public class TestRandomDVFaceting extends SolrTestCaseJ4 {
       List<String> methods = multiValued ? multiValuedMethods : singleValuedMethods;
       List<String> responses = new ArrayList<>(methods.size());
       for (String method : methods) {
+        if (method.equals("uif") && params.get("facet.prefix")!=null) {
+          continue; // it's not supported there
+        }
         if (method.equals("dv")) {
           params.set("facet.field", "{!key="+facet_field+"}"+facet_field+"_dv");
           params.set("facet.method",(String) null);
@@ -238,7 +241,7 @@ public class TestRandomDVFaceting extends SolrTestCaseJ4 {
       **/
 
       if (validate) {
-        for (int i=1; i<methods.size(); i++) {
+        for (int i=1; i<responses.size(); i++) {
           String err = JSONTestUtil.match("/", responses.get(i), responses.get(0), 0.0);
           if (err != null) {
             log.error("ERROR: mismatch facet response: " + err +

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
index 15bb6f9..042e840 100644
--- a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
+++ b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
@@ -39,6 +39,7 @@ import org.apache.solr.schema.SchemaField;
 import org.apache.solr.util.DateFormatUtil;
 import org.apache.solr.util.TimeZoneUtils;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.noggit.ObjectBuilder;
 import org.slf4j.Logger;
@@ -494,9 +495,11 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
 
     ModifiableSolrParams params = params("q","*:*", "rows","0", "facet","true", "facet.field","{!key=myalias}"+field);
     
-    String[] methods = {null, "fc","enum","fcs"};
+    String[] methods = {null, "fc","enum","fcs", "uif"
+        };
     if (sf.multiValued() || sf.getType().multiValuedFieldCache()) {
-      methods = new String[]{null, "fc","enum"};
+      methods = new String[]{null, "fc","enum", "uif"
+          };
     }
 
     prefixes = prefixes==null ? new String[]{null} : prefixes;
@@ -509,7 +512,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
         params.set("facet.method", method);
       }
       for (String prefix : prefixes) {
-        if (prefix == null) {
+        if (prefix == null || "uif".equals(method)) {// there is no support 
           params.remove("facet.prefix");
         } else {
           params.set("facet.prefix", prefix);
@@ -559,31 +562,36 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             "*[count(//doc)=1]"
             );
  
-    assertQ("check counts for facet queries",
-            req("q", "id:[42 TO 47]"
-                ,"facet", "true"
-                ,"facet.query", "trait_s:Obnoxious"
-                ,"facet.query", "id:[42 TO 45]"
-                ,"facet.query", "id:[43 TO 47]"
-                ,"facet.field", "trait_s"
-                )
-            ,"*[count(//doc)=6]"
- 
-            ,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
-            ,"//lst[@name='facet_queries']/int[@name='trait_s:Obnoxious'][.='2']"
-            ,"//lst[@name='facet_queries']/int[@name='id:[42 TO 45]'][.='4']"
-            ,"//lst[@name='facet_queries']/int[@name='id:[43 TO 47]'][.='5']"
- 
-            ,"//lst[@name='facet_counts']/lst[@name='facet_fields']"
-            ,"//lst[@name='facet_fields']/lst[@name='trait_s']"
-            ,"*[count(//lst[@name='trait_s']/int)=4]"
-            ,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
-            ,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
-            ,"//lst[@name='trait_s']/int[@name='Pig'][.='1']"
-            );
-
-    assertQ("check multi-select facets with naming",
-            req("q", "id:[42 TO 47]"
+    final String[] uifSwitch = new String[]{(random().nextBoolean() ? "":"f.trait_s.")+"facet.method", "uif"};
+    final String[] none = new String[]{};
+    
+    for(String[] methodParam : new String[][]{ none, uifSwitch}){
+      assertQ("check counts for facet queries",
+          req(methodParam
+              ,"q", "id:[42 TO 47]"
+              ,"facet", "true"
+              ,"facet.query", "trait_s:Obnoxious"
+              ,"facet.query", "id:[42 TO 45]"
+              ,"facet.query", "id:[43 TO 47]"
+              ,"facet.field", "trait_s"
+              )
+          ,"*[count(//doc)=6]"
+
+          ,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
+          ,"//lst[@name='facet_queries']/int[@name='trait_s:Obnoxious'][.='2']"
+          ,"//lst[@name='facet_queries']/int[@name='id:[42 TO 45]'][.='4']"
+          ,"//lst[@name='facet_queries']/int[@name='id:[43 TO 47]'][.='5']"
+
+          ,"//lst[@name='facet_counts']/lst[@name='facet_fields']"
+          ,"//lst[@name='facet_fields']/lst[@name='trait_s']"
+          ,"*[count(//lst[@name='trait_s']/int)=4]"
+          ,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
+          ,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
+          ,"//lst[@name='trait_s']/int[@name='Pig'][.='1']"
+          );
+      
+      assertQ("check multi-select facets with naming",
+            req(methodParam, "q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"facet.query", "{!ex=1}trait_s:Obnoxious"
                 ,"facet.query", "{!ex=2 key=foo}id:[42 TO 45]"    // tag=2 same as 1
@@ -605,7 +613,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
             ,"//lst[@name='trait_s']/int[@name='Pig'][.='1']"
             );
-
+    }
     // test excluding main query
     assertQ(req("q", "{!tag=main}id:43"
                  ,"facet", "true"
@@ -616,8 +624,10 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
              ,"//lst[@name='facet_queries']/int[@name='bar'][.='1']"
              );
 
-    assertQ("check counts for applied facet queries using filtering (fq)",
-            req("q", "id:[42 TO 47]"
+    for(String[] methodParam : new String[][]{ none, uifSwitch}){
+      assertQ("check counts for applied facet queries using filtering (fq)",
+            req(methodParam
+                ,"q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"fq", "id:[42 TO 45]"
                 ,"facet.field", "trait_s"
@@ -635,8 +645,9 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//lst[@name='trait_s']/int[@name='Pig'][.='0']"
             );
  
-    assertQ("check counts with facet.zero=false&facet.missing=true using fq",
-            req("q", "id:[42 TO 47]"
+      assertQ("check counts with facet.zero=false&facet.missing=true using fq",
+            req(methodParam
+                ,"q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"facet.zeros", "false"
                 ,"f.trait_s.facet.missing", "true"
@@ -651,8 +662,9 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//lst[@name='trait_s']/int[not(@name)][.='1']"
             );
 
-    assertQ("check counts with facet.mincount=1&facet.missing=true using fq",
-            req("q", "id:[42 TO 47]"
+      assertQ("check counts with facet.mincount=1&facet.missing=true using fq",
+            req(methodParam
+                ,"q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"facet.mincount", "1"
                 ,"f.trait_s.facet.missing", "true"
@@ -667,8 +679,9 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//lst[@name='trait_s']/int[not(@name)][.='1']"
             );
 
-    assertQ("check counts with facet.mincount=2&facet.missing=true using fq",
-            req("q", "id:[42 TO 47]"
+      assertQ("check counts with facet.mincount=2&facet.missing=true using fq",
+            req(methodParam
+                ,"q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"facet.mincount", "2"
                 ,"f.trait_s.facet.missing", "true"
@@ -681,8 +694,9 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//lst[@name='trait_s']/int[not(@name)][.='1']"               
             );
 
-    assertQ("check sorted paging",
-            req("q", "id:[42 TO 47]"
+      assertQ("check sorted paging",
+            req(methodParam
+                ,"q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"fq", "id:[42 TO 45]"
                 ,"facet.field", "trait_s"
@@ -697,9 +711,9 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//lst[@name='trait_s']/int[@name='Pig'][.='0']"
             );
 
-    // check that the default sort is by count
-    assertQ("check sorted paging",
-            req("q", "id:[42 TO 47]"
+      // check that the default sort is by count
+      assertQ("check sorted paging",
+            req(methodParam, "q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"fq", "id:[42 TO 45]"
                 ,"facet.field", "trait_s"
@@ -713,10 +727,10 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//int[3][@name='Obnoxious'][.='1']"
             );
 
-    //
-    // check that legacy facet.sort=true/false works
-    //
-    assertQ(req("q", "id:[42 TO 47]"
+      //
+      // check that legacy facet.sort=true/false works
+      //
+      assertQ(req(methodParam, "q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"fq", "id:[42 TO 45]"
                 ,"facet.field", "trait_s"
@@ -731,7 +745,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//int[3][@name='Obnoxious'][.='1']"
             );
 
-     assertQ(req("q", "id:[42 TO 47]"
+       assertQ(req(methodParam, "q", "id:[42 TO 47]"
                 ,"facet", "true"
                 ,"fq", "id:[42 TO 45]"
                 ,"facet.field", "trait_s"
@@ -745,16 +759,18 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
             ,"//int[2][@name='Obnoxious'][.='1']"
             ,"//int[3][@name='Tool'][.='2']"
             );
+    }
 
-
-     assertQ(req("q", "id:[42 TO 47]"
+    for(String method : new String[]{ "fc","uif"}){
+       assertQ(req("q", "id:[42 TO 47]"
                 ,"facet", "true"
-                ,"facet.method","fc"
                 ,"fq", "id:[42 TO 45]"
                 ,"facet.field", "zerolen_s"
+                ,(random().nextBoolean() ? "":"f.zerolen_s.")+"facet.method", method
                 )
-            ,"*[count(//lst[@name='zerolen_s']/int)=1]"
-     );
+            ,"*[count(//lst[@name='zerolen_s']/int[@name=''])=1]"
+       );
+    }
 
     assertQ("a facet.query that analyzes to no query shoud not NPE",
         req("q", "*:*",
@@ -2021,6 +2037,24 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
     doFacetPrefix("tt_s1", "{!threads=-1}", "", "facet.method","fcs");  // default / unlimited threads
     doFacetPrefix("tt_s1", "{!threads=2}", "", "facet.method","fcs");   // specific number of threads
   }
+  
+  /** no prefix for uif */
+  @Test(expected=RuntimeException.class)
+  public void testNOFacetPrefixForUif() {
+    if (random().nextBoolean()) {
+      doFacetPrefix("tt_s1", null, "", "facet.method", "uif");
+    } else {
+      doFacetPrefix("t_s", null, "", "facet.method", "uif");
+    }
+  }
+  
+  @Test
+  @Ignore("SOLR-8466 - facet.method=uif ignores facet.contains")
+  public void testFacetContainsUif() {
+    doFacetContains("contains_s1", "contains_group_s1", "Astra", "BAst", "Ast", "facet.method", "uif");
+    doFacetPrefix("contains_s1", null, "Astra", "facet.method", "uif", "facet.contains", "Ast");
+    doFacetPrefix("contains_s1", null, "Astra", "facet.method", "uif", "facet.contains", "aST", "facet.contains.ignoreCase", "true");
+  }
 
   static void indexFacetContains() {
     indexFacetPrefix("70","contains_s1","","contains_group_s1");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/core/src/test/org/apache/solr/request/TestFaceting.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/request/TestFaceting.java b/solr/core/src/test/org/apache/solr/request/TestFaceting.java
index 00878b9..a557dc1 100644
--- a/solr/core/src/test/org/apache/solr/request/TestFaceting.java
+++ b/solr/core/src/test/org/apache/solr/request/TestFaceting.java
@@ -28,6 +28,7 @@ import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.uninverting.DocTermOrds;
 import org.apache.lucene.util.BytesRef;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.params.FacetParams;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.util.RefCounted;
@@ -158,9 +159,10 @@ public class TestFaceting extends SolrTestCaseJ4 {
     assertU(adoc("id", "1", "many_ws", sb.toString()));
     assertU(commit());
 
-    assertQ("check many tokens",
+    for(String method:new String[]{"fc","uif"}){
+      assertQ("check many tokens",
             req("q", "*:*","indent","true"
-                ,"facet", "true", "facet.method","fc"
+                ,"facet", "true", "facet.method",method
                 ,"facet.field", "many_ws"
                 ,"facet.limit", "-1"
                 )
@@ -181,6 +183,7 @@ public class TestFaceting extends SolrTestCaseJ4 {
             ,"//lst[@name='many_ws']/int[@name='" + t(4090) + "'][.='1']"
             ,"//lst[@name='many_ws']/int[@name='" + t(4999) + "'][.='1']"
             );
+    }
 
     // add second document, check facets for items with count =2
     sb = new StringBuilder();
@@ -189,9 +192,11 @@ public class TestFaceting extends SolrTestCaseJ4 {
     sb.append(t(4999)).append(' ');
     assertU(adoc("id", "2", "many_ws", sb.toString()));
     assertU(commit());
-    assertQ("check many tokens",
+    
+    for(String method:new String[]{"fc","uif"}){
+      assertQ("check many tokens",
             req("q", "*:*","indent","true"
-                ,"facet", "true", "facet.method","fc"
+                ,"facet", "true", "facet.method",method
                 ,"facet.field", "many_ws"
                 ,"facet.limit", "-1"
                 )
@@ -202,6 +207,7 @@ public class TestFaceting extends SolrTestCaseJ4 {
             ,"//lst[@name='many_ws']/int[@name='" + t(4998) + "'][.='1']"
             ,"//lst[@name='many_ws']/int[@name='" + t(4999) + "'][.='2']"
               );
+    }
   }
 
   @Test
@@ -230,10 +236,13 @@ public class TestFaceting extends SolrTestCaseJ4 {
     }
     assertU(commit());
 
+    final int methodSeed = random().nextInt(2);
+    
     for (int i=0; i<iter; i+=iter/10) {
     assertQ("check many tokens",
             req("q", "id:"+t(i),"indent","true"
-                ,"facet", "true", "facet.method","fc"
+                ,"facet", "true",
+                "facet.method",((methodSeed + i)%2 ==0 ?"fc":"uif")
                 ,"facet.field", "many_ws"
                 ,"facet.limit", "-1"
                 ,"facet.mincount", "1"
@@ -247,7 +256,7 @@ public class TestFaceting extends SolrTestCaseJ4 {
     int i=iter-1;
     assertQ("check many tokens",
             req("q", "id:"+t(i),"indent","true"
-                ,"facet", "true", "facet.method","fc"
+                ,"facet", "true", "facet.method",((methodSeed + i)%2 ==0 ?"fc":"uif")
                 ,"facet.field", "many_ws"
                 ,"facet.limit", "-1"
                 ,"facet.mincount", "1"
@@ -274,7 +283,7 @@ public class TestFaceting extends SolrTestCaseJ4 {
     assertU(adoc(fields.toArray(new String[0])));
     assertU(commit());
     for (String suffix : suffixes) {
-      for (String facetMethod : new String[] {FacetParams.FACET_METHOD_enum, FacetParams.FACET_METHOD_fc, FacetParams.FACET_METHOD_fcs}) {
+      for (String facetMethod : new String[] {FacetParams.FACET_METHOD_enum, FacetParams.FACET_METHOD_fc, FacetParams.FACET_METHOD_fcs, FacetParams.FACET_METHOD_uif}) {
         for (String facetSort : new String[] {FacetParams.FACET_SORT_COUNT, FacetParams.FACET_SORT_INDEX}) {
           for (String value : new String[] {"42", "43"}) { // match or not
             final String field = "f_" + suffix;
@@ -299,13 +308,13 @@ public class TestFaceting extends SolrTestCaseJ4 {
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[1][@name='-420.126']",
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[2][@name='-285.672']",
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[3][@name='-1.218']");
-
+   
     assertQ(req("q", "*:*", FacetParams.FACET, "true", FacetParams.FACET_FIELD, "f_td", "f.f_td.facet.sort", FacetParams.FACET_SORT_INDEX, FacetParams.FACET_MINCOUNT, "1", FacetParams.FACET_METHOD, FacetParams.FACET_METHOD_fc),
         "*[count(//lst[@name='f_td']/int)=3]",
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[1][@name='-420.126']",
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[2][@name='-285.672']",
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[3][@name='-1.218']");
-
+    
     assertQ(req("q", "*:*", FacetParams.FACET, "true", FacetParams.FACET_FIELD, "f_td", "f.f_td.facet.sort", FacetParams.FACET_SORT_INDEX, FacetParams.FACET_MINCOUNT, "1", "indent","true"),
         "*[count(//lst[@name='f_td']/int)=3]",
         "//lst[@name='facet_fields']/lst[@name='f_td']/int[1][@name='-420.126']",
@@ -442,8 +451,10 @@ public class TestFaceting extends SolrTestCaseJ4 {
           "text_t", "line up and fly directly at the enemy death cannons, clogging them with wreckage!"));
       assertU(commit());
   
-      assertQ("checking facets when one has missing=true&mincount=2 and the other has missing=false&mincount=0",
-              req("q", "id:[42 TO 47]"
+      for(String [] methodParam: new String[][]{ new String[]{}, new String []{"facet.method", "uif"}}) {
+        assertQ("checking facets when one has missing=true&mincount=2 and the other has missing=false&mincount=0",
+              req(methodParam
+                  , "q", "id:[42 TO 47]"
                   ,"facet", "true"
                   ,"facet.zeros", "false"
                   ,"fq", "id:[42 TO 45]"
@@ -467,8 +478,9 @@ public class TestFaceting extends SolrTestCaseJ4 {
               ,"//lst[@name='bar']/int[not(@name)][.='1']"
               );
   
-      assertQ("checking facets when one has missing=true&mincount=2 and the other has missing=false&mincount=0",
-              req("q", "id:[42 TO 47]"
+      assertQforUIF("checking facets when one has missing=true&mincount=2 and the other has missing=false&mincount=0",
+              req(methodParam
+                  ,"q", "id:[42 TO 47]"
                   ,"facet", "true"
                   ,"facet.zeros", "false"
                   ,"fq", "id:[42 TO 45]"
@@ -489,7 +501,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
               );
 
       assertQ("localparams in one facet variant should not affect defaults in another: facet.sort vs facet.missing",
-                  req("q", "id:[42 TO 47]"
+                  req(methodParam
+                      ,"q", "id:[42 TO 47]"
                           ,"rows","0"
                           ,"facet", "true"
                           ,"fq", "id:[42 TO 45]"
@@ -515,7 +528,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
                   );
 
       assertQ("localparams in one facet variant should not affect defaults in another: facet.mincount",
-                  req("q", "id:[42 TO 47]"
+                  req(methodParam
+                      ,"q", "id:[42 TO 47]"
                           ,"rows","0"
                           ,"facet", "true"
                           ,"fq", "id:[42 TO 45]"
@@ -535,7 +549,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
                   );
 
       assertQ("localparams in one facet variant should not affect defaults in another: facet.missing",
-                  req("q", "id:[42 TO 47]"
+                  req(methodParam
+                      ,"q", "id:[42 TO 47]"
                           ,"rows","0"
                           ,"facet", "true"
                           ,"fq", "id:[42 TO 45]"
@@ -557,8 +572,9 @@ public class TestFaceting extends SolrTestCaseJ4 {
                   ,"//lst[@name='bar']/int[4][@name='Pig'][.='0']"
                   );
 
-      assertQ("checking facets when local facet.prefix param used after regular/raw field faceting",
-          req("q", "*:*"
+      assertQforUIF("checking facets when local facet.prefix param used after regular/raw field faceting",
+          req(methodParam
+              ,"q", "*:*"
               ,"facet", "true"
               ,"facet.field", fname
               ,"facet.field", "{!key=foo " +
@@ -571,8 +587,9 @@ public class TestFaceting extends SolrTestCaseJ4 {
           ,"//lst[@name='foo']/int[@name='Tool'][.='2']"
       );
 
-      assertQ("checking facets when local facet.prefix param used before regular/raw field faceting",
-          req("q", "*:*"
+        assertQforUIF("checking facets when local facet.prefix param used before regular/raw field faceting",
+          req(methodParam
+              ,"q", "*:*"
               ,"facet", "true"
               ,"facet.field", "{!key=foo " +
               "facet.prefix=T "+
@@ -583,7 +600,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
           ,"*[count(//lst[@name='" + fname + "']/int)=4]"
           ,"*[count(//lst[@name='foo']/int)=1]"
           ,"//lst[@name='foo']/int[@name='Tool'][.='2']"
-      );
+        );
+      }
 
       final String foo_range_facet = "{!key=foo facet.range.gap=2}val_i";
       final String val_range_facet = "val_i";
@@ -606,6 +624,15 @@ public class TestFaceting extends SolrTestCaseJ4 {
       clearIndex();
       assertU(commit());
   }
+      
+  private void assertQforUIF(String message, SolrQueryRequest request, String ... tests) {
+    final String paramString = request.getParamString();
+    if (paramString.contains("uif") && paramString.contains("prefix")){
+      assertQEx("uif prohibits prefix", "not supported", request, ErrorCode.BAD_REQUEST);
+    }else{
+      assertQ(message,request, tests);
+    }
+  }
 
   private void add50ocs() {
     // Gimme 50 docs with 10 facet fields each
@@ -642,11 +669,14 @@ public class TestFaceting extends SolrTestCaseJ4 {
   public void testThreadWait() throws Exception {
 
     add50ocs();
+    String[] methodParam = random().nextBoolean() ? new String[]{} : new String[]{"facet.method","uif"} ;
+    
     // All I really care about here is the chance to fire off a bunch of threads to the UnIninvertedField.get method
     // to insure that we get into/out of the lock. Again, it's not entirely deterministic, but it might catch bad
     // stuff occasionally...
     assertQ("check threading, more threads than fields",
-        req("q", "id:*", "indent", "true", "fl", "id", "rows", "1"
+        req(methodParam
+            , "q", "id:*", "indent", "true", "fl", "id", "rows", "1"
             , "facet", "true"
             , "facet.field", "f0_ws"
             , "facet.field", "f0_ws"
@@ -710,8 +740,12 @@ public class TestFaceting extends SolrTestCaseJ4 {
   @Test
   public void testMultiThreadedFacets() throws Exception {
     add50ocs();
+    
+    String[] methodParam = random().nextBoolean() ? new String[]{} : new String[]{"facet.method","uif"} ;
+    
     assertQ("check no threading, threads == 0",
-        req("q", "id:*", "indent", "true", "fl", "id", "rows", "1"
+        req(methodParam
+            , "q", "id:*", "indent", "true", "fl", "id", "rows", "1"
             , "facet", "true"
             , "facet.field", "f0_ws"
             , "facet.field", "f1_ws"
@@ -766,7 +800,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
       SortedSetDocValues ui9 = DocValues.getSortedSet(currentSearcher.getLeafReader(), "f9_ws");
 
       assertQ("check threading, more threads than fields",
-          req("q", "id:*", "indent", "true", "fl", "id", "rows", "1"
+          req(methodParam
+              ,"q", "id:*", "indent", "true", "fl", "id", "rows", "1"
               , "facet", "true"
               , "facet.field", "f0_ws"
               , "facet.field", "f1_ws"
@@ -806,7 +841,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
 
       );
       assertQ("check threading, fewer threads than fields",
-          req("q", "id:*", "indent", "true", "fl", "id", "rows", "1"
+          req(methodParam
+              ,"q", "id:*", "indent", "true", "fl", "id", "rows", "1"
               , "facet", "true"
               , "facet.field", "f0_ws"
               , "facet.field", "f1_ws"
@@ -852,7 +888,8 @@ public class TestFaceting extends SolrTestCaseJ4 {
       // It's NOT testing whether the pending/sleep is actually functioning, I had to do that by hand since I don't
       // see how to make sure that uninverting the field multiple times actually happens to hit the wait state.
       assertQ("check threading, more threads than fields",
-          req("q", "id:*", "indent", "true", "fl", "id", "rows", "1"
+          req(methodParam
+              ,"q", "id:*", "indent", "true", "fl", "id", "rows", "1"
               , "facet", "true"
               , "facet.field", "f0_ws"
               , "facet.field", "f0_ws"

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/eac3bb9b/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java b/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
index cd20923..ee2e91b 100644
--- a/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
+++ b/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
@@ -56,6 +56,11 @@ public interface FacetParams {
   public static final String FACET_METHOD_fcs = "fcs";
 
   /**
+   * Value for FACET_METHOD param to indicate that Solr should use an UnInvertedField
+   */
+  public static final String FACET_METHOD_uif = "uif";
+
+  /**
    * Any lucene formated queries the user would like to use for
    * Facet Constraint Counts (multi-value)
    */