SOLR-2128: full param substitution for function queries

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1000000 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2010-09-22 14:48:10 +00:00
parent aade8aaa95
commit 9cc4437578
5 changed files with 107 additions and 19 deletions

View File

@ -268,6 +268,10 @@ New Features
* SOLR-792: Adding PivotFacetComponent for Hierarchical faceting * SOLR-792: Adding PivotFacetComponent for Hierarchical faceting
(erik, Jeremy Hinegardner, Thibaut Lassalle, ryan) (erik, Jeremy Hinegardner, Thibaut Lassalle, ryan)
* SOLR-2128: Full parameter substitution for function queries.
Example: q=add($v1,$v2)&v1=mul(popularity,5)&v2=20.0
(yonik)
Optimizations Optimizations
---------------------- ----------------------

View File

@ -70,8 +70,8 @@ public class FunctionQParser extends QParser {
* @throws ParseException * @throws ParseException
*/ */
public String parseId() throws ParseException { public String parseId() throws ParseException {
String value = sp.getId(); String value = parseArg();
consumeArgumentDelimiter(); if (argWasQuoted) throw new ParseException("Expected identifier instead of quoted string:" + value);
return value; return value;
} }
@ -82,8 +82,9 @@ public class FunctionQParser extends QParser {
* @throws ParseException * @throws ParseException
*/ */
public Float parseFloat() throws ParseException { public Float parseFloat() throws ParseException {
float value = sp.getFloat(); String str = parseArg();
consumeArgumentDelimiter(); if (argWasQuoted()) throw new ParseException("Expected float instead of quoted string:" + str);
float value = Float.parseFloat(str);
return value; return value;
} }
@ -93,8 +94,9 @@ public class FunctionQParser extends QParser {
* @throws ParseException * @throws ParseException
*/ */
public double parseDouble() throws ParseException { public double parseDouble() throws ParseException {
double value = sp.getDouble(); String str = parseArg();
consumeArgumentDelimiter(); if (argWasQuoted()) throw new ParseException("Expected double instead of quoted string:" + str);
double value = Double.parseDouble(str);
return value; return value;
} }
@ -104,12 +106,21 @@ public class FunctionQParser extends QParser {
* @throws ParseException * @throws ParseException
*/ */
public int parseInt() throws ParseException { public int parseInt() throws ParseException {
int value = sp.getInt(); String str = parseArg();
consumeArgumentDelimiter(); if (argWasQuoted()) throw new ParseException("Expected double instead of quoted string:" + str);
int value = Integer.parseInt(str);
return value; return value;
} }
private boolean argWasQuoted;
public boolean argWasQuoted() {
return argWasQuoted;
}
public String parseArg() throws ParseException { public String parseArg() throws ParseException {
argWasQuoted = false;
sp.eatws(); sp.eatws();
char ch = sp.peek(); char ch = sp.peek();
String val = null; String val = null;
@ -123,6 +134,7 @@ public class FunctionQParser extends QParser {
case '\'': case '\'':
case '"': case '"':
val = sp.getQuotedString(); val = sp.getQuotedString();
argWasQuoted = true;
break; break;
default: default:
// read unquoted literal ended by whitespace ',' or ')' // read unquoted literal ended by whitespace ',' or ')'
@ -232,11 +244,65 @@ public class FunctionQParser extends QParser {
int ch = sp.peek(); int ch = sp.peek();
if (ch>='0' && ch<='9' || ch=='.' || ch=='+' || ch=='-') { if (ch>='0' && ch<='9' || ch=='.' || ch=='+' || ch=='-') {
valueSource = new ConstValueSource(sp.getFloat()); Number num = sp.getNumber();
if (num instanceof Long) {
valueSource = new LongConstValueSource(num.longValue());
} else if (num instanceof Double) {
valueSource = new DoubleConstValueSource(num.doubleValue());
} else {
// shouldn't happen
valueSource = new ConstValueSource(num.floatValue());
}
} else if (ch == '"' || ch == '\''){ } else if (ch == '"' || ch == '\''){
valueSource = new LiteralValueSource(sp.getQuotedString()); valueSource = new LiteralValueSource(sp.getQuotedString());
} } else if (ch == '$') {
else { sp.pos++;
String param = sp.getId();
String val = getParam(param);
if (val == null) {
throw new ParseException("Missing param " + param + " while parsing function '" + sp.val + "'");
}
QParser subParser = subQuery(val, "func");
Query subQuery = subParser.getQuery();
if (subQuery instanceof FunctionQuery) {
valueSource = ((FunctionQuery) subQuery).getValueSource();
} else {
valueSource = new QueryValueSource(subQuery, 0.0f);
}
/***
// dereference *simple* argument (i.e., can't currently be a function)
// In the future we could support full function dereferencing via a stack of ValueSource (or StringParser) objects
ch = val.length()==0 ? '\0' : val.charAt(0);
if (ch>='0' && ch<='9' || ch=='.' || ch=='+' || ch=='-') {
QueryParsing.StrParser sp = new QueryParsing.StrParser(val);
Number num = sp.getNumber();
if (num instanceof Long) {
valueSource = new LongConstValueSource(num.longValue());
} else if (num instanceof Double) {
valueSource = new DoubleConstValueSource(num.doubleValue());
} else {
// shouldn't happen
valueSource = new ConstValueSource(num.floatValue());
}
} else if (ch == '"' || ch == '\'') {
QueryParsing.StrParser sp = new QueryParsing.StrParser(val);
val = sp.getQuotedString();
valueSource = new LiteralValueSource(val);
} else {
if (val.length()==0) {
valueSource = new LiteralValueSource(val);
} else {
String id = val;
SchemaField f = req.getSchema().getField(id);
valueSource = f.getType().getValueSource(f, this);
}
}
***/
} else {
String id = sp.getId(); String id = sp.getId();
if (sp.opt("(")) { if (sp.opt("(")) {
@ -252,6 +318,7 @@ public class FunctionQParser extends QParser {
SchemaField f = req.getSchema().getField(id); SchemaField f = req.getSchema().getField(id);
valueSource = f.getType().getValueSource(f, this); valueSource = f.getType().getValueSource(f, this);
} }
} }
if (doConsumeDelimiter) if (doConsumeDelimiter)

View File

@ -694,6 +694,27 @@ public class QueryParsing {
return Float.parseFloat(new String(arr, 0, i)); return Float.parseFloat(new String(arr, 0, i));
} }
Number getNumber() throws ParseException {
eatws();
int start = pos;
boolean flt = false;
while (pos < end) {
char ch = val.charAt(pos);
if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-') {
pos++;
} else if (ch == '.' || ch =='e' || ch=='E') {
flt = true;
pos++;
} else {
break;
}
}
String v = val.substring(start,pos);
return flt ? Double.parseDouble(v) : Long.parseLong(v);
}
double getDouble() throws ParseException { double getDouble() throws ParseException {
eatws(); eatws();
char[] arr = new char[end - pos]; char[] arr = new char[end - pos];

View File

@ -42,14 +42,6 @@ public class FunctionQParserTest extends AbstractSolrTestCase {
assertTrue("query is not a FunctionQuery", query instanceof FunctionQuery); assertTrue("query is not a FunctionQuery", query instanceof FunctionQuery);
fq = (FunctionQuery) query; fq = (FunctionQuery) query;
assertTrue("ValueSource is not a LiteralValueSource", fq.getValueSource() instanceof LiteralValueSource); assertTrue("ValueSource is not a LiteralValueSource", fq.getValueSource() instanceof LiteralValueSource);
parser = new FunctionQParser("1.5", local, params, req);
query = parser.parse();
assertTrue("query is not a FunctionQuery", query instanceof FunctionQuery);
fq = (FunctionQuery) query;
assertTrue("ValueSource is not a LiteralValueSource", fq.getValueSource() instanceof ConstValueSource);
//TODO: Add more tests here to test the parser
} }
} }

View File

@ -339,6 +339,10 @@ public class TestFunctionQuery extends SolrTestCaseJ4 {
assertQ(req("fl","*,score","q", q, "qq","text:superman", "fq",fq), "//float[@name='score']>'1.0'"); assertQ(req("fl","*,score","q", q, "qq","text:superman", "fq",fq), "//float[@name='score']>'1.0'");
// test full param dereferencing
assertQ(req("fl","*,score","q", "{!func}add($v1,$v2)", "v1","add($v3,$v4)", "v2","1", "v3","2", "v4","5"
, "fq","id:1"), "//float[@name='score']='8.0'");
purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity
} }