diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index cc6c8202d87..a45bfd80197 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -698,6 +698,9 @@ Bug Fixes * SOLR-3260: DataImportHandler: ScriptTransformer gives better error messages when problems arise on initalization (no Script Engine, invalid script, etc). (James Dyer) +* SOLR-2959: edismax now respects the magic fields '_val_' and '_query_' + (Michael Watts, hossman) + Other Changes ---------------------- * SOLR-2922: Upgrade commons-io and commons-lang to 2.1 and 2.6, respectively. (koji) diff --git a/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java index b74e924ae25..3978eaaa20a 100755 --- a/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java @@ -40,6 +40,7 @@ import org.apache.lucene.queries.function.valuesource.QueryValueSource; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.*; +import org.apache.solr.search.SolrQueryParser.MagicFieldName; import org.apache.solr.common.params.DisMaxParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -792,8 +793,9 @@ class ExtendedDismaxQParser extends QParser { String fname = s.substring(pos, p); boolean isInSchema = getReq().getSchema().getFieldTypeNoEx(fname) != null; boolean isAlias = solrParams.get("f."+fname+".qf") != null; + boolean isMagic = (null != MagicFieldName.get(fname)); - return (isInSchema || isAlias) ? fname : null; + return (isInSchema || isAlias || isMagic) ? fname : null; } public static List split(String s, boolean ignoreQuote) { @@ -1082,7 +1084,9 @@ class ExtendedDismaxQParser extends QParser { // literal when we try the escape+re-parse. if (exceptions) { FieldType ft = schema.getFieldTypeNoEx(field); - if (ft == null) throw unknownField; + if (ft == null && null == MagicFieldName.get(field)) { + throw unknownField; + } } return getQuery(); diff --git a/solr/core/src/java/org/apache/solr/search/SolrQueryParser.java b/solr/core/src/java/org/apache/solr/search/SolrQueryParser.java index 441a26a9137..630c6ada985 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrQueryParser.java +++ b/solr/core/src/java/org/apache/solr/search/SolrQueryParser.java @@ -17,6 +17,7 @@ package org.apache.solr.search; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -59,6 +60,33 @@ public class SolrQueryParser extends QueryParser { protected final QParser parser; protected final String defaultField; + /** + * Identifies the list of all known "magic fields" that trigger + * special parsing behavior + */ + public static enum MagicFieldName { + VAL("_val_", "func"), QUERY("_query_", null); + + public final String field; + public final String subParser; + MagicFieldName(final String field, final String subParser) { + this.field = field; + this.subParser = subParser; + } + public String toString() { + return field; + } + private final static Map lookup + = new HashMap(); + static { + for(MagicFieldName s : EnumSet.allOf(MagicFieldName.class)) + lookup.put(s.toString(), s); + } + public static MagicFieldName get(final String field) { + return lookup.get(field); + } + } + // implementation detail - caching ReversedWildcardFilterFactory based on type private Map leadingWildcards; @@ -124,13 +152,12 @@ public class SolrQueryParser extends QueryParser { checkNullField(field); // intercept magic field name of "_" to use as a hook for our // own functions. - if (field.charAt(0) == '_') { - if ("_val_".equals(field)) { - QParser nested = parser.subQuery(queryText, "func"); + if (field.charAt(0) == '_' && parser != null) { + MagicFieldName magic = MagicFieldName.get(field); + if (null != magic) { + QParser nested = parser.subQuery(queryText, magic.subParser); return nested.getQuery(); - } else if ("_query_".equals(field) && parser != null) { - return parser.subQuery(queryText, null).getQuery(); - } + } } SchemaField sf = schema.getFieldOrNull(field); if (sf != null) { diff --git a/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java b/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java index b49935e190f..eb76eec29c0 100755 --- a/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java +++ b/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java @@ -54,7 +54,7 @@ public class TestExtendedDismaxParser extends AbstractSolrTestCase { assertU(adoc("id", "48", "text_sw", "this has gigabyte potential", "foo_i","100")); assertU(adoc("id", "49", "text_sw", "start the big apple end", "foo_i","-100")); assertU(adoc("id", "50", "text_sw", "start new big city end")); - + assertU(adoc("id", "51", "store", "12.34,-56.78")); assertU(commit()); } @Override @@ -66,8 +66,8 @@ public class TestExtendedDismaxParser extends AbstractSolrTestCase { // test the edismax query parser based on the dismax parser public void testFocusQueryParser() { - String allq = "id:[42 TO 50]"; - String allr = "*[count(//doc)=9]"; + String allq = "id:[42 TO 51]"; + String allr = "*[count(//doc)=10]"; String oner = "*[count(//doc)=1]"; String twor = "*[count(//doc)=2]"; String nor = "*[count(//doc)=0]"; @@ -227,6 +227,43 @@ public class TestExtendedDismaxParser extends AbstractSolrTestCase { assertQ(req("defType","edismax", "mm","0", "q","movies_t:Terminator 100", "qf","movies_t foo_i"), twor); + + // special psuedo-fields like _query_ and _val_ + + // special fields (and real field id) should be included by default + assertQ(req("defType", "edismax", + "mm", "100%", + "fq", "id:51", + "q", "_query_:\"{!geofilt d=20 sfield=store pt=12.34,-56.78}\""), + oner); + // should also work when explicitly allowed + assertQ(req("defType", "edismax", + "mm", "100%", + "fq", "id:51", + "uf", "id _query_", + "q", "_query_:\"{!geofilt d=20 sfield=store pt=12.34,-56.78}\""), + oner); + assertQ(req("defType", "edismax", + "mm", "100%", + "fq", "id:51", + "uf", "id", + "uf", "_query_", + "q", "_query_:\"{!geofilt d=20 sfield=store pt=12.34,-56.78}\""), + oner); + + // should fail when prohibited + assertQ(req("defType", "edismax", + "mm", "100%", + "fq", "id:51", + "uf", "* -_query_", // explicitly excluded + "q", "_query_:\"{!geofilt d=20 sfield=store pt=12.34,-56.78}\""), + nor); + assertQ(req("defType", "edismax", + "mm", "100%", + "fq", "id:51", + "uf", "id", // excluded by ommision + "q", "_query_:\"{!geofilt d=20 sfield=store pt=12.34,-56.78}\""), + nor); /** stopword removal in conjunction with multi-word synonyms at query time