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-9912: Add facet.excludeTerms parameter support. (Jonny Marks, David Smiley, Christine Poerschke)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -21,9 +21,12 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
@ -343,15 +346,46 @@ public class SimpleFacets {
|
|||
ENUM, FC, FCS, UIF;
|
||||
}
|
||||
|
||||
protected Predicate<BytesRef> newBytesRefFilter(String field, SolrParams params) {
|
||||
final String contains = params.getFieldParam(field, FacetParams.FACET_CONTAINS);
|
||||
final boolean ignoreCase = params.getFieldBool(field, FacetParams.FACET_CONTAINS_IGNORE_CASE, false);
|
||||
|
||||
if (contains == null) {
|
||||
protected Predicate<BytesRef> newExcludeBytesRefFilter(String field, SolrParams params) {
|
||||
final String exclude = params.getFieldParam(field, FacetParams.FACET_EXCLUDETERMS);
|
||||
if (exclude == 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=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
|
||||
//@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";
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* only use the filterCache for terms with a df >= to this parameter.
|
||||
|
|
Loading…
Reference in New Issue