SOLR-5163: edismax now throws an exception when qf refers to a nonexistent field

This commit is contained in:
Charles Sanders 2018-09-27 15:53:26 -04:00 committed by David Smiley
parent 044bc2a485
commit 9481c1f623
3 changed files with 120 additions and 4 deletions

View File

@ -71,6 +71,8 @@ Other Changes
java.time.DateTimeFormatter instead of Joda time (see upgrade notes). "Lenient" is enabled. Removed Joda Time dependency. java.time.DateTimeFormatter instead of Joda time (see upgrade notes). "Lenient" is enabled. Removed Joda Time dependency.
(David Smiley, Bar Rotstein) (David Smiley, Bar Rotstein)
* SOLR-5163: edismax now throws an exception when qf refers to a nonexistent field (Charles Sanders, David Smiley)
* SOLR-12805: Store previous term (generation) of replica when start recovery process (Cao Manh Dat) * SOLR-12805: Store previous term (generation) of replica when start recovery process (Cao Manh Dat)
* SOLR-12652: Remove SolrMetricManager.overridableRegistryName method (Peter Somogyi via David Smiley) * SOLR-12652: Remove SolrMetricManager.overridableRegistryName method (Peter Somogyi via David Smiley)

View File

@ -48,6 +48,7 @@ import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.Version; import org.apache.lucene.util.Version;
import org.apache.solr.analysis.TokenizerChain; import org.apache.solr.analysis.TokenizerChain;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.DisMaxParams; import org.apache.solr.common.params.DisMaxParams;
import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.NamedList;
@ -55,6 +56,8 @@ import org.apache.solr.parser.QueryParser;
import org.apache.solr.parser.SolrQueryParserBase.MagicFieldName; import org.apache.solr.parser.SolrQueryParserBase.MagicFieldName;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.FieldType; import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.search.ExtendedDismaxQParser.ExtendedSolrQueryParser.Alias;
import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.SolrPluginUtils;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -144,6 +147,7 @@ public class ExtendedDismaxQParser extends QParser {
ExtendedSolrQueryParser up = createEdismaxQueryParser(this, IMPOSSIBLE_FIELD_NAME); ExtendedSolrQueryParser up = createEdismaxQueryParser(this, IMPOSSIBLE_FIELD_NAME);
up.addAlias(IMPOSSIBLE_FIELD_NAME, config.tiebreaker, config.queryFields); up.addAlias(IMPOSSIBLE_FIELD_NAME, config.tiebreaker, config.queryFields);
addAliasesFromRequest(up, config.tiebreaker); addAliasesFromRequest(up, config.tiebreaker);
validateQueryFields(up);
up.setPhraseSlop(config.qslop); // slop for explicit user phrase queries up.setPhraseSlop(config.qslop); // slop for explicit user phrase queries
up.setAllowLeadingWildcard(true); up.setAllowLeadingWildcard(true);
up.setAllowSubQueryParsing(config.userFields.isAllowed(MagicFieldName.QUERY.field)); up.setAllowSubQueryParsing(config.userFields.isAllowed(MagicFieldName.QUERY.field));
@ -205,6 +209,84 @@ public class ExtendedDismaxQParser extends QParser {
return topQuery; return topQuery;
} }
/**
* Validate query field names. Must be explicitly defined in the schema or match a dynamic field pattern.
* Checks source field(s) represented by a field alias
*
* @param up parser used
* @throws SyntaxError for invalid field name
*/
protected void validateQueryFields(ExtendedSolrQueryParser up) throws SyntaxError {
List<String> flds = new ArrayList<>(config.queryFields.keySet().size());
for (String fieldName : config.queryFields.keySet()) {
buildQueryFieldList(fieldName, up.getAlias(fieldName), flds, up);
}
checkFieldsInSchema(flds);
}
/**
* Build list of source (non-alias) query field names. Recursive through aliases.
*
* @param fieldName query field name
* @param alias field alias
* @param flds list of query field names
* @param up parser used
* @throws SyntaxError for invalid field name
*/
private void buildQueryFieldList(String fieldName, Alias alias, List<String> flds, ExtendedSolrQueryParser up) throws SyntaxError {
if (null == alias) {
flds.add(fieldName);
return;
}
up.validateCyclicAliasing(fieldName);
flds.addAll(getFieldsFromAlias(up, alias));
}
/**
* Return list of source (non-alias) field names from an alias
*
* @param up parser used
* @param a field alias
* @return list of source fields
* @throws SyntaxError for invalid field name
*/
private List<String> getFieldsFromAlias(ExtendedSolrQueryParser up, Alias a) throws SyntaxError {
List<String> lst = new ArrayList<>();
for (String s : a.fields.keySet()) {
buildQueryFieldList(s, up.getAlias(s), lst, up);
}
return lst;
}
/**
* Verify field name exists in schema, explicit or dynamic field pattern
*
* @param fieldName source field name to verify
* @throws SyntaxError for invalid field name
*/
private void checkFieldInSchema(String fieldName) throws SyntaxError {
try {
config.schema.getField(fieldName);
} catch (SolrException se) {
throw new SyntaxError("Query Field '" + fieldName + "' is not a valid field name", se);
}
}
/**
* Verify list of source field names
*
* @param flds list of source field names to verify
* @throws SyntaxError for invalid field name
*/
private void checkFieldsInSchema(List<String> flds) throws SyntaxError {
for (String fieldName : flds) {
checkFieldInSchema(fieldName);
}
}
/** /**
* Adds shingled phrase queries to all the fields specified in the pf, pf2 anf pf3 parameters * Adds shingled phrase queries to all the fields specified in the pf, pf2 anf pf3 parameters
* *
@ -1598,14 +1680,17 @@ public class ExtendedDismaxQParser extends QParser {
protected boolean splitOnWhitespace; protected boolean splitOnWhitespace;
protected IndexSchema schema;
public ExtendedDismaxConfiguration(SolrParams localParams, public ExtendedDismaxConfiguration(SolrParams localParams,
SolrParams params, SolrQueryRequest req) { SolrParams params, SolrQueryRequest req) {
solrParams = SolrParams.wrapDefaults(localParams, params); solrParams = SolrParams.wrapDefaults(localParams, params);
minShouldMatch = DisMaxQParser.parseMinShouldMatch(req.getSchema(), solrParams); // req.getSearcher() here causes searcher refcount imbalance schema = req.getSchema();
minShouldMatch = DisMaxQParser.parseMinShouldMatch(schema, solrParams); // req.getSearcher() here causes searcher refcount imbalance
final boolean forbidSubQueryByDefault = req.getCore().getSolrConfig().luceneMatchVersion.onOrAfter(Version.LUCENE_7_2_0); final boolean forbidSubQueryByDefault = req.getCore().getSolrConfig().luceneMatchVersion.onOrAfter(Version.LUCENE_7_2_0);
userFields = new UserFields(U.parseFieldBoosts(solrParams.getParams(DMP.UF)), forbidSubQueryByDefault); userFields = new UserFields(U.parseFieldBoosts(solrParams.getParams(DMP.UF)), forbidSubQueryByDefault);
try { try {
queryFields = DisMaxQParser.parseQueryFields(req.getSchema(), solrParams); // req.getSearcher() here causes searcher refcount imbalance queryFields = DisMaxQParser.parseQueryFields(schema, solrParams); // req.getSearcher() here causes searcher refcount imbalance
} catch (SyntaxError e) { } catch (SyntaxError e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -38,6 +38,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.SolrPluginUtils;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.noggit.ObjectBuilder; import org.noggit.ObjectBuilder;
@ -672,7 +673,8 @@ public class TestExtendedDismaxParser extends SolrTestCaseJ4 {
try { try {
h.query(req("defType","edismax", "q","blarg", "qf","field1", "f.field1.qf","field2 field3","f.field2.qf","field4 field5", "f.field4.qf","field5", "f.field5.qf","field6", "f.field3.qf","field6")); h.query(req("defType","edismax", "q","blarg", "qf","field1", "f.field1.qf","field2 field3","f.field2.qf","field4 field5", "f.field4.qf","field5", "f.field5.qf","field6", "f.field3.qf","field6"));
} catch (SolrException e) { } catch (SolrException e) {
fail("This is not cyclic alising"); assertFalse("This is not cyclic alising", e.getCause().getMessage().contains("Field aliases lead to a cycle"));
assertTrue(e.getCause().getMessage().contains("not a valid field name"));
} }
try { try {
@ -683,7 +685,7 @@ public class TestExtendedDismaxParser extends SolrTestCaseJ4 {
} }
try { try {
h.query(req("defType","edismax", "q","who:(Zapp Pig)", "qf","field1", "f.who.qf","name","f.name.qf","myalias", "f.myalias.qf","who")); h.query(req("defType","edismax", "q","who:(Zapp Pig)", "qf","text", "f.who.qf","name","f.name.qf","myalias", "f.myalias.qf","who"));
fail("Cyclic alising not detected"); fail("Cyclic alising not detected");
} catch (SolrException e) { } catch (SolrException e) {
assertTrue(e.getCause().getMessage().contains("Field aliases lead to a cycle")); assertTrue(e.getCause().getMessage().contains("Field aliases lead to a cycle"));
@ -2091,4 +2093,31 @@ public class TestExtendedDismaxParser extends SolrTestCaseJ4 {
assertJQ(req("defType", "edismax", "q", "*", "qq", "{!edismax v=something}", "bq", "{!edismax v=$qq}")); assertJQ(req("defType", "edismax", "q", "*", "qq", "{!edismax v=something}", "bq", "{!edismax v=$qq}"));
} }
/** SOLR-5163 */
@Test
public void testValidateQueryFields() throws Exception {
// field aliasing covered by test - testAliasing
ModifiableSolrParams params = new ModifiableSolrParams();
params.add("defType", "edismax");
params.add("df", "text");
params.add("q", "olive AND other");
params.add("qf", "subject^3 title");
params.add("debugQuery", "true");
// test valid field names
try (SolrQueryRequest req = req(params)) {
String response = h.query(req);
response.contains("+DisjunctionMaxQuery((title:olive | (subject:oliv)^3.0)) +DisjunctionMaxQuery((title:other | (subject:other)^3.0))");
}
// test invalid field name
params.set("qf", "subject^3 nosuchfield");
try (SolrQueryRequest req = req(params)) {
h.query(req);
} catch (Exception e) {
Assert.assertEquals("org.apache.solr.search.SyntaxError: Query Field 'nosuchfield' is not a valid field name", e.getMessage());
}
}
} }