You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2016/11/02 23:59:24 UTC

[27/50] [abbrv] lucene-solr:apiv2: SOLR-9442: Adds Array of NamedValuePair (json.nl=arrnvp) style to JSONResponseWriter. (Jonny Marks, Christine Poerschke)

SOLR-9442: Adds Array of NamedValuePair (json.nl=arrnvp) style to JSONResponseWriter. (Jonny Marks, Christine Poerschke)


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/87c6ec4c
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/87c6ec4c
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/87c6ec4c

Branch: refs/heads/apiv2
Commit: 87c6ec4cb0a91e1952e4dff31d6e1f92ed0806bf
Parents: 72bdbd2
Author: Christine Poerschke <cp...@apache.org>
Authored: Mon Oct 31 11:17:47 2016 +0000
Committer: Christine Poerschke <cp...@apache.org>
Committed: Mon Oct 31 11:19:39 2016 +0000

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   3 +
 .../solr/response/JSONResponseWriter.java       | 173 ++++++++++++++++++-
 .../apache/solr/response/JSONWriterTest.java    | 102 ++++++++++-
 3 files changed, 274 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/87c6ec4c/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 2f46416..09bf007 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -86,6 +86,9 @@ New Features
   Example:  { type:terms, field:category, filter:"user:yonik" } 
   (yonik)
 
+* SOLR-9442: Adds Array of NamedValuePair (json.nl=arrnvp) style to JSONResponseWriter.
+  (Jonny Marks, Christine Poerschke)
+
 Optimizations
 ----------------------
 * SOLR-9704: Facet Module / JSON Facet API: Optimize blockChildren facets that have

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/87c6ec4c/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java b/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java
index cd6648b..ad128d2 100644
--- a/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java
+++ b/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java
@@ -26,6 +26,7 @@ import java.util.Set;
 
 import org.apache.solr.common.SolrDocument;
 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.SimpleOrderedMap;
 import org.apache.solr.request.SolrQueryRequest;
@@ -50,7 +51,19 @@ public class JSONResponseWriter implements QueryResponseWriter {
 
   @Override
   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 {
       w.writeResponse();
     } finally {
@@ -66,13 +79,14 @@ public class JSONResponseWriter implements QueryResponseWriter {
 
 class JSONWriter extends TextResponseWriter {
   protected String wrapperFunction;
-  final private String namedListStyle;
+  final protected String namedListStyle;
 
   static final String JSON_NL_STYLE="json.nl";
   static final String JSON_NL_MAP="map";
   static final String JSON_NL_FLAT="flat";
   static final String JSON_NL_ARROFARR="arrarr";
   static final String JSON_NL_ARROFMAP="arrmap";
+  static final String JSON_NL_ARROFNVP="arrnvp";
   static final String JSON_WRAPPER_FUNCTION="json.wrf";
 
   public JSONWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) {
@@ -306,6 +320,9 @@ class JSONWriter extends TextResponseWriter {
       writeNamedListAsArrArr(name,val);
     } else if (namedListStyle==JSON_NL_ARROFMAP) {
       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 protected String getNaN();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/87c6ec4c/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java b/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java
index ad390cb..a61cff3 100644
--- a/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java
+++ b/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java
@@ -18,7 +18,11 @@ package org.apache.solr.response;
 
 import java.io.IOException;
 import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
@@ -72,7 +76,8 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
 
   @Test
   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();
     JSONResponseWriter w = new JSONResponseWriter();
 
@@ -87,7 +92,18 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
     rsp.add("bytes", "abc".getBytes(StandardCharsets.UTF_8));
 
     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();
   }
 
@@ -130,6 +146,87 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
 
     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
   public void testConstantsUnchanged() {
@@ -138,6 +235,7 @@ public class JSONWriterTest extends SolrTestCaseJ4 {
     assertEquals("flat", JSONWriter.JSON_NL_FLAT);
     assertEquals("arrarr", JSONWriter.JSON_NL_ARROFARR);
     assertEquals("arrmap", JSONWriter.JSON_NL_ARROFMAP);
+    assertEquals("arrnvp", JSONWriter.JSON_NL_ARROFNVP);
     assertEquals("json.wrf", JSONWriter.JSON_WRAPPER_FUNCTION);
   }