mirror of https://github.com/apache/lucene.git
SOLR-9127: Excel workbook (.xlsx) response writer. use 'wt=xlsx'
This commit is contained in:
parent
ecbb588f97
commit
1a61fb6858
|
@ -78,7 +78,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
|
||||
----------------------
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"/>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -2215,6 +2215,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() {
|
||||
|
@ -2237,7 +2243,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
|
||||
|
|
Loading…
Reference in New Issue