mirror of https://github.com/apache/lucene.git
SOLR-9442: Adds Array of NamedValuePair (json.nl=arrnvp) style to JSONResponseWriter. (Jonny Marks, Christine Poerschke)
This commit is contained in:
parent
72bdbd234e
commit
87c6ec4cb0
|
@ -86,6 +86,9 @@ New Features
|
||||||
Example: { type:terms, field:category, filter:"user:yonik" }
|
Example: { type:terms, field:category, filter:"user:yonik" }
|
||||||
(yonik)
|
(yonik)
|
||||||
|
|
||||||
|
* SOLR-9442: Adds Array of NamedValuePair (json.nl=arrnvp) style to JSONResponseWriter.
|
||||||
|
(Jonny Marks, Christine Poerschke)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-9704: Facet Module / JSON Facet API: Optimize blockChildren facets that have
|
* SOLR-9704: Facet Module / JSON Facet API: Optimize blockChildren facets that have
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.apache.solr.common.SolrDocument;
|
import org.apache.solr.common.SolrDocument;
|
||||||
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.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||||
import org.apache.solr.request.SolrQueryRequest;
|
import org.apache.solr.request.SolrQueryRequest;
|
||||||
|
@ -50,7 +51,19 @@ public class JSONResponseWriter implements QueryResponseWriter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
|
public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
|
||||||
JSONWriter w = new JSONWriter(writer, req, rsp);
|
final SolrParams params = req.getParams();
|
||||||
|
final String wrapperFunction = params.get(JSONWriter.JSON_WRAPPER_FUNCTION);
|
||||||
|
final String namedListStyle = params.get(JSONWriter.JSON_NL_STYLE, JSONWriter.JSON_NL_FLAT).intern();
|
||||||
|
|
||||||
|
final JSONWriter w;
|
||||||
|
if (namedListStyle.equals(JSONWriter.JSON_NL_ARROFNVP)) {
|
||||||
|
w = new ArrayOfNamedValuePairJSONWriter(
|
||||||
|
writer, req, rsp, wrapperFunction, namedListStyle);
|
||||||
|
} else {
|
||||||
|
w = new JSONWriter(
|
||||||
|
writer, req, rsp, wrapperFunction, namedListStyle);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
w.writeResponse();
|
w.writeResponse();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -66,13 +79,14 @@ public class JSONResponseWriter implements QueryResponseWriter {
|
||||||
|
|
||||||
class JSONWriter extends TextResponseWriter {
|
class JSONWriter extends TextResponseWriter {
|
||||||
protected String wrapperFunction;
|
protected String wrapperFunction;
|
||||||
final private String namedListStyle;
|
final protected String namedListStyle;
|
||||||
|
|
||||||
static final String JSON_NL_STYLE="json.nl";
|
static final String JSON_NL_STYLE="json.nl";
|
||||||
static final String JSON_NL_MAP="map";
|
static final String JSON_NL_MAP="map";
|
||||||
static final String JSON_NL_FLAT="flat";
|
static final String JSON_NL_FLAT="flat";
|
||||||
static final String JSON_NL_ARROFARR="arrarr";
|
static final String JSON_NL_ARROFARR="arrarr";
|
||||||
static final String JSON_NL_ARROFMAP="arrmap";
|
static final String JSON_NL_ARROFMAP="arrmap";
|
||||||
|
static final String JSON_NL_ARROFNVP="arrnvp";
|
||||||
static final String JSON_WRAPPER_FUNCTION="json.wrf";
|
static final String JSON_WRAPPER_FUNCTION="json.wrf";
|
||||||
|
|
||||||
public JSONWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) {
|
public JSONWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||||
|
@ -306,6 +320,9 @@ class JSONWriter extends TextResponseWriter {
|
||||||
writeNamedListAsArrArr(name,val);
|
writeNamedListAsArrArr(name,val);
|
||||||
} else if (namedListStyle==JSON_NL_ARROFMAP) {
|
} else if (namedListStyle==JSON_NL_ARROFMAP) {
|
||||||
writeNamedListAsArrMap(name,val);
|
writeNamedListAsArrMap(name,val);
|
||||||
|
} else if (namedListStyle==JSON_NL_ARROFNVP) {
|
||||||
|
throw new UnsupportedOperationException(namedListStyle
|
||||||
|
+ " namedListStyle must only be used with "+ArrayOfNamedValuePairJSONWriter.class.getSimpleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,6 +605,158 @@ class JSONWriter extends TextResponseWriter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes NamedLists directly as an array of NamedValuePair JSON objects...
|
||||||
|
* NamedList("a"=1,"b"=2,null=3) => [{"name":"a","int":1},{"name":"b","int":2},{"int":3}]
|
||||||
|
* NamedList("a"=1,"bar"="foo",null=3.4f) => [{"name":"a","int":1},{"name":"bar","str":"foo"},{"float":3.4}]
|
||||||
|
*/
|
||||||
|
class ArrayOfNamedValuePairJSONWriter extends JSONWriter {
|
||||||
|
private boolean writeTypeAsKey = false;
|
||||||
|
|
||||||
|
public ArrayOfNamedValuePairJSONWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp,
|
||||||
|
String wrapperFunction, String namedListStyle) {
|
||||||
|
super(writer, req, rsp, wrapperFunction, namedListStyle);
|
||||||
|
if (namedListStyle != JSON_NL_ARROFNVP) {
|
||||||
|
throw new UnsupportedOperationException(ArrayOfNamedValuePairJSONWriter.class.getSimpleName()+" must only be used with "
|
||||||
|
+ JSON_NL_ARROFNVP + " style");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeNamedList(String name, NamedList val) throws IOException {
|
||||||
|
|
||||||
|
if (val instanceof SimpleOrderedMap) {
|
||||||
|
super.writeNamedList(name, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int sz = val.size();
|
||||||
|
indent();
|
||||||
|
|
||||||
|
writeArrayOpener(sz);
|
||||||
|
incLevel();
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
for (int i=0; i<sz; i++) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
writeArraySeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
indent();
|
||||||
|
|
||||||
|
final String elementName = val.getName(i);
|
||||||
|
final Object elementVal = val.getVal(i);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JSONWriter's writeNamedListAsArrMap turns NamedList("bar"="foo") into [{"foo":"bar"}]
|
||||||
|
* but we here wish to turn it into [ {"name":"bar","str":"foo"} ] instead.
|
||||||
|
*
|
||||||
|
* So first we write the <code>{"name":"bar",</code> portion ...
|
||||||
|
*/
|
||||||
|
writeMapOpener(-1);
|
||||||
|
if (elementName != null) {
|
||||||
|
writeKey("name", false);
|
||||||
|
writeVal("name", elementName);
|
||||||
|
writeMapSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ... and then we write the <code>"str":"foo"}</code> portion.
|
||||||
|
*/
|
||||||
|
writeTypeAsKey = true;
|
||||||
|
writeVal(null, elementVal); // passing null since writeVal doesn't actually use name (and we already wrote elementName above)
|
||||||
|
if (writeTypeAsKey) {
|
||||||
|
throw new RuntimeException("writeTypeAsKey should have been reset to false by writeVal('"+elementName+"','"+elementVal+"')");
|
||||||
|
}
|
||||||
|
writeMapCloser();
|
||||||
|
}
|
||||||
|
|
||||||
|
decLevel();
|
||||||
|
writeArrayCloser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ifNeededWriteTypeAsKey(String type) throws IOException {
|
||||||
|
if (writeTypeAsKey) {
|
||||||
|
writeTypeAsKey = false;
|
||||||
|
writeKey(type, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeInt(String name, String val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("int");
|
||||||
|
super.writeInt(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeLong(String name, String val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("long");
|
||||||
|
super.writeLong(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeFloat(String name, String val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("float");
|
||||||
|
super.writeFloat(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeDouble(String name, String val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("double");
|
||||||
|
super.writeDouble(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBool(String name, String val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("bool");
|
||||||
|
super.writeBool(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeDate(String name, String val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("date");
|
||||||
|
super.writeDate(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeStr(String name, String val, boolean needsEscaping) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("str");
|
||||||
|
super.writeStr(name, val, needsEscaping);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeSolrDocument(String name, SolrDocument doc, ReturnFields returnFields, int idx) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("doc");
|
||||||
|
super.writeSolrDocument(name, doc, returnFields, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeStartDocumentList(String name, long start, int size, long numFound, Float maxScore) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("doclist");
|
||||||
|
super.writeStartDocumentList(name, start, size, numFound, maxScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeMap(String name, Map val, boolean excludeOuter, boolean isFirstVal) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("map");
|
||||||
|
super.writeMap(name, val, excludeOuter, isFirstVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeArray(String name, Iterator val) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("array");
|
||||||
|
super.writeArray(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeNull(String name) throws IOException {
|
||||||
|
ifNeededWriteTypeAsKey("null");
|
||||||
|
super.writeNull(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class NaNFloatWriter extends JSONWriter {
|
abstract class NaNFloatWriter extends JSONWriter {
|
||||||
|
|
||||||
abstract protected String getNaN();
|
abstract protected String getNaN();
|
||||||
|
|
|
@ -18,7 +18,11 @@ package org.apache.solr.response;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.solr.SolrTestCaseJ4;
|
import org.apache.solr.SolrTestCaseJ4;
|
||||||
import org.apache.solr.common.SolrDocument;
|
import org.apache.solr.common.SolrDocument;
|
||||||
import org.apache.solr.common.SolrDocumentList;
|
import org.apache.solr.common.SolrDocumentList;
|
||||||
|
@ -72,7 +76,8 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJSON() throws IOException {
|
public void testJSON() throws IOException {
|
||||||
SolrQueryRequest req = req("wt","json","json.nl","arrarr");
|
final String namedListStyle = (random().nextBoolean() ? JSONWriter.JSON_NL_ARROFARR : JSONWriter.JSON_NL_ARROFNVP);
|
||||||
|
SolrQueryRequest req = req("wt","json","json.nl",namedListStyle);
|
||||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||||
JSONResponseWriter w = new JSONResponseWriter();
|
JSONResponseWriter w = new JSONResponseWriter();
|
||||||
|
|
||||||
|
@ -87,7 +92,18 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
|
||||||
rsp.add("bytes", "abc".getBytes(StandardCharsets.UTF_8));
|
rsp.add("bytes", "abc".getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
w.write(buf, req, rsp);
|
w.write(buf, req, rsp);
|
||||||
jsonEq("{\"nl\":[[\"data1\",\"he\\u2028llo\\u2029!\"],[null,42]],\"byte\":-3,\"short\":-4,\"bytes\":\"YWJj\"}", buf.toString());
|
|
||||||
|
final String expectedNLjson;
|
||||||
|
if (namedListStyle == JSONWriter.JSON_NL_ARROFARR) {
|
||||||
|
expectedNLjson = "\"nl\":[[\"data1\",\"he\\u2028llo\\u2029!\"],[null,42]]";
|
||||||
|
} else if (namedListStyle == JSONWriter.JSON_NL_ARROFNVP) {
|
||||||
|
expectedNLjson = "\"nl\":[{\"name\":\"data1\",\"str\":\"he\\u2028llo\\u2029!\"},{\"int\":42}]";
|
||||||
|
} else {
|
||||||
|
expectedNLjson = null;
|
||||||
|
fail("unexpected namedListStyle="+namedListStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonEq("{"+expectedNLjson+",\"byte\":-3,\"short\":-4,\"bytes\":\"YWJj\"}", buf.toString());
|
||||||
req.close();
|
req.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +147,87 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
|
||||||
req.close();
|
req.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrnvpWriterOverridesAllWrites() {
|
||||||
|
// List rather than Set because two not-overridden methods could share name but not signature
|
||||||
|
final List<String> methodsExpectedNotOverriden = new ArrayList<>(14);
|
||||||
|
methodsExpectedNotOverriden.add("writeResponse");
|
||||||
|
methodsExpectedNotOverriden.add("writeKey");
|
||||||
|
methodsExpectedNotOverriden.add("writeNamedListAsMapMangled");
|
||||||
|
methodsExpectedNotOverriden.add("writeNamedListAsMapWithDups");
|
||||||
|
methodsExpectedNotOverriden.add("writeNamedListAsArrMap");
|
||||||
|
methodsExpectedNotOverriden.add("writeNamedListAsArrArr");
|
||||||
|
methodsExpectedNotOverriden.add("writeNamedListAsFlat");
|
||||||
|
methodsExpectedNotOverriden.add("writeEndDocumentList");
|
||||||
|
methodsExpectedNotOverriden.add("writeMapOpener");
|
||||||
|
methodsExpectedNotOverriden.add("writeMapSeparator");
|
||||||
|
methodsExpectedNotOverriden.add("writeMapCloser");
|
||||||
|
methodsExpectedNotOverriden.add("writeArrayOpener");
|
||||||
|
methodsExpectedNotOverriden.add("writeArraySeparator");
|
||||||
|
methodsExpectedNotOverriden.add("writeArrayCloser");
|
||||||
|
|
||||||
|
final Class<?> subClass = ArrayOfNamedValuePairJSONWriter.class;
|
||||||
|
final Class<?> superClass = subClass.getSuperclass();
|
||||||
|
|
||||||
|
for (final Method superClassMethod : superClass.getDeclaredMethods()) {
|
||||||
|
final String methodName = superClassMethod.getName();
|
||||||
|
if (!methodName.startsWith("write")) continue;
|
||||||
|
|
||||||
|
final int modifiers = superClassMethod.getModifiers();
|
||||||
|
if (Modifier.isFinal(modifiers)) continue;
|
||||||
|
if (Modifier.isStatic(modifiers)) continue;
|
||||||
|
if (Modifier.isPrivate(modifiers)) continue;
|
||||||
|
|
||||||
|
final boolean expectOverriden = !methodsExpectedNotOverriden.contains(methodName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Method subClassMethod = subClass.getDeclaredMethod(
|
||||||
|
superClassMethod.getName(),
|
||||||
|
superClassMethod.getParameterTypes());
|
||||||
|
|
||||||
|
if (expectOverriden) {
|
||||||
|
assertEquals("getReturnType() difference",
|
||||||
|
superClassMethod.getReturnType(),
|
||||||
|
subClassMethod.getReturnType());
|
||||||
|
} else {
|
||||||
|
fail(subClass + " must not override '" + superClassMethod + "'");
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
if (expectOverriden) {
|
||||||
|
fail(subClass + " needs to override '" + superClassMethod + "'");
|
||||||
|
} else {
|
||||||
|
assertTrue(methodName+" not found in remaining "+methodsExpectedNotOverriden, methodsExpectedNotOverriden.remove(methodName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue("methodsExpected NotOverriden but NotFound instead: "+methodsExpectedNotOverriden,
|
||||||
|
methodsExpectedNotOverriden.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrnvpWriterLacksMethodsOfItsOwn() {
|
||||||
|
final Class<?> subClass = ArrayOfNamedValuePairJSONWriter.class;
|
||||||
|
final Class<?> superClass = subClass.getSuperclass();
|
||||||
|
// ArrayOfNamedValuePairJSONWriter is a simple sub-class
|
||||||
|
// which should have (almost) no methods of its own
|
||||||
|
for (final Method subClassMethod : subClass.getDeclaredMethods()) {
|
||||||
|
// only own private method of its own
|
||||||
|
if (subClassMethod.getName().equals("ifNeededWriteTypeAsKey")) continue;
|
||||||
|
try {
|
||||||
|
final Method superClassMethod = superClass.getDeclaredMethod(
|
||||||
|
subClassMethod.getName(),
|
||||||
|
subClassMethod.getParameterTypes());
|
||||||
|
|
||||||
|
assertEquals("getReturnType() difference",
|
||||||
|
subClassMethod.getReturnType(),
|
||||||
|
superClassMethod.getReturnType());
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
fail(subClass + " should not have '" + subClassMethod + "' method of its own");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstantsUnchanged() {
|
public void testConstantsUnchanged() {
|
||||||
assertEquals("json.nl", JSONWriter.JSON_NL_STYLE);
|
assertEquals("json.nl", JSONWriter.JSON_NL_STYLE);
|
||||||
|
@ -138,6 +235,7 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
|
||||||
assertEquals("flat", JSONWriter.JSON_NL_FLAT);
|
assertEquals("flat", JSONWriter.JSON_NL_FLAT);
|
||||||
assertEquals("arrarr", JSONWriter.JSON_NL_ARROFARR);
|
assertEquals("arrarr", JSONWriter.JSON_NL_ARROFARR);
|
||||||
assertEquals("arrmap", JSONWriter.JSON_NL_ARROFMAP);
|
assertEquals("arrmap", JSONWriter.JSON_NL_ARROFMAP);
|
||||||
|
assertEquals("arrnvp", JSONWriter.JSON_NL_ARROFNVP);
|
||||||
assertEquals("json.wrf", JSONWriter.JSON_WRAPPER_FUNCTION);
|
assertEquals("json.wrf", JSONWriter.JSON_WRAPPER_FUNCTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue