mirror of https://github.com/apache/lucene.git
SOLR-9912: Add facet.excludeTerms parameter support. (Jonny Marks, David Smiley, Christine Poerschke)
This commit is contained in:
parent
55afc2031c
commit
0b817e6e49
|
@ -130,6 +130,8 @@ New Features
|
||||||
|
|
||||||
* SOLR-9997: Enable configuring SolrHttpClientBuilder via java system property. (Hrishikesh Gadre via Mark Miller)
|
* SOLR-9997: Enable configuring SolrHttpClientBuilder via java system property. (Hrishikesh Gadre via Mark Miller)
|
||||||
|
|
||||||
|
* SOLR-9912: Add facet.excludeTerms parameter support. (Jonny Marks, David Smiley, Christine Poerschke)
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,12 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
@ -343,15 +346,46 @@ public class SimpleFacets {
|
||||||
ENUM, FC, FCS, UIF;
|
ENUM, FC, FCS, UIF;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Predicate<BytesRef> newBytesRefFilter(String field, SolrParams params) {
|
protected Predicate<BytesRef> newExcludeBytesRefFilter(String field, SolrParams params) {
|
||||||
final String contains = params.getFieldParam(field, FacetParams.FACET_CONTAINS);
|
final String exclude = params.getFieldParam(field, FacetParams.FACET_EXCLUDETERMS);
|
||||||
final boolean ignoreCase = params.getFieldBool(field, FacetParams.FACET_CONTAINS_IGNORE_CASE, false);
|
if (exclude == null) {
|
||||||
|
|
||||||
if (contains == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SubstringBytesRefFilter(contains, ignoreCase);
|
final Set<String> excludeTerms = new HashSet<>(StrUtils.splitSmart(exclude, ",", true));
|
||||||
|
|
||||||
|
return new Predicate<BytesRef>() {
|
||||||
|
@Override
|
||||||
|
public boolean test(BytesRef bytesRef) {
|
||||||
|
return !excludeTerms.contains(bytesRef.utf8ToString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Predicate<BytesRef> newBytesRefFilter(String field, SolrParams params) {
|
||||||
|
final String contains = params.getFieldParam(field, FacetParams.FACET_CONTAINS);
|
||||||
|
|
||||||
|
final Predicate<BytesRef> containsFilter;
|
||||||
|
if (contains != null) {
|
||||||
|
final boolean containsIgnoreCase = params.getFieldBool(field, FacetParams.FACET_CONTAINS_IGNORE_CASE, false);
|
||||||
|
containsFilter = new SubstringBytesRefFilter(contains, containsIgnoreCase);
|
||||||
|
} else {
|
||||||
|
containsFilter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Predicate<BytesRef> excludeFilter = newExcludeBytesRefFilter(field, params);
|
||||||
|
|
||||||
|
if (containsFilter == null && excludeFilter == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containsFilter != null && excludeFilter == null) {
|
||||||
|
return containsFilter;
|
||||||
|
} else if (containsFilter == null && excludeFilter != null) {
|
||||||
|
return excludeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return containsFilter.and(excludeFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2081,6 +2081,158 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
|
||||||
doFacetPrefix("tt_s1", "{!threads=-1}", "", "facet.method","fcs"); // default / unlimited threads
|
doFacetPrefix("tt_s1", "{!threads=-1}", "", "facet.method","fcs"); // default / unlimited threads
|
||||||
doFacetPrefix("tt_s1", "{!threads=2}", "", "facet.method","fcs"); // specific number of threads
|
doFacetPrefix("tt_s1", "{!threads=2}", "", "facet.method","fcs"); // specific number of threads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFacetExclude() {
|
||||||
|
for (String method : new String[] {"enum", "fcs", "fc", "uif"}) {
|
||||||
|
doFacetExclude("contains_s1", "contains_group_s1", "Astra", "facet.method", method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doFacetExclude(String f, String g, String termSuffix, String... params) {
|
||||||
|
String indent="on";
|
||||||
|
String pre = "//lst[@name='"+f+"']";
|
||||||
|
|
||||||
|
final SolrQueryRequest req = req(params, "q", "id:[* TO *]"
|
||||||
|
,"indent",indent
|
||||||
|
,"facet","true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","0"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","100"
|
||||||
|
,"facet.sort","count"
|
||||||
|
,"facet.excludeTerms","B,BBB"+termSuffix
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet.exclude",
|
||||||
|
req
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=10]"
|
||||||
|
,pre+"/int[1][@name='BBB'][.='3']"
|
||||||
|
,pre+"/int[2][@name='CCC'][.='3']"
|
||||||
|
,pre+"/int[3][@name='CCC"+termSuffix+"'][.='3']"
|
||||||
|
,pre+"/int[4][@name='BB'][.='2']"
|
||||||
|
,pre+"/int[5][@name='BB"+termSuffix+"'][.='2']"
|
||||||
|
,pre+"/int[6][@name='CC'][.='2']"
|
||||||
|
,pre+"/int[7][@name='CC"+termSuffix+"'][.='2']"
|
||||||
|
,pre+"/int[8][@name='AAA'][.='1']"
|
||||||
|
,pre+"/int[9][@name='AAA"+termSuffix+"'][.='1']"
|
||||||
|
,pre+"/int[10][@name='B"+termSuffix+"'][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
final SolrQueryRequest groupReq = req(params, "q", "id:[* TO *]"
|
||||||
|
,"indent",indent
|
||||||
|
,"facet","true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","0"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","100"
|
||||||
|
,"facet.sort","count"
|
||||||
|
,"facet.excludeTerms","B,BBB"+termSuffix
|
||||||
|
,"group","true"
|
||||||
|
,"group.field",g
|
||||||
|
,"group.facet","true"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet.exclude for grouped facets",
|
||||||
|
groupReq
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=10]"
|
||||||
|
,pre+"/int[1][@name='CCC'][.='3']"
|
||||||
|
,pre+"/int[2][@name='CCC"+termSuffix+"'][.='3']"
|
||||||
|
,pre+"/int[3][@name='BBB'][.='2']"
|
||||||
|
,pre+"/int[4][@name='AAA'][.='1']"
|
||||||
|
,pre+"/int[5][@name='AAA"+termSuffix+"'][.='1']"
|
||||||
|
,pre+"/int[6][@name='B"+termSuffix+"'][.='1']"
|
||||||
|
,pre+"/int[7][@name='BB'][.='1']"
|
||||||
|
,pre+"/int[8][@name='BB"+termSuffix+"'][.='1']"
|
||||||
|
,pre+"/int[9][@name='CC'][.='1']"
|
||||||
|
,pre+"/int[10][@name='CC"+termSuffix+"'][.='1']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFacetContainsAndExclude() {
|
||||||
|
for (String method : new String[] {"enum", "fcs", "fc", "uif"}) {
|
||||||
|
String contains = "BAst";
|
||||||
|
String groupContains = "Ast";
|
||||||
|
final boolean ignoreCase = random().nextBoolean();
|
||||||
|
if (ignoreCase) {
|
||||||
|
contains = randomizeStringCasing(contains);
|
||||||
|
groupContains = randomizeStringCasing(groupContains);
|
||||||
|
doFacetContainsAndExclude("contains_s1", "contains_group_s1", "Astra", contains, groupContains, "facet.method", method, "facet.contains.ignoreCase", "true");
|
||||||
|
} else {
|
||||||
|
doFacetContainsAndExclude("contains_s1", "contains_group_s1", "Astra", contains, groupContains, "facet.method", method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String randomizeStringCasing(String str) {
|
||||||
|
final char[] characters = str.toCharArray();
|
||||||
|
|
||||||
|
for (int i = 0; i != characters.length; ++i) {
|
||||||
|
final boolean switchCase = random().nextBoolean();
|
||||||
|
if (!switchCase) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char c = str.charAt(i);
|
||||||
|
if (Character.isUpperCase(c)) {
|
||||||
|
characters[i] = Character.toLowerCase(c);
|
||||||
|
} else {
|
||||||
|
characters[i] = Character.toUpperCase(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(characters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doFacetContainsAndExclude(String f, String g, String termSuffix, String contains, String groupContains, String... params) {
|
||||||
|
String indent="on";
|
||||||
|
String pre = "//lst[@name='"+f+"']";
|
||||||
|
|
||||||
|
final SolrQueryRequest req = req(params, "q", "id:[* TO *]"
|
||||||
|
,"indent",indent
|
||||||
|
,"facet","true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","0"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","100"
|
||||||
|
,"facet.sort","count"
|
||||||
|
,"facet.contains",contains
|
||||||
|
,"facet.excludeTerms","BBB"+termSuffix
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet.contains with facet.exclude",
|
||||||
|
req
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
|
||||||
|
,pre+"/int[1][@name='BB"+termSuffix+"'][.='2']"
|
||||||
|
,pre+"/int[2][@name='B"+termSuffix+"'][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
final SolrQueryRequest groupReq = req(params, "q", "id:[* TO *]"
|
||||||
|
,"indent",indent
|
||||||
|
,"facet","true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","0"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","100"
|
||||||
|
,"facet.sort","count"
|
||||||
|
,"facet.contains",groupContains
|
||||||
|
,"facet.excludeTerms","AAA"+termSuffix
|
||||||
|
,"group","true"
|
||||||
|
,"group.field",g
|
||||||
|
,"group.facet","true"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet.contains with facet.exclude for grouped facets",
|
||||||
|
groupReq
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=5]"
|
||||||
|
,pre+"/int[1][@name='CCC"+termSuffix+"'][.='3']"
|
||||||
|
,pre+"/int[2][@name='BBB"+termSuffix+"'][.='2']"
|
||||||
|
,pre+"/int[3][@name='B"+termSuffix+"'][.='1']"
|
||||||
|
,pre+"/int[4][@name='BB"+termSuffix+"'][.='1']"
|
||||||
|
,pre+"/int[5][@name='CC"+termSuffix+"'][.='1']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
//@Ignore("SOLR-8466 - facet.method=uif ignores facet.contains")
|
//@Ignore("SOLR-8466 - facet.method=uif ignores facet.contains")
|
||||||
|
|
|
@ -180,6 +180,11 @@ public interface FacetParams {
|
||||||
*/
|
*/
|
||||||
public static final String FACET_CONTAINS_IGNORE_CASE = FACET_CONTAINS + ".ignoreCase";
|
public static final String FACET_CONTAINS_IGNORE_CASE = FACET_CONTAINS + ".ignoreCase";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only return constraints of a facet field excluding the given string.
|
||||||
|
*/
|
||||||
|
public static final String FACET_EXCLUDETERMS = FACET + ".excludeTerms";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When faceting by enumerating the terms in a field,
|
* When faceting by enumerating the terms in a field,
|
||||||
* only use the filterCache for terms with a df >= to this parameter.
|
* only use the filterCache for terms with a df >= to this parameter.
|
||||||
|
|
Loading…
Reference in New Issue