blob: 803905fcef63c9c0486c29679e178c86d25e318d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.search;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import junit.framework.AssertionFailedError;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.AfterClass;
import org.junit.BeforeClass;
/**
* Sanity checks that queries (generated by the QParser and ValueSourceParser
* framework) are appropriately {@link Object#equals} and
* {@link Object#hashCode()} equivalent. If you are adding a new default
* QParser or ValueSourceParser, you will most likely get a failure from
* {@link #testParserCoverage} until you add a new test method to this class.
*
* @see ValueSourceParser#standardValueSourceParsers
* @see QParserPlugin#standardPlugins
* @see QueryUtils
**/
public class QueryEqualityTest extends SolrTestCaseJ4 {
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml","schema15.xml");
}
/** @see #testParserCoverage */
@AfterClass
public static void afterClassParserCoverageTest() {
if ( ! doAssertParserCoverage) return;
for (String name : QParserPlugin.standardPlugins.keySet()) {
assertTrue("testParserCoverage was run w/o any other method explicitly testing qparser: " + name, qParsersTested.contains(name));
}
for (final String name : ValueSourceParser.standardValueSourceParsers.keySet()) {
assertTrue("testParserCoverage was run w/o any other method explicitly testing val parser: " + name, valParsersTested.contains(name));
}
}
/** @see #testParserCoverage */
private static boolean doAssertParserCoverage = false;
/** @see #testParserCoverage */
private static final Set<String> qParsersTested = new HashSet<>();
/** @see #testParserCoverage */
private static final Set<String> valParsersTested = new HashSet<>();
public void testDateMathParsingEquality() throws Exception {
// regardless of parser, these should all be equivalent queries
assertQueryEquals
(null
,"{!lucene}f_tdt:2013-09-11T00\\:00\\:00Z"
,"{!lucene}f_tdt:2013-03-08T00\\:46\\:15Z/DAY+6MONTHS+3DAYS"
,"{!lucene}f_tdt:\"2013-03-08T00:46:15Z/DAY+6MONTHS+3DAYS\""
,"{!field f=f_tdt}2013-03-08T00:46:15Z/DAY+6MONTHS+3DAYS"
,"{!field f=f_tdt}2013-09-11T00:00:00Z"
,"{!term f=f_tdt}2013-03-08T00:46:15Z/DAY+6MONTHS+3DAYS"
,"{!term f=f_tdt}2013-09-11T00:00:00Z"
);
}
public void testQueryLucene() throws Exception {
assertQueryEquals("lucene", "{!lucene}apache solr",
"apache solr", "apache solr ");
assertQueryEquals("lucene", "+apache +solr", "apache AND solr",
" +apache +solr");
}
public void testQueryLuceneAllDocsWithField() throws Exception {
// for all "primative" types except for doubles/floats, 'foo:*' should be functionally equivilent to "foo:[* TO *]"
// whatever implementation/optimizations exist for one syntax, should exist for the other syntax as well
// (regardless of docValues, multivalued, etc...)
for (String field : Arrays.asList("foo_sI", "foo_sS", "foo_s1", "foo_s",
"t_foo", "tv_foo", "tv_mv_foo",
"foo_b", "foo_b_dvo",
"foo_i", "foo_is", "foo_i_dvo",
"foo_l", "foo_l_dvo",
"foo_dt", "foo_dt_dvo")) {
assertQueryEquals("lucene", field + ":*", field + ":[* TO *]");
}
}
public void testQueryPrefix() throws Exception {
SolrQueryRequest req = req("myField","foo_s");
try {
assertQueryEquals("prefix", req,
"{!prefix f=$myField}asdf",
"{!prefix f=foo_s}asdf");
} finally {
req.close();
}
}
public void testQueryBoost() throws Exception {
SolrQueryRequest req = req("df","foo_s","myBoost","sum(3,foo_i)");
try {
assertQueryEquals("boost", req,
"{!boost b=$myBoost}asdf",
"{!boost b=$myBoost v=asdf}",
"{!boost b=sum(3,foo_i)}foo_s:asdf");
} finally {
req.close();
}
}
public void testReRankQuery() throws Exception {
final String defType = ReRankQParserPlugin.NAME;
SolrQueryRequest req = req("q", "*:*",
"rqq", "{!edismax}hello",
"rdocs", "20",
"rweight", "2",
"rows", "10",
"start", "0");
try {
assertQueryEquals(defType, req,
"{!"+defType+" "+ReRankQParserPlugin.RERANK_QUERY+"=$rqq "+ReRankQParserPlugin.RERANK_DOCS+"=$rdocs "+ReRankQParserPlugin.RERANK_WEIGHT+"=$rweight}",
"{!"+defType+" "+ReRankQParserPlugin.RERANK_QUERY+"=$rqq "+ReRankQParserPlugin.RERANK_DOCS+"=20 "+ReRankQParserPlugin.RERANK_WEIGHT+"=2}");
} finally {
req.close();
}
req = req("qq", "*:*",
"rqq", "{!edismax}hello",
"rdocs", "20",
"rweight", "2",
"rows", "100",
"start", "50");
try {
assertQueryEquals(defType, req,
"{!"+defType+" mainQuery=$qq "+ReRankQParserPlugin.RERANK_QUERY+"=$rqq "+ReRankQParserPlugin.RERANK_DOCS+"=$rdocs "+ReRankQParserPlugin.RERANK_WEIGHT+"=$rweight}",
"{!"+defType+" mainQuery=$qq "+ReRankQParserPlugin.RERANK_QUERY+"=$rqq "+ReRankQParserPlugin.RERANK_DOCS+"=20 "+ReRankQParserPlugin.RERANK_WEIGHT+"=2}");
} finally {
req.close();
}
}
public void testExportQuery() throws Exception {
SolrQueryRequest req = req("q", "*:*");
try {
assertQueryEquals("xport", req, "{!xport}");
} finally {
req.close();
}
}
public void testGraphTermsQuery() throws Exception {
SolrQueryRequest req = req("q", "*:*");
try {
assertQueryEquals("graphTerms", req, "{!graphTerms f=field1_s maxDocFreq=1000}term1,term2");
} finally {
req.close();
}
}
public void testTlogitQuery() throws Exception {
SolrQueryRequest req = req("q", "*:*", "feature", "f", "terms","a,b,c", "weights", "100,200,300", "idfs","1,5,7","iteration","1", "outcome","a","positiveLabel","1");
try {
assertQueryEquals("tlogit", req, "{!tlogit}");
} finally {
req.close();
}
}
public void testIGainQuery() throws Exception {
SolrQueryRequest req = req("q", "*:*", "outcome", "b", "positiveLabel", "1", "field", "x", "numTerms","200");
try {
assertQueryEquals("igain", req, "{!igain}");
} finally {
req.close();
}
}
public void testSignificantTermsQuery() throws Exception {
SolrQueryRequest req = req("q", "*:*");
try {
assertQueryEquals(SignificantTermsQParserPlugin.NAME,
req, "{!"+SignificantTermsQParserPlugin.NAME+"}");
} finally {
req.close();
}
}
public void testQuerySwitch() throws Exception {
SolrQueryRequest req = req("myXXX", "XXX",
"myField", "foo_s",
"myQ", "{!prefix f=$myField}asdf");
try {
assertQueryEquals("switch", req,
"{!switch case.foo=XXX case.bar=zzz case.yak=qqq}foo",
"{!switch case.foo=qqq case.bar=XXX case.yak=zzz} bar ",
"{!switch case.foo=qqq case.bar=XXX case.yak=zzz v=' bar '}",
"{!switch default=XXX case.foo=qqq case.bar=zzz}asdf",
"{!switch default=$myXXX case.foo=qqq case.bar=zzz}asdf",
"{!switch case=XXX case.bar=zzz case.yak=qqq v=''}",
"{!switch case.bar=zzz case=XXX case.yak=qqq v=''}",
"{!switch case=XXX case.bar=zzz case.yak=qqq}",
"{!switch case=XXX case.bar=zzz case.yak=qqq} ",
"{!switch case=$myXXX case.bar=zzz case.yak=qqq} ");
assertQueryEquals("switch", req,
"{!switch case.foo=$myQ case.bar=zzz case.yak=qqq}foo",
"{!query v=$myQ}");
} finally {
req.close();
}
}
public void testMatchAllDocsQueryXmlParser() throws Exception {
final String type = "xmlparser";
assertQueryEquals(type,
"{!"+type+"}<MatchAllDocsQuery/>",
"<MatchAllDocsQuery/>",
"<MatchAllDocsQuery></MatchAllDocsQuery>");
}
public void testQueryDismax() throws Exception {
for (final String type : new String[]{"dismax","edismax"}) {
assertQueryEquals(type, "{!"+type+"}apache solr",
"apache solr", "apache solr", "apache solr ");
assertQueryEquals(type, "+apache +solr", "apache AND solr",
" +apache +solr");
}
}
public void testField() throws Exception {
SolrQueryRequest req = req("myField","foo_s");
try {
assertQueryEquals("field", req,
"{!field f=$myField}asdf",
"{!field f=$myField v=asdf}",
"{!field f=foo_s}asdf");
} finally {
req.close();
}
}
public void testQueryRaw() throws Exception {
SolrQueryRequest req = req("myField","foo_s");
try {
assertQueryEquals("raw", req,
"{!raw f=$myField}asdf",
"{!raw f=$myField v=asdf}",
"{!raw f=foo_s}asdf");
} finally {
req.close();
}
}
public void testQueryTerm() throws Exception {
SolrQueryRequest req = req("myField","foo_s");
try {
assertQueryEquals("term", req,
"{!term f=$myField}asdf",
"{!term f=$myField v=asdf}",
"{!term f=foo_s}asdf");
} finally {
req.close();
}
}
@SuppressWarnings({"unchecked"})
public void testQueryCollapse() throws Exception {
SolrQueryRequest req = req("myField","foo_s1",
"g_sort","foo_s1 asc, foo_i desc");
try {
assertQueryEquals("collapse", req,
"{!collapse field=$myField}");
assertQueryEquals("collapse", req,
"{!collapse field=$myField max=a}");
assertQueryEquals("collapse", req,
"{!collapse field=$myField min=a}",
"{!collapse field=$myField min=a nullPolicy=ignore}");
assertQueryEquals("collapse", req,
"{!collapse field=$myField sort=$g_sort}",
"{!collapse field=$myField sort='foo_s1 asc, foo_i desc'}",
"{!collapse field=$myField sort=$g_sort nullPolicy=ignore}");
assertQueryEquals("collapse", req,
"{!collapse field=$myField max=a nullPolicy=expand}");
//Add boosted documents to the request context.
@SuppressWarnings({"rawtypes"})
Map context = req.getContext();
@SuppressWarnings({"rawtypes"})
Set boosted = new HashSet();
boosted.add("doc1");
boosted.add("doc2");
context.put("BOOSTED", boosted);
assertQueryEquals("collapse", req,
"{!collapse field=$myField min=a}",
"{!collapse field=$myField min=a nullPolicy=ignore}");
} finally {
req.close();
}
}
public void testHash() throws Exception {
SolrQueryRequest req = req("partitionKeys","foo_s");
try {
assertQueryEquals("hash", req,
"{!hash workers=3 worker=0}");
} finally {
req.close();
}
}
public void testMinHash() throws Exception {
SolrQueryRequest req = req("q","apache lucene is a search library",
"df", "min_hash_analyzed");
try {
assertQueryEquals("min_hash", req,
"{!min_hash field=\"min_hash_analysed\"}apache lucene is a search library");
} finally {
req.close();
}
}
public void testRankQuery() throws Exception {
SolrQueryRequest req = req("df", "foo_s");
try {
assertQueryEquals("rank", req,
"{!rank f='rank_1'}",
"{!rank f='rank_1' function='satu'}",
"{!rank f='rank_1' function='satu' weight=1}");
} finally {
req.close();
}
}
public void testQueryNested() throws Exception {
SolrQueryRequest req = req("df", "foo_s");
try {
assertQueryEquals("query", req,
"{!query defType=lucene}asdf",
"{!query v='foo_s:asdf'}",
"{!query}foo_s:asdf",
"{!query}asdf");
} finally {
req.close();
}
}
public void testQueryFunc() throws Exception {
// more involved tests of specific functions in other methods
SolrQueryRequest req = req("myVar", "5",
"myField","foo_i",
"myInner","product(4,foo_i)");
try {
assertQueryEquals("func", req,
"{!func}sum(4,5)",
"{!func}sum(4,$myVar)",
"sum(4,5)");
assertQueryEquals("func", req,
"{!func}sum(1,2,3,4,5)",
"{!func}sum(1,2,3,4,$myVar)",
"sum(1,2,3,4,5)");
assertQueryEquals("func", req,
"{!func}sum(4,$myInner)",
"{!func}sum(4,product(4,foo_i))",
"{!func}sum(4,product(4,$myField))",
"{!func}sum(4,product(4,field(foo_i)))");
} finally {
req.close();
}
}
public void testQueryFrange() throws Exception {
SolrQueryRequest req = req("myVar", "5",
"low","0.2",
"high", "20.4",
"myField","foo_i",
"myInner","product(4,foo_i)");
try {
// NOTE: unlike most queries, frange defaultsto cost==100
assertQueryEquals("frange", req,
"{!frange l=0.2 h=20.4}sum(4,5)",
"{!frange l=0.2 h=20.4 cost=100}sum(4,5)",
"{!frange l=$low h=$high}sum(4,$myVar)");
} finally {
req.close();
}
}
public void testQueryGeofilt() throws Exception {
checkQuerySpatial("geofilt");
}
public void testQueryBbox() throws Exception {
checkQuerySpatial("bbox");
}
public void testLocalParamsWithRepeatingParam() throws Exception {
SolrQueryRequest req = req("q", "foo",
"bq", "111",
"bq", "222");
try {
assertQueryEquals("dismax",
req,
"{!dismax}foo",
"{!dismax bq=111 bq=222}foo",
"{!dismax bq=222 bq=111}foo");
} finally {
req.close();
}
}
private void checkQuerySpatial(final String type) throws Exception {
SolrQueryRequest req = req("myVar", "5",
"d","109",
"pt","10.312,-20.556",
"sfield","store");
try {
assertQueryEquals(type, req,
"{!"+type+" d=109}",
"{!"+type+" sfield=$sfield}",
"{!"+type+" sfield=store d=109}",
"{!"+type+" sfield=store d=$d pt=$pt}",
"{!"+type+" sfield=store d=$d pt=10.312,-20.556}",
"{!"+type+"}");
// diff SpatialQueryable FieldTypes matter for determining final query
assertQueryEquals(type, req,
"{!"+type+" sfield=point_hash}",
"{!"+type+" sfield=point_hash d=109}",
"{!"+type+" sfield=point_hash d=$d pt=$pt}",
"{!"+type+" sfield=point_hash d=$d pt=10.312,-20.556}");
assertQueryEquals(type, req,
"{!"+type+" sfield=point}",
"{!"+type+" sfield=point d=109}",
"{!"+type+" sfield=point d=$d pt=$pt}",
"{!"+type+" sfield=point d=$d pt=10.312,-20.556}");
} finally {
req.close();
}
}
public void testQueryJoin() throws Exception {
SolrQueryRequest req = req("myVar", "5",
"df","text",
"ff","foo_s",
"tt", "bar_s");
try {
assertQueryEquals("join", req,
"{!join from=foo_s to=bar_s}asdf",
"{!join from=$ff to=$tt}asdf",
"{!join from=$ff to='bar_s'}text:asdf");
} finally {
req.close();
}
}
public void testQueryScoreJoin() throws Exception {
SolrQueryRequest req = req("myVar", "5",
"df", "text",
"ff", "foo_s",
"tt", "bar_s",
"scoreavg","avg");
try {
assertQueryEquals("join", req,
"{!join from=foo_s to=bar_s score=avg}asdf",
"{!join from=$ff to=$tt score=Avg}asdf",
"{!join from=$ff to='bar_s' score=$scoreavg}text:asdf");
} finally {
req.close();
}
}
public void testTerms() throws Exception {
assertQueryEquals("terms", "{!terms f=foo_i}10,20,30,-10,-20,-30", "{!terms f=foo_i}10,20,30,-10,-20,-30");
}
public void testBlockJoin() throws Exception {
assertQueryEquals("parent", "{!parent which=foo_s:parent}dude",
"{!parent which=foo_s:parent}dude");
assertQueryEquals("child", "{!child of=foo_s:parent}dude",
"{!child of=foo_s:parent}dude");
// zero query case
assertQueryEquals(null, "{!parent which=foo_s:parent}",
"{!parent which=foo_s:parent}");
assertQueryEquals(null, "{!child of=foo_s:parent}",
"{!child of=foo_s:parent}");
assertQueryEquals(null, "{!parent which='+*:* -foo_s:parent'}",
"{!child of=foo_s:parent}");
try (SolrQueryRequest req = req("fq","bar_s:baz","fq","{!tag=fqban}bar_s:ban",
"ffq","bar_s:baz","ffq","{!tag=ffqban}bar_s:ban")) {
assertQueryEquals("filters", req,
"{!parent which=foo_s:parent param=$fq}foo_s:bar",
"{!parent which=foo_s:parent param=$ffq}foo_s:bar" // differently named params
);
assertQueryEquals("filters", req,
"{!parent which=foo_s:parent param=$fq excludeTags=fqban}foo_s:bar",
"{!parent which=foo_s:parent param=$ffq excludeTags=ffqban}foo_s:bar" // differently named params
);
QueryUtils.checkUnequal(// parent filter is not an equal to child
QParser.getParser("{!child of=foo_s:parent}", req).getQuery(),
QParser.getParser("{!parent which=foo_s:parent}", req).getQuery());
}
// sanity check multiple ways of specifing _nest_path_ prefixes
final String parent_path = "/aa/bb";
try (SolrQueryRequest req = req("parent_filt", "(*:* -{!prefix f='_nest_path_' v='"+parent_path+"/'})",
"child_q", "(+foo +{!prefix f='_nest_path_' v='"+parent_path+"/'})",
"parent_q", "(+bar +{!field f='_nest_path_' v='"+parent_path+"'})")) {
assertQueryEquals("parent", req,
// using local params to refer to other query params using 'prefix' parser...
"{!parent which=$parent_filt v=$child_q}",
// using 'inline' prefix query syntax...
//
// '/' has to be escaped other wise it will be treated as a regex query...
// ...and when used inside the 'which' param it has to be escaped *AGAIN* because of
// the "quoted" localparam evaluation layer...
// (and of course '\' escaping is the java syntax as well, we have to double it)
"{!parent which='*:* -_nest_path_:"+(parent_path + "/").replace("/","\\\\/") +"*'}"
+ "(+foo +_nest_path_:" + (parent_path + "/").replace("/", "\\/") + "*)");
assertQueryEquals("child", req,
// using local params to refer to other query params using 'prefix' parser...
"{!child of=$parent_filt v=$parent_q}",
// using 'inline' prefix query syntax...
//
// '/' has to be escaped other wise it will be treated as a regex query...
// ...and when used inside the 'which' param it has to be escaped *AGAIN* because of
// the "quoted" localparam evaluation layer...
// (and of course '\' escaping is the java syntax as well, we have to double it)
"{!child of='*:* -_nest_path_:"+(parent_path + "/").replace("/","\\\\/") +"*'}"
+ "(+bar +_nest_path_:" + parent_path.replace("/", "\\/") + ")");
}
}
public void testFilters() throws Exception {
final SolrQueryRequest req = req(
"fq","bar_s:baz","fq","{!tag=fqban}bar_s:ban",
"ffq","{!tag=ffqbaz}bar_s:baz","ffq","{!tag=ffqban}bar_s:ban");
try {
assertQueryEquals("filters", req,
"{!filters param=$fq}foo_s:bar",
"{!filters param=$fq}foo_s:bar",
"{!filters param=$ffq}foo_s:bar" // differently named params
);
assertQueryEquals("filters", req,
"{!filters param=$fq excludeTags=fqban}foo_s:bar",
"{!filters param=$ffq excludeTags=ffqban}foo_s:bar"
);
assertQueryEquals("filters", req,
"{!filters excludeTags=top}{!tag=top v='foo_s:bar'}",
"{!filters param=$ffq excludeTags='ffqban,ffqbaz'}"
);
QueryUtils.checkUnequal(
QParser.getParser("{!filters param=$fq}foo_s:bar", req).getQuery(),
QParser.getParser("{!filters param=$fq excludeTags=fqban}foo_s:bar", req).getQuery());
} finally {
req.close();
}
}
public void testGraphQuery() throws Exception {
SolrQueryRequest req = req("from", "node_s",
"to","edge_s",
"traversalFilter","foo",
"returnOnlyLeaf","true",
"returnRoot","false",
"maxDepth","2",
"useAutn","false"
);
// make sure all param subsitution works for all args to graph query.
assertQueryEquals("graph", req,
"{!graph from=node_s to=edge_s}*:*",
"{!graph from=$from to=$to}*:*");
assertQueryEquals("graph", req,
"{!graph from=node_s to=edge_s traversalFilter=foo}*:*",
"{!graph from=$from to=$to traversalFilter=$traversalFilter}*:*");
assertQueryEquals("graph", req,
"{!graph from=node_s to=edge_s traversalFilter=foo returnOnlyLeaf=true}*:*",
"{!graph from=$from to=$to traversalFilter=$traversalFilter returnOnlyLeaf=$returnOnlyLeaf}*:*");
assertQueryEquals("graph", req,
"{!graph from=node_s to=edge_s traversalFilter=foo returnOnlyLeaf=true returnRoot=false}*:*",
"{!graph from=$from to=$to traversalFilter=$traversalFilter returnOnlyLeaf=$returnOnlyLeaf returnRoot=$returnRoot}*:*");
assertQueryEquals("graph", req,
"{!graph from=node_s to=edge_s traversalFilter=foo returnOnlyLeaf=true returnRoot=false maxDepth=2}*:*",
"{!graph from=$from to=$to traversalFilter=$traversalFilter returnOnlyLeaf=$returnOnlyLeaf returnRoot=$returnRoot maxDepth=$maxDepth}*:*");
assertQueryEquals("graph", req,
"{!graph from=node_s to=edge_s traversalFilter=foo returnOnlyLeaf=true returnRoot=false maxDepth=2 useAutn=false}*:*",
"{!graph from=$from to=$to traversalFilter=$traversalFilter returnOnlyLeaf=$returnOnlyLeaf returnRoot=$returnRoot maxDepth=$maxDepth useAutn=$useAutn}*:*");
}
public void testQuerySurround() throws Exception {
assertQueryEquals("surround", "{!surround}and(apache,solr)",
"and(apache,solr)", "apache AND solr");
}
public void testQueryComplexPhrase() throws Exception {
assertQueryEquals("complexphrase", "{!complexphrase df=text}\"jo* smith\"",
"text:\"jo* smith\"");
assertQueryEquals("complexphrase", "{!complexphrase df=title}\"jo* smith\"",
"title:\"jo* smith\"");
}
public void testFuncTestfunc() throws Exception {
assertFuncEquals("testfunc(foo_i)","testfunc(field(foo_i))");
assertFuncEquals("testfunc(23)");
assertFuncEquals("testfunc(sum(23,foo_i))",
"testfunc(sum(23,field(foo_i)))");
}
public void testFuncOrd() throws Exception {
assertFuncEquals("ord(foo_s)","ord(foo_s )");
}
public void testFuncLiteral() throws Exception {
SolrQueryRequest req = req("someVar","a string");
try {
assertFuncEquals(req,
"literal('a string')","literal(\"a string\")",
"literal($someVar)");
} finally {
req.close();
}
}
public void testFuncRord() throws Exception {
assertFuncEquals("rord(foo_s)","rord(foo_s )");
}
public void testFuncCscore() throws Exception {
assertFuncEquals("cscore()", "cscore( )");
}
public void testFuncTop() throws Exception {
assertFuncEquals("top(sum(3,foo_i))");
}
public void testFuncLinear() throws Exception {
SolrQueryRequest req = req("someVar","27");
try {
assertFuncEquals(req,
"linear(foo_i,$someVar,42)",
"linear(foo_i, 27, 42)");
} finally {
req.close();
}
}
public void testFuncRecip() throws Exception {
SolrQueryRequest req = req("someVar","27");
try {
assertFuncEquals(req,
"recip(foo_i,$someVar,42, 27 )",
"recip(foo_i, 27, 42,$someVar)");
} finally {
req.close();
}
}
public void testFuncScale() throws Exception {
SolrQueryRequest req = req("someVar","27");
try {
assertFuncEquals(req,
"scale(field(foo_i),$someVar,42)",
"scale(foo_i, 27, 42)");
} finally {
req.close();
}
}
public void testFuncDiv() throws Exception {
assertFuncEquals("div(5,4)", "div(5, 4)");
assertFuncEquals("div(foo_i,4)", "div(foo_i, 4)",
"div(field('foo_i'), 4)");
assertFuncEquals("div(foo_i,sub(4,field('bar_i')))",
"div(field(foo_i), sub(4,bar_i))");
}
public void testFuncMod() throws Exception {
assertFuncEquals("mod(5,4)", "mod(5, 4)");
assertFuncEquals("mod(foo_i,4)", "mod(foo_i, 4)",
"mod(field('foo_i'), 4)");
assertFuncEquals("mod(foo_i,sub(4,field('bar_i')))",
"mod(field(foo_i), sub(4,bar_i))");
}
public void testFuncMap() throws Exception {
assertFuncEquals("map(field(foo_i), 0, 45, 100)",
"map(foo_i, 0.0, 45, 100)");
}
public void testFuncSum() throws Exception {
assertFuncEquals("sum(5,4)", "add(5, 4)");
assertFuncEquals("sum(5,4,3,2,1)", "add(5, 4, 3, 2, 1)");
assertFuncEquals("sum(foo_i,4)", "sum(foo_i, 4)",
"sum(field('foo_i'), 4)");
assertFuncEquals("add(foo_i,sub(4,field('bar_i')))",
"sum(field(foo_i), sub(4,bar_i))");
}
public void testFuncProduct() throws Exception {
assertFuncEquals("product(5,4,3,2,1)", "mul(5, 4, 3, 2, 1)");
assertFuncEquals("product(5,4)", "mul(5, 4)");
assertFuncEquals("product(foo_i,4)", "product(foo_i, 4)",
"product(field('foo_i'), 4)");
assertFuncEquals("mul(foo_i,sub(4,field('bar_i')))",
"product(field(foo_i), sub(4,bar_i))");
}
public void testFuncSub() throws Exception {
assertFuncEquals("sub(5,4)", "sub(5, 4)");
assertFuncEquals("sub(foo_i,4)", "sub(foo_i, 4)");
assertFuncEquals("sub(foo_i,sum(4,bar_i))", "sub(foo_i, sum(4,bar_i))");
}
public void testFuncVector() throws Exception {
assertFuncEquals("vector(5,4, field(foo_i))", "vector(5, 4, foo_i)");
assertFuncEquals("vector(foo_i,4)", "vector(foo_i, 4)");
assertFuncEquals("vector(foo_i,sum(4,bar_i))", "vector(foo_i, sum(4,bar_i))");
}
public void testFuncQuery() throws Exception {
SolrQueryRequest req = req("myQ","asdf");
try {
assertFuncEquals(req,
"query($myQ)",
"query($myQ,0)",
"query({!lucene v=$myQ},0)");
} finally {
req.close();
}
}
public void testFuncBoost() throws Exception {
SolrQueryRequest req = req("myQ","asdf");
try {
assertFuncEquals(req,
"boost($myQ,sum(4,5))",
"boost({!lucene v=$myQ},sum(4,5))");
} finally {
req.close();
}
}
public void testFuncJoindf() throws Exception {
assertFuncEquals("joindf(foo,bar)");
}
public void testFuncGeodist() throws Exception {
SolrQueryRequest req = req("pt","10.312,-20.556",
"sfield","store");
try {
assertFuncEquals(req,
"geodist()",
"geodist($sfield,$pt)",
"geodist(store,$pt)",
"geodist(field(store),$pt)",
"geodist(store,10.312,-20.556)");
} finally {
req.close();
}
}
public void testFuncHsin() throws Exception {
assertFuncEquals("hsin(45,true,0,0,45,45)");
}
public void testFuncGhhsin() throws Exception {
assertFuncEquals("ghhsin(45,point_hash,'asdf')",
"ghhsin(45,field(point_hash),'asdf')");
}
public void testFuncGeohash() throws Exception {
assertFuncEquals("geohash(45,99)");
}
public void testFuncDist() throws Exception {
assertFuncEquals("dist(2,45,99,101,111)",
"dist(2,vector(45,99),vector(101,111))");
}
public void testFuncSqedist() throws Exception {
assertFuncEquals("sqedist(45,99,101,111)",
"sqedist(vector(45,99),vector(101,111))");
}
public void testFuncMin() throws Exception {
assertFuncEquals("min(5,4,3,2,1)", "min(5, 4, 3, 2, 1)");
assertFuncEquals("min(foo_i,4)", "min(field('foo_i'), 4)");
assertFuncEquals("min(foo_i,sub(4,field('bar_i')))",
"min(field(foo_i), sub(4,bar_i))");
}
public void testFuncMax() throws Exception {
assertFuncEquals("max(5,4,3,2,1)", "max(5, 4, 3, 2, 1)");
assertFuncEquals("max(foo_i,4)", "max(field('foo_i'), 4)");
assertFuncEquals("max(foo_i,sub(4,field('bar_i')))",
"max(field(foo_i), sub(4,bar_i))");
}
public void testFuncMs() throws Exception {
// Note ms() takes in field name, not field(...)
assertFuncEquals("ms()", "ms(NOW)");
assertFuncEquals("ms(2000-01-01T00:00:00Z)",
"ms('2000-01-01T00:00:00Z')");
assertFuncEquals("ms(myDateField_dt)",
"ms('myDateField_dt')");
assertFuncEquals("ms(2000-01-01T00:00:00Z,myDateField_dt)",
"ms('2000-01-01T00:00:00Z','myDateField_dt')");
assertFuncEquals("ms(myDateField_dt, NOW)",
"ms('myDateField_dt', NOW)");
}
public void testFuncMathConsts() throws Exception {
assertFuncEquals("pi()");
assertFuncEquals("e()");
}
public void testFuncTerms() throws Exception {
SolrQueryRequest req = req("myField","field_t","myTerm","my term");
try {
for (final String type : new String[]{"docfreq","termfreq",
"totaltermfreq","ttf",
"idf","tf"}) {
// NOTE: these functions takes a field *name* not a field(..) source
assertFuncEquals(req,
type + "('field_t','my term')",
type + "(field_t,'my term')",
type + "(field_t,$myTerm)",
type + "(field_t,$myTerm)",
type + "($myField,$myTerm)");
}
// ttf is an alias for totaltermfreq
assertFuncEquals(req,
"ttf(field_t,'my term')", "ttf('field_t','my term')",
"totaltermfreq(field_t,'my term')");
} finally {
req.close();
}
}
public void testFuncSttf() throws Exception {
// sttf is an alias for sumtotaltermfreq
assertFuncEquals("sttf(foo_t)", "sttf('foo_t')",
"sumtotaltermfreq(foo_t)", "sumtotaltermfreq('foo_t')");
assertFuncEquals("sumtotaltermfreq('foo_t')");
}
public void testFuncNorm() throws Exception {
assertFuncEquals("norm(foo_t)","norm('foo_t')");
}
public void testFuncMaxdoc() throws Exception {
assertFuncEquals("maxdoc()");
}
public void testFuncNumdocs() throws Exception {
assertFuncEquals("numdocs()");
}
public void testFuncBools() throws Exception {
SolrQueryRequest req = req("myTrue","true","myFalse","false");
try {
assertFuncEquals(req, "true","$myTrue");
assertFuncEquals(req, "false","$myFalse");
} finally {
req.close();
}
}
public void testFuncExists() throws Exception {
SolrQueryRequest req = req("myField","field_t","myQ","asdf");
try {
assertFuncEquals(req,
"exists(field_t)",
"exists($myField)",
"exists(field('field_t'))",
"exists(field($myField))");
assertFuncEquals(req,
"exists(query($myQ))",
"exists(query({!lucene v=$myQ}))");
} finally {
req.close();
}
}
public void testFuncNot() throws Exception {
SolrQueryRequest req = req("myField","field_b", "myTrue","true");
try {
assertFuncEquals(req, "not(true)", "not($myTrue)");
assertFuncEquals(req, "not(not(true))", "not(not($myTrue))");
assertFuncEquals(req,
"not(field_b)",
"not($myField)",
"not(field('field_b'))",
"not(field($myField))");
assertFuncEquals(req,
"not(exists(field_b))",
"not(exists($myField))",
"not(exists(field('field_b')))",
"not(exists(field($myField)))");
} finally {
req.close();
}
}
public void testFuncDoubleValueBools() throws Exception {
SolrQueryRequest req = req("myField","field_b","myTrue","true");
try {
for (final String type : new String[]{"and","or","xor"}) {
assertFuncEquals(req,
type + "(field_b,true)",
type + "(field_b,$myTrue)",
type + "(field('field_b'),true)",
type + "(field($myField),$myTrue)",
type + "($myField,$myTrue)");
}
} finally {
req.close();
}
}
public void testFuncIf() throws Exception {
SolrQueryRequest req = req("myBoolField","foo_b",
"myIntField","bar_i",
"myTrue","true");
try {
assertFuncEquals(req,
"if(foo_b,bar_i,25)",
"if($myBoolField,bar_i,25)",
"if(field('foo_b'),$myIntField,25)",
"if(field($myBoolField),field('bar_i'),25)");
assertFuncEquals(req,
"if(true,37,field($myIntField))",
"if($myTrue,37,$myIntField)");
} finally {
req.close();
}
}
public void testFuncDef() throws Exception {
SolrQueryRequest req = req("myField","bar_f");
try {
assertFuncEquals(req,
"def(bar_f,25)",
"def($myField,25)",
"def(field('bar_f'),25)");
assertFuncEquals(req,
"def(ceil(bar_f),25)",
"def(ceil($myField),25)",
"def(ceil(field('bar_f')),25)");
} finally {
req.close();
}
}
public void testFuncConcat() throws Exception {
SolrQueryRequest req = req("myField","bar_f","myOtherField","bar_t");
try {
assertFuncEquals(req,
"concat(bar_f,bar_t)",
"concat($myField,bar_t)",
"concat(bar_f,$myOtherField)",
"concat($myField,$myOtherField)");
} finally {
req.close();
}
}
public void testFuncSingleValueMathFuncs() throws Exception {
SolrQueryRequest req = req("myVal","45", "myField","foo_i");
for (final String func : new String[] {"abs","rad","deg","sqrt","cbrt",
"log","ln","exp","sin","cos","tan",
"asin","acos","atan",
"sinh","cosh","tanh",
"ceil","floor","rint"}) {
try {
assertFuncEquals(req,
func + "(field(foo_i))", func + "(foo_i)",
func + "($myField)");
assertFuncEquals(req, func + "(45)", func+ "($myVal)");
} finally {
req.close();
}
}
}
public void testFuncDoubleValueMathFuncs() throws Exception {
SolrQueryRequest req = req("myVal","45", "myOtherVal", "27",
"myField","foo_i");
for (final String func : new String[] {"pow","hypot","atan2"}) {
try {
assertFuncEquals(req,
func + "(field(foo_i),$myVal)", func+"(foo_i,$myVal)",
func + "($myField,45)");
assertFuncEquals(req,
func+"(45,$myOtherVal)", func+"($myVal,27)",
func+"($myVal,$myOtherVal)");
} finally {
req.close();
}
}
}
public void testFuncStrdist() throws Exception {
SolrQueryRequest req = req("myVal","zot", "myOtherVal", "yak",
"myField","foo_s1");
try {
assertFuncEquals(req,
"strdist(\"zot\",literal('yak'),edit)",
"strdist(literal(\"zot\"),'yak', edit )",
"strdist(literal($myVal),literal($myOtherVal),edit)");
assertFuncEquals(req,
"strdist(\"zot\",literal($myOtherVal),ngram)",
"strdist(\"zot\",'yak', ngram, 2)");
assertFuncEquals(req,
"strdist(field('foo_s1'),literal($myOtherVal),jw)",
"strdist(field($myField),\"yak\",jw)",
"strdist($myField,'yak', jw)");
} finally {
req.close();
}
}
public void testFuncField() throws Exception {
assertFuncEquals("field(\"foo_i\")",
"field('foo_i\')",
"foo_i");
// simple VS of single valued field should be same as asking for min/max on that field
assertFuncEquals("field(\"foo_i\")",
"field('foo_i',min)",
"field(foo_i,'min')",
"field('foo_i',max)",
"field(foo_i,'max')",
"foo_i");
// multivalued field with selector
String multif = "multi_int_with_docvals";
SolrQueryRequest req = req("my_field", multif);
// this test is only viable if it's a multivalued field, sanity check the schema
assertTrue(multif + " is no longer multivalued, who broke this schema?",
req.getSchema().getField(multif).multiValued());
assertFuncEquals(req,
"field($my_field,'MIN')",
"field('"+multif+"',min)");
assertFuncEquals(req,
"field($my_field,'max')",
"field('"+multif+"',Max)");
}
public void testFuncCurrency() throws Exception {
assertFuncEquals("currency(\"amount\")",
"currency('amount\')",
"currency(amount)",
"currency(amount,USD)",
"currency('amount',USD)");
}
public void testFuncRelatedness() throws Exception {
SolrQueryRequest req = req("fore","foo_s:front", "back","foo_s:back");
try {
assertFuncEquals(req,
"agg_relatedness({!query v='foo_s:front'}, {!query v='foo_s:back'})",
"agg_relatedness($fore, $back)");
} finally {
req.close();
}
}
public void testTestFuncs() throws Exception {
assertFuncEquals("sleep(1,5)", "sleep(1,5)");
assertFuncEquals("threadid()", "threadid()");
}
// TODO: more tests
public void testQueryMaxScore() throws Exception {
assertQueryEquals("maxscore", "{!maxscore}A OR B OR C",
"A OR B OR C");
assertQueryEquals("maxscore", "{!maxscore}A AND B",
"A AND B");
assertQueryEquals("maxscore", "{!maxscore}apache -solr",
"apache -solr", "apache -solr ");
assertQueryEquals("maxscore", "+apache +solr", "apache AND solr",
"+apache +solr");
}
/**
* this test does not assert anything itself, it simply toggles a static
* boolean informing an @AfterClass method to assert that every default
* qparser and valuesource parser configured was recorded by
* assertQueryEquals and assertFuncEquals.
*/
public void testParserCoverage() {
doAssertParserCoverage = true;
}
public void testQuerySimple() throws Exception {
SolrQueryRequest req = req("myField","foo_s");
try {
assertQueryEquals("simple", req,
"{!simple f=$myField}asdf",
"{!simple f=$myField v=asdf}",
"{!simple f=foo_s}asdf");
} finally {
req.close();
}
}
public void testQueryMLT() throws Exception {
assertU(adoc("id", "1", "lowerfilt", "sample data"));
assertU(commit());
try {
assertQueryEquals("mlt", "{!mlt qf=lowerfilt}1",
"{!mlt qf=lowerfilt v=1}");
} finally {
delQ("*:*");
assertU(commit());
}
}
/**
* NOTE: defType is not only used to pick the parser, but also to record
* the parser being tested for coverage sanity checking
* @see #testParserCoverage
* @see #assertQueryEquals
*/
protected void assertQueryEquals(final String defType,
final String... inputs) throws Exception {
SolrQueryRequest req = req(new String[] {"df", "text"});
try {
assertQueryEquals(defType, req, inputs);
} finally {
req.close();
}
}
/**
* NOTE: defType is not only used to pick the parser, but, if non-null it is
* also to record the parser being tested for coverage sanity checking
*
* @see QueryUtils#check
* @see QueryUtils#checkEqual
* @see #testParserCoverage
*/
protected void assertQueryEquals(final String defType,
final SolrQueryRequest req,
final String... inputs) throws Exception {
if (null != defType) qParsersTested.add(defType);
final Query[] queries = new Query[inputs.length];
try {
SolrQueryResponse rsp = new SolrQueryResponse();
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req,rsp));
for (int i = 0; i < inputs.length; i++) {
queries[i] = QParser.getParser(inputs[i], defType, true, req).getQuery();
}
} finally {
SolrRequestInfo.clearRequestInfo();
}
for (int i = 0; i < queries.length; i++) {
QueryUtils.check(queries[i]);
// yes starting j=0 is redundent, we're making sure every query
// is equal to itself, and that the quality checks work regardless
// of which caller/callee is used.
for (int j = 0; j < queries.length; j++) {
QueryUtils.checkEqual(queries[i], queries[j]);
}
}
}
/**
* the function name for val parser coverage checking is extracted from
* the first input
* @see #assertQueryEquals
* @see #testParserCoverage
*/
protected void assertFuncEquals(final String... inputs) throws Exception {
SolrQueryRequest req = req();
try {
assertFuncEquals(req, inputs);
} finally {
req.close();
}
}
/**
* the function name for val parser coverage checking is extracted from
* the first input
* @see #assertQueryEquals
* @see #testParserCoverage
*/
protected void assertFuncEquals(final SolrQueryRequest req,
final String... inputs) throws Exception {
// pull out the function name
final String funcName = (new StrParser(inputs[0])).getId();
valParsersTested.add(funcName);
assertQueryEquals(FunctionQParserPlugin.NAME, req, inputs);
}
public void testAggs() throws Exception {
assertFuncEquals("agg(avg(foo_i))", "agg(avg(foo_i))");
assertFuncEquals("agg(avg(foo_i))", "agg_avg(foo_i)");
assertFuncEquals("agg_min(foo_i)", "agg(min(foo_i))");
assertFuncEquals("agg_max(foo_i)", "agg(max(foo_i))");
assertFuncEquals("agg_avg(foo_i)", "agg_avg(foo_i)");
assertFuncEquals("agg_sum(foo_i)", "agg_sum(foo_i)");
assertFuncEquals("agg_count()", "agg_count()");
assertFuncEquals("agg_unique(foo_i)", "agg_unique(foo_i)");
assertFuncEquals("agg_uniqueBlock(foo_i)", "agg_uniqueBlock(foo_i)");
assertFuncEquals("agg_hll(foo_i)", "agg_hll(foo_i)");
assertFuncEquals("agg_sumsq(foo_i)", "agg_sumsq(foo_i)");
assertFuncEquals("agg_percentile(foo_i,50)", "agg_percentile(foo_i,50)");
assertFuncEquals("agg_variance(foo_i)", "agg_variance(foo_i)");
assertFuncEquals("agg_stddev(foo_i)", "agg_stddev(foo_i)");
assertFuncEquals("agg_missing(foo_i)", "agg_missing(foo_i)");
assertFuncEquals("agg(missing(foo_i))", "agg(missing(foo_i))");
assertFuncEquals("agg_missing(field(foo_i))", "agg_missing(field(foo_i))");
assertFuncEquals("agg_countvals(foo_i)", "agg_countvals(foo_i)");
assertFuncEquals("agg(countvals(foo_i))", "agg(countvals(foo_i))");
assertFuncEquals("agg_countvals(field(foo_i))", "agg_countvals(field(foo_i))");
// assertFuncEquals("agg_multistat(foo_i)", "agg_multistat(foo_i)");
}
public void testCompares() throws Exception {
assertFuncEquals("gt(foo_i,2)", "gt(foo_i, 2)");
assertFuncEquals("gt(foo_i,2)", "gt(foo_i,2)");
assertFuncEquals("lt(foo_i,2)", "lt(foo_i,2)");
assertFuncEquals("lte(foo_i,2)", "lte(foo_i,2)");
assertFuncEquals("gte(foo_i,2)", "gte(foo_i,2)");
assertFuncEquals("eq(foo_i,2)", "eq(foo_i,2)");
expectThrows(AssertionError.class, "expected error, functions are not equal",
() -> assertFuncEquals("eq(foo_i,2)", "lt(foo_i,2)"));
}
public void testChildField() throws Exception {
final SolrQueryRequest req = req("q", "{!parent which=type_s1:parent}whatever_s1:foo");
try {
assertFuncEquals(req,
"childfield(name_s1,$q)", "childfield(name_s1,$q)");
} finally {
req.close();
}
}
public void testPayloadScoreQuery() throws Exception {
// There was a bug with PayloadScoreQuery's .equals() method that said two queries were equal with different includeSpanScore settings
expectThrows(AssertionFailedError.class, "queries should not have been equal",
() -> assertQueryEquals
("payload_score"
, "{!payload_score f=foo_dpf v=query func=min includeSpanScore=false}"
, "{!payload_score f=foo_dpf v=query func=min includeSpanScore=true}"
)
);
}
public void testPayloadCheckQuery() throws Exception {
expectThrows(AssertionFailedError.class, "queries should not have been equal",
() -> assertQueryEquals
("payload_check"
, "{!payload_check f=foo_dpf payloads=2}one"
, "{!payload_check f=foo_dpf payloads=2}two"
)
);
}
public void testPayloadFunction() throws Exception {
SolrQueryRequest req = req("myField","bar_f");
try {
assertFuncEquals(req,
"payload(foo_dpf,some_term)",
"payload(foo_dpf,some_term)");
} finally {
req.close();
}
}
public void testBoolQuery() throws Exception {
assertQueryEquals("bool",
"{!bool must='{!lucene}foo_s:a' must='{!lucene}foo_s:b'}",
"{!bool must='{!lucene}foo_s:b' must='{!lucene}foo_s:a'}");
assertQueryEquals("bool",
"{!bool must_not='{!lucene}foo_s:a' should='{!lucene}foo_s:b' " +
"must='{!lucene}foo_s:c' filter='{!lucene}foo_s:d' filter='{!lucene}foo_s:e'}",
"{!bool must='{!lucene}foo_s:c' filter='{!lucene}foo_s:d' " +
"must_not='{!lucene}foo_s:a' should='{!lucene}foo_s:b' filter='{!lucene}foo_s:e'}");
expectThrows(AssertionFailedError.class, "queries should not have been equal",
() -> assertQueryEquals
("bool"
, "{!bool must='{!lucene}foo_s:a'}"
, "{!bool should='{!lucene}foo_s:a'}"
)
);
}
public void testHashRangeQuery() throws Exception {
assertQueryEquals("hash_range",
"{!hash_range f=x_id l=107347968 u=214695935}",
"{!hash_range l='107347968' u='214695935' f='x_id'}");
}
// Override req to add df param
public static SolrQueryRequest req(String... q) {
return SolrTestCaseJ4.req(q, "df", "text");
}
}