/* * 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.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.analysis.MockTokenFilter; import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.xml.CoreParser; import org.apache.lucene.queryparser.xml.ParserException; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.spans.SpanBoostQuery; import org.apache.lucene.search.spans.SpanNearQuery; import org.apache.lucene.search.spans.SpanOrQuery; import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.search.spans.SpanTermQuery; import org.apache.lucene.util.LuceneTestCase; import org.apache.solr.common.util.NamedList; import org.apache.solr.request.SolrQueryRequest; public class TestSolrCoreParser extends LuceneTestCase { private SolrCoreParser solrCoreParser; private CoreParser solrCoreParser() { if (solrCoreParser == null) { final String defaultField = "contents"; final Analyzer analyzer = new MockAnalyzer(random(), MockTokenizer.WHITESPACE, true, MockTokenFilter.ENGLISH_STOPSET); final SolrQueryRequest req = null; solrCoreParser = new SolrCoreParser(defaultField, analyzer, req); { final NamedList<String> args = new NamedList<>(); args.add("HelloQuery", HelloQueryBuilder.class.getCanonicalName()); args.add("GoodbyeQuery", GoodbyeQueryBuilder.class.getCanonicalName()); args.add("HandyQuery", HandyQueryBuilder.class.getCanonicalName()); args.add("ApacheLuceneSolr", ApacheLuceneSolrNearQueryBuilder.class.getCanonicalName()); args.add("ChooseOneWord", ChooseOneWordQueryBuilder.class.getCanonicalName()); solrCoreParser.init(args); } } return solrCoreParser; } private Query parseXmlString(String xml) throws IOException, ParserException { final byte[] xmlBytes = xml.getBytes(StandardCharsets.UTF_8); final InputStream xmlStream = new ByteArrayInputStream(xmlBytes); return solrCoreParser().parse(xmlStream); } private Query parseHandyQuery(String lhsXml, String rhsXml) throws IOException, ParserException { final String xml = "<HandyQuery>" + "<Left>" + lhsXml + "</Left>" + "<Right>" + rhsXml + "</Right>" + "</HandyQuery>"; return parseXmlString(xml); } public void testHello() throws IOException, ParserException { final Query query = parseXmlString("<HelloQuery/>"); assertTrue(query instanceof MatchAllDocsQuery); } public void testGoodbye() throws IOException, ParserException { final Query query = parseXmlString("<GoodbyeQuery/>"); assertTrue(query instanceof MatchNoDocsQuery); } public void testApacheLuceneSolr() throws IOException, ParserException { final String fieldName = "contents"; final Query query = parseXmlString("<ApacheLuceneSolr fieldName='"+fieldName+"'/>"); checkApacheLuceneSolr(query, fieldName); } private static void checkApacheLuceneSolr(Query query, String fieldName) { assertTrue(query instanceof SpanNearQuery); final SpanNearQuery snq = (SpanNearQuery)query; assertEquals(fieldName, snq.getField()); assertEquals(42, snq.getSlop()); assertFalse(snq.isInOrder()); assertEquals(3, snq.getClauses().length); assertTrue(snq.getClauses()[0] instanceof SpanTermQuery); assertTrue(snq.getClauses()[1] instanceof SpanTermQuery); assertTrue(snq.getClauses()[2] instanceof SpanTermQuery); } // test custom query (HandyQueryBuilder) wrapping a Query public void testHandyQuery() throws IOException, ParserException { final String lhsXml = "<HelloQuery/>"; final String rhsXml = "<GoodbyeQuery/>"; final Query query = parseHandyQuery(lhsXml, rhsXml); assertTrue(query instanceof BooleanQuery); final BooleanQuery bq = (BooleanQuery)query; assertEquals(2, bq.clauses().size()); assertTrue(bq.clauses().get(0).getQuery() instanceof MatchAllDocsQuery); assertTrue(bq.clauses().get(1).getQuery() instanceof MatchNoDocsQuery); } private static SpanQuery unwrapSpanBoostQuery(Query query) { assertTrue(query instanceof SpanBoostQuery); final SpanBoostQuery spanBoostQuery = (SpanBoostQuery)query; return spanBoostQuery.getQuery(); } // test custom query (HandyQueryBuilder) wrapping a SpanQuery public void testHandySpanQuery() throws IOException, ParserException { final String lhsXml = "<SpanOr fieldName='contents'>" + "<SpanTerm>rain</SpanTerm>" + "<SpanTerm>spain</SpanTerm>" + "<SpanTerm>plain</SpanTerm>" + "</SpanOr>"; final String rhsXml = "<SpanNear fieldName='contents' slop='2' inOrder='true'>" + "<SpanTerm>sunny</SpanTerm>" + "<SpanTerm>sky</SpanTerm>" + "</SpanNear>"; final Query query = parseHandyQuery(lhsXml, rhsXml); final BooleanQuery bq = (BooleanQuery)query; assertEquals(2, bq.clauses().size()); for (int ii=0; ii<bq.clauses().size(); ++ii) { final Query clauseQuery = bq.clauses().get(ii).getQuery(); switch (ii) { case 0: assertTrue(unwrapSpanBoostQuery(clauseQuery) instanceof SpanOrQuery); break; case 1: assertTrue(unwrapSpanBoostQuery(clauseQuery) instanceof SpanNearQuery); break; default: fail("unexpected clause index "+ii); } } } private static String composeChooseOneWordQueryXml(String fieldName, String... termTexts) { final StringBuilder sb = new StringBuilder("<ChooseOneWord fieldName='"+fieldName+"'>"); for (String termText : termTexts) { sb.append("<Word>").append(termText).append("</Word>"); } sb.append("</ChooseOneWord>"); return sb.toString(); } // test custom queries being wrapped in a Query or SpanQuery public void testCustomQueryWrapping() throws IOException, ParserException { final boolean span = random().nextBoolean(); // the custom queries final String fieldName = "contents"; final String[] randomTerms = new String[] {"bumble", "honey", "solitary"}; final String randomQuery = composeChooseOneWordQueryXml(fieldName, randomTerms); final String apacheLuceneSolr = "<ApacheLuceneSolr fieldName='"+fieldName+"'/>"; // the wrapping query final String parentQuery = (span ? "SpanOr" : "BooleanQuery"); final String subQueryPrefix = (span ? "" : "<Clause occurs='must'>"); final String subQuerySuffix = (span ? "" : "</Clause>"); final String xml = "<"+parentQuery+">" + subQueryPrefix+randomQuery+subQuerySuffix + subQueryPrefix+apacheLuceneSolr+subQuerySuffix + "</"+parentQuery+">"; // the test final Query query = parseXmlString(xml); if (span) { assertTrue(unwrapSpanBoostQuery(query) instanceof SpanOrQuery); final SpanOrQuery soq = (SpanOrQuery)unwrapSpanBoostQuery(query); assertEquals(2, soq.getClauses().length); checkChooseOneWordQuery(span, soq.getClauses()[0], fieldName, randomTerms); checkApacheLuceneSolr(soq.getClauses()[1], fieldName); } else { assertTrue(query instanceof BooleanQuery); final BooleanQuery bq = (BooleanQuery)query; assertEquals(2, bq.clauses().size()); checkChooseOneWordQuery(span, bq.clauses().get(0).getQuery(), fieldName, randomTerms); checkApacheLuceneSolr(bq.clauses().get(1).getQuery(), fieldName); } } private static void checkChooseOneWordQuery(boolean span, Query query, String fieldName, String ... expectedTermTexts) { final Term term; if (span) { assertTrue(query instanceof SpanTermQuery); final SpanTermQuery stq = (SpanTermQuery)query; term = stq.getTerm(); } else { assertTrue(query instanceof TermQuery); final TermQuery tq = (TermQuery)query; term = tq.getTerm(); } final String text = term.text(); boolean foundExpected = false; for (String expected : expectedTermTexts) { foundExpected |= expected.equals(text); } assertEquals(fieldName, term.field()); assertTrue("expected term text ("+text+") not found in ("+expectedTermTexts+")", foundExpected); } }