SOLR-12947: Add MapWriter compatibility to JsonQueryRequest

JsonQueryRequest had `setQuery` methods that took in a query either as a
String or as a Map.  But no such overload for MapWriter, a SolrJ
interface used to transmit Maps via "push writing" over the wire.  This
commit adds an overload taking this type, so that users can specify
their queries this way as well.

This commit also changes JsonQueryRequest writes out the request, to
ensure it uses "push writing" in non-MapWriter cases as well.
This commit is contained in:
Jason Gerlowski 2018-11-07 15:11:24 -05:00
parent 65dc312daf
commit 1b084db901
3 changed files with 79 additions and 8 deletions

View File

@ -19,8 +19,6 @@ package org.apache.solr.client.solrj.request.json;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -29,6 +27,7 @@ import java.util.Map;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.Utils;
@ -102,6 +101,37 @@ public class JsonQueryRequest extends QueryRequest {
return this;
}
/**
* Specify the query sent as a part of this JSON request.
*
* This method may be called multiple times, but each call overwrites the value specified by previous calls.
* <p>
* <b>Example:</b> You wish to send the JSON request: "{'limit': 5, 'query': {'lucene': {'df':'genre_s', 'query': 'scifi'}}}". The
* query subtree of this request is: "{'lucene': {'df': 'genre_s', 'query': 'scifi'}}". You would represent this query
* JSON as follows:
* <pre>
* final MapWriter queryWriter = new MapWriter() {
* &#64;Override
* public void writeMap(EntryWriter ew) throws IOException {
* ew.put("lucene", (MapWriter) queryParamWriter -&#62; {
* queryParamWriter.put("df", "genre_s");
* queryParamWriter.put("query", "scifi");
* });
* }
* };
* </pre>
*
* @param queryWriter a MapWriter capable of writing out the query subtree of the JSON request you wish to send.
* @throws IllegalArgumentException if {@code queryWriter} is null.
*/
public JsonQueryRequest setQuery(MapWriter queryWriter) {
if (queryWriter == null) {
throw new IllegalArgumentException("'queryWriter' parameter must be non-null");
}
jsonRequestMap.put("query", queryWriter);
return this;
}
/**
* Specify whether results should be fetched starting from a particular offset (or 'start').
*
@ -266,11 +296,7 @@ public class JsonQueryRequest extends QueryRequest {
return new RequestWriter.ContentWriter() {
@Override
public void write(OutputStream os) throws IOException {
//TODO consider whether using Utils.writeJson would work here as that'd be more mem efficient
OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8);
writer.write(Utils.toJSONString(jsonRequestMap));
writer.flush();
Utils.writeJson(jsonRequestMap, os, true);
}
@Override

View File

@ -18,6 +18,7 @@
package org.apache.solr.client.solrj.request.json;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -27,10 +28,10 @@ import java.util.Map;
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
import org.apache.solr.client.solrj.request.json.JsonQueryRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.ModifiableSolrParams;
@ -115,6 +116,25 @@ public class JsonQueryRequestIntegrationTest extends SolrCloudTestCase {
assertEquals(NUM_SCIFI_BOOKS, queryResponse.getResults().getNumFound());
}
@Test
public void testQueriesCanBeRepresentedUsingMapWriters() throws Exception {
final MapWriter queryWriter = new MapWriter() {
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put("lucene", (MapWriter) queryParamWriter -> {
queryParamWriter.put("df", "genre_s");
queryParamWriter.put("query", "scifi");
});
}
};
final JsonQueryRequest simpleQuery = new JsonQueryRequest()
.setQuery(queryWriter);
QueryResponse queryResponse = simpleQuery.process(cluster.getSolrClient(), COLLECTION_NAME);
assertEquals(0, queryResponse.getStatus());
assertEquals(NUM_SCIFI_BOOKS, queryResponse.getResults().getNumFound());
}
@Test
public void testQueriesCanBeNested() throws Exception {
final Map<String, Object> queryJsonMap = new HashMap<>();

View File

@ -27,6 +27,7 @@ import org.apache.lucene.util.LuceneTestCase;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.json.JsonQueryRequest;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.common.MapWriter;
import org.junit.Test;
import static org.junit.internal.matchers.StringContains.containsString;
@ -53,6 +54,14 @@ public class JsonQueryRequestUnitTest extends LuceneTestCase {
assertThat(thrown.getMessage(),containsString("must be non-null"));
}
@Test
public void testRejectsNullQueryMapWriter() {
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
new JsonQueryRequest().setQuery((MapWriter)null);
});
assertThat(thrown.getMessage(),containsString("must be non-null"));
}
@Test
public void testWritesProvidedQueryStringToJsonCorrectly() {
final JsonQueryRequest request = new JsonQueryRequest().setQuery("text:solr");
@ -72,6 +81,22 @@ public class JsonQueryRequestUnitTest extends LuceneTestCase {
assertThat(requestBody, containsString("\"query\":{\"lucene\":{\"q\":\"*:*\",\"df\":\"text\"}}"));
}
@Test
public void testWritesProvidedQueryMapWriterToJsonCorrectly() {
final MapWriter queryWriter = new MapWriter() {
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put("lucene", (MapWriter) ew1 -> {
ew1.put("q", "*:*");
ew1.put("df", "text");
});
}
};
final JsonQueryRequest request = new JsonQueryRequest().setQuery(queryWriter);
final String requestBody = writeRequestToJson(request);
assertThat(requestBody, containsString("\"query\":{\"lucene\":{\"q\":\"*:*\",\"df\":\"text\"}}"));
}
@Test
public void testRejectsInvalidLimit() {
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {