diff --git a/CHANGES.txt b/CHANGES.txt index 1a07b79a720..fe86cd29179 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -181,6 +181,10 @@ New Features The jsp files /admin/get-file.jsp and /admin/raw-schema.jsp have been deprecated. (ryan) +36. SOLR-446: TextResponseWriter can write SolrDocuments and SolrDocumentLists the + same way it writes Document and DocList. (yonik, ryan) + + Changes in runtime behavior Optimizations diff --git a/src/java/org/apache/solr/request/JSONResponseWriter.java b/src/java/org/apache/solr/request/JSONResponseWriter.java index 0eb4477b44e..5097e68c6b1 100644 --- a/src/java/org/apache/solr/request/JSONResponseWriter.java +++ b/src/java/org/apache/solr/request/JSONResponseWriter.java @@ -19,6 +19,8 @@ package org.apache.solr.request; import org.apache.lucene.document.Document; import org.apache.lucene.document.Fieldable; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.schema.SchemaField; @@ -386,6 +388,47 @@ class JSONWriter extends TextResponseWriter { writeMapCloser(); } + public void writeSolrDocument(String name, SolrDocument doc, Set returnFields, Map pseudoFields) throws IOException { + writeMapOpener(-1); // no trivial way to determine map size + // TODO: could easily figure out size for SolrDocument if needed... + incLevel(); + + boolean first=true; + for (String fname : doc.getFieldNames()) { + if (first) { + first=false; + } + else { + writeMapSeparator(); + } + + indent(); + writeKey(fname, true); + Object val = doc.getFieldValue(fname); + + if (val instanceof Collection) { + writeVal(fname, val); + } else { + // if multivalued field, write single value as an array + SchemaField sf = schema.getFieldOrNull(fname); + if (sf != null && sf.multiValued()) { + writeArrayOpener(-1); // no trivial way to determine array size + writeVal(fname, val); + writeArrayCloser(); + } + writeVal(fname, val); + } + + if (pseudoFields !=null && pseudoFields.size()>0) { + writeMap(null,pseudoFields,true,first); + } + } + + decLevel(); + writeMapCloser(); + } + + // reusable map to store the "score" pseudo-field. // if a Doc can ever contain another doc, this optimization would have to go. private final HashMap scoreMap = new HashMap(1); @@ -457,6 +500,64 @@ class JSONWriter extends TextResponseWriter { writeMapCloser(); } + + @Override + public void writeSolrDocumentList(String name, SolrDocumentList docs, Set fields, Map otherFields) throws IOException { + boolean includeScore=false; + if (fields!=null) { + includeScore = fields.contains("score"); + if (fields.size()==0 || (fields.size()==1 && includeScore) || fields.contains("*")) { + fields=null; // null means return all stored fields + } + } + + int sz=docs.size(); + + writeMapOpener(includeScore ? 4 : 3); + incLevel(); + writeKey("numFound",false); + writeInt(null,docs.getNumFound()); + writeMapSeparator(); + writeKey("start",false); + writeInt(null,docs.getStart()); + + if (includeScore) { + writeMapSeparator(); + writeKey("maxScore",false); + writeFloat(null,docs.getMaxScore()); + } + writeMapSeparator(); + // indent(); + writeKey("docs",false); + writeArrayOpener(sz); + + incLevel(); + boolean first=true; + + SolrIndexSearcher searcher = req.getSearcher(); + for (SolrDocument doc : docs) { + + if (first) { + first=false; + } else { + writeArraySeparator(); + } + indent(); + writeSolrDocument(null, doc, fields, otherFields); + } + decLevel(); + writeArrayCloser(); + + if (otherFields !=null) { + writeMap(null, otherFields, true, false); + } + + decLevel(); + indent(); + writeMapCloser(); + } + + // // Data structure tokens // NOTE: a positive size paramater indicates the number of elements diff --git a/src/java/org/apache/solr/request/TextResponseWriter.java b/src/java/org/apache/solr/request/TextResponseWriter.java index b1b151c04f9..9b6971106a3 100644 --- a/src/java/org/apache/solr/request/TextResponseWriter.java +++ b/src/java/org/apache/solr/request/TextResponseWriter.java @@ -20,6 +20,8 @@ package org.apache.solr.request; import org.apache.lucene.document.Document; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.FastWriter; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; import org.apache.solr.schema.IndexSchema; import org.apache.solr.search.DocList; import java.io.IOException; @@ -131,6 +133,8 @@ public abstract class TextResponseWriter { writeDouble(name, ((Double)val).doubleValue()); } else if (val instanceof Document) { writeDoc(name, (Document)val, returnFields, 0.0f, false); + } else if (val instanceof SolrDocument) { + writeSolrDocument(name, (SolrDocument)val, returnFields, null); } else if (val instanceof DocList) { // requires access to IndexReader writeDocList(name, (DocList)val, returnFields,null); @@ -139,6 +143,8 @@ public abstract class TextResponseWriter { // how do we know what fields to read? // todo: have a DocList/DocSet wrapper that // restricts the fields to write...? + } else if (val instanceof SolrDocumentList) { + writeSolrDocumentList(name, (SolrDocumentList)val, returnFields, null); } else if (val instanceof Map) { writeMap(name, (Map)val, false, true); } else if (val instanceof NamedList) { @@ -161,8 +167,18 @@ public abstract class TextResponseWriter { public abstract void writeDoc(String name, Document doc, Set returnFields, float score, boolean includeScore) throws IOException; + /** + * @since solr 1.3 + */ + public abstract void writeSolrDocument(String name, SolrDocument doc, Set returnFields, Map pseudoFields) throws IOException; + public abstract void writeDocList(String name, DocList ids, Set fields, Map otherFields) throws IOException; + /** + * @since solr 1.3 + */ + public abstract void writeSolrDocumentList(String name, SolrDocumentList docs, Set fields, Map otherFields) throws IOException; + public abstract void writeStr(String name, String val, boolean needsEscaping) throws IOException; public abstract void writeMap(String name, Map val, boolean excludeOuter, boolean isFirstVal) throws IOException; diff --git a/src/java/org/apache/solr/request/XMLWriter.java b/src/java/org/apache/solr/request/XMLWriter.java index a6e4151c3c6..e3ef1aaea50 100644 --- a/src/java/org/apache/solr/request/XMLWriter.java +++ b/src/java/org/apache/solr/request/XMLWriter.java @@ -17,6 +17,8 @@ package org.apache.solr.request; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.XML; import org.apache.solr.search.SolrIndexSearcher; @@ -333,7 +335,56 @@ final public class XMLWriter { writer.write(""); } - public final void writeDocList(String name, DocList ids, Set fields) throws IOException { + /** + * @since solr 1.3 + */ + final void writeDoc(String name, SolrDocument doc, Set returnFields, boolean includeScore) throws IOException { + startTag("doc", name, false); + incLevel(); + + if (includeScore) { + writeVal("score", doc.getFirstValue("score")); + } + + for (String fname : doc.getFieldNames()) { + Object val = doc.getFieldValue(fname); + + if (val instanceof Collection) { + writeVal(fname, val); + } else { + // single valued... figure out if we should put tags around it anyway + SchemaField sf = schema.getFieldOrNull(fname); + if (version>=2100 && sf!=null && sf.multiValued()) { + startTag("arr",fname,false); + doIndent=false; + writeVal(fname, val); + writer.write(""); + doIndent=defaultIndent; + } else { + writeVal(fname, val); + } + } + } + + decLevel(); + if (doIndent) indent(); + writer.write(""); + } + + + private static interface DocumentListInfo { + Float getMaxScore(); + int getCount(); + int getNumFound(); + int getStart(); + void writeDocs( boolean includeScore, Set fields ) throws IOException; + } + + private final void writeDocuments( + String name, + DocumentListInfo docs, + Set fields) throws IOException + { boolean includeScore=false; if (fields!=null) { includeScore = fields.contains("score"); @@ -341,16 +392,16 @@ final public class XMLWriter { fields=null; // null means return all stored fields } } - - int sz=ids.size(); - + + int sz=docs.getCount(); if (doIndent) indent(); + writer.write(""); @@ -360,18 +411,73 @@ final public class XMLWriter { } incLevel(); - SolrIndexSearcher searcher = request.getSearcher(); - DocIterator iterator = ids.iterator(); - for (int i=0; i"); } + + public final void writeSolrDocumentList(String name, final SolrDocumentList docs, Set fields) throws IOException + { + this.writeDocuments( name, new DocumentListInfo() + { + public int getCount() { + return docs.size(); + } + + public Float getMaxScore() { + return docs.getMaxScore(); + } + + public int getNumFound() { + return docs.getNumFound(); + } + + public int getStart() { + return docs.getStart(); + } + + public void writeDocs(boolean includeScore, Set fields) throws IOException { + for( SolrDocument doc : docs ) { + writeDoc(null, doc, fields, includeScore); + } + } + }, fields ); + } + + public final void writeDocList(String name, final DocList ids, Set fields) throws IOException + { + this.writeDocuments( name, new DocumentListInfo() + { + public int getCount() { + return ids.size(); + } + + public Float getMaxScore() { + return ids.maxScore(); + } + + public int getNumFound() { + return ids.matches(); + } + + public int getStart() { + return ids.offset(); + } + + public void writeDocs(boolean includeScore, Set fields) throws IOException { + SolrIndexSearcher searcher = request.getSearcher(); + DocIterator iterator = ids.iterator(); + int sz = ids.size(); + for (int i=0; i