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
b0d322d528
commit
80a2e73ccd
|
@ -63,6 +63,9 @@ New Features
|
||||||
|
|
||||||
* SOLR-9240: Support parallel ETL with the topic expression (Joel Bernstein)
|
* 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
|
Bug Fixes
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -16,23 +16,54 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.search;
|
package org.apache.solr.search;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
import org.apache.lucene.queryparser.xml.CoreParser;
|
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.request.SolrQueryRequest;
|
||||||
|
import org.apache.solr.util.plugin.NamedListInitializedPlugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assembles a QueryBuilder which uses Query objects from Solr's <code>search</code> module
|
* 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>.
|
* 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,
|
public SolrCoreParser(String defaultField, Analyzer analyzer,
|
||||||
SolrQueryRequest req) {
|
SolrQueryRequest req) {
|
||||||
super(defaultField, analyzer);
|
super(defaultField, analyzer);
|
||||||
|
this.req = req;
|
||||||
|
}
|
||||||
|
|
||||||
// final IndexSchema schema = req.getSchema();
|
@Override
|
||||||
// lucene_parser.addQueryBuilder("SomeOtherQuery", new SomeOtherQueryBuilder(schema));
|
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.CommonParams;
|
||||||
import org.apache.solr.common.params.SolrParams;
|
import org.apache.solr.common.params.SolrParams;
|
||||||
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.request.SolrQueryRequest;
|
import org.apache.solr.request.SolrQueryRequest;
|
||||||
import org.apache.solr.schema.IndexSchema;
|
import org.apache.solr.schema.IndexSchema;
|
||||||
|
|
||||||
public class XmlQParserPlugin extends QParserPlugin {
|
public class XmlQParserPlugin extends QParserPlugin {
|
||||||
public static final String NAME = "xmlparser";
|
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 {
|
private class XmlQParser extends QParser {
|
||||||
|
|
||||||
public XmlQParser(String qstr, SolrParams localParams,
|
public XmlQParser(String qstr, SolrParams localParams,
|
||||||
|
@ -46,7 +55,9 @@ public class XmlQParserPlugin extends QParserPlugin {
|
||||||
final IndexSchema schema = req.getSchema();
|
final IndexSchema schema = req.getSchema();
|
||||||
final String defaultField = QueryParsing.getDefaultField(schema, getParam(CommonParams.DF));
|
final String defaultField = QueryParsing.getDefaultField(schema, getParam(CommonParams.DF));
|
||||||
final Analyzer analyzer = schema.getQueryAnalyzer();
|
final Analyzer analyzer = schema.getQueryAnalyzer();
|
||||||
|
|
||||||
final SolrCoreParser solrParser = new SolrCoreParser(defaultField, analyzer, req);
|
final SolrCoreParser solrParser = new SolrCoreParser(defaultField, analyzer, req);
|
||||||
|
solrParser.init(args);
|
||||||
try {
|
try {
|
||||||
return solrParser.parse(new ByteArrayInputStream(qstr.getBytes(StandardCharsets.UTF_8)));
|
return solrParser.parse(new ByteArrayInputStream(qstr.getBytes(StandardCharsets.UTF_8)));
|
||||||
} catch (ParserException e) {
|
} 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