| /* |
| * 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 org.apache.solr.common.SolrException; |
| import org.apache.solr.common.params.CommonParams; |
| import org.apache.solr.SolrTestCaseJ4; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| public class TestQueryTypes extends SolrTestCaseJ4 { |
| |
| @BeforeClass |
| public static void beforeClass() throws Exception { |
| initCore("solrconfig.xml", "schema11.xml"); |
| } |
| |
| public String getCoreName() { return "basic"; } |
| |
| public void testQueryTypes() { |
| assertU(adoc("id","0")); |
| assertU(adoc("id","1", "v_t","Hello Dude")); |
| assertU(adoc("id","2", "v_t","Hello Yonik")); |
| assertU(adoc("id","3", "v_s","{!literal}")); |
| assertU(adoc("id","4", "v_s","other stuff")); |
| assertU(adoc("id","5", "v_f","3.14159")); |
| assertU(adoc("id","6", "v_f","8983")); |
| assertU(adoc("id","7", "v_f","1.5")); |
| assertU(adoc("id","8", "v_ti","5")); |
| assertU(adoc("id","9", "v_s","internal\"quote")); |
| assertU(adoc("id","10","text_no_analyzer","should just work")); |
| |
| |
| assertU(adoc("id", "200", "subject_t", "Sony Netzteil")); |
| assertU(adoc("id", "201", "subject_t", "Other Netzteil")); |
| assertU(adoc("id", "202", "subject_t", "Other Product")); |
| |
| Object[] arr = new Object[] { |
| "id",999 |
| ,"v_s","wow dude" |
| ,"v_t","wow" |
| ,"v_ti",-1 |
| ,"v_tis",-1 |
| ,"v_tl",-1234567891234567890L |
| ,"v_tls",-1234567891234567890L |
| ,"v_tf",-2.0f |
| ,"v_tfs",-2.0f |
| ,"v_td",-2.0 |
| ,"v_tds",-2.0 |
| ,"v_tdt","2000-05-10T01:01:01Z" |
| ,"v_tdts","2002-08-26T01:01:01Z" |
| }; |
| String[] sarr = new String[arr.length]; |
| for (int i=0; i<arr.length; i++) { |
| sarr[i] = arr[i].toString(); |
| } |
| |
| assertU(adoc(sarr)); |
| assertU(optimize()); |
| |
| // test field queries |
| for (int i=0; i<arr.length; i+=2) { |
| String f = arr[i].toString(); |
| String v = arr[i+1].toString(); |
| |
| // normal lucene fielded query |
| assertQ(req( "q",f+":\""+v+'"') |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='999']" |
| ,"//*[@name='" + f + "'][.='" + v + "']" |
| ); |
| |
| // field qparser |
| assertQ(req( "q", "{!field f="+f+"}"+v) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // term qparser |
| assertQ(req( "q", "{!term f="+f+"}"+v) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // terms qparser |
| //wrap in spaces sometimes if space separated |
| final String separator = f == "v_s" ? "" : "separator=' '";//use space separated when field isn't v_s |
| String vMod = separator != "" && random().nextBoolean() ? " " + v + " " : v; |
| assertQ(req( "q", "{!terms " + separator + " f=" +f+"}"+vMod) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // lucene range |
| assertQ(req( "q", f + ":[\"" + v + "\" TO \"" + v + "\"]" ) |
| ,"//result[@numFound='1']" |
| ); |
| } |
| |
| // terms qparser, no values matches nothing |
| assertQ(req( "q", "*:*", "fq", "{!terms f=v_s}") |
| ,"//result[@numFound='0']" |
| ); |
| |
| String termsMethod = new String[]{"termsFilter", "booleanQuery", "automaton", "docValuesTermsFilter"}[random().nextInt(4)]; |
| assertQ(req( "q", "{!terms f=v_s method=" + termsMethod + " }wow dude,other stuff")//terms reverse sorted to show this works |
| ,"//result[@numFound='2']" |
| ); |
| |
| |
| // frange and function query only work on single valued field types |
| Object[] fc_vals = new Object[] { |
| "id_i",999 |
| ,"v_s","wow dude" |
| ,"v_ti",-1 |
| ,"v_tl",-1234567891234567890L |
| ,"v_tf",-2.0f |
| ,"v_td",-2.0 |
| ,"v_tdt","2000-05-10T01:01:01Z" |
| }; |
| |
| for (int i=0; i<fc_vals.length; i+=2) { |
| String f = fc_vals[i].toString(); |
| String v = fc_vals[i+1].toString(); |
| |
| // frange qparser |
| assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" ) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // frange as filter not cached |
| assertQ(req( "q","*:*", "fq", "{!frange cache=false v="+f+" l='"+v+"' u='"+v+"'}" ) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // frange as filter run after the main query |
| assertQ(req( "q","*:*", "fq", "{!frange cache=false cost=100 v="+f+" l='"+v+"' u='"+v+"'}" ) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // exists() |
| assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" ) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // boolean value of non-zero values (just leave off the exists from the prev test) |
| assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if("+f+",1,0)" ) |
| ,"//result[@numFound='1']" |
| ); |
| |
| if (!"id_i".equals(f)) { |
| assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" ) |
| ,"//result[@numFound='0']" |
| ); |
| |
| // boolean value of zero/missing values (just leave off the exists from the prev test) |
| assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if("+f+",1,0)" ) |
| ,"//result[@numFound='0']" |
| ); |
| |
| } |
| |
| // function query... just make sure it doesn't throw an exception |
| if ("v_s".equals(f)) continue; // in this context, functions must be able to be interpreted as a float |
| assertQ(req( "q", "+id:999 _val_:\"" + f + "\"") |
| ,"//result[@numFound='1']" |
| ); |
| } |
| |
| // Some basic tests to ensure that parsing local params is working |
| assertQ("test prefix query", |
| req("q","{!prefix f=v_t}hel") |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test raw query", |
| req("q","{!raw f=v_t}hello") |
| ,"//result[@numFound='2']" |
| ); |
| |
| // no analysis is done, so these should match nothing |
| assertQ("test raw query", |
| req("q","{!raw f=v_t}Hello") |
| ,"//result[@numFound='0']" |
| ); |
| assertQ("test raw query", |
| req("q","{!raw f=v_f}1.5") |
| ,"//result[@numFound='0']" |
| ); |
| |
| // test "term" qparser, which should only do readableToIndexed |
| assertQ( |
| req("q","{!term f=v_f}1.5") |
| ,"//result[@numFound='1']" |
| ); |
| |
| // text fields are *not* analyzed since they may not be idempotent |
| assertQ( |
| req("q","{!term f=v_t}Hello") |
| ,"//result[@numFound='0']" |
| ); |
| assertQ( |
| req("q","{!term f=v_t}hello") |
| ,"//result[@numFound='2']" |
| ); |
| |
| // |
| // test escapes in quoted strings |
| // |
| |
| // the control... unescaped queries looking for internal"quote |
| assertQ(req("q","{!raw f=v_s}internal\"quote") |
| ,"//result[@numFound='1']" |
| ); |
| |
| // test that single quoted string needs no escape |
| assertQ(req("q","{!raw f=v_s v='internal\"quote'}") |
| ,"//result[@numFound='1']" |
| ); |
| |
| // but it's OK if the escape is done |
| assertQ(req("q","{!raw f=v_s v='internal\\\"quote'}") |
| ,"//result[@numFound='1']" |
| ); |
| |
| // test unicode escape |
| assertQ(req("q","{!raw f=v_s v=\"internal\\u0022quote\"}") |
| ,"//result[@numFound='1']" |
| ); |
| |
| // inside a quoted string, internal"quote needs to be escaped |
| assertQ(req("q","{!raw f=v_s v=\"internal\\\"quote\"}") |
| ,"//result[@numFound='1']" |
| ); |
| |
| assertQ("test custom plugin query", |
| req("q","{!foo f=v_t}hello") |
| ,"//result[@numFound='2']" |
| ); |
| |
| |
| assertQ("test single term field query on text type", |
| req("q","{!field f=v_t}HELLO") |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test single term field query on type with diff internal rep", |
| req("q","{!field f=v_f}1.5") |
| ,"//result[@numFound='1']" |
| ); |
| |
| assertQ( |
| req("q","{!field f=v_ti}5") |
| ,"//result[@numFound='1']" |
| ); |
| |
| assertQ("test multi term field query on text type", |
| req("q","{!field f=v_t}Hello DUDE") |
| ,"//result[@numFound='1']" |
| ); |
| |
| |
| assertQ("test prefix query with value in local params", |
| req("q","{!prefix f=v_t v=hel}") |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test optional quotes", |
| req("q","{!prefix f='v_t' v=\"hel\"}") |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test extra whitespace", |
| req("q","{!prefix f=v_t v=hel }") |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test literal with {! in it", |
| req("q","{!prefix f=v_s}{!lit") |
| ,"//result[@numFound='1']" |
| ); |
| |
| assertQ("test param subst", |
| req("q","{!prefix f=$myf v=$my.v}" |
| ,"myf","v_t", "my.v", "hel" |
| ) |
| ,"//result[@numFound='2']" |
| ); |
| |
| // test wacky param names |
| assertQ( |
| req("q","{!prefix f=$a/b/c v=$'a b/c'}" |
| ,"a/b/c","v_t", "a b/c", "hel" |
| ) |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test param subst with literal", |
| req("q","{!prefix f=$myf v=$my.v}" |
| ,"myf","v_s", "my.v", "{!lit" |
| ) |
| ,"//result[@numFound='1']" |
| ); |
| |
| // lucene queries |
| assertQ("test lucene query", |
| req("q","{!lucene}v_t:hel*") |
| ,"//result[@numFound='2']" |
| ); |
| |
| // lucene queries |
| assertQ("test lucene default field", |
| req("q","{!df=v_t}hel*") |
| ,"//result[@numFound='2']" |
| ); |
| |
| // lucene operator |
| assertQ("test lucene operator", |
| req("q","{!q.op=OR df=v_t}Hello Yonik") |
| ,"//result[@numFound='2']" |
| ); |
| assertQ("test lucene operator", |
| req("q","{!q.op=AND df=v_t}Hello Yonik") |
| ,"//result[@numFound='1']" |
| ); |
| |
| // test boost queries |
| assertQ("test boost", |
| req("q","{!boost b=sum(v_f,1)}id:[5 TO 6]" |
| ,"fl","*,score" |
| ) |
| ,"//result[@numFound='2']" |
| ,"//doc[./float[@name='v_f']='3.14159' and ./float[@name='score']='4.14159']" |
| ); |
| |
| assertQ("test boost and default type of func", |
| req("q","{!boost v=$q1 b=$q2}" |
| ,"q1", "{!func}v_f", "q2","v_f" |
| ,"fl","*,score" |
| ) |
| ,"//doc[./float[@name='v_f']='1.5' and ./float[@name='score']='2.25']" |
| ); |
| |
| // multiplicative boosts combine correctly |
| assertQ( |
| req("q", "{!boost b=$ymb}(+{!lucene v=$yq})", |
| "ymb", "product(query({!v=subject_t:Netzteil^=2.0},1),query({!v=subject_t:Sony^=3.0},1))", |
| "yq", "subject_t:*", |
| "fl", "*,score", "indent", "on"), |
| "//doc[str[@name='id'][.='200'] and float[@name='score'][.=6.0]]", |
| "//doc[str[@name='id'][.='202'] and float[@name='score'][.=1.0]]", |
| "//doc[str[@name='id'][.='201'] and float[@name='score'][.=2.0]]" |
| ); |
| |
| // switch queries |
| assertQ("test matching switch query", |
| req("df", "v_t", |
| "q", "{!switch case.x=Dude case.z=Yonik} x ") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='1']"); |
| assertQ("test empty matching switch query", |
| req("df", "v_t", |
| "q", "{!switch case.x=Dude case=Yonik} ") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='2']"); |
| assertQ("test empty matching switch query", |
| req("df", "v_t", |
| "q", "{!switch case.x=Dude case=Yonik v=''}") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='2']"); |
| assertQ("test empty matching switch query", |
| req("df", "v_t", |
| "q", "{!switch case.x=Dude case=Yonik v=$qq}") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='2']"); |
| assertQ("test matching switch query w/deref", |
| req("q", "{!switch case.x=$d case.z=Yonik} x ", |
| "df", "v_t", |
| "d", "Dude") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='1']"); |
| assertQ("test default switch query", |
| req("q", "{!switch default=$d case.x=$d case.z=Yonik}asdf", |
| "df", "v_t", |
| "d", "Dude") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='1']"); |
| assertQ("test empty default switch query", |
| req("q", "{!switch default=$d case.x=$d case.z=Yonik v=$qq}", |
| "df", "v_t", |
| "d", "Dude") |
| ,"//result[@numFound='1']" |
| ,"//*[@name='id'][.='1']"); |
| |
| try { |
| ignoreException("No\\ default\\, and no switch case"); |
| RuntimeException exp = expectThrows(RuntimeException.class, "Should have gotten an error w/o default", |
| () -> assertQ("no match and no default", |
| req("q", "{!switch case.x=Dude case.z=Yonik}asdf") |
| , "//result[@numFound='BOGUS']") |
| ); |
| assertTrue("exp cause is wrong", |
| exp.getCause() instanceof SolrException); |
| SolrException e = (SolrException) exp.getCause(); |
| assertEquals("error isn't user error", 400, e.code()); |
| assertTrue("Error doesn't include bad switch case: " + e.getMessage(), |
| e.getMessage().contains("asdf")); |
| } finally { |
| resetExceptionIgnores(); |
| } |
| |
| |
| // dismax query from std request handler |
| assertQ("test dismax query", |
| req("q","{!dismax}hello" |
| ,"qf","v_t" |
| ,"bf","sqrt(v_f)^100 log(sum(v_f,1))^50" |
| ,"bq","{!prefix f=v_t}he" |
| , CommonParams.DEBUG_QUERY,"on" |
| ) |
| ,"//result[@numFound='2']" |
| ); |
| |
| // dismax query from std request handler, using local params |
| assertQ("test dismax query w/ local params", |
| req("q","{!dismax qf=v_t}hello" |
| ,"qf","v_f" |
| ) |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test nested query", |
| req("q","_query_:\"{!query v=$q1}\"", "q1","{!prefix f=v_t}hel") |
| ,"//result[@numFound='2']" |
| ); |
| |
| assertQ("test nested nested query", |
| req("q","_query_:\"{!query v=$q1}\"", "q1","{!v=$q2}","q2","{!prefix f=v_t v=$qqq}","qqq","hel") |
| ,"//result[@numFound='2']" |
| ); |
| assertQ("Test text field with no analysis doesn't NPE with wildcards (SOLR-4318)", |
| req("q", "text_no_analyzer:should*"), "//result[@numFound='1']"); |
| |
| |
| } |
| |
| @Test |
| public void testNumericBadRequests() { |
| String[] suffixes = new String[50]; |
| int fieldNum = 0; |
| for (String type:new String[]{"i", "l", "f", "d", "dt"}) { |
| for (String s:new String[]{"", "s"}) { |
| //Trie |
| suffixes[fieldNum++] = "t" + type + s; |
| suffixes[fieldNum++] = "t" + type + s + "_dv"; |
| suffixes[fieldNum++] = "t" + type + s + "_ni_dv"; |
| |
| //Points |
| suffixes[fieldNum++] = type + s + "_p"; |
| suffixes[fieldNum++] = type + s + "_ni_p"; |
| } |
| } |
| assertEquals(fieldNum,suffixes.length); |
| |
| String badNumber = "NOT_A_NUMBER"; |
| for (String suffix:suffixes) { |
| // Numeric bad requests |
| assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!term f=foo_" + suffix + "}" + badNumber), SolrException.ErrorCode.BAD_REQUEST); |
| assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!terms f=foo_" + suffix + "}1 2 3 4 5 " + badNumber), SolrException.ErrorCode.BAD_REQUEST); |
| assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!lucene}foo_" + suffix + ":" + badNumber), SolrException.ErrorCode.BAD_REQUEST); |
| assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!field f=foo_" + suffix + "}" + badNumber), SolrException.ErrorCode.BAD_REQUEST); |
| assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!maxscore}foo_" + suffix + ":" + badNumber), SolrException.ErrorCode.BAD_REQUEST); |
| assertQEx("Expecting exception for suffix: " + suffix, badNumber, |
| req("q","{!xmlparser}<PointRangeQuery fieldName=\"foo_"+ suffix + "\" lowerTerm=\"1\" upperTerm=\"" + badNumber + "\"/>"), SolrException.ErrorCode.BAD_REQUEST); |
| if (suffix.contains("_p")) { |
| // prefix queries work in Trie fields |
| assertQEx("Expecting exception for suffix: " + suffix, "Can't run prefix queries on numeric fields", |
| req("q","{!prefix f=foo_" + suffix + "}NOT_A_NUMBER"), SolrException.ErrorCode.BAD_REQUEST); |
| assertQEx("Expecting exception for suffix: " + suffix, "Can't run prefix queries on numeric fields", |
| req("q","{!lucene}foo_" + suffix + ":123*"), SolrException.ErrorCode.BAD_REQUEST); |
| } |
| |
| // Skipping: func, boost, raw, nested, frange, spatial*, join, surround, switch, parent, child, collapsing, |
| // complexphrase, rerank, export, mlt, hash, graph, graphTerms, igain, tlogit, significantTerms, payload* |
| // Maybe add: raw, join, parent, child, collapsing, graphTerms, igain, significantTerms, simple |
| } |
| |
| } |
| } |