mirror of https://github.com/apache/lucene.git
SOLR-9275: make XML QueryParser support (defType=xmlparser) extensible via configuration
This commit is contained in:
parent
9f13bf6718
commit
6f76ac1314
|
@ -97,6 +97,9 @@ New Features
|
|||
|
||||
* SOLR-9240: Support parallel ETL with the topic expression (Joel Bernstein)
|
||||
|
||||
* SOLR-9275: XML QueryParser support (defType=xmlparser) now extensible via configuration.
|
||||
(Christine Poerschke)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -16,23 +16,54 @@
|
|||
*/
|
||||
package org.apache.solr.search;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryparser.xml.CoreParser;
|
||||
import org.apache.lucene.queryparser.xml.QueryBuilder;
|
||||
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.util.plugin.NamedListInitializedPlugin;
|
||||
|
||||
/**
|
||||
* Assembles a QueryBuilder which uses Query objects from Solr's <code>search</code> module
|
||||
* in addition to Query objects supported by the Lucene <code>CoreParser</code>.
|
||||
*/
|
||||
public class SolrCoreParser extends CoreParser {
|
||||
public class SolrCoreParser extends CoreParser implements NamedListInitializedPlugin {
|
||||
|
||||
protected final SolrQueryRequest req;
|
||||
|
||||
public SolrCoreParser(String defaultField, Analyzer analyzer,
|
||||
SolrQueryRequest req) {
|
||||
super(defaultField, analyzer);
|
||||
this.req = req;
|
||||
}
|
||||
|
||||
// final IndexSchema schema = req.getSchema();
|
||||
// lucene_parser.addQueryBuilder("SomeOtherQuery", new SomeOtherQueryBuilder(schema));
|
||||
@Override
|
||||
public void init(NamedList initArgs) {
|
||||
final SolrResourceLoader loader;
|
||||
if (req == null) {
|
||||
loader = new SolrResourceLoader();
|
||||
} else {
|
||||
loader = req.getCore().getResourceLoader();
|
||||
}
|
||||
|
||||
final Iterable<Map.Entry<String,Object>> args = initArgs;
|
||||
for (final Map.Entry<String,Object> entry : args) {
|
||||
final String queryName = entry.getKey();
|
||||
final String queryBuilderClassName = (String)entry.getValue();
|
||||
|
||||
final SolrQueryBuilder queryBuilder = loader.newInstance(
|
||||
queryBuilderClassName,
|
||||
SolrQueryBuilder.class,
|
||||
null,
|
||||
new Class[] {String.class, Analyzer.class, SolrQueryRequest.class, QueryBuilder.class},
|
||||
new Object[] {defaultField, analyzer, req, this});
|
||||
|
||||
this.queryFactory.addBuilder(queryName, queryBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryparser.xml.QueryBuilder;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
|
||||
public abstract class SolrQueryBuilder implements QueryBuilder {
|
||||
|
||||
protected final SolrQueryRequest req;
|
||||
protected final QueryBuilder queryFactory;
|
||||
|
||||
public SolrQueryBuilder(String defaultField, Analyzer analyzer,
|
||||
SolrQueryRequest req, QueryBuilder queryFactory) {
|
||||
this.req = req;
|
||||
this.queryFactory = queryFactory;
|
||||
}
|
||||
|
||||
}
|
|
@ -25,12 +25,21 @@ import org.apache.lucene.search.Query;
|
|||
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.schema.IndexSchema;
|
||||
|
||||
public class XmlQParserPlugin extends QParserPlugin {
|
||||
public static final String NAME = "xmlparser";
|
||||
|
||||
private NamedList args;
|
||||
|
||||
@Override
|
||||
public void init( NamedList args ) {
|
||||
super.init(args);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
private class XmlQParser extends QParser {
|
||||
|
||||
public XmlQParser(String qstr, SolrParams localParams,
|
||||
|
@ -46,7 +55,9 @@ public class XmlQParserPlugin extends QParserPlugin {
|
|||
final IndexSchema schema = req.getSchema();
|
||||
final String defaultField = QueryParsing.getDefaultField(schema, getParam(CommonParams.DF));
|
||||
final Analyzer analyzer = schema.getQueryAnalyzer();
|
||||
|
||||
final SolrCoreParser solrParser = new SolrCoreParser(defaultField, analyzer, req);
|
||||
solrParser.init(args);
|
||||
try {
|
||||
return solrParser.parse(new ByteArrayInputStream(qstr.getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (ParserException e) {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<!-- solrconfig-basic.xml plus a queryParser element -->
|
||||
<config>
|
||||
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||
<dataDir>${solr.data.dir:}</dataDir>
|
||||
<xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/>
|
||||
<schemaFactory class="ClassicIndexSchemaFactory"/>
|
||||
<requestHandler name="standard" class="solr.StandardRequestHandler" />
|
||||
<queryParser name="testxmlparser" class="XmlQParserPlugin">
|
||||
<str name="HandyQuery">org.apache.solr.search.HandyQueryBuilder</str>
|
||||
<str name="HelloQuery">org.apache.solr.search.HelloQueryBuilder</str>
|
||||
<str name="GoodbyeQuery">org.apache.solr.search.GoodbyeQueryBuilder</str>
|
||||
</queryParser>
|
||||
</config>
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryparser.xml.ParserException;
|
||||
import org.apache.lucene.queryparser.xml.QueryBuilder;
|
||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
public class GoodbyeQueryBuilder extends SolrQueryBuilder {
|
||||
|
||||
public GoodbyeQueryBuilder(String defaultField, Analyzer analyzer,
|
||||
SolrQueryRequest req, QueryBuilder queryFactory) {
|
||||
super(defaultField, analyzer, req, queryFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getQuery(Element e) throws ParserException {
|
||||
return new MatchNoDocsQuery();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryparser.xml.DOMUtils;
|
||||
import org.apache.lucene.queryparser.xml.ParserException;
|
||||
import org.apache.lucene.queryparser.xml.QueryBuilder;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
// A simple test query builder to demonstrate use of
|
||||
// SolrQueryBuilder's queryFactory constructor argument.
|
||||
public class HandyQueryBuilder extends SolrQueryBuilder {
|
||||
|
||||
public HandyQueryBuilder(String defaultField, Analyzer analyzer,
|
||||
SolrQueryRequest req, QueryBuilder queryFactory) {
|
||||
super(defaultField, analyzer, req, queryFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getQuery(Element e) throws ParserException {
|
||||
final BooleanQuery.Builder bq = new BooleanQuery.Builder();
|
||||
final Query lhsQ = getSubQuery(e, "Left");
|
||||
final Query rhsQ = getSubQuery(e, "Right");
|
||||
bq.add(new BooleanClause(lhsQ, BooleanClause.Occur.SHOULD));
|
||||
bq.add(new BooleanClause(rhsQ, BooleanClause.Occur.SHOULD));
|
||||
return bq.build();
|
||||
}
|
||||
|
||||
private Query getSubQuery(Element e, String name) throws ParserException {
|
||||
Element subE = DOMUtils.getChildByTagOrFail(e, name);
|
||||
subE = DOMUtils.getFirstChildOrFail(subE);
|
||||
return queryFactory.getQuery(subE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryparser.xml.ParserException;
|
||||
import org.apache.lucene.queryparser.xml.QueryBuilder;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
public class HelloQueryBuilder extends SolrQueryBuilder {
|
||||
|
||||
public HelloQueryBuilder(String defaultField, Analyzer analyzer,
|
||||
SolrQueryRequest req, QueryBuilder queryFactory) {
|
||||
super(defaultField, analyzer, req, queryFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getQuery(Element e) throws ParserException {
|
||||
return new MatchAllDocsQuery();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestXmlQParserPlugin extends SolrTestCaseJ4 {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
initCore("solrconfig-testxmlparser.xml", "schema-minimal.xml");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// if you override setUp or tearDown, you better call
|
||||
// the super classes version
|
||||
super.setUp();
|
||||
clearIndex();
|
||||
assertU(commit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloQuery() throws Exception {
|
||||
final int numDocs = random().nextInt(10);
|
||||
implTestQuery(numDocs, "<HelloQuery/>", numDocs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoodbyeQuery() throws Exception {
|
||||
final int numDocs = random().nextInt(10);
|
||||
implTestQuery(numDocs, "<GoodbyeQuery/>", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandyQuery() throws Exception {
|
||||
final int numDocs = random().nextInt(10);
|
||||
final String q = "<HandyQuery><Left><HelloQuery/></Left><Right><GoodbyeQuery/></Right></HandyQuery>";
|
||||
implTestQuery(numDocs, q, numDocs);
|
||||
}
|
||||
|
||||
public void implTestQuery(int numDocs, String q, int expectedCount) throws Exception {
|
||||
// add some documents
|
||||
for (int ii=1; ii<=numDocs; ++ii) {
|
||||
String[] doc = {"id",ii+"0"};
|
||||
assertU(adoc(doc));
|
||||
if (random().nextBoolean()) {
|
||||
assertU(commit());
|
||||
}
|
||||
}
|
||||
assertU(commit());
|
||||
// and then run the query
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
params.add("defType", "testxmlparser");
|
||||
params.add("q", q);
|
||||
assertQ(req(params), "*[count(//doc)="+expectedCount+"]");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue