blob: 653c0935879191aa1f3b72adbce2254694145627 [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 org.apache.lucene.search.Query;
import org.apache.lucene.tests.search.QueryUtils;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrInputDocument;
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 "primitive" types except for doubles/floats, 'foo:*' should be functionally
// equivalent 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 defaults to 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=xy}",
"{!" + type + " sfield=xy d=109}",
"{!" + type + " sfield=xy d=$d pt=$pt}",
"{!" + type + " sfield=xy 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());
}
// check multiple ways of specifying _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 otherwise 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 substitution 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 testFuncKnnVector() throws Exception {
try (SolrQueryRequest req =
req(
"v1", "[1,2,3]",
"v2", " [1,2,3] ",
"v3", " [1, 2, 3] ")) {
assertFuncEquals(
req,
"vectorSimilarity(FLOAT32,COSINE,[1,2,3],[4,5,6])",
"vectorSimilarity(FLOAT32, COSINE, [1, 2, 3], [4, 5, 6])",
"vectorSimilarity(FLOAT32, COSINE,$v1, [4, 5, 6])",
"vectorSimilarity(FLOAT32, COSINE, $v2 , [4, 5, 6])",
"vectorSimilarity(FLOAT32, COSINE, $v3 , [4, 5, 6])");
}
try (SolrQueryRequest req =
req(
"f1", "bar_i",
"f2", " bar_i ",
"f3", " field(bar_i) ")) {
assertFuncEquals(
req,
"vectorSimilarity(BYTE, EUCLIDEAN, bar_i, [4,5,6])",
"vectorSimilarity(BYTE, EUCLIDEAN, field(bar_i), [4, 5, 6])",
"vectorSimilarity(BYTE, EUCLIDEAN,$f1, [4, 5, 6])",
"vectorSimilarity(BYTE, EUCLIDEAN, $f1, [4, 5, 6])",
"vectorSimilarity(BYTE, EUCLIDEAN, $f2, [4, 5, 6])",
"vectorSimilarity(BYTE, EUCLIDEAN, $f3, [4, 5, 6])");
}
try (SolrQueryRequest req =
req(
"f", "vector",
"v1", "[1,2,3,4]",
"v2", " [1, 2, 3, 4]")) {
assertFuncEquals(
req,
"vectorSimilarity(FLOAT32,COSINE,vector,[1,2,3,4])",
"vectorSimilarity(FLOAT32,COSINE,vector,$v1)",
"vectorSimilarity(FLOAT32,COSINE,vector, $v1)",
"vectorSimilarity(FLOAT32,COSINE,vector,$v2)",
"vectorSimilarity(FLOAT32,COSINE,vector, $v2)",
"vectorSimilarity(vector,[1,2,3,4])",
"vectorSimilarity( vector,[1,2,3,4])",
"vectorSimilarity( $f,[1,2,3,4])",
"vectorSimilarity(vector,$v1)",
"vectorSimilarity(vector, $v1)",
"vectorSimilarity( $f, $v1)",
"vectorSimilarity(vector,$v2)",
"vectorSimilarity(vector, $v2)");
}
// contrived, but helps us test the param resolution
// for both field names in the 2arg usecase
try (SolrQueryRequest req = req("f", "vector")) {
assertFuncEquals(
req,
"vectorSimilarity($f, $f)",
"vectorSimilarity($f, vector)",
"vectorSimilarity(vector, $f)",
"vectorSimilarity(vector, vector)");
}
}
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 {
String pt = "10.312,-20.556";
try (SolrQueryRequest req = req("pt", pt, "sfield", "store")) {
assertFuncEquals(
req, "geodist($pt)", "geodist(" + pt + ")", "geodist(" + pt + "," + pt + ")");
assertFuncEquals(req, "geodist()");
// geodist() does not support field names in its arguments sometimes
// "geodist(store,$pt)",
// "geodist(field(store),$pt)",
}
}
public void testFuncHsin() throws Exception {
assertFuncEquals("hsin(45,true,0,0,45,45)");
}
public void testFuncGhhsin() throws Exception {
assertFuncEquals(
"ghhsin(45,id,'asdf')",
"ghhsin(45,field(id),'asdf')"); // "id" is just a single-valued string field
}
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 testFuncIsnan() throws Exception {
SolrQueryRequest req = req("num", "12.3456", "zero", "0");
try {
assertFuncEquals(req, "isnan(12.3456)", "isnan(12.3456)", "isnan($num)");
assertFuncEquals(req, "isnan(div(0,0))", "isnan(div($zero,$zero))");
} 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());
}
}
public void testQueryMLTContent() throws Exception {
assertU(adoc("id", "1", "lowerfilt", "sample data", "standardfilt", "sample data"));
assertU(commit());
try {
assertQueryEquals(
"mlt_content",
"{!mlt_content qf=lowerfilt mindf=0 mintf=0}sample data",
"{!mlt_content qf=lowerfilt mindf=0 mintf=0 v='sample data'}",
"{!qf=lowerfilt mindf=0 mintf=0}sample data");
SolrQueryRequest req = req(new String[] {"df", "text"});
try {
QueryUtils.checkUnequal(
QParser.getParser("{!mlt_content qf=lowerfilt mindf=0 mintf=0}sample data", req)
.getQuery(),
QParser.getParser(
"{!mlt_content qf=lowerfilt qf=standardfilt mindf=0 mintf=0}sample data", req)
.getQuery());
} finally {
req.close();
}
} finally {
delQ("*:*");
assertU(commit());
}
}
public void testQueryKNN() throws Exception {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", "0");
doc.addField("vector", Arrays.asList(1, 2, 3, 4));
assertU(adoc(doc));
assertU(commit());
final String qvec = "[1.0,2.0,3.0,4.0]";
try (SolrQueryRequest req0 = req()) {
// no filters
final Query fqNull =
assertQueryEqualsAndReturn(
"knn",
req0,
"{!knn f=vector}" + qvec,
"{!knn f=vector preFilter=''}" + qvec,
"{!knn f=vector v=" + qvec + "}");
try (SolrQueryRequest req1 = req("fq", "{!tag=t1}id:1", "xxx", "id:1")) {
// either global fq, or (same) preFilter as localparam
final Query fqOne =
assertQueryEqualsAndReturn(
"knn",
req1,
"{!knn f=vector}" + qvec,
"{!knn f=vector includeTags=t1}" + qvec,
"{!knn f=vector preFilter='id:1'}" + qvec,
"{!knn f=vector preFilter=$xxx}" + qvec,
"{!knn f=vector v=" + qvec + "}");
QueryUtils.checkUnequal(fqNull, fqOne);
try (SolrQueryRequest req2 = req("fq", "{!tag=t2}id:2", "xxx", "id:1", "yyy", "")) {
// override global fq with local param to use different preFilter
final Query fqOneOverride =
assertQueryEqualsAndReturn(
"knn",
req2,
"{!knn f=vector preFilter='id:1'}" + qvec,
"{!knn f=vector preFilter=$xxx}" + qvec);
QueryUtils.checkEqual(fqOne, fqOneOverride);
// override global fq with local param to use no preFilters
final Query fqNullOverride =
assertQueryEqualsAndReturn(
"knn",
req2,
"{!knn f=vector preFilter=''}" + qvec,
"{!knn f=vector excludeTags=t2}" + qvec,
"{!knn f=vector preFilter=$yyy}" + qvec);
QueryUtils.checkEqual(fqNull, fqNullOverride);
}
}
try (SolrQueryRequest reqPostFilter = req("fq", "{!tag=post frange cache=false l=0}9.9")) {
// global post-filter fq should always be ignored
final Query fqPostFilter =
assertQueryEqualsAndReturn(
"knn",
reqPostFilter,
"{!knn f=vector}" + qvec,
"{!knn f=vector includeTags=post}" + qvec);
QueryUtils.checkEqual(fqNull, fqPostFilter);
}
} finally {
delQ("id:0");
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 #assertQueryEqualsAndReturn
*/
protected void assertQueryEquals(final String defType, final String... inputs) throws Exception {
SolrQueryRequest req = req(new String[] {"df", "text"});
try {
assertQueryEqualsAndReturn(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 #testParserCoverage
* @see #assertQueryEqualsAndReturn
*/
protected void assertQueryEquals(
final String defType, final SolrQueryRequest req, final String... inputs) throws Exception {
assertQueryEqualsAndReturn(defType, req, inputs);
}
/**
* Parses a set of input strings in the context of a request, making various assertions about the
* resulting Query objects, including that they must all be equals.
*
* <p>Returns one of the (all equal) Query objects so it may be used in other comparisons with
* other Query objects, possibly parsed in the context of different requests.
*
* <p>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 Query assertQueryEqualsAndReturn(
final String defType, final SolrQueryRequest req, final String... inputs) throws Exception {
assertTrue(
"At least one input string for parsing must be passed to this method", 0 < inputs.length);
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 (Query query1 : queries) {
QueryUtils.check(query1);
// 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 (Query query2 : queries) {
QueryUtils.checkEqual(query1, query2);
}
}
return queries[0];
}
/**
* 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() {
// There was a bug with PayloadScoreQuery's .equals() method that said two queries were equal
// with different includeSpanScore settings
expectThrows(
AssertionError.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() {
expectThrows(
AssertionError.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 testBoolMmQuery() throws Exception {
assertQueryEquals(
"lucene",
"{!bool should=foo_s:a should=foo_s:b}",
"{!bool should=foo_s:a should=foo_s:b mm=0}");
assertQueryEquals(
"lucene",
"{!bool should=foo_s:a should=foo_s:b mm=1}",
"{!bool should=foo_s:a should=foo_s:b mm=1}");
expectThrows(
AssertionError.class,
"queries should not have been equal",
() ->
assertQueryEquals(
"lucene",
"{!bool should=foo_s:a should=foo_s:b mm=1}",
"{!bool should=foo_s:a should=foo_s:b}"));
}
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(
AssertionError.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");
}
}