mirror of https://github.com/apache/lucene.git
SOLR-10921: raise lucene BooleanQuery.maxClauseCount, add higher level checking via QueryUtils.build
This commit is contained in:
parent
5de15ff403
commit
98276481e4
|
@ -194,6 +194,11 @@ Bug Fixes
|
||||||
|
|
||||||
* SOLR-10886: Using V2Request.process(solrClient) method throws NPE if the API returns an error (Cao Manh Dat)
|
* SOLR-10886: Using V2Request.process(solrClient) method throws NPE if the API returns an error (Cao Manh Dat)
|
||||||
|
|
||||||
|
* SOLR-10921: Work around the static Lucene BooleanQuery.maxClauseCount that causes Solr's maxBooleanClauses
|
||||||
|
setting behavior to be "last core wins". This patch sets BooleanQuery.maxClauseCount to its maximum value,
|
||||||
|
thus disabling the global check, and replaces it with specific checks where desired via
|
||||||
|
QueryUtils.build(). (yonik)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -367,6 +367,9 @@ public class SolrConfig extends Config implements MapSerializable {
|
||||||
public static final Map<String, SolrPluginInfo> classVsSolrPluginInfo;
|
public static final Map<String, SolrPluginInfo> classVsSolrPluginInfo;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
// Raise the Lucene static limit so we can control this with higher granularity. See SOLR-10921
|
||||||
|
BooleanQuery.setMaxClauseCount(Integer.MAX_VALUE-1);
|
||||||
|
|
||||||
Map<String, SolrPluginInfo> map = new HashMap<>();
|
Map<String, SolrPluginInfo> map = new HashMap<>();
|
||||||
for (SolrPluginInfo plugin : plugins) map.put(plugin.clazz.getName(), plugin);
|
for (SolrPluginInfo plugin : plugins) map.put(plugin.clazz.getName(), plugin);
|
||||||
classVsSolrPluginInfo = Collections.unmodifiableMap(map);
|
classVsSolrPluginInfo = Collections.unmodifiableMap(map);
|
||||||
|
|
|
@ -68,7 +68,6 @@ import org.apache.lucene.index.IndexDeletionPolicy;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.apache.lucene.index.IndexWriter;
|
import org.apache.lucene.index.IndexWriter;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.apache.lucene.store.IOContext;
|
import org.apache.lucene.store.IOContext;
|
||||||
import org.apache.lucene.store.IndexInput;
|
import org.apache.lucene.store.IndexInput;
|
||||||
|
@ -249,18 +248,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
|
||||||
}
|
}
|
||||||
|
|
||||||
static int boolean_query_max_clause_count = Integer.MIN_VALUE;
|
static int boolean_query_max_clause_count = Integer.MIN_VALUE;
|
||||||
|
|
||||||
// only change the BooleanQuery maxClauseCount once for ALL cores...
|
|
||||||
void booleanQueryMaxClauseCount() {
|
|
||||||
synchronized(SolrCore.class) {
|
|
||||||
if (boolean_query_max_clause_count == Integer.MIN_VALUE) {
|
|
||||||
boolean_query_max_clause_count = solrConfig.booleanQueryMaxClauseCount;
|
|
||||||
BooleanQuery.setMaxClauseCount(boolean_query_max_clause_count);
|
|
||||||
} else if (boolean_query_max_clause_count != solrConfig.booleanQueryMaxClauseCount ) {
|
|
||||||
log.debug("BooleanQuery.maxClauseCount={}, ignoring {}", boolean_query_max_clause_count, solrConfig.booleanQueryMaxClauseCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SolrResourceLoader used to load all resources for this core.
|
* The SolrResourceLoader used to load all resources for this core.
|
||||||
|
@ -931,8 +919,6 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
|
||||||
this.maxWarmingSearchers = config.maxWarmingSearchers;
|
this.maxWarmingSearchers = config.maxWarmingSearchers;
|
||||||
this.slowQueryThresholdMillis = config.slowQueryThresholdMillis;
|
this.slowQueryThresholdMillis = config.slowQueryThresholdMillis;
|
||||||
|
|
||||||
booleanQueryMaxClauseCount();
|
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -56,6 +56,7 @@ import org.apache.solr.schema.IndexSchema;
|
||||||
import org.apache.solr.schema.SchemaField;
|
import org.apache.solr.schema.SchemaField;
|
||||||
import org.apache.solr.schema.TextField;
|
import org.apache.solr.schema.TextField;
|
||||||
import org.apache.solr.search.QParser;
|
import org.apache.solr.search.QParser;
|
||||||
|
import org.apache.solr.search.QueryUtils;
|
||||||
import org.apache.solr.search.SolrConstantScoreQuery;
|
import org.apache.solr.search.SolrConstantScoreQuery;
|
||||||
import org.apache.solr.search.SyntaxError;
|
import org.apache.solr.search.SyntaxError;
|
||||||
|
|
||||||
|
@ -657,7 +658,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BooleanQuery bq = booleanBuilder.build();
|
BooleanQuery bq = QueryUtils.build(booleanBuilder,parser);
|
||||||
if (bq.clauses().size() == 1) { // Unwrap single SHOULD query
|
if (bq.clauses().size() == 1) { // Unwrap single SHOULD query
|
||||||
BooleanClause clause = bq.clauses().iterator().next();
|
BooleanClause clause = bq.clauses().iterator().next();
|
||||||
if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
|
if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
|
||||||
|
@ -910,7 +911,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
|
||||||
Query subq = ft.getFieldQuery(this.parser, rawq.sfield, externalVal);
|
Query subq = ft.getFieldQuery(this.parser, rawq.sfield, externalVal);
|
||||||
booleanBuilder.add(subq, BooleanClause.Occur.SHOULD);
|
booleanBuilder.add(subq, BooleanClause.Occur.SHOULD);
|
||||||
}
|
}
|
||||||
normal = booleanBuilder.build();
|
normal = QueryUtils.build(booleanBuilder, parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.apache.solr.common.util.StrUtils;
|
||||||
import org.apache.solr.query.SolrRangeQuery;
|
import org.apache.solr.query.SolrRangeQuery;
|
||||||
import org.apache.solr.response.TextResponseWriter;
|
import org.apache.solr.response.TextResponseWriter;
|
||||||
import org.apache.solr.search.QParser;
|
import org.apache.solr.search.QParser;
|
||||||
|
import org.apache.solr.search.QueryUtils;
|
||||||
import org.apache.solr.search.Sorting;
|
import org.apache.solr.search.Sorting;
|
||||||
import org.apache.solr.uninverting.UninvertingReader;
|
import org.apache.solr.uninverting.UninvertingReader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -754,12 +755,13 @@ public abstract class FieldType extends FieldProperties {
|
||||||
/** @lucene.experimental */
|
/** @lucene.experimental */
|
||||||
public Query getSetQuery(QParser parser, SchemaField field, Collection<String> externalVals) {
|
public Query getSetQuery(QParser parser, SchemaField field, Collection<String> externalVals) {
|
||||||
if (!field.indexed()) {
|
if (!field.indexed()) {
|
||||||
|
// TODO: if the field isn't indexed, this feels like the wrong query type to use?
|
||||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||||
for (String externalVal : externalVals) {
|
for (String externalVal : externalVals) {
|
||||||
Query subq = getFieldQuery(parser, field, externalVal);
|
Query subq = getFieldQuery(parser, field, externalVal);
|
||||||
builder.add(subq, BooleanClause.Occur.SHOULD);
|
builder.add(subq, BooleanClause.Occur.SHOULD);
|
||||||
}
|
}
|
||||||
return builder.build();
|
return QueryUtils.build(builder, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BytesRef> lst = new ArrayList<>(externalVals.size());
|
List<BytesRef> lst = new ArrayList<>(externalVals.size());
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class DisMaxQParser extends QParser {
|
||||||
addBoostQuery(query, solrParams);
|
addBoostQuery(query, solrParams);
|
||||||
addBoostFunctions(query, solrParams);
|
addBoostFunctions(query, solrParams);
|
||||||
|
|
||||||
return query.build();
|
return QueryUtils.build(query, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addBoostFunctions(BooleanQuery.Builder query, SolrParams solrParams) throws SyntaxError {
|
protected void addBoostFunctions(BooleanQuery.Builder query, SolrParams solrParams) throws SyntaxError {
|
||||||
|
@ -252,7 +252,7 @@ public class DisMaxQParser extends QParser {
|
||||||
SolrPluginUtils.flattenBooleanQuery(t, (BooleanQuery) dis);
|
SolrPluginUtils.flattenBooleanQuery(t, (BooleanQuery) dis);
|
||||||
boolean mmAutoRelax = params.getBool(DisMaxParams.MM_AUTORELAX, false);
|
boolean mmAutoRelax = params.getBool(DisMaxParams.MM_AUTORELAX, false);
|
||||||
SolrPluginUtils.setMinShouldMatch(t, minShouldMatch, mmAutoRelax);
|
SolrPluginUtils.setMinShouldMatch(t, minShouldMatch, mmAutoRelax);
|
||||||
query = t.build();
|
query = QueryUtils.build(t, this);
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,7 @@ public class ExtendedDismaxQParser extends QParser {
|
||||||
//
|
//
|
||||||
// create a boosted query (scores multiplied by boosts)
|
// create a boosted query (scores multiplied by boosts)
|
||||||
//
|
//
|
||||||
Query topQuery = query.build();
|
Query topQuery = QueryUtils.build(query, this);
|
||||||
List<ValueSource> boosts = getMultiplicativeBoosts();
|
List<ValueSource> boosts = getMultiplicativeBoosts();
|
||||||
if (boosts.size()>1) {
|
if (boosts.size()>1) {
|
||||||
ValueSource prod = new ProductFloatFunction(boosts.toArray(new ValueSource[boosts.size()]));
|
ValueSource prod = new ProductFloatFunction(boosts.toArray(new ValueSource[boosts.size()]));
|
||||||
|
@ -282,7 +282,7 @@ public class ExtendedDismaxQParser extends QParser {
|
||||||
BooleanQuery.Builder t = new BooleanQuery.Builder();
|
BooleanQuery.Builder t = new BooleanQuery.Builder();
|
||||||
SolrPluginUtils.flattenBooleanQuery(t, (BooleanQuery)query);
|
SolrPluginUtils.flattenBooleanQuery(t, (BooleanQuery)query);
|
||||||
SolrPluginUtils.setMinShouldMatch(t, config.minShouldMatch, config.mmAutoRelax);
|
SolrPluginUtils.setMinShouldMatch(t, config.minShouldMatch, config.mmAutoRelax);
|
||||||
query = t.build();
|
query = QueryUtils.build(t, this);
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
@ -1163,7 +1163,7 @@ public class ExtendedDismaxQParser extends QParser {
|
||||||
for (Query sub : lst) {
|
for (Query sub : lst) {
|
||||||
q.add(sub, BooleanClause.Occur.SHOULD);
|
q.add(sub, BooleanClause.Occur.SHOULD);
|
||||||
}
|
}
|
||||||
return q.build();
|
return QueryUtils.build(q, parser);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -1225,7 +1225,7 @@ public class ExtendedDismaxQParser extends QParser {
|
||||||
}
|
}
|
||||||
q.add(newBooleanClause(new DisjunctionMaxQuery(subs, a.tie), BooleanClause.Occur.SHOULD));
|
q.add(newBooleanClause(new DisjunctionMaxQuery(subs, a.tie), BooleanClause.Occur.SHOULD));
|
||||||
}
|
}
|
||||||
return q.build();
|
return QueryUtils.build(q, parser);
|
||||||
} else {
|
} else {
|
||||||
return new DisjunctionMaxQuery(lst, a.tie);
|
return new DisjunctionMaxQuery(lst, a.tie);
|
||||||
}
|
}
|
||||||
|
@ -1234,7 +1234,7 @@ public class ExtendedDismaxQParser extends QParser {
|
||||||
for (Query sub : lst) {
|
for (Query sub : lst) {
|
||||||
q.add(sub, BooleanClause.Occur.SHOULD);
|
q.add(sub, BooleanClause.Occur.SHOULD);
|
||||||
}
|
}
|
||||||
return q.build();
|
return QueryUtils.build(q, parser);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// verify that a fielded query is actually on a field that exists... if not,
|
// verify that a fielded query is actually on a field that exists... if not,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.BoostQuery;
|
import org.apache.lucene.search.BoostQuery;
|
||||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.apache.solr.common.SolrException;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@ -129,4 +130,14 @@ public class QueryUtils {
|
||||||
return new BoostQuery(newBq, boost);
|
return new BoostQuery(newBq, boost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @lucene.experimental throw exception if max boolean clauses are exceeded */
|
||||||
|
public static BooleanQuery build(BooleanQuery.Builder builder, QParser parser) {
|
||||||
|
int configuredMax = parser != null ? parser.getReq().getCore().getSolrConfig().booleanQueryMaxClauseCount : BooleanQuery.getMaxClauseCount();
|
||||||
|
BooleanQuery bq = builder.build();
|
||||||
|
if (bq.clauses().size() > configuredMax) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||||
|
"Too many clauses in boolean query: encountered=" + bq.clauses().size() + " configured in solrconfig.xml via maxBooleanClauses=" + configuredMax);
|
||||||
|
}
|
||||||
|
return bq;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.apache.solr.response.SolrQueryResponse;
|
||||||
import org.apache.solr.schema.SchemaField;
|
import org.apache.solr.schema.SchemaField;
|
||||||
import org.apache.solr.search.QParser;
|
import org.apache.solr.search.QParser;
|
||||||
import org.apache.solr.search.QueryParsing;
|
import org.apache.solr.search.QueryParsing;
|
||||||
|
import org.apache.solr.search.QueryUtils;
|
||||||
import org.apache.solr.util.SolrPluginUtils;
|
import org.apache.solr.util.SolrPluginUtils;
|
||||||
|
|
||||||
import static org.apache.solr.common.params.CommonParams.ID;
|
import static org.apache.solr.common.params.CommonParams.ID;
|
||||||
|
@ -161,7 +162,7 @@ public class CloudMLTQParser extends QParser {
|
||||||
newQ.add(q, clause.getOccur());
|
newQ.add(q, clause.getOccur());
|
||||||
}
|
}
|
||||||
|
|
||||||
boostedMLTQuery = newQ.build();
|
boostedMLTQuery = QueryUtils.build(newQ, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// exclude current document from results
|
// exclude current document from results
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.apache.solr.request.SolrQueryRequest;
|
||||||
import org.apache.solr.schema.SchemaField;
|
import org.apache.solr.schema.SchemaField;
|
||||||
import org.apache.solr.search.QParser;
|
import org.apache.solr.search.QParser;
|
||||||
import org.apache.solr.search.QueryParsing;
|
import org.apache.solr.search.QueryParsing;
|
||||||
|
import org.apache.solr.search.QueryUtils;
|
||||||
import org.apache.solr.search.SolrIndexSearcher;
|
import org.apache.solr.search.SolrIndexSearcher;
|
||||||
import org.apache.solr.util.SolrPluginUtils;
|
import org.apache.solr.util.SolrPluginUtils;
|
||||||
|
|
||||||
|
@ -134,7 +135,7 @@ public class SimpleMLTQParser extends QParser {
|
||||||
newQ.add(q, clause.getOccur());
|
newQ.add(q, clause.getOccur());
|
||||||
}
|
}
|
||||||
|
|
||||||
boostedMLTQuery = newQ.build();
|
boostedMLTQuery = QueryUtils.build(newQ, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// exclude current document from results
|
// exclude current document from results
|
||||||
|
|
|
@ -358,6 +358,16 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 {
|
||||||
String q = sb.toString();
|
String q = sb.toString();
|
||||||
|
|
||||||
// This will still fail when used as the main query, but will pass in a filter query since TermsQuery can be used.
|
// This will still fail when used as the main query, but will pass in a filter query since TermsQuery can be used.
|
||||||
|
try {
|
||||||
|
ignoreException("Too many clauses");
|
||||||
|
assertJQ(req("q",q)
|
||||||
|
,"/response/numFound==6");
|
||||||
|
fail();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// expect "too many clauses" exception... see SOLR-10921
|
||||||
|
assertTrue(e.getMessage().contains("many clauses"));
|
||||||
|
}
|
||||||
|
|
||||||
assertJQ(req("q","*:*", "fq", q)
|
assertJQ(req("q","*:*", "fq", q)
|
||||||
,"/response/numFound==6");
|
,"/response/numFound==6");
|
||||||
assertJQ(req("q","*:*", "fq", q, "sow", "false")
|
assertJQ(req("q","*:*", "fq", q, "sow", "false")
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeTests() throws Exception {
|
public static void beforeTests() throws Exception {
|
||||||
JSONTestUtil.failRepeatedKeys = true;
|
JSONTestUtil.failRepeatedKeys = true;
|
||||||
initCore("solrconfig-tlog.xml","schema_latest.xml");
|
initCore("solrconfig-tlog.xml", "schema_latest.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void initServers() throws Exception {
|
public static void initServers() throws Exception {
|
||||||
|
@ -67,7 +67,7 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
assertNull(json);
|
assertNull(json);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (test.length()==0) continue;
|
if (test.length() == 0) continue;
|
||||||
|
|
||||||
String err = JSONTestUtil.match(json, test, delta);
|
String err = JSONTestUtil.match(json, test, delta);
|
||||||
|
|
||||||
|
@ -104,7 +104,9 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Use SimpleOrderedMap rather than Map to match responses from shards */
|
/**
|
||||||
|
* Use SimpleOrderedMap rather than Map to match responses from shards
|
||||||
|
*/
|
||||||
public static Object fromJSON(String json) throws IOException {
|
public static Object fromJSON(String json) throws IOException {
|
||||||
JSONParser parser = new JSONParser(json);
|
JSONParser parser = new JSONParser(json);
|
||||||
ObjectBuilder ob = new ObjectBuilder(parser) {
|
ObjectBuilder ob = new ObjectBuilder(parser) {
|
||||||
|
@ -115,7 +117,7 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addKeyVal(Object map, Object key, Object val) throws IOException {
|
public void addKeyVal(Object map, Object key, Object val) throws IOException {
|
||||||
((SimpleOrderedMap)map).add(key.toString(), val);
|
((SimpleOrderedMap) map).add(key.toString(), val);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,19 +134,19 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
|
|
||||||
FacetMerger merger = null;
|
FacetMerger merger = null;
|
||||||
FacetMerger.Context ctx = new FacetMerger.Context(nShards);
|
FacetMerger.Context ctx = new FacetMerger.Context(nShards);
|
||||||
for (int i=0; i<nShards; i++) {
|
for (int i = 0; i < nShards; i++) {
|
||||||
Object response = fromJSON(responsesAndTests[i]);
|
Object response = fromJSON(responsesAndTests[i]);
|
||||||
if (i==0) {
|
if (i == 0) {
|
||||||
merger = facetRequest.createFacetMerger(response);
|
merger = facetRequest.createFacetMerger(response);
|
||||||
}
|
}
|
||||||
ctx.newShard("s"+i);
|
ctx.newShard("s" + i);
|
||||||
merger.merge(response, ctx);
|
merger.merge(response, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<nShards; i++) {
|
for (int i = 0; i < nShards; i++) {
|
||||||
ctx.setShard("s"+i);
|
ctx.setShard("s" + i);
|
||||||
Object refinement = merger.getRefinement(ctx);
|
Object refinement = merger.getRefinement(ctx);
|
||||||
String tests = responsesAndTests[nShards+i];
|
String tests = responsesAndTests[nShards + i];
|
||||||
match(refinement, 1e-5, tests);
|
match(refinement, 1e-5, tests);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +163,7 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
"{x: {buckets:[{val:x2, count:4}, {val:x3, count:2}] } }", // shard1 response
|
"{x: {buckets:[{val:x2, count:4}, {val:x3, count:2}] } }", // shard1 response
|
||||||
null, // shard0 expected refinement info
|
null, // shard0 expected refinement info
|
||||||
"=={x:{_l:[x1]}}" // shard1 expected refinement info
|
"=={x:{_l:[x1]}}" // shard1 expected refinement info
|
||||||
);
|
);
|
||||||
|
|
||||||
// same test w/o refinement turned on
|
// same test w/o refinement turned on
|
||||||
doTestRefine("{x : {type:terms, field:X, limit:2} }", // the facet request
|
doTestRefine("{x : {type:terms, field:X, limit:2} }", // the facet request
|
||||||
|
@ -236,25 +238,25 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicRefinement() throws Exception {
|
public void testBasicRefinement() throws Exception {
|
||||||
ModifiableSolrParams p = params("cat_s", "cat_s", "cat_i", "cat_i", "xy_s", "xy_s", "num_d", "num_d", "qw_s", "qw_s", "er_s","er_s");
|
ModifiableSolrParams p = params("cat_s", "cat_s", "cat_i", "cat_i", "xy_s", "xy_s", "num_d", "num_d", "qw_s", "qw_s", "er_s", "er_s");
|
||||||
doBasicRefinement( p );
|
doBasicRefinement(p);
|
||||||
|
|
||||||
p.set("terms","method:dvhash,");
|
p.set("terms", "method:dvhash,");
|
||||||
doBasicRefinement( p );
|
doBasicRefinement(p);
|
||||||
|
|
||||||
// multi-valued
|
// multi-valued
|
||||||
p = params("cat_s", "cat_ss", "cat_i", "cat_is", "xy_s", "xy_ss", "num_d", "num_d", "qw_s", "qw_ss", "er_s","er_ss");
|
p = params("cat_s", "cat_ss", "cat_i", "cat_is", "xy_s", "xy_ss", "num_d", "num_d", "qw_s", "qw_ss", "er_s", "er_ss");
|
||||||
doBasicRefinement( p );
|
doBasicRefinement(p);
|
||||||
|
|
||||||
// single valued docvalues
|
// single valued docvalues
|
||||||
p = params("cat_s", "cat_sd", "cat_i", "cat_id", "xy_s", "xy_sd", "num_d", "num_dd", "qw_s", "qw_sd", "er_s","er_sd");
|
p = params("cat_s", "cat_sd", "cat_i", "cat_id", "xy_s", "xy_sd", "num_d", "num_dd", "qw_s", "qw_sd", "er_s", "er_sd");
|
||||||
doBasicRefinement( p );
|
doBasicRefinement(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doBasicRefinement(ModifiableSolrParams p) throws Exception {
|
public void doBasicRefinement(ModifiableSolrParams p) throws Exception {
|
||||||
initServers();
|
initServers();
|
||||||
Client client = servers.getClient(random().nextInt());
|
Client client = servers.getClient(random().nextInt());
|
||||||
client.queryDefaults().set( "shards", servers.getShards(), "debugQuery", Boolean.toString(random().nextBoolean()) );
|
client.queryDefaults().set("shards", servers.getShards(), "debugQuery", Boolean.toString(random().nextBoolean()));
|
||||||
|
|
||||||
List<SolrClient> clients = client.getClientProvider().all();
|
List<SolrClient> clients = client.getClientProvider().all();
|
||||||
assertTrue(clients.size() >= 3);
|
assertTrue(clients.size() >= 3);
|
||||||
|
@ -268,16 +270,16 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
String er_s = p.get("er_s"); // this field is designed to test numBuckets refinement... the first phase will only have a single bucket returned for the top count bucket of cat_s
|
String er_s = p.get("er_s"); // this field is designed to test numBuckets refinement... the first phase will only have a single bucket returned for the top count bucket of cat_s
|
||||||
String num_d = p.get("num_d");
|
String num_d = p.get("num_d");
|
||||||
|
|
||||||
clients.get(0).add( sdoc("id", "01", "all_s","all", cat_s, "A", cat_i,1, xy_s, "X" ,num_d, -1, qw_s, "Q", er_s,"E") ); // A wins count tie
|
clients.get(0).add(sdoc("id", "01", "all_s", "all", cat_s, "A", cat_i, 1, xy_s, "X", num_d, -1, qw_s, "Q", er_s, "E")); // A wins count tie
|
||||||
clients.get(0).add( sdoc("id", "02", "all_s","all", cat_s, "B", cat_i,2, xy_s, "Y", num_d, 3 ) );
|
clients.get(0).add(sdoc("id", "02", "all_s", "all", cat_s, "B", cat_i, 2, xy_s, "Y", num_d, 3));
|
||||||
|
|
||||||
clients.get(1).add( sdoc("id", "11", "all_s","all", cat_s, "B", cat_i,2, xy_s, "X", num_d, -5 , er_s,"E") ); // B highest count
|
clients.get(1).add(sdoc("id", "11", "all_s", "all", cat_s, "B", cat_i, 2, xy_s, "X", num_d, -5, er_s, "E")); // B highest count
|
||||||
clients.get(1).add( sdoc("id", "12", "all_s","all", cat_s, "B", cat_i,2, xy_s, "Y", num_d, -11, qw_s, "W" ) );
|
clients.get(1).add(sdoc("id", "12", "all_s", "all", cat_s, "B", cat_i, 2, xy_s, "Y", num_d, -11, qw_s, "W"));
|
||||||
clients.get(1).add( sdoc("id", "13", "all_s","all", cat_s, "A", cat_i,1, xy_s, "X", num_d, 7 , er_s,"R") ); // "R" will only be picked up via refinement when parent facet is cat_s
|
clients.get(1).add(sdoc("id", "13", "all_s", "all", cat_s, "A", cat_i, 1, xy_s, "X", num_d, 7, er_s, "R")); // "R" will only be picked up via refinement when parent facet is cat_s
|
||||||
|
|
||||||
clients.get(2).add( sdoc("id", "21", "all_s","all", cat_s, "A", cat_i,1, xy_s, "X", num_d, 17, qw_s, "W", er_s,"E") ); // A highest count
|
clients.get(2).add(sdoc("id", "21", "all_s", "all", cat_s, "A", cat_i, 1, xy_s, "X", num_d, 17, qw_s, "W", er_s, "E")); // A highest count
|
||||||
clients.get(2).add( sdoc("id", "22", "all_s","all", cat_s, "A", cat_i,1, xy_s, "Y", num_d, -19 ) );
|
clients.get(2).add(sdoc("id", "22", "all_s", "all", cat_s, "A", cat_i, 1, xy_s, "Y", num_d, -19));
|
||||||
clients.get(2).add( sdoc("id", "23", "all_s","all", cat_s, "B", cat_i,2, xy_s, "X", num_d, 11 ) );
|
clients.get(2).add(sdoc("id", "23", "all_s", "all", cat_s, "B", cat_i, 2, xy_s, "X", num_d, 11));
|
||||||
|
|
||||||
client.commit();
|
client.commit();
|
||||||
|
|
||||||
|
@ -285,16 +287,16 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
// One shard will have _facet_={"refine":{"cat0":{"_l":["A"]}}} on the second phase
|
// One shard will have _facet_={"refine":{"cat0":{"_l":["A"]}}} on the second phase
|
||||||
|
|
||||||
/****
|
/****
|
||||||
// fake a refinement request... good for development/debugging
|
// fake a refinement request... good for development/debugging
|
||||||
assertJQ(clients.get(1),
|
assertJQ(clients.get(1),
|
||||||
params(p, "q", "*:*", "_facet_","{refine:{cat0:{_l:[A]}}}", "isShard","true", "distrib","false", "shards.purpose","2097216", "ids","11,12,13",
|
params(p, "q", "*:*", "_facet_","{refine:{cat0:{_l:[A]}}}", "isShard","true", "distrib","false", "shards.purpose","2097216", "ids","11,12,13",
|
||||||
"json.facet", "{" +
|
"json.facet", "{" +
|
||||||
"cat0:{type:terms, field:cat_s, sort:'count desc', limit:1, overrequest:0, refine:true}" +
|
"cat0:{type:terms, field:cat_s, sort:'count desc', limit:1, overrequest:0, refine:true}" +
|
||||||
"}"
|
"}"
|
||||||
)
|
)
|
||||||
, "facets=={foo:555}"
|
, "facets=={foo:555}"
|
||||||
);
|
);
|
||||||
****/
|
****/
|
||||||
|
|
||||||
client.testJQ(params(p, "q", "*:*",
|
client.testJQ(params(p, "q", "*:*",
|
||||||
"json.facet", "{" +
|
"json.facet", "{" +
|
||||||
|
@ -473,51 +475,69 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
final String sort_limit_over = "sort:'count desc', limit:1, overrequest:0, ";
|
final String sort_limit_over = "sort:'count desc', limit:1, overrequest:0, ";
|
||||||
// simplistic join domain testing: no refinement == low count
|
// simplistic join domain testing: no refinement == low count
|
||||||
client.testJQ(params(p, "q", "${xy_s}:Y", // query only matches one doc per shard
|
client.testJQ(params(p, "q", "${xy_s}:Y", // query only matches one doc per shard
|
||||||
"json.facet", "{"+
|
"json.facet", "{" +
|
||||||
" cat0:{${terms} type:terms, field:${cat_s}, "+sort_limit_over+" refine:false,"+
|
" cat0:{${terms} type:terms, field:${cat_s}, " + sort_limit_over + " refine:false," +
|
||||||
// self join on all_s ensures every doc on every shard included in facets
|
// self join on all_s ensures every doc on every shard included in facets
|
||||||
" domain: { join: { from:all_s, to:all_s } } }" +
|
" domain: { join: { from:all_s, to:all_s } } }" +
|
||||||
"}"
|
"}"
|
||||||
)
|
)
|
||||||
,
|
,
|
||||||
"/response/numFound==3",
|
"/response/numFound==3",
|
||||||
"facets=={ count:3, " +
|
"facets=={ count:3, " +
|
||||||
// w/o overrequest and refinement, count for 'A' is lower than it should be
|
// w/o overrequest and refinement, count for 'A' is lower than it should be
|
||||||
// (we don't see the A from the middle shard)
|
// (we don't see the A from the middle shard)
|
||||||
" cat0:{ buckets:[ {val:A,count:3} ] } }");
|
" cat0:{ buckets:[ {val:A,count:3} ] } }");
|
||||||
// simplistic join domain testing: refinement == correct count
|
// simplistic join domain testing: refinement == correct count
|
||||||
client.testJQ(params(p, "q", "${xy_s}:Y", // query only matches one doc per shard
|
client.testJQ(params(p, "q", "${xy_s}:Y", // query only matches one doc per shard
|
||||||
"json.facet", "{" +
|
"json.facet", "{" +
|
||||||
" cat0:{${terms} type:terms, field:${cat_s}, "+sort_limit_over+" refine:true,"+
|
" cat0:{${terms} type:terms, field:${cat_s}, " + sort_limit_over + " refine:true," +
|
||||||
// self join on all_s ensures every doc on every shard included in facets
|
// self join on all_s ensures every doc on every shard included in facets
|
||||||
" domain: { join: { from:all_s, to:all_s } } }" +
|
" domain: { join: { from:all_s, to:all_s } } }" +
|
||||||
"}"
|
"}"
|
||||||
)
|
)
|
||||||
,
|
,
|
||||||
"/response/numFound==3",
|
"/response/numFound==3",
|
||||||
"facets=={ count:3," +
|
"facets=={ count:3," +
|
||||||
// w/o overrequest, we need refining to get the correct count for 'A'.
|
// w/o overrequest, we need refining to get the correct count for 'A'.
|
||||||
" cat0:{ buckets:[ {val:A,count:4} ] } }");
|
" cat0:{ buckets:[ {val:A,count:4} ] } }");
|
||||||
|
|
||||||
// contrived join domain + refinement (at second level) + testing
|
// contrived join domain + refinement (at second level) + testing
|
||||||
client.testJQ(params(p, "q", "${xy_s}:Y", // query only matches one doc per shard
|
client.testJQ(params(p, "q", "${xy_s}:Y", // query only matches one doc per shard
|
||||||
"json.facet", "{" +
|
"json.facet", "{" +
|
||||||
// top level facet has a single term
|
// top level facet has a single term
|
||||||
" all:{${terms} type:terms, field:all_s, "+sort_limit_over+" refine:true, " +
|
" all:{${terms} type:terms, field:all_s, " + sort_limit_over + " refine:true, " +
|
||||||
" facet:{ "+
|
" facet:{ " +
|
||||||
// subfacet will facet on cat after joining on all (so all docs should be included in subfacet)
|
// subfacet will facet on cat after joining on all (so all docs should be included in subfacet)
|
||||||
" cat0:{${terms} type:terms, field:${cat_s}, "+sort_limit_over+" refine:true,"+
|
" cat0:{${terms} type:terms, field:${cat_s}, " + sort_limit_over + " refine:true," +
|
||||||
" domain: { join: { from:all_s, to:all_s } } } } }" +
|
" domain: { join: { from:all_s, to:all_s } } } } }" +
|
||||||
"}"
|
"}"
|
||||||
)
|
)
|
||||||
,
|
,
|
||||||
"/response/numFound==3",
|
"/response/numFound==3",
|
||||||
"facets=={ count:3," +
|
"facets=={ count:3," +
|
||||||
// all 3 docs matching base query have same 'all' value in top facet
|
// all 3 docs matching base query have same 'all' value in top facet
|
||||||
" all:{ buckets:[ { val:all, count:3, " +
|
" all:{ buckets:[ { val:all, count:3, " +
|
||||||
// sub facet has refinement, so count for 'A' should be correct
|
// sub facet has refinement, so count for 'A' should be correct
|
||||||
" cat0:{ buckets: [{val:A,count:4}] } } ] } }");
|
" cat0:{ buckets: [{val:A,count:4}] } } ] } }");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlike solrconfig.xml this test using solrconfig-tlog.xml should not fail with too-many-exceptions (see TestSolrQueryParser.testManyClauses)
|
||||||
|
@Test
|
||||||
|
public void testManyClauses() throws Exception {
|
||||||
|
String a = "1 a 2 b 3 c 10 d 11 12 "; // 10 terms
|
||||||
|
StringBuilder sb = new StringBuilder("id:(");
|
||||||
|
for (int i = 0; i < 1024; i++) { // historically, the max number of boolean clauses defaulted to 1024
|
||||||
|
sb.append('z').append(i).append(' ');
|
||||||
|
}
|
||||||
|
sb.append(a);
|
||||||
|
sb.append(")");
|
||||||
|
|
||||||
|
String q = sb.toString();
|
||||||
|
|
||||||
|
ignoreException("Too many clauses");
|
||||||
|
assertJQ(req("q", q)
|
||||||
|
, "/response/numFound==");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,18 +389,11 @@
|
||||||
Query section - these settings control query time things like caches
|
Query section - these settings control query time things like caches
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
||||||
<query>
|
<query>
|
||||||
<!-- Max Boolean Clauses
|
|
||||||
|
|
||||||
Maximum number of clauses in each BooleanQuery, an exception
|
<!-- Maximum number of clauses in each BooleanQuery, an exception
|
||||||
is thrown if exceeded.
|
is thrown if exceeded. It is safe to increase or remove this setting,
|
||||||
|
since it is purely an arbitrary limit to try and catch user errors where
|
||||||
** WARNING **
|
large boolean queries may not be the best implementation choice.
|
||||||
|
|
||||||
This option actually modifies a global Lucene property that
|
|
||||||
will affect all SolrCores. If multiple solrconfig.xml files
|
|
||||||
disagree on this property, the value at any given moment will
|
|
||||||
be based on the last SolrCore to be initialized.
|
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<maxBooleanClauses>1024</maxBooleanClauses>
|
<maxBooleanClauses>1024</maxBooleanClauses>
|
||||||
|
|
||||||
|
|
|
@ -389,22 +389,14 @@
|
||||||
Query section - these settings control query time things like caches
|
Query section - these settings control query time things like caches
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
||||||
<query>
|
<query>
|
||||||
<!-- Max Boolean Clauses
|
|
||||||
|
|
||||||
Maximum number of clauses in each BooleanQuery, an exception
|
<!-- Maximum number of clauses in each BooleanQuery, an exception
|
||||||
is thrown if exceeded.
|
is thrown if exceeded. It is safe to increase or remove this setting,
|
||||||
|
since it is purely an arbitrary limit to try and catch user errors where
|
||||||
** WARNING **
|
large boolean queries may not be the best implementation choice.
|
||||||
|
|
||||||
This option actually modifies a global Lucene property that
|
|
||||||
will affect all SolrCores. If multiple solrconfig.xml files
|
|
||||||
disagree on this property, the value at any given moment will
|
|
||||||
be based on the last SolrCore to be initialized.
|
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<maxBooleanClauses>1024</maxBooleanClauses>
|
<maxBooleanClauses>1024</maxBooleanClauses>
|
||||||
|
|
||||||
|
|
||||||
<!-- Solr Internal Query Caches
|
<!-- Solr Internal Query Caches
|
||||||
|
|
||||||
There are two implementations of cache available for Solr,
|
There are two implementations of cache available for Solr,
|
||||||
|
|
|
@ -395,18 +395,11 @@
|
||||||
Query section - these settings control query time things like caches
|
Query section - these settings control query time things like caches
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
||||||
<query>
|
<query>
|
||||||
<!-- Max Boolean Clauses
|
|
||||||
|
|
||||||
Maximum number of clauses in each BooleanQuery, an exception
|
<!-- Maximum number of clauses in each BooleanQuery, an exception
|
||||||
is thrown if exceeded.
|
is thrown if exceeded. It is safe to increase or remove this setting,
|
||||||
|
since it is purely an arbitrary limit to try and catch user errors where
|
||||||
** WARNING **
|
large boolean queries may not be the best implementation choice.
|
||||||
|
|
||||||
This option actually modifies a global Lucene property that
|
|
||||||
will affect all SolrCores. If multiple solrconfig.xml files
|
|
||||||
disagree on this property, the value at any given moment will
|
|
||||||
be based on the last SolrCore to be initialized.
|
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<maxBooleanClauses>1024</maxBooleanClauses>
|
<maxBooleanClauses>1024</maxBooleanClauses>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue