mirror of https://github.com/apache/lucene.git
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:
parent
aade8aaa95
commit
9cc4437578
|
@ -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
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue