You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2016/09/06 07:53:59 UTC
lucene-solr:branch_6x: SOLR-9127: Excel workbook (.xlsx) response
writer. use 'wt=xlsx'
Repository: lucene-solr
Updated Branches:
refs/heads/branch_6x b32de1ec7 -> 932c436d0
SOLR-9127: Excel workbook (.xlsx) response writer. use 'wt=xlsx'
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/932c436d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/932c436d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/932c436d
Branch: refs/heads/branch_6x
Commit: 932c436d07dd3fd2cef0f1cb8319d5953433687e
Parents: b32de1e
Author: Noble Paul <no...@apache.org>
Authored: Tue Sep 6 13:22:18 2016 +0530
Committer: Noble Paul <no...@apache.org>
Committed: Tue Sep 6 13:23:29 2016 +0530
----------------------------------------------------------------------
solr/CHANGES.txt | 4 +-
.../handler/extraction/XLSXResponseWriter.java | 414 +++++++++++++++++++
.../extraction/solr/collection1/conf/schema.xml | 2 +
.../extraction/TestXLSXResponseWriter.java | 257 ++++++++++++
.../src/java/org/apache/solr/core/SolrCore.java | 8 +-
5 files changed, 683 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/932c436d/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 2c77acc..32004ca 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -44,7 +44,9 @@ prefix, then you will now get an error as these options are incompatible with nu
New Features
----------------------
* SOLR-5725: facet.method=enum can bypass exact counts calculation with facet.exists=true, it just returns 1 for
- terms which exists in result docset. (Alexey Kozhemiakin, Sebastian Koziel, Radoslaw Zielinski via Mikhail Khludnev)
+ terms which exists in result docset. (Alexey Kozhemiakin, Sebastian Koziel, Radoslaw Zielinski via Mikhail Khludnev)
+
+* SOLR-9127: Excel workbook (.xlsx) response writer. use 'wt=xlsx' (Tony Moriarty, noble)
Bug Fixes
----------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/932c436d/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java b/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java
new file mode 100644
index 0000000..27a30d1
--- /dev/null
+++ b/solr/contrib/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java
@@ -0,0 +1,414 @@
+/*
+ * 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.handler.extraction;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import org.apache.lucene.index.IndexableField;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.BasicResultContext;
+import org.apache.solr.response.RawResponseWriter;
+import org.apache.solr.response.ResultContext;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.response.TextResponseWriter;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.schema.StrField;
+import org.apache.solr.search.DocList;
+import org.apache.solr.search.ReturnFields;
+
+public class XLSXResponseWriter extends RawResponseWriter {
+
+ @Override
+ public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
+ // throw away arraywriter just to satisfy super requirements; we're grabbing
+ // all writes before they go to it anyway
+ XLSXWriter w = new XLSXWriter(new CharArrayWriter(), req, rsp);
+
+ LinkedHashMap<String,String> reqNamesMap = new LinkedHashMap<>();
+ LinkedHashMap<String,Integer> reqWidthsMap = new LinkedHashMap<>();
+
+ Iterator<String> paramNamesIter = req.getParams().getParameterNamesIterator();
+ while (paramNamesIter.hasNext()) {
+ String nextParam = paramNamesIter.next();
+ if (nextParam.startsWith("colname.")) {
+ String field = nextParam.substring("colname.".length());
+ reqNamesMap.put(field, req.getParams().get(nextParam));
+ } else if (nextParam.startsWith("colwidth.")) {
+ String field = nextParam.substring("colwidth.".length());
+ reqWidthsMap.put(field, req.getParams().getInt(nextParam));
+ }
+ }
+
+ try {
+ w.writeResponse(out, reqNamesMap, reqWidthsMap);
+ } finally {
+ w.close();
+ }
+ }
+
+ @Override
+ public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ }
+}
+
+class XLSXWriter extends TextResponseWriter {
+
+ SolrQueryRequest req;
+ SolrQueryResponse rsp;
+
+ class SerialWriteWorkbook {
+ SXSSFWorkbook swb;
+ Sheet sh;
+
+ XSSFCellStyle headerStyle;
+ int rowIndex;
+ Row curRow;
+ int cellIndex;
+
+ SerialWriteWorkbook() {
+ this.swb = new SXSSFWorkbook(100);
+ this.sh = this.swb.createSheet();
+
+ this.rowIndex = 0;
+
+ this.headerStyle = (XSSFCellStyle)swb.createCellStyle();
+ this.headerStyle.setFillBackgroundColor(IndexedColors.BLACK.getIndex());
+ //solid fill
+ this.headerStyle.setFillPattern((short)1);
+ Font headerFont = swb.createFont();
+ headerFont.setFontHeightInPoints((short)14);
+ headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
+ headerFont.setColor(IndexedColors.WHITE.getIndex());
+ this.headerStyle.setFont(headerFont);
+ }
+
+ void addRow() {
+ curRow = sh.createRow(rowIndex++);
+ cellIndex = 0;
+ }
+
+ void setHeaderRow() {
+ curRow.setHeightInPoints((short)21);
+ }
+
+ //sets last created cell to have header style
+ void setHeaderCell() {
+ curRow.getCell(cellIndex - 1).setCellStyle(this.headerStyle);
+ }
+
+ //set the width of the most recently created column
+ void setColWidth(int charWidth) {
+ //width in poi is units of 1/256th of a character width for some reason
+ this.sh.setColumnWidth(cellIndex - 1, 256*charWidth);
+ }
+
+ void writeCell(String value) {
+ Cell cell = curRow.createCell(cellIndex++);
+ cell.setCellValue(value);
+ }
+
+ void flush(OutputStream out) {
+ try {
+ swb.write(out);
+ } catch (IOException e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ String stacktrace = sw.toString();
+ }finally {
+ swb.dispose();
+ }
+ }
+ }
+
+ private SerialWriteWorkbook wb = new SerialWriteWorkbook();
+
+ static class XLField {
+ String name;
+ SchemaField sf;
+ }
+
+ private Map<String,XLField> xlFields = new LinkedHashMap<String,XLField>();
+
+ public XLSXWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp){
+ super(writer, req, rsp);
+ this.req = req;
+ this.rsp = rsp;
+ }
+
+ public void writeResponse(OutputStream out, LinkedHashMap<String, String> colNamesMap,
+ LinkedHashMap<String, Integer> colWidthsMap) throws IOException {
+ SolrParams params = req.getParams();
+
+ Collection<String> fields = returnFields.getRequestedFieldNames();
+ Object responseObj = rsp.getValues().get("response");
+ boolean returnOnlyStored = false;
+ if (fields==null||returnFields.hasPatternMatching()) {
+ if (responseObj instanceof SolrDocumentList) {
+ // get the list of fields from the SolrDocumentList
+ if(fields==null) {
+ fields = new LinkedHashSet<String>();
+ }
+ for (SolrDocument sdoc: (SolrDocumentList)responseObj) {
+ fields.addAll(sdoc.getFieldNames());
+ }
+ } else {
+ // get the list of fields from the index
+ Iterable<String> all = req.getSearcher().getFieldNames();
+ if (fields == null) {
+ fields = Sets.newHashSet(all);
+ } else {
+ Iterables.addAll(fields, all);
+ }
+ }
+ if (returnFields.wantsScore()) {
+ fields.add("score");
+ } else {
+ fields.remove("score");
+ }
+ returnOnlyStored = true;
+ }
+
+ for (String field : fields) {
+ if (!returnFields.wantsField(field)) {
+ continue;
+ }
+ if (field.equals("score")) {
+ XLField xlField = new XLField();
+ xlField.name = "score";
+ xlFields.put("score", xlField);
+ continue;
+ }
+
+ SchemaField sf = schema.getFieldOrNull(field);
+ if (sf == null) {
+ FieldType ft = new StrField();
+ sf = new SchemaField(field, ft);
+ }
+
+ // Return only stored fields, unless an explicit field list is specified
+ if (returnOnlyStored && sf != null && !sf.stored()) {
+ continue;
+ }
+
+ XLField xlField = new XLField();
+ xlField.name = field;
+ xlField.sf = sf;
+ xlFields.put(field, xlField);
+ }
+
+
+
+ wb.addRow();
+ //write header
+ for (XLField xlField : xlFields.values()) {
+ String printName = xlField.name;
+ int colWidth = 14;
+
+ String niceName = colNamesMap.get(xlField.name);
+ if (niceName != null) {
+ printName = niceName;
+ }
+
+ Integer niceWidth = colWidthsMap.get(xlField.name);
+ if (niceWidth != null) {
+ colWidth = niceWidth.intValue();
+ }
+
+ writeStr(xlField.name, printName, false);
+ wb.setColWidth(colWidth);
+ wb.setHeaderCell();
+ }
+ wb.setHeaderRow();
+ wb.addRow();
+
+ if (responseObj instanceof ResultContext) {
+ writeDocuments(null, (ResultContext)responseObj );
+ }
+ else if (responseObj instanceof DocList) {
+ ResultContext ctx = new BasicResultContext((DocList)responseObj, returnFields, null, null, req);
+ writeDocuments(null, ctx );
+ } else if (responseObj instanceof SolrDocumentList) {
+ writeSolrDocumentList(null, (SolrDocumentList)responseObj, returnFields );
+ }
+
+ wb.flush(out);
+ wb = null;
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ }
+
+ @Override
+ public void writeNamedList(String name, NamedList val) throws IOException {
+ }
+
+ @Override
+ public void writeStartDocumentList(String name,
+ long start, int size, long numFound, Float maxScore) throws IOException
+ {
+ // nothing
+ }
+
+ @Override
+ public void writeEndDocumentList() throws IOException
+ {
+ // nothing
+ }
+
+ //NOTE: a document cannot currently contain another document
+ List tmpList;
+ @Override
+ public void writeSolrDocument(String name, SolrDocument doc, ReturnFields returnFields, int idx ) throws IOException {
+ if (tmpList == null) {
+ tmpList = new ArrayList(1);
+ tmpList.add(null);
+ }
+
+ for (XLField xlField : xlFields.values()) {
+ Object val = doc.getFieldValue(xlField.name);
+ int nVals = val instanceof Collection ? ((Collection)val).size() : (val==null ? 0 : 1);
+ if (nVals == 0) {
+ writeNull(xlField.name);
+ continue;
+ }
+
+ if ((xlField.sf != null && xlField.sf.multiValued()) || nVals > 1) {
+ Collection values;
+ // normalize to a collection
+ if (val instanceof Collection) {
+ values = (Collection)val;
+ } else {
+ tmpList.set(0, val);
+ values = tmpList;
+ }
+
+ writeArray(xlField.name, values.iterator());
+
+ } else {
+ // normalize to first value
+ if (val instanceof Collection) {
+ Collection values = (Collection)val;
+ val = values.iterator().next();
+ }
+ writeVal(xlField.name, val);
+ }
+ }
+ wb.addRow();
+ }
+
+ @Override
+ public void writeStr(String name, String val, boolean needsEscaping) throws IOException {
+ wb.writeCell(val);
+ }
+
+ @Override
+ public void writeMap(String name, Map val, boolean excludeOuter, boolean isFirstVal) throws IOException {
+ }
+
+ @Override
+ public void writeArray(String name, Iterator val) throws IOException {
+ StringBuffer output = new StringBuffer();
+ while (val.hasNext()) {
+ Object v = val.next();
+ if (v instanceof IndexableField) {
+ IndexableField f = (IndexableField)v;
+ if (v instanceof Date) {
+ output.append(((Date) val).toInstant().toString() + "; ");
+ } else {
+ output.append(f.stringValue() + "; ");
+ }
+ } else {
+ output.append(v.toString() + "; ");
+ }
+ }
+ if (output.length() > 0) {
+ output.deleteCharAt(output.length()-1);
+ output.deleteCharAt(output.length()-1);
+ }
+ writeStr(name, output.toString(), false);
+ }
+
+ @Override
+ public void writeNull(String name) throws IOException {
+ wb.writeCell("");
+ }
+
+ @Override
+ public void writeInt(String name, String val) throws IOException {
+ wb.writeCell(val);
+ }
+
+ @Override
+ public void writeLong(String name, String val) throws IOException {
+ wb.writeCell(val);
+ }
+
+ @Override
+ public void writeBool(String name, String val) throws IOException {
+ wb.writeCell(val);
+ }
+
+ @Override
+ public void writeFloat(String name, String val) throws IOException {
+ wb.writeCell(val);
+ }
+
+ @Override
+ public void writeDouble(String name, String val) throws IOException {
+ wb.writeCell(val);
+ }
+
+ @Override
+ public void writeDate(String name, Date val) throws IOException {
+ writeDate(name, val.toInstant().toString());
+ }
+
+ @Override
+ public void writeDate(String name, String val) throws IOException {
+ wb.writeCell(val);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/932c436d/solr/contrib/extraction/src/test-files/extraction/solr/collection1/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/extraction/src/test-files/extraction/solr/collection1/conf/schema.xml b/solr/contrib/extraction/src/test-files/extraction/solr/collection1/conf/schema.xml
index 35d123f..bd9adbe 100644
--- a/solr/contrib/extraction/src/test-files/extraction/solr/collection1/conf/schema.xml
+++ b/solr/contrib/extraction/src/test-files/extraction/solr/collection1/conf/schema.xml
@@ -415,6 +415,7 @@
-->
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
+ <dynamicField name="*_ss" type="string" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_s1" type="string" indexed="true" stored="true" multiValued="false"/>
<dynamicField name="*_l" type="long" indexed="true" stored="true"/>
<dynamicField name="*_t" type="text" indexed="true" stored="true"/>
@@ -422,6 +423,7 @@
<dynamicField name="*_f" type="float" indexed="true" stored="true"/>
<dynamicField name="*_d" type="double" indexed="true" stored="true"/>
<dynamicField name="*_dt" type="date" indexed="true" stored="true"/>
+ <dynamicField name="*_dt1" type="date" indexed="true" stored="true" multiValued="false"/>
<dynamicField name="*_sI" type="string" indexed="true" stored="false"/>
<dynamicField name="*_sS" type="string" indexed="false" stored="true"/>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/932c436d/solr/contrib/extraction/src/test/org/apache/solr/handler/extraction/TestXLSXResponseWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/extraction/src/test/org/apache/solr/handler/extraction/TestXLSXResponseWriter.java b/solr/contrib/extraction/src/test/org/apache/solr/handler/extraction/TestXLSXResponseWriter.java
new file mode 100644
index 0000000..fd4e63d
--- /dev/null
+++ b/solr/contrib/extraction/src/test/org/apache/solr/handler/extraction/TestXLSXResponseWriter.java
@@ -0,0 +1,257 @@
+/*
+ * 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.handler.extraction;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Date;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.QueryResponseWriter;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.response.RawResponseWriter;
+import org.apache.solr.search.SolrReturnFields;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestXLSXResponseWriter extends SolrTestCaseJ4 {
+
+ private static XLSXResponseWriter writerXlsx;
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ System.setProperty("enable.update.log", "false");
+ initCore("solrconfig.xml","schema.xml",getFile("extraction/solr").getAbsolutePath());
+ createIndex();
+ //find a reference to the default response writer so we can redirect its output later
+ SolrCore testCore = h.getCore();
+ QueryResponseWriter writer = testCore.getQueryResponseWriter("xlsx");
+ if (writer instanceof XLSXResponseWriter) {
+ writerXlsx = (XLSXResponseWriter) testCore.getQueryResponseWriter("xlsx");
+ } else {
+ throw new Exception("XLSXResponseWriter not registered with solr core");
+ }
+ }
+
+ public static void createIndex() {
+ assertU(adoc("id","1", "foo_i","-1", "foo_s","hi", "foo_l","12345678987654321", "foo_b","false", "foo_f","1.414","foo_d","-1.0E300","foo_dt1","2000-01-02T03:04:05Z"));
+ assertU(adoc("id","2", "v_ss","hi", "v_ss","there", "v2_ss","nice", "v2_ss","output", "shouldbeunstored","foo"));
+ assertU(adoc("id","3", "shouldbeunstored","foo"));
+ assertU(adoc("id","4", "foo_s1","foo"));
+ assertU(commit());
+ }
+
+ @AfterClass
+ public static void cleanupWriter() throws Exception {
+ writerXlsx = null;
+ }
+
+ @Test
+ public void testStructuredDataViaBaseWriters() throws IOException, Exception {
+ SolrQueryResponse rsp = new SolrQueryResponse();
+ // Don't send a ContentStream back, this will fall back to the configured base writer.
+ // But abuse the CONTENT key to ensure writer is also checking type
+ rsp.add(RawResponseWriter.CONTENT, "test");
+ rsp.add("foo", "bar");
+
+ SolrQueryRequest r = req();
+
+ // check Content-Type
+ assertEquals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", writerXlsx.getContentType(r, rsp));
+
+ // test our basic types,and that fields come back in the requested order
+ XSSFSheet resultSheet = getWSResultForQuery(req("q","id:1", "wt","xlsx", "fl","id,foo_s,foo_i,foo_l,foo_b,foo_f,foo_d,foo_dt1"));
+
+ assertEquals("id,foo_s,foo_i,foo_l,foo_b,foo_f,foo_d,foo_dt1\n1,hi,-1,12345678987654321,F,1.414,-1.0E300,2000-01-02T03:04:05Z\n"
+ , getStringFromSheet(resultSheet));
+
+ resultSheet = getWSResultForQuery(req("q","id:1^0", "wt","xlsx", "fl","id,score,foo_s"));
+ // test retrieving score
+ assertEquals("id,score,foo_s\n1,0.0,hi\n", getStringFromSheet(resultSheet));
+
+ resultSheet = getWSResultForQuery(req("q","id:1^0", "wt","xlsx", "colname.id", "I.D.", "colwidth.id", "10",
+ "fl","id,score,foo_s"));
+ // test override colname/width
+ assertEquals("I.D.,score,foo_s\n1,0.0,hi\n", getStringFromSheet(resultSheet));
+ // test colwidth (value returned is in 256ths of a character as per excel standard)
+ assertEquals(10*256, resultSheet.getColumnWidth(0));
+
+ resultSheet = getWSResultForQuery(req("q","id:2", "wt","xlsx", "fl","id,v_ss"));
+ // test multivalued
+ assertEquals("id,v_ss\n2,hi; there\n", getStringFromSheet(resultSheet));
+
+ // test retrieving fields from index
+ resultSheet = getWSResultForQuery(req("q","*:*", "wt","xslx", "fl","*,score"));
+ String result = getStringFromSheet(resultSheet);
+ for (String field : "id,foo_s,foo_i,foo_l,foo_b,foo_f,foo_d,foo_dt1,v_ss,v2_ss,score".split(",")) {
+ assertTrue(result.indexOf(field) >= 0);
+ }
+
+ // test null values
+ resultSheet = getWSResultForQuery(req("q","id:2", "wt","xlsx", "fl","id,foo_s,v_ss"));
+ assertEquals("id,foo_s,v_ss\n2,,hi; there\n", getStringFromSheet(resultSheet));
+
+ // now test SolrDocumentList
+ SolrDocument d = new SolrDocument();
+ SolrDocument d1 = d;
+ d.addField("id","1");
+ d.addField("foo_i",-1);
+ d.addField("foo_s","hi");
+ d.addField("foo_l","12345678987654321L");
+ d.addField("foo_b",false);
+ d.addField("foo_f",1.414f);
+ d.addField("foo_d",-1.0E300);
+ d.addField("foo_dt1", new Date(Instant.parse("2000-01-02T03:04:05Z").toEpochMilli()));
+ d.addField("score", "2.718");
+
+ d = new SolrDocument();
+ SolrDocument d2 = d;
+ d.addField("id","2");
+ d.addField("v_ss","hi");
+ d.addField("v_ss","there");
+ d.addField("v2_ss","nice");
+ d.addField("v2_ss","output");
+ d.addField("score", "89.83");
+ d.addField("shouldbeunstored","foo");
+
+ SolrDocumentList sdl = new SolrDocumentList();
+ sdl.add(d1);
+ sdl.add(d2);
+
+ SolrQueryRequest req = req("q","*:*");
+ rsp = new SolrQueryResponse();
+ rsp.addResponse(sdl);
+
+ rsp.setReturnFields( new SolrReturnFields("id,foo_s", req) );
+
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("id,foo_s\n1,hi\n2,\n", getStringFromSheet(resultSheet));
+
+ // try scores
+ rsp.setReturnFields( new SolrReturnFields("id,score,foo_s", req) );
+
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("id,score,foo_s\n1,2.718,hi\n2,89.83,\n", getStringFromSheet(resultSheet));
+
+ // get field values from docs... should be ordered and not include score unless requested
+ rsp.setReturnFields( new SolrReturnFields("*", req) );
+
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("id,foo_i,foo_s,foo_l,foo_b,foo_f,foo_d,foo_dt1,v_ss,v2_ss\n" +
+ "1,-1,hi,12345678987654321L,false,1.414,-1.0E300,2000-01-02T03:04:05Z,,\n" +
+ "2,,,,,,,,hi; there,nice; output\n", getStringFromSheet(resultSheet));
+
+ // get field values and scores - just check that the scores are there... we don't guarantee where
+ rsp.setReturnFields( new SolrReturnFields("*,score", req) );
+ resultSheet = getWSResultForQuery(req, rsp);
+ String s = getStringFromSheet(resultSheet);
+ assertTrue(s.indexOf("score") >=0 && s.indexOf("2.718") > 0 && s.indexOf("89.83") > 0 );
+
+ // Test field globs
+ rsp.setReturnFields( new SolrReturnFields("id,foo*", req) );
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("id,foo_i,foo_s,foo_l,foo_b,foo_f,foo_d,foo_dt1\n" +
+ "1,-1,hi,12345678987654321L,false,1.414,-1.0E300,2000-01-02T03:04:05Z\n" +
+ "2,,,,,,,\n", getStringFromSheet(resultSheet));
+
+ rsp.setReturnFields( new SolrReturnFields("id,*_d*", req) );
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("id,foo_d,foo_dt1\n" +
+ "1,-1.0E300,2000-01-02T03:04:05Z\n" +
+ "2,,\n", getStringFromSheet(resultSheet));
+
+ // Test function queries
+ rsp.setReturnFields( new SolrReturnFields("sum(1,1),id,exists(foo_s1),div(9,1),foo_f", req) );
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("sum(1,1),id,exists(foo_s1),div(9,1),foo_f\n" +
+ ",1,,,1.414\n" +
+ ",2,,,\n", getStringFromSheet(resultSheet));
+
+ // Test transformers
+ rsp.setReturnFields( new SolrReturnFields("mydocid:[docid],[explain]", req) );
+ resultSheet = getWSResultForQuery(req, rsp);
+ assertEquals("mydocid,[explain]\n" +
+ ",\n" +
+ ",\n", getStringFromSheet(resultSheet));
+
+ req.close();
+ }
+
+
+ @Test
+ public void testPseudoFields() throws Exception {
+ // Use Pseudo Field
+ SolrQueryRequest req = req("q","id:1", "wt","xlsx", "fl","XXX:id,foo_s");
+ XSSFSheet resultSheet = getWSResultForQuery(req);
+ assertEquals("XXX,foo_s\n1,hi\n", getStringFromSheet(resultSheet));
+
+ String txt = getStringFromSheet(getWSResultForQuery(req("q","id:1", "wt","xlsx", "fl","XXX:id,YYY:[docid],FOO:foo_s")));
+ String[] lines = txt.split("\n");
+ assertEquals(2, lines.length);
+ assertEquals("XXX,YYY,FOO", lines[0] );
+ assertEquals("1,0,hi", lines[1] );
+
+ //assertions specific to multiple pseudofields functions like abs, div, exists, etc.. (SOLR-5423)
+ String funcText = getStringFromSheet(getWSResultForQuery(req("q","*", "wt","xlsx", "fl","XXX:id,YYY:exists(foo_s1)")));
+ String[] funcLines = funcText.split("\n");
+ assertEquals(5, funcLines.length);
+ assertEquals("XXX,YYY", funcLines[0] );
+ assertEquals("1,false", funcLines[1] );
+ assertEquals("3,false", funcLines[3] );
+ }
+
+ // returns first worksheet as XLSXResponseWriter only returns one sheet
+ private XSSFSheet getWSResultForQuery(SolrQueryRequest req) throws IOException, Exception {
+ SolrQueryResponse rsp = h.queryAndResponse("standard", req);
+ return getWSResultForQuery(req, rsp);
+ }
+
+ private XSSFSheet getWSResultForQuery(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, Exception {
+ ByteArrayOutputStream xmlBout = new ByteArrayOutputStream();
+ writerXlsx.write(xmlBout, req, rsp);
+ XSSFWorkbook output = new XSSFWorkbook(new ByteArrayInputStream(xmlBout.toByteArray()));
+ XSSFSheet sheet = output.getSheetAt(0);
+ req.close();
+ output.close();
+ return sheet;
+ }
+
+ private String getStringFromSheet(XSSFSheet sheet) {
+ StringBuilder output = new StringBuilder();
+ for (Row row: sheet) {
+ for (Cell cell: row) {
+ output.append(cell.getStringCellValue());
+ output.append(",");
+ }
+ output.setLength(output.length() - 1);
+ output.append("\n");
+ }
+ return output.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/932c436d/solr/core/src/java/org/apache/solr/core/SolrCore.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index 2704e4a..ac966f5 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -2197,6 +2197,12 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
m.put("smile", new SmileResponseWriter());
m.put(ReplicationHandler.FILE_STREAM, getFileStreamWriter());
DEFAULT_RESPONSE_WRITERS = Collections.unmodifiableMap(m);
+ try {
+ m.put("xlsx",
+ (QueryResponseWriter) Class.forName("org.apache.solr.handler.extraction.XLSXResponseWriter").newInstance());
+ } catch (Exception e) {
+ //don't worry; solrcell contrib not in class path
+ }
}
private static BinaryResponseWriter getFileStreamWriter() {
@@ -2219,7 +2225,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
}
public interface RawWriter {
- public void write(OutputStream os) throws IOException ;
+ void write(OutputStream os) throws IOException ;
}
/** Configure the query response writers. There will always be a default writer; additional