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)
*/