mirror of https://github.com/apache/lucene.git
SOLR-8208: [subquery] document transformer executes separate requests per result document.
This commit is contained in:
parent
4a58b3d803
commit
7571e747c3
|
@ -133,6 +133,8 @@ New Features
|
|||
Auto Add Replica feature. For this you can set a "maxCoresPerNode" property via the Cluster Property API
|
||||
(Varun Thacker, Mark Miller)
|
||||
|
||||
* SOLR-8208: [subquery] document transformer executes separate requests per result document. (Cao Manh Dat via Mikhail Khludnev)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -74,16 +74,17 @@ public class MoreLikeThisComponent extends SearchComponent {
|
|||
public void process(ResponseBuilder rb) throws IOException {
|
||||
|
||||
SolrParams params = rb.req.getParams();
|
||||
ReturnFields returnFields = new SolrReturnFields( rb.req );
|
||||
|
||||
int flags = 0;
|
||||
if (returnFields.wantsScore()) {
|
||||
flags |= SolrIndexSearcher.GET_SCORES;
|
||||
}
|
||||
|
||||
rb.setFieldFlags(flags);
|
||||
|
||||
|
||||
if (params.getBool(MoreLikeThisParams.MLT, false)) {
|
||||
ReturnFields returnFields = new SolrReturnFields( rb.req );
|
||||
|
||||
int flags = 0;
|
||||
if (returnFields.wantsScore()) {
|
||||
flags |= SolrIndexSearcher.GET_SCORES;
|
||||
}
|
||||
|
||||
rb.setFieldFlags(flags);
|
||||
|
||||
log.debug("Starting MoreLikeThis.Process. isShard: "
|
||||
+ params.getBool(ShardParams.IS_SHARD));
|
||||
SolrIndexSearcher searcher = rb.req.getSearcher();
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* 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.response.transform;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
|
||||
import org.apache.solr.client.solrj.request.QueryRequest;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.SolrException.ErrorCode;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestInfo;
|
||||
import org.apache.solr.response.ResultContext;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.search.DocList;
|
||||
import org.apache.solr.search.DocSlice;
|
||||
import org.apache.solr.search.JoinQParserPlugin;
|
||||
import org.apache.solr.search.ReturnFields;
|
||||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
import org.apache.solr.search.SolrReturnFields;
|
||||
import org.apache.solr.search.TermsQParserPlugin;
|
||||
|
||||
/**
|
||||
*
|
||||
* This transformer executes subquery per every result document. It must be be given uniq name.
|
||||
* There might be a few of them, eg <code>fl=*,foo:[subquery],bar:[subquery]</code>.
|
||||
* Every [subquery] occurrence adds a field into a result document with the given name,
|
||||
* the value of this field is a document list, which is a result of executing subquery using
|
||||
* document fields as an input.
|
||||
*
|
||||
* <h3>Subquery Parameters Shift</h3>
|
||||
* if subquery is declared as <code>fl=*,foo:[subquery]</code>, subquery parameters
|
||||
* are prefixed with the given name and period. eg <br>
|
||||
* <code>q=*:*&fl=*,foo:[subquery]&foo.q=to be continued&foo.rows=10&foo.sort=id desc</code>
|
||||
*
|
||||
* <h3>Document field as an input param for subquery</h3>
|
||||
*
|
||||
* It's necessary to pass some document field value as a parametr for subquery. It's supported via
|
||||
* implicit <code>row.<i>fieldname</i></code> parameter, and can be (but might not only) referred via
|
||||
* Local Parameters syntax.<br>
|
||||
* <code>q=namne:john&fl=name,id,depts:[subquery]&depts.q={!terms f=id v=$row.dept_id}&depts.rows=10</code>
|
||||
* Here departmens are retrieved per every employee in search result. We can say that it's like SQL
|
||||
* <code> join ON emp.dept_id=dept.id </code><br>
|
||||
* Note, when document field has multiple values they are concatenated with comma by default, it can be changed by
|
||||
* <code>foo:[subquery separator=' ']</code> local parameter, this mimics {@link TermsQParserPlugin} to work smoothly with.
|
||||
*
|
||||
* <h3>Cores and Collections in Cloud</h3>
|
||||
* use <code>foo:[subquery fromIndex=departments]</code> invoke subquery on another core on this node, it's like
|
||||
* {@link JoinQParserPlugin} for non SolrCloud mode. <b>But for SolrCloud</b> just (and only) <b>explicitly specify</b>
|
||||
* its' native parameters like <code>collection, shards</code> for subquery, eg<br>
|
||||
* <code>q=*:*&fl=*,foo:[subquery]&foo.q=cloud&foo.collection=departments</code>
|
||||
*
|
||||
*/
|
||||
public class SubQueryAugmenterFactory extends TransformerFactory{
|
||||
|
||||
@Override
|
||||
public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) {
|
||||
|
||||
if (field.contains("[") || field.contains("]")) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
"please give an exlicit name for [subquery] column ie fl=relation:[subquery ..]");
|
||||
}
|
||||
|
||||
checkThereIsNoDupe(field, req.getContext());
|
||||
|
||||
String fromIndex = params.get("fromIndex");
|
||||
final SolrClient solrClient;
|
||||
|
||||
solrClient = new EmbeddedSolrServer(req.getCore());
|
||||
|
||||
SolrParams subParams = retainAndShiftPrefix(req.getParams(), field+".");
|
||||
|
||||
|
||||
return new SubQueryAugmenter(solrClient, fromIndex, field,
|
||||
field,
|
||||
subParams,
|
||||
params.get(TermsQParserPlugin.SEPARATOR, ","));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void checkThereIsNoDupe(String field, Map<Object,Object> context) {
|
||||
// find a map
|
||||
final Map conflictMap;
|
||||
final String conflictMapKey = getClass().getSimpleName();
|
||||
if (context.containsKey(conflictMapKey)) {
|
||||
conflictMap = (Map) context.get(conflictMapKey);
|
||||
} else {
|
||||
conflictMap = new HashMap<>();
|
||||
context.put(conflictMapKey, conflictMap);
|
||||
}
|
||||
// check entry absence
|
||||
if (conflictMap.containsKey(field)) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST,
|
||||
"[subquery] name "+field+" is duplicated");
|
||||
} else {
|
||||
conflictMap.put(field, true);
|
||||
}
|
||||
}
|
||||
|
||||
private SolrParams retainAndShiftPrefix(SolrParams params, String subPrefix) {
|
||||
ModifiableSolrParams out = new ModifiableSolrParams();
|
||||
Iterator<String> baseKeyIt = params.getParameterNamesIterator();
|
||||
while (baseKeyIt.hasNext()) {
|
||||
String key = baseKeyIt.next();
|
||||
|
||||
if (key.startsWith(subPrefix)) {
|
||||
out.set(key.substring(subPrefix.length()), params.getParams(key));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SubQueryAugmenter extends DocTransformer {
|
||||
|
||||
private static final class Result extends ResultContext {
|
||||
private final SolrDocumentList docList;
|
||||
final SolrReturnFields justWantAllFields = new SolrReturnFields();
|
||||
|
||||
private Result(SolrDocumentList docList) {
|
||||
this.docList = docList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReturnFields getReturnFields() {
|
||||
return justWantAllFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<SolrDocument> getProcessedDocuments(){
|
||||
return docList.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsScores() {
|
||||
return justWantAllFields.wantsScore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocList getDocList() {
|
||||
return new DocSlice((int)docList.getStart(),
|
||||
docList.size(), new int[0], new float[docList.size()],
|
||||
(int) docList.getNumFound(),
|
||||
docList.getMaxScore() == null ? Float.NaN : docList.getMaxScore());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SolrIndexSearcher getSearcher() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SolrQueryRequest getRequest() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getQuery() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** project document values to prefixed parameters
|
||||
* multivalues are joined with a separator, it always return single value */
|
||||
static final class DocRowParams extends SolrParams {
|
||||
|
||||
final private SolrDocument doc;
|
||||
final private String prefixDotRowDot;
|
||||
final private String separator;
|
||||
|
||||
public DocRowParams(SolrDocument doc, String prefix, String separator ) {
|
||||
this.doc = doc;
|
||||
this.prefixDotRowDot = "row.";//prefix+ ".row.";
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParams(String param) {
|
||||
|
||||
final Collection<Object> vals = mapToDocField(param);
|
||||
|
||||
if (vals != null) {
|
||||
StringBuilder rez = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Iterator iterator = vals.iterator(); iterator.hasNext();) {
|
||||
Object object = (Object) iterator.next();
|
||||
rez.append(convertFieldValue(object));
|
||||
if (iterator.hasNext()) {
|
||||
rez.append(separator);
|
||||
}
|
||||
}
|
||||
return new String[]{rez.toString()};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String get(String param) {
|
||||
|
||||
final String[] aVal = this.getParams(param);
|
||||
|
||||
if (aVal != null) {
|
||||
assert aVal.length == 1 : "that's how getParams is written" ;
|
||||
return aVal[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return null if prefix doesn't match, field is absent or empty */
|
||||
protected Collection<Object> mapToDocField(String param) {
|
||||
|
||||
if (param.startsWith(prefixDotRowDot)) {
|
||||
final String docFieldName = param.substring(prefixDotRowDot.length());
|
||||
final Collection<Object> vals = doc.getFieldValues(docFieldName);
|
||||
|
||||
if (vals == null || vals.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return vals;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected String convertFieldValue(Object val) {
|
||||
|
||||
if (val instanceof IndexableField) {
|
||||
IndexableField f = (IndexableField)val;
|
||||
return f.stringValue();
|
||||
}
|
||||
return val.toString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getParameterNamesIterator() {
|
||||
final Iterator<String> fieldNames = doc.getFieldNames().iterator();
|
||||
return new Iterator<String>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return fieldNames.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
final String fieldName = fieldNames.next();
|
||||
return prefixDotRowDot + fieldName;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final private String name;
|
||||
final private SolrParams baseSubParams;
|
||||
final private String prefix;
|
||||
final private String separator;
|
||||
final private SolrClient server;
|
||||
final private String coreName;
|
||||
|
||||
public SubQueryAugmenter(SolrClient server, String coreName,
|
||||
String name,String prefix, SolrParams baseSubParams, String separator) {
|
||||
this.name = name;
|
||||
this.prefix = prefix;
|
||||
this.baseSubParams = baseSubParams;
|
||||
this.separator = separator;
|
||||
this.server = server;
|
||||
this.coreName = coreName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(SolrDocument doc, int docid, float score) {
|
||||
|
||||
final SolrParams docWithDeprefixed = SolrParams.wrapDefaults(
|
||||
new DocRowParams(doc, prefix, separator), baseSubParams);
|
||||
try {
|
||||
Callable<QueryResponse> subQuery = new Callable<QueryResponse>() {
|
||||
@Override
|
||||
public QueryResponse call() throws Exception {
|
||||
try {
|
||||
return new QueryResponse(
|
||||
server.request(
|
||||
new QueryRequest(docWithDeprefixed), coreName)
|
||||
, server);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
};
|
||||
QueryResponse response =
|
||||
SolrRequestInfoSuspender.doInSuspension(subQuery);
|
||||
|
||||
final SolrDocumentList docList = (SolrDocumentList) response.getResults();
|
||||
|
||||
doc.setField(getName(), new Result(docList));
|
||||
|
||||
} catch (Exception e) {
|
||||
String docString = doc.toString();
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "while invoking " +
|
||||
name + ":[subquery"+ (coreName!=null ? "fromIndex="+coreName : "") +"] on doc=" +
|
||||
docString.substring(0, Math.min(100, docString.length())), e.getCause());
|
||||
} finally {}
|
||||
}
|
||||
|
||||
// look ma!! no hands..
|
||||
final static class SolrRequestInfoSuspender extends SolrRequestInfo {
|
||||
|
||||
private SolrRequestInfoSuspender(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
super(req, rsp);
|
||||
}
|
||||
|
||||
/** Suspends current SolrRequestInfo invoke the given action, and resumes then */
|
||||
static <T> T doInSuspension(Callable<T> action) throws Exception {
|
||||
|
||||
final SolrRequestInfo info = threadLocal.get();
|
||||
try {
|
||||
threadLocal.remove();
|
||||
return action.call();
|
||||
} finally {
|
||||
setRequestInfo(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -47,6 +47,7 @@ public abstract class TransformerFactory implements NamedListInitializedPlugin
|
|||
defaultFactories.put( "docid", new DocIdAugmenterFactory() );
|
||||
defaultFactories.put( "shard", new ShardAugmenterFactory() );
|
||||
defaultFactories.put( "child", new ChildDocTransformerFactory() );
|
||||
defaultFactories.put( "subquery", new SubQueryAugmenterFactory() );
|
||||
defaultFactories.put( "json", new RawValueTransformerFactory("json") );
|
||||
defaultFactories.put( "xml", new RawValueTransformerFactory("xml") );
|
||||
defaultFactories.put( "geo", new GeoTransformerFactory() );
|
||||
|
|
|
@ -87,6 +87,10 @@
|
|||
<dynamicField name="*_dt_dv" type="date" indexed="true" stored="false" docValues="true"/>
|
||||
<dynamicField name="*_dts_dv" type="date" indexed="true" stored="false" docValues="true" multiValued="true"/>
|
||||
<dynamicField name="*_t" type="text" indexed="true" stored="true"/>
|
||||
|
||||
|
||||
<field name="_version_" type="long" indexed="true" stored="true" multiValued="false"/>
|
||||
|
||||
</fields>
|
||||
|
||||
<defaultSearchField>id</defaultSearchField>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.solr.response;
|
||||
package org.apache.solr.response.transform;
|
||||
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
|
@ -47,6 +47,8 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
|||
createIndex(titleVals);
|
||||
testParentFilterJSON();
|
||||
testParentFilterXML();
|
||||
testSubQueryParentFilterJSON();
|
||||
testSubQueryParentFilterXML();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -54,6 +56,9 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
|||
createSimpleIndex();
|
||||
testChildDoctransformerJSON();
|
||||
testChildDoctransformerXML();
|
||||
|
||||
testSubQueryXML();
|
||||
testSubQueryJSON();
|
||||
}
|
||||
|
||||
private void testChildDoctransformerXML() {
|
||||
|
@ -88,6 +93,96 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
|||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:bar\" limit=2]"), test3);
|
||||
}
|
||||
|
||||
private void testSubQueryXML() {
|
||||
String test1[];
|
||||
{
|
||||
final String subqueryPath = "/result[@name='children'][@numFound='6']";
|
||||
test1 = new String[] {
|
||||
"//*[@numFound='1']",
|
||||
"/response/result/doc[1]" + subqueryPath + "/doc[1]/int[@name='id']='2'" ,
|
||||
"/response/result/doc[1]" + subqueryPath + "/doc[2]/int[@name='id']='3'" ,
|
||||
"/response/result/doc[1]" + subqueryPath + "/doc[3]/int[@name='id']='4'" ,
|
||||
"/response/result/doc[1]" + subqueryPath + "/doc[4]/int[@name='id']='5'" ,
|
||||
"/response/result/doc[1]" + subqueryPath + "/doc[5]/int[@name='id']='6'" ,
|
||||
"/response/result/doc[1]" + subqueryPath + "/doc[6]/int[@name='id']='7'"};
|
||||
}
|
||||
|
||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.rows","10"),
|
||||
test1);
|
||||
|
||||
String test2[] = new String[] {
|
||||
"//*[@numFound='1']",
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound='3']/doc[1]/int[@name='id']='2'" ,
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound='3']/doc[2]/int[@name='id']='4'" ,
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound='3']/doc[3]/int[@name='id']='6'" };
|
||||
|
||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.rows","10",
|
||||
"children.fq","title:foo"
|
||||
), test2);
|
||||
|
||||
|
||||
String test3[] = new String[] {
|
||||
"//*[@numFound='1']",
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound='3']/doc[1]/int[@name='id']='3'" ,
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound='3']/doc[2]/int[@name='id']='5'" };
|
||||
|
||||
|
||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.rows","2",
|
||||
"children.fq","title:bar",
|
||||
"children.sort","_docid_ asc"
|
||||
), test3);
|
||||
}
|
||||
|
||||
private void testSubQueryJSON() throws Exception {
|
||||
String[] test1 = new String[] {
|
||||
"/response/docs/[0]/children/docs/[0]/id==2",
|
||||
"/response/docs/[0]/children/docs/[1]/id==3",
|
||||
"/response/docs/[0]/children/docs/[2]/id==4",
|
||||
"/response/docs/[0]/children/docs/[3]/id==5",
|
||||
"/response/docs/[0]/children/docs/[4]/id==6",
|
||||
"/response/docs/[0]/children/docs/[5]/id==7"
|
||||
};
|
||||
|
||||
String[] test2 = new String[] {
|
||||
"/response/docs/[0]/children/docs/[0]/id==2",
|
||||
"/response/docs/[0]/children/docs/[1]/id==4",
|
||||
"/response/docs/[0]/children/docs/[2]/id==6"
|
||||
};
|
||||
|
||||
String[] test3 = new String[] {
|
||||
"/response/docs/[0]/children/docs/[0]/id==3",
|
||||
"/response/docs/[0]/children/docs/[1]/id==5"
|
||||
};
|
||||
|
||||
|
||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.rows","10"), test1);
|
||||
|
||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.rows","10",
|
||||
"children.fq","title:foo"), test2);
|
||||
|
||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.rows","2",
|
||||
"children.fq","title:bar",
|
||||
"children.sort","_docid_ asc"), test3);
|
||||
}
|
||||
|
||||
private void testChildDoctransformerJSON() throws Exception {
|
||||
String[] test1 = new String[] {
|
||||
|
@ -214,6 +309,38 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
|||
tests);
|
||||
|
||||
}
|
||||
|
||||
private void testSubQueryParentFilterJSON() throws Exception {
|
||||
|
||||
String[] tests = new String[] {
|
||||
"/response/docs/[0]/id==1",
|
||||
"/response/docs/[0]/children/docs/[0]/id==2",
|
||||
"/response/docs/[0]/children/docs/[0]/cat/[0]/=='childDocument'",
|
||||
"/response/docs/[0]/children/docs/[0]/title/[0]/=='" + titleVals[0] + "'",
|
||||
"/response/docs/[1]/id==4",
|
||||
"/response/docs/[1]/children/docs/[0]/id==5",
|
||||
"/response/docs/[1]/children/docs/[0]/cat/[0]/=='childDocument'",
|
||||
"/response/docs/[1]/children/docs/[0]/title/[0]/=='" + titleVals[1] + "'"
|
||||
};
|
||||
|
||||
|
||||
assertJQ(req(
|
||||
"q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"sort", "id asc",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.fq","cat:childDocument",
|
||||
"children.sort","_docid_ asc"),
|
||||
tests);
|
||||
assertJQ(req(
|
||||
"q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "id,children:[subquery]",
|
||||
"sort", "id asc",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.fq","cat:childDocument",
|
||||
"children.sort","_docid_ asc"),
|
||||
tests);
|
||||
}
|
||||
|
||||
private void testParentFilterXML() {
|
||||
|
||||
|
@ -241,4 +368,36 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
|||
tests);
|
||||
}
|
||||
|
||||
private void testSubQueryParentFilterXML() {
|
||||
|
||||
String tests[] = new String[] {
|
||||
"//*[@numFound='2']",
|
||||
"/response/result/doc[1]/int[@name='id']='1'" ,
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound=1]/doc[1]/int[@name='id']='2'" ,
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound=1]/doc[1]/arr[@name='cat']/str[1]='childDocument'" ,
|
||||
"/response/result/doc[1]/result[@name='children'][@numFound=1]/doc[1]/arr[@name='title']/str[1]='" + titleVals[0] + "'" ,
|
||||
"/response/result/doc[2]/int[@name='id']='4'" ,
|
||||
"/response/result/doc[2]/result[@name='children'][@numFound=1]/doc[1]/int[@name='id']='5'",
|
||||
"/response/result/doc[2]/result[@name='children'][@numFound=1]/doc[1]/arr[@name='cat']/str[1]='childDocument'",
|
||||
"/response/result/doc[2]/result[@name='children'][@numFound=1]/doc[1]/arr[@name='title']/str[1]='" + titleVals[1] + "'"};
|
||||
|
||||
assertQ(req(
|
||||
"q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "*,children:[subquery]",
|
||||
"sort", "id asc",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.fq","cat:childDocument",
|
||||
"children.sort","_docid_ asc"
|
||||
),
|
||||
tests);
|
||||
|
||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||
"fl", "id,children:[subquery]",
|
||||
"sort", "id asc",
|
||||
"children.q","{!child of=subject:parentDocument}{!terms f=id v=$row.id}",
|
||||
"children.fq","cat:childDocument",
|
||||
"children.sort","_docid_ asc"),
|
||||
tests);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,571 @@
|
|||
/*
|
||||
* 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.response.transform;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.SolrException.ErrorCode;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.util.JavaBinCodec;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestInfo;
|
||||
import org.apache.solr.response.BinaryQueryResponseWriter;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestSubQueryTransformer extends SolrTestCaseJ4 {
|
||||
private static int peopleMultiplier;
|
||||
private static int deptMultiplier;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeTests() throws Exception {
|
||||
System.setProperty("enable.update.log", "false");
|
||||
initCore("solrconfig-basic.xml", "schema-docValuesJoin.xml");
|
||||
peopleMultiplier = atLeast(1);
|
||||
deptMultiplier = atLeast(1);
|
||||
|
||||
int id=0;
|
||||
for (int p=0; p < peopleMultiplier; p++){
|
||||
assertU(add(doc("id", ""+id++,"name_s", "john", "title_s", "Director",
|
||||
"dept_ss_dv","Engineering",
|
||||
"dept_i", "0",
|
||||
"dept_is", "0")));
|
||||
assertU(add(doc("id", ""+id++,"name_s", "mark", "title_s", "VP",
|
||||
"dept_ss_dv","Marketing",
|
||||
"dept_i", "1",
|
||||
"dept_is", "1")));
|
||||
assertU(add(doc("id", ""+id++,"name_s", "nancy", "title_s", "MTS",
|
||||
"dept_ss_dv","Sales",
|
||||
"dept_i", "2",
|
||||
"dept_is", "2")));
|
||||
assertU(add(doc("id", ""+id++,"name_s", "dave", "title_s", "MTS",
|
||||
"dept_ss_dv","Support", "dept_ss_dv","Engineering",
|
||||
"dept_i", "3",
|
||||
"dept_is", "3", "dept_is", "0")));
|
||||
assertU(add(doc("id", ""+id++,"name_s", "tina", "title_s", "VP",
|
||||
"dept_ss_dv","Engineering",
|
||||
"dept_i", "0",
|
||||
"dept_is", "0")));
|
||||
|
||||
if (rarely()) {
|
||||
assertU(commit("softCommit", "true"));
|
||||
}
|
||||
}
|
||||
|
||||
for (int d=0; d < deptMultiplier; d++){
|
||||
assertU(add(doc("id",""+id, "id_i",""+id++,
|
||||
"dept_id_s", "Engineering", "text_t","These guys develop stuff", "salary_i_dv", "1000",
|
||||
"dept_id_i", "0")));
|
||||
assertU(add(doc("id",""+id++,"id_i",""+id++,
|
||||
"dept_id_s", "Marketing", "text_t","These guys make you look good","salary_i_dv", "1500",
|
||||
"dept_id_i", "1")));
|
||||
assertU(add(doc("id",""+id, "id_i",""+id++,
|
||||
"dept_id_s", "Sales", "text_t","These guys sell stuff","salary_i_dv", "1600",
|
||||
"dept_id_i", "2")));
|
||||
assertU(add(doc("id",""+id,"id_i",""+id++,
|
||||
"dept_id_s", "Support", "text_t","These guys help customers","salary_i_dv", "800",
|
||||
"dept_id_i", "3")));
|
||||
|
||||
if (rarely()) {
|
||||
assertU(commit("softCommit", "true"));
|
||||
}
|
||||
}
|
||||
assertU(commit());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testJohnOrNancySingleField() throws Exception {
|
||||
//System.out.println("p "+peopleMultiplier+" d "+deptMultiplier);
|
||||
assertQ("subq1.fl is limited to single field",
|
||||
req("q","name_s:(john nancy)", "indent","true",
|
||||
"fl","name_s_dv,depts:[subquery]",
|
||||
"rows","" + (2 * peopleMultiplier),
|
||||
"depts.q","{!term f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"depts.fl","text_t",
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+deptMultiplier),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'][@numFound='" +
|
||||
deptMultiplier+ "']/doc/str[@name='text_t'][.='These guys develop stuff'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts'][@numFound='" +
|
||||
deptMultiplier+ "']/doc/str[@name='text_t'][.='These guys sell stuff'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count((//result/doc/str[@name='name_s_dv'][.='john']/..)[1]/result[@name='depts']/doc[1]/*)=1",
|
||||
"count((//result/doc/str[@name='name_s_dv'][.='john']/..)[1]/result[@name='depts']/doc["+ deptMultiplier+ "]/*)=1",
|
||||
"count((//result/doc/str[@name='name_s_dv'][.='john']/..)["+ peopleMultiplier +"]/result[@name='depts'][@numFound='" +
|
||||
deptMultiplier+ "']/doc[1]/*)=1",
|
||||
"count((//result/doc/str[@name='name_s_dv'][.='john']/..)["+ peopleMultiplier +"]/result[@name='depts'][@numFound='" +
|
||||
deptMultiplier+ "']/doc["+ deptMultiplier+ "]/*)=1"
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
final String[] johnAndNancyParams = new String[]{"q","name_s:(john nancy)", "indent","true",
|
||||
"fl","name_s_dv,depts:[subquery]",
|
||||
"fl","depts_i:[subquery]",
|
||||
"rows","" + (2 * peopleMultiplier),
|
||||
"depts.q","{!term f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"depts.fl","text_t",
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+deptMultiplier,
|
||||
|
||||
"depts_i.q","{!term f=dept_id_i v=$row.dept_i_dv}",
|
||||
"depts_i.fl","text_t", // multi val subquery param check
|
||||
"depts_i.fl","dept_id_s_dv",
|
||||
"depts_i.indent","true",
|
||||
"depts_i.rows",""+deptMultiplier};
|
||||
|
||||
@Test
|
||||
public void testTwoSubQueriesAndByNumberWithTwoFields() throws Exception {
|
||||
final SolrQueryRequest johnOrNancyTwoFL = req(johnAndNancyParams);
|
||||
|
||||
assertQ("call subquery twice a row, once by number, with two fls via multival params",
|
||||
johnOrNancyTwoFL,
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc/str[@name='text_t'][.='These guys develop stuff'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts_i']/doc/str[@name='dept_id_s_dv'][.='Engineering'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts_i']/doc/str[@name='text_t'][.='These guys sell stuff'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts_i']/doc/str[@name='dept_id_s_dv'][.='Sales'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count((//result/doc/str[@name='name_s_dv'][.='john']/..)["+ peopleMultiplier +"]/result[@name='depts_i']/doc["+ deptMultiplier+ "]/str[@name='dept_id_s_dv'][.='Engineering'])=1",
|
||||
"count((//result/doc/str[@name='name_s_dv'][.='john']/..)["+ peopleMultiplier +"]/result[@name='depts_i']/doc["+ deptMultiplier+ "]/str[@name='text_t'][.='These guys develop stuff'])=1"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRowsStartForSubqueryAndScores() throws Exception {
|
||||
|
||||
String johnDeptsIds = h.query(req(new String[]{"q","{!join from=dept_ss_dv to=dept_id_s}name_s:john",
|
||||
"wt","csv",
|
||||
"csv.header","false",
|
||||
"fl","id",
|
||||
"rows",""+deptMultiplier,
|
||||
"sort", "id_i desc"
|
||||
}));
|
||||
|
||||
ArrayList<Object> deptIds = Collections.list(
|
||||
new StringTokenizer( johnDeptsIds));
|
||||
|
||||
final int a = random().nextInt(deptMultiplier+1);
|
||||
final int b = random().nextInt(deptMultiplier+1);
|
||||
final int start = Math.min(a, b) ;
|
||||
final int toIndex = Math.max(a, b) ;
|
||||
List<Object> expectIds = deptIds.subList(start , toIndex);
|
||||
ArrayList<String> assertions = new ArrayList<>();
|
||||
// count((//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'])[1]/doc/str[@name='id'])
|
||||
// random().nextInt(peopleMultiplier);
|
||||
assertions.add("count((//result/doc/str[@name='name_s_dv'][.='john']/.."
|
||||
+ "/result[@name='depts'][@numFound='"+deptMultiplier+"'][@start='"+start+"'])["+
|
||||
(random().nextInt(peopleMultiplier)+1)
|
||||
+"]/doc/str[@name='id'])=" +(toIndex-start));
|
||||
|
||||
// System.out.println(expectIds);
|
||||
|
||||
for (int i=0; i< expectIds.size(); i++) {
|
||||
// (//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'])[1]/doc[1]/str[@name='id']='15'
|
||||
String ithDoc = "(//result/doc/str[@name='name_s_dv'][.='john']/.."
|
||||
+ "/result[@name='depts'][@numFound='"+deptMultiplier+"'][@start='"+start+"'])["+
|
||||
(random().nextInt(peopleMultiplier)+1) +
|
||||
"]/doc[" +(i+1)+ "]";
|
||||
assertions.add(ithDoc+"/str[@name='id'][.='"+expectIds.get(i)+"']");
|
||||
// let's test scores right there
|
||||
assertions.add(ithDoc+"/float[@name='score'][.='"+expectIds.get(i)+".0']");
|
||||
|
||||
}
|
||||
|
||||
String[] john = new String[]{"q","name_s:john", "indent","true",
|
||||
"fl","name_s_dv,depts:[subquery]",
|
||||
"rows","" + (2 * peopleMultiplier),
|
||||
"depts.q","+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i",
|
||||
"depts.fl","id",
|
||||
"depts.fl","score",
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+(toIndex-start),
|
||||
"depts.start",""+start};
|
||||
|
||||
assertQ(req(john), assertions.toArray(new String[]{}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreeLevel() throws Exception {
|
||||
List<String> asserts = new ArrayList<>();
|
||||
// dave works in both dept, get his coworkers from both
|
||||
for (String dept : new String[] {"Engineering", "Support"}) { //dept_id_s_dv">Engineering
|
||||
|
||||
ArrayList<Object> deptWorkers = Collections.list(
|
||||
new StringTokenizer( h.query(req(
|
||||
"q","dept_ss_dv:"+dept ,//dept_id_i_dv
|
||||
"wt","csv",
|
||||
"csv.header","false",
|
||||
"fl","name_s_dv",
|
||||
"rows",""+peopleMultiplier*3, // dave has three coworkers in two depts
|
||||
"sort", "id desc"
|
||||
))));
|
||||
// System.out.println(deptWorkers);
|
||||
|
||||
// looping dave clones
|
||||
for (int p : new int []{1, peopleMultiplier}) {
|
||||
// looping dept clones
|
||||
for (int d : new int []{1, deptMultiplier}) {
|
||||
// looping coworkers
|
||||
int wPos = 1;
|
||||
for (Object mate : deptWorkers) {
|
||||
// (/response/result/doc/str[@name='name_s_dv'][.='dave']/..)[1]
|
||||
// /result[@name='subq1']/doc/str[@name='dept_id_s_dv'][.='Engineering']/..
|
||||
// /result[@name='neighbours']/doc/str[@name='name_s_dv'][.='tina']
|
||||
asserts.add("((/response/result/doc/str[@name='name_s_dv'][.='dave']/..)["+p+"]"+
|
||||
"/result[@name='subq1']/doc/str[@name='dept_id_s_dv'][.='"+dept+"']/..)["+ d +"]"+
|
||||
"/result[@name='neighbours']/doc[" + wPos + "]/str[@name='name_s_dv'][.='"+ mate+"']");
|
||||
wPos ++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
//System.out.println(asserts);
|
||||
assertQ("dave works at both dept with other folks",
|
||||
// System.out.println(h.query(
|
||||
req(new String[]{"q","name_s:dave", "indent","true",
|
||||
"fl","name_s_dv,subq1:[subquery]",
|
||||
"rows","" + peopleMultiplier,
|
||||
"subq1.q","{!terms f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"subq1.fl","text_t,dept_id_s_dv,neighbours:[subquery]",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2),
|
||||
"subq1.neighbours.q",//flipping via numbers
|
||||
random().nextBoolean() ?
|
||||
"{!terms f=dept_ss_dv v=$row.dept_id_s_dv}"
|
||||
: "{!terms f=dept_is v=$row.dept_id_i_dv}",
|
||||
"subq1.neighbours.fl", "name_s_dv" ,
|
||||
"subq1.neighbours.rows", ""+peopleMultiplier*3},
|
||||
"subq1.neighbours.sort", "id desc")//,
|
||||
,asserts.toArray(new String[]{})
|
||||
// )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoExplicitName() throws Exception {
|
||||
String[] john = new String[]{"q","name_s:john", "indent","true",
|
||||
"fl","name_s_dv,"
|
||||
+ "[subquery]",
|
||||
"rows","" + (2 * peopleMultiplier),
|
||||
"depts.q","+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i",
|
||||
"depts.fl","id",
|
||||
"depts.fl","score",
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+deptMultiplier,
|
||||
"depts.start","0"};
|
||||
|
||||
assertQEx("no prefix, no subquery", req(john), ErrorCode.BAD_REQUEST);
|
||||
|
||||
|
||||
assertQEx("no prefix, no subsubquery",
|
||||
req("q","name_s:john", "indent","true",
|
||||
"fl","name_s_dv,"
|
||||
+ "depts:[subquery]",
|
||||
"rows","" + (2 * peopleMultiplier),
|
||||
"depts.q","+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i",
|
||||
"depts.fl","id",
|
||||
"depts.fl","score",
|
||||
"depts.fl","[subquery]",// <- here is a trouble
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+deptMultiplier,
|
||||
"depts.start","0"), ErrorCode.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDupePrefix() throws Exception {
|
||||
assertQEx("subquery name clash", req(new String[]{"q","name_s:(john nancy)", "indent","true",
|
||||
"fl","name_s_dv,depts:[subquery]",
|
||||
"fl","depts:[subquery]",
|
||||
"rows","" + (2 * peopleMultiplier),
|
||||
"depts.q","{!term f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"depts.fl","text_t",
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+deptMultiplier,
|
||||
|
||||
"depts_i.q","{!term f=dept_id_i v=$depts_i.row.dept_i_dv}",
|
||||
"depts_i.fl","text_t", // multi val subquery param check
|
||||
"depts_i.fl","dept_id_s_dv",
|
||||
"depts_i.indent","true",
|
||||
"depts_i.rows",""+deptMultiplier}
|
||||
), ErrorCode.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJustJohnJson() throws Exception {
|
||||
|
||||
final SolrQueryRequest johnTwoFL = req(johnAndNancyParams);
|
||||
ModifiableSolrParams params = new ModifiableSolrParams(johnTwoFL.getParams());
|
||||
params.set("q","name_s:john");
|
||||
johnTwoFL.setParams(params);
|
||||
assertJQ(johnTwoFL,
|
||||
"/response/docs/[0]/depts/docs/[0]=={text_t:\"These guys develop stuff\"}",
|
||||
"/response/docs/[" + (peopleMultiplier-1) + "]/depts/docs/[" + (deptMultiplier-1) + "]=={text_t:\"These guys develop stuff\"}",
|
||||
|
||||
"/response/docs/[0]/depts_i/docs/[0]=={dept_id_s_dv:\"Engineering\", text_t:\"These guys develop stuff\"}",// seem like key order doesn't matter , well
|
||||
"/response/docs/[" + (peopleMultiplier-1) + "]/depts_i/docs/[" + (deptMultiplier-1) + "]=="
|
||||
+ "{text_t:\"These guys develop stuff\", dept_id_s_dv:\"Engineering\"}");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testJustJohnJavabin() throws Exception {
|
||||
final SolrQueryRequest johnTwoFL = req(johnAndNancyParams);
|
||||
ModifiableSolrParams params = new ModifiableSolrParams(johnTwoFL.getParams());
|
||||
params.set("q","name_s:john");
|
||||
params.set("wt","javabin");
|
||||
|
||||
johnTwoFL.setParams(params);
|
||||
|
||||
final NamedList<Object> unmarshalled;
|
||||
{
|
||||
SolrCore core = johnTwoFL.getCore();
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(johnTwoFL, rsp));
|
||||
|
||||
SolrQueryResponse response = h.queryAndResponse(
|
||||
johnTwoFL.getParams().get(CommonParams.QT), johnTwoFL);
|
||||
|
||||
BinaryQueryResponseWriter responseWriter = (BinaryQueryResponseWriter) core.getQueryResponseWriter(johnTwoFL);
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
responseWriter.write(bytes,johnTwoFL,response);
|
||||
|
||||
unmarshalled = (NamedList<Object>) new JavaBinCodec().unmarshal(
|
||||
new ByteArrayInputStream(bytes.toByteArray()));
|
||||
|
||||
johnTwoFL.close();
|
||||
SolrRequestInfo.clearRequestInfo();
|
||||
}
|
||||
|
||||
SolrDocumentList resultDocs = (SolrDocumentList)(unmarshalled.get("response"));
|
||||
|
||||
{
|
||||
Map<String,String> engText = new HashMap<>();
|
||||
engText.put("text_t", "These guys develop stuff");
|
||||
|
||||
Map<String,String> engId = new HashMap<>();
|
||||
engId.put("text_t", "These guys develop stuff");
|
||||
engId.put("dept_id_s_dv", "Engineering");
|
||||
|
||||
for (int docNum : new int []{0, peopleMultiplier-1}) {
|
||||
SolrDocument employeeDoc = resultDocs.get(docNum);
|
||||
assertEquals("john", employeeDoc.getFieldValue("name_s_dv"));
|
||||
for (String subResult : new String []{"depts", "depts_i"}) {
|
||||
|
||||
SolrDocumentList subDoc = (SolrDocumentList)employeeDoc.getFieldValue(subResult);
|
||||
for (int deptNum : new int []{0, deptMultiplier-1}) {
|
||||
SolrDocument deptDoc = subDoc.get(deptNum);
|
||||
Object expectedDept = (subResult.equals("depts") ? engText : engId);
|
||||
assertTrue( "" + expectedDept + " equals to " + deptDoc,
|
||||
expectedDept.equals(deptDoc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionPropagation() throws Exception {
|
||||
final SolrQueryRequest r = req("q","name_s:dave", "indent","true",
|
||||
"fl","depts:[subquery]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"depts.q","{!lucene}(",
|
||||
"depts.fl","text_t",
|
||||
"depts.indent","true",
|
||||
"depts.rows",""+(deptMultiplier*2),
|
||||
"depts.logParamsList","q,fl,rows,subq1.row.dept_ss_dv");
|
||||
|
||||
// System.out.println(h.query(r));
|
||||
|
||||
assertQEx("wrong subquery",
|
||||
r,
|
||||
ErrorCode.BAD_REQUEST);
|
||||
|
||||
assertQEx( "", req("q","name_s:dave", "indent","true",
|
||||
"fl","depts:[subquery]",
|
||||
"rows","1",
|
||||
"depts.q","{!lucene}",
|
||||
"depts.fl","text_t",
|
||||
"depts.indent","true",
|
||||
"depts.rows","NAN",
|
||||
"depts.logParamsList","q,fl,rows,subq1.row.dept_ss_dv"),
|
||||
ErrorCode.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiValue() throws Exception {
|
||||
|
||||
String [] happyPathAsserts = new String[]{
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1']/doc/str[@name='text_t'][.='These guys develop stuff'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1']/doc/str[@name='text_t'][.='These guys help customers'])="+
|
||||
(peopleMultiplier * deptMultiplier),
|
||||
"//result[@numFound="+peopleMultiplier+"]"};
|
||||
Random random1 = random();
|
||||
|
||||
assertQ("dave works at both, whether we set a default separator or both",
|
||||
req(new String[]{"q","name_s:dave", "indent","true",
|
||||
"fl",(random().nextBoolean() ? "name_s_dv" : "*")+ //"dept_ss_dv,
|
||||
",subq1:[subquery "
|
||||
+((random1.nextBoolean() ? "" : "separator=,"))+"]",
|
||||
"rows","" + peopleMultiplier,
|
||||
"subq1.q","{!terms f=dept_id_s v=$row.dept_ss_dv "+((random1.nextBoolean() ? "" : "separator=,"))+"}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2),
|
||||
"subq1.logParamsList","q,fl,rows,row.dept_ss_dv"}),
|
||||
happyPathAsserts
|
||||
);
|
||||
|
||||
assertQ("even via numbers",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_is_dv,name_s_dv,subq1:[subquery]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!terms f=dept_id_i v=$row.dept_is_dv}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
happyPathAsserts
|
||||
);
|
||||
|
||||
|
||||
assertQ("even if we set a separator both",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_ss_dv,name_s_dv,name_s_dv,subq1:[subquery separator=\" \"]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!terms f=dept_id_s v=$row.dept_ss_dv separator=\" \"}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
happyPathAsserts
|
||||
);
|
||||
|
||||
String [] noMatchAtSubQ = new String[] {
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1'][@numFound=0])="+
|
||||
(peopleMultiplier),
|
||||
"//result[@numFound="+peopleMultiplier+"]" };
|
||||
|
||||
assertQ("different separators, no match",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_ss_dv,name_s_dv,subq1:[subquery]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!terms f=dept_id_s v=$row.dept_ss_dv separator=\" \"}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
noMatchAtSubQ
|
||||
);
|
||||
|
||||
assertQ("and no matter where",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_ss_dv,name_s_dv,subq1:[subquery separator=\" \"]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!terms f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
noMatchAtSubQ
|
||||
);
|
||||
|
||||
assertQ("setting a wrong parser gets you nowhere",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_ss_dv,name_s_dv,subq1:[subquery]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!term f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
noMatchAtSubQ
|
||||
);
|
||||
|
||||
assertQ("but it luckily works with default query parser, but it's not really reliable",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_ss_dv,name_s_dv,subq1:[subquery separator=\" \"]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!lucene df=dept_id_s v=$row.dept_ss_dv}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
happyPathAsserts
|
||||
);
|
||||
|
||||
assertQ("even lucene qp can't help at any separator but space",
|
||||
req("q","name_s:dave", "indent","true",
|
||||
"fl","dept_ss_dv,name_s_dv,"
|
||||
+ "subq1:[subquery "+(random().nextBoolean() ? "" : "separator=" +((random().nextBoolean() ? "" : ",")))+"]",
|
||||
"rows","" + ( peopleMultiplier),
|
||||
"subq1.q","{!lucene df=dept_id_s v=$row.dept_ss_dv}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMultiplier*2)),
|
||||
noMatchAtSubQ
|
||||
);
|
||||
}
|
||||
|
||||
static String[] daveMultiValueSearchParams(Random random, int peopleMult, int deptMult) {
|
||||
return new String[]{"q","name_s:dave", "indent","true",
|
||||
"fl",(random().nextBoolean() ? "name_s_dv" : "*")+ //"dept_ss_dv,
|
||||
",subq1:[subquery "
|
||||
+((random.nextBoolean() ? "" : "separator=,"))+"]",
|
||||
"rows","" + peopleMult,
|
||||
"subq1.q","{!terms f=dept_id_s v=$row.dept_ss_dv "+((random.nextBoolean() ? "" : "separator=,"))+"}",
|
||||
"subq1.fl","text_t",
|
||||
"subq1.indent","true",
|
||||
"subq1.rows",""+(deptMult*2),
|
||||
"subq1.logParamsList","q,fl,rows,row.dept_ss_dv"};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.response.transform;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.SolrRequestHandler;
|
||||
import org.apache.solr.servlet.DirectSolrConnection;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class TestSubQueryTransformerCrossCore extends SolrTestCaseJ4 {
|
||||
|
||||
private static SolrCore fromCore;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeTests() throws Exception {
|
||||
System.setProperty("enable.update.log", "false"); // schema12 doesn't support _version_
|
||||
initCore("solrconfig-basic.xml","schema-docValuesJoin.xml");
|
||||
final CoreContainer coreContainer = h.getCoreContainer();
|
||||
|
||||
fromCore = coreContainer.create("fromCore", //FileSystems.getDefault().getPath( TEST_HOME()), ImmutableMap.of("config","solrconfig-basic.xml","schema","schema-docValuesJoin.xml"
|
||||
ImmutableMap.of("configSet", "minimal")
|
||||
);
|
||||
assertU(add(doc("id", "1","name_s", "john", "title_s", "Director", "dept_ss_dv","Engineering",
|
||||
"text_t","These guys develop stuff")));
|
||||
assertU(add(doc("id", "2","name_s", "mark", "title_s", "VP", "dept_ss_dv","Marketing",
|
||||
"text_t","These guys make you look good")));
|
||||
assertU(add(doc("id", "3","name_s", "nancy", "title_s", "MTS", "dept_ss_dv","Sales",
|
||||
"text_t","These guys sell stuff")));
|
||||
assertU(add(doc("id", "4","name_s", "dave", "title_s", "MTS", "dept_ss_dv","Support", "dept_ss_dv","Engineering"
|
||||
, "text_t","These guys help customers")));
|
||||
assertU(add(doc("id", "5","name_s", "tina", "title_s", "VP", "dept_ss_dv","Engineering",
|
||||
"text_t","These guys develop stuff")));
|
||||
assertU(commit());
|
||||
|
||||
update(fromCore, add(doc("id","10", "dept_id_s", "Engineering", "text_t","These guys develop stuff", "salary_i_dv", "1000")));
|
||||
update(fromCore, add(doc("id","11", "dept_id_s", "Marketing", "text_t","These guys make you look good","salary_i_dv", "1500")));
|
||||
update(fromCore, add(doc("id","12", "dept_id_s", "Sales", "text_t","These guys sell stuff","salary_i_dv", "1600")));
|
||||
update(fromCore, add(doc("id","13", "dept_id_s", "Support", "text_t","These guys help customers","salary_i_dv", "800")));
|
||||
update(fromCore, commit());
|
||||
}
|
||||
|
||||
|
||||
public static String update(SolrCore core, String xml) throws Exception {
|
||||
DirectSolrConnection connection = new DirectSolrConnection(core);
|
||||
SolrRequestHandler handler = core.getRequestHandler("/update");
|
||||
return connection.request(handler, null, xml);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameCoreSingleField() throws Exception {
|
||||
assertQ("subq1.fl is limited to single field",
|
||||
req("q","name_s:john",
|
||||
"fl","*,depts:[subquery fromIndex=fromCore]",
|
||||
"depts.q","{!term f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"depts.fl","text_t"),
|
||||
"//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc/str[@name='text_t'][.='These guys develop stuff']",
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc/*)=1");// only text_t
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbsentCore() throws Exception {
|
||||
assertQEx("from index not exist",
|
||||
req("q","name_s:dave",
|
||||
"fl","*,depts:[subquery fromIndex=fromCore2]",
|
||||
"depts.q","{!term f=dept_id_s v=$row.dept_ss_dv}",
|
||||
"depts.fl","text_t"),
|
||||
SolrException.ErrorCode.BAD_REQUEST
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCrossCoreSubQueryTransformer() throws Exception {
|
||||
|
||||
assertQ("make sure request is parsed in this core",
|
||||
req("q","name_s:john",
|
||||
"fl","*,depts:[subquery]",
|
||||
// text is tokenized and can be found, despite there is no substitution magic
|
||||
"depts.q","{!field f=text_t}These guys"),
|
||||
"//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc"
|
||||
);
|
||||
|
||||
assertQ("make sure request is parsed in that core",
|
||||
req("q","name_s:john",
|
||||
"fl","*,depts:[subquery fromIndex=fromCore]",
|
||||
// text is NOT tokenized and can NOT be found
|
||||
"depts.q","{!field f=text_t}These guys"),
|
||||
"count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc)=0"
|
||||
);
|
||||
|
||||
assertQ("make sure request is parsed in that core",
|
||||
req("q","-name_s:dave", "indent", "true",
|
||||
"fl","*,depts:[subquery fromIndex=fromCore]",
|
||||
// stored text (text_t is string in minimal configset) can be found as
|
||||
"depts.q","{!field f=text_t v=$row.text_t}",
|
||||
"depts.fl", "dept_id_s" ),
|
||||
"//result/doc/str[@name='name_s_dv'][.='john']/.."
|
||||
+ "/result[@name='depts']/doc/str[@name='dept_id_s'][.='Engineering']",
|
||||
"//result/doc/str[@name='name_s_dv'][.='tina']/.."
|
||||
+ "/result[@name='depts']/doc/str[@name='dept_id_s'][.='Engineering']",
|
||||
"//result/doc/str[@name='name_s_dv'][.='mark']/.."
|
||||
+ "/result[@name='depts']/doc/str[@name='dept_id_s'][.='Marketing']"
|
||||
);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void nukeAll() {
|
||||
fromCore = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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.response.transform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
|
||||
import org.apache.solr.client.solrj.request.QueryRequest;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.util.ContentStreamBase;
|
||||
import org.junit.Test;
|
||||
|
||||
@SuppressSSL
|
||||
public class TestSubQueryTransformerDistrib extends AbstractFullDistribZkTestBase {
|
||||
|
||||
@Override
|
||||
protected String getCloudSchemaFile() {
|
||||
return "schema-docValuesJoin.xml";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCloudSolrConfig() {
|
||||
return "solrconfig-basic.xml";
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void test() throws SolrServerException, IOException {
|
||||
int peopleMultiplier = atLeast(1);
|
||||
int deptMultiplier = atLeast(1);
|
||||
|
||||
String people = "people";
|
||||
int numPeopleShards;
|
||||
createCollection(people, atLeast(1), numPeopleShards = atLeast(2), numPeopleShards);
|
||||
|
||||
|
||||
String depts = "departments";
|
||||
int numDeptsShards;
|
||||
createCollection(depts, atLeast(1), numDeptsShards = atLeast(2), numDeptsShards);
|
||||
|
||||
|
||||
createIndex(people, peopleMultiplier, depts, deptMultiplier);
|
||||
|
||||
Random random1 = random();
|
||||
|
||||
{
|
||||
|
||||
final QueryRequest qr = new QueryRequest(params(
|
||||
new String[]{"q","name_s:dave", "indent","true",
|
||||
"fl","*,depts:[subquery "+((random1.nextBoolean() ? "" : "separator=,"))+"]",
|
||||
"rows","" + peopleMultiplier,
|
||||
"depts.q","{!terms f=dept_id_s v=$row.dept_ss_dv "+((random1.nextBoolean() ? "" : "separator=,"))+"}",
|
||||
"depts.fl","text_t",
|
||||
"depts.indent","true",
|
||||
"depts.collection","departments",
|
||||
"depts.rows",""+(deptMultiplier*2),
|
||||
"depts.logParamsList","q,fl,rows,row.dept_ss_dv"}));
|
||||
final QueryResponse rsp = new QueryResponse();
|
||||
rsp.setResponse(cloudClient.request(qr, people));
|
||||
final SolrDocumentList hits = rsp.getResults();
|
||||
|
||||
assertEquals(peopleMultiplier, hits.getNumFound());
|
||||
|
||||
Map<String,String> engText = new HashMap<String,String>() {
|
||||
{ put("text_t", "These guys develop stuff");
|
||||
}
|
||||
};
|
||||
Map<String,String> suppText = new HashMap<String,String>() {
|
||||
{ put("text_t", "These guys help customers");
|
||||
}
|
||||
};
|
||||
|
||||
int engineer = 0;
|
||||
int support = 0;
|
||||
|
||||
for (int res : new int [] {0, (peopleMultiplier-1) /2, peopleMultiplier-1}) {
|
||||
SolrDocument doc = hits.get(res);
|
||||
assertEquals("dave", doc.getFieldValue("name_s_dv"));
|
||||
SolrDocumentList relDepts = (SolrDocumentList) doc.getFieldValue("depts");
|
||||
assertEquals("dave works in both depts "+rsp,
|
||||
deptMultiplier * 2, relDepts.getNumFound());
|
||||
for (int deptN = 0 ; deptN < relDepts.getNumFound(); deptN++ ) {
|
||||
SolrDocument deptDoc = relDepts.get(deptN);
|
||||
assertTrue(deptDoc + "should be either "+engText +" or "+suppText,
|
||||
(engText.equals(deptDoc) && ++engineer>0) ||
|
||||
(suppText.equals(deptDoc) && ++support>0));
|
||||
}
|
||||
}
|
||||
assertEquals(hits.toString(), engineer, support);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void createIndex(String people, int peopleMultiplier, String depts, int deptMultiplier)
|
||||
throws SolrServerException, IOException {
|
||||
int id=0;
|
||||
List<String> peopleDocs = new ArrayList<>();
|
||||
for (int p=0; p < peopleMultiplier; p++){
|
||||
|
||||
peopleDocs.add(add(doc("id", ""+id++,"name_s", "john", "title_s", "Director",
|
||||
"dept_ss_dv","Engineering",
|
||||
"dept_i", "0",
|
||||
"dept_is", "0")));
|
||||
peopleDocs.add(add(doc("id", ""+id++,"name_s", "mark", "title_s", "VP",
|
||||
"dept_ss_dv","Marketing",
|
||||
"dept_i", "1",
|
||||
"dept_is", "1")));
|
||||
peopleDocs.add(add(doc("id", ""+id++,"name_s", "nancy", "title_s", "MTS",
|
||||
"dept_ss_dv","Sales",
|
||||
"dept_i", "2",
|
||||
"dept_is", "2")));
|
||||
peopleDocs.add(add(doc("id", ""+id++,"name_s", "dave", "title_s", "MTS",
|
||||
"dept_ss_dv","Support", "dept_ss_dv","Engineering",
|
||||
"dept_i", "3",
|
||||
"dept_is", "3", "dept_is", "0")));
|
||||
peopleDocs.add(add(doc("id", ""+id++,"name_s", "tina", "title_s", "VP",
|
||||
"dept_ss_dv","Engineering",
|
||||
"dept_i", "0",
|
||||
"dept_is", "0")));
|
||||
}
|
||||
|
||||
addDocs(people, peopleDocs);
|
||||
|
||||
List<String> deptsDocs = new ArrayList<>();
|
||||
for (int d=0; d < deptMultiplier; d++) {
|
||||
deptsDocs.add(add(doc("id",""+id++, "dept_id_s", "Engineering", "text_t","These guys develop stuff", "salary_i_dv", "1000",
|
||||
"dept_id_i", "0")));
|
||||
deptsDocs.add(add(doc("id",""+id++, "dept_id_s", "Marketing", "text_t","These guys make you look good","salary_i_dv", "1500",
|
||||
"dept_id_i", "1")));
|
||||
deptsDocs.add(add(doc("id",""+id++, "dept_id_s", "Sales", "text_t","These guys sell stuff","salary_i_dv", "1600",
|
||||
"dept_id_i", "2")));
|
||||
deptsDocs.add(add(doc("id",""+id++, "dept_id_s", "Support", "text_t","These guys help customers","salary_i_dv", "800",
|
||||
"dept_id_i", "3")));
|
||||
|
||||
}
|
||||
addDocs(depts, deptsDocs);
|
||||
}
|
||||
|
||||
private void addDocs(String collection, List<String> docs) throws SolrServerException, IOException {
|
||||
StringBuilder upd = new StringBuilder("<update>");
|
||||
for (Iterator<String> iterator = docs.iterator(); iterator.hasNext();) {
|
||||
String add = iterator.next();
|
||||
upd.append(add);
|
||||
if (rarely()) {
|
||||
upd.append(commit("softCommit", "true"));
|
||||
}
|
||||
if (!rarely() || !iterator.hasNext()) {
|
||||
if (!iterator.hasNext()) {
|
||||
upd.append(commit("softCommit", "false"));
|
||||
}
|
||||
upd.append("</update>");
|
||||
|
||||
ContentStreamUpdateRequest req = new ContentStreamUpdateRequest("/update");
|
||||
req.addContentStream(new ContentStreamBase.StringStream(upd.toString(),"text/xml"));
|
||||
|
||||
cloudClient.request(req, collection);
|
||||
upd.setLength("<update>".length());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ public abstract class AbstractDistribZkTestBase extends BaseDistributedSearchTes
|
|||
System.setProperty(ZOOKEEPER_FORCE_SYNC, "false");
|
||||
System.setProperty(MockDirectoryFactory.SOLR_TESTS_ALLOW_READING_FILES_STILL_OPEN_FOR_WRITE, "true");
|
||||
|
||||
String schema = getSchemaFile();
|
||||
String schema = getCloudSchemaFile();
|
||||
if (schema == null) schema = "schema.xml";
|
||||
AbstractZkTestCase.buildZooKeeper(zkServer.getZkHost(), zkServer.getZkAddress(), getCloudSolrConfig(), schema);
|
||||
|
||||
|
@ -83,6 +83,10 @@ public abstract class AbstractDistribZkTestBase extends BaseDistributedSearchTes
|
|||
return "solrconfig-tlog.xml";
|
||||
}
|
||||
|
||||
protected String getCloudSchemaFile() {
|
||||
return getSchemaFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createServers(int numShards) throws Exception {
|
||||
// give everyone there own solrhome
|
||||
|
|
Loading…
Reference in New Issue