You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by no...@apache.org on 2009/11/25 12:07:33 UTC

svn commit: r884037 - in /lucene/solr/trunk/src/java/org/apache/solr/request: BaseResponseWriter.java BinaryResponseWriter.java GenericBinaryResponseWriter.java GenericTextResponseWriter.java

Author: noble
Date: Wed Nov 25 11:07:33 2009
New Revision: 884037

URL: http://svn.apache.org/viewvc?rev=884037&view=rev
Log:
SOLR-1516 Helper classes to implement QueryResponseWriter

Added:
    lucene/solr/trunk/src/java/org/apache/solr/request/BaseResponseWriter.java
    lucene/solr/trunk/src/java/org/apache/solr/request/GenericBinaryResponseWriter.java
    lucene/solr/trunk/src/java/org/apache/solr/request/GenericTextResponseWriter.java
Modified:
    lucene/solr/trunk/src/java/org/apache/solr/request/BinaryResponseWriter.java

Added: lucene/solr/trunk/src/java/org/apache/solr/request/BaseResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/BaseResponseWriter.java?rev=884037&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/BaseResponseWriter.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/BaseResponseWriter.java Wed Nov 25 11:07:33 2009
@@ -0,0 +1,320 @@
+/**
+ * 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.request;
+
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.search.DocList;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.DocIterator;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.SchemaField;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Fieldable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+
+/**
+ * 
+ * 
+ * This class serves as a basis from which {@link QueryResponseWriter}s can be
+ * developed. The class provides a single method
+ * {@link #write(SingleResponseWriter, SolrQueryRequest, SolrQueryResponse)}
+ * that allows users to implement a {@link SingleResponseWriter} sub-class which
+ * defines how to output {@link SolrInputDocument}s or a
+ * {@link SolrDocumentList}.
+ * 
+ * @version $Id
+ * @since 1.5
+ * 
+ */
+public abstract class BaseResponseWriter {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(BaseResponseWriter.class);
+
+  private static final String RESPONSE_HEADER = "responseHeader";
+
+  private static final String SCORE_FIELD = "score";
+
+  /**
+   * 
+   * The main method that allows users to write {@link SingleResponseWriter}s
+   * and provide them as the initial parameter <code>responseWriter</code> to
+   * this method which defines how output should be generated.
+   * 
+   * @param responseWriter
+   *          The user-provided {@link SingleResponseWriter} implementation.
+   * @param request
+   *          The provided {@link SolrQueryRequest}.
+   * @param response
+   *          The provided {@link SolrQueryResponse}.
+   * @throws IOException
+   *           If any error occurs.
+   */
+  public void write(SingleResponseWriter responseWriter,
+      SolrQueryRequest request, SolrQueryResponse response) throws IOException {
+    responseWriter.start();
+    NamedList nl = response.getValues();
+    for (int i = 0; i < nl.size(); i++) {
+      String name = nl.getName(i);
+      Object val = nl.getVal(i);
+      if (RESPONSE_HEADER.equals(name)) {
+        Boolean omitHeader = request.getParams().getBool(CommonParams.OMIT_HEADER);
+        if (omitHeader == null || !omitHeader) responseWriter.writeResponseHeader((NamedList) val);
+      } else if (val instanceof SolrDocumentList) {
+        SolrDocumentList list = (SolrDocumentList) val;
+        DocListInfo info = new DocListInfo(list.getNumFound(), list.getStart(),
+            list.getMaxScore());
+        if (responseWriter.isStreamingDocs()) {
+          responseWriter.startDocumentList(info);
+          for (SolrDocument solrDocument : list)
+            responseWriter.writeDoc(solrDocument);
+          responseWriter.endDocumentList();
+        } else {
+          responseWriter.writeAllDocs(info, list);
+        }
+      } else if (val instanceof DocList) {
+        DocList docList = (DocList) val;
+        int sz = docList.size();
+        IdxInfo idxInfo = new IdxInfo(request.getSchema(), request
+            .getSearcher(), response.getReturnFields());
+        DocListInfo info = new DocListInfo(docList.matches(), docList.offset(),
+            docList.maxScore());
+        DocIterator iterator = docList.iterator();
+        if (responseWriter.isStreamingDocs()) {
+          responseWriter.startDocumentList(info);
+          for (int j = 0; j < sz; j++) {
+            SolrDocument sdoc = getDoc(iterator.nextDoc(), idxInfo);
+            if (idxInfo.includeScore && docList.hasScores()) {
+              sdoc.addField(SCORE_FIELD, iterator.score());
+            }
+            responseWriter.writeDoc(sdoc);
+          }
+          responseWriter.end();
+        } else {
+          ArrayList<SolrDocument> list = new ArrayList<SolrDocument>(docList
+              .size());
+          for (int j = 0; j < sz; j++) {
+            SolrDocument sdoc = getDoc(iterator.nextDoc(), idxInfo);
+            if (idxInfo.includeScore && docList.hasScores()) {
+              sdoc.addField(SCORE_FIELD, iterator.score());
+            }
+          }
+          responseWriter.writeAllDocs(info, list);
+        }
+
+      } else {
+        responseWriter.writeOther(name, val);
+
+      }
+    }
+    responseWriter.end();
+
+  }
+
+  private static class IdxInfo {
+    IndexSchema schema;
+    SolrIndexSearcher searcher;
+    Set<String> returnFields;
+    boolean includeScore;
+
+    private IdxInfo(IndexSchema schema, SolrIndexSearcher searcher,
+        Set<String> returnFields) {
+      this.schema = schema;
+      this.searcher = searcher;
+      this.returnFields = returnFields;
+      this.includeScore = returnFields != null
+          && returnFields.contains("score");
+    }
+  }
+
+  private static SolrDocument getDoc(int id, IdxInfo info) throws IOException {
+    Document doc = info.searcher.doc(id);
+    SolrDocument solrDoc = new SolrDocument();
+    for (Fieldable f : (List<Fieldable>) doc.getFields()) {
+      String fieldName = f.name();
+      if (info.returnFields != null && !info.returnFields.contains(fieldName))
+        continue;
+      SchemaField sf = info.schema.getFieldOrNull(fieldName);
+      FieldType ft = null;
+      if (sf != null) ft = sf.getType();
+      Object val = null;
+      if (ft == null) { // handle fields not in the schema
+        if (f.isBinary())
+          val = f.binaryValue();
+        else
+          val = f.stringValue();
+      } else {
+        try {
+          if (BinaryResponseWriter.KNOWN_TYPES.contains(ft.getClass())) {
+            val = ft.toObject(f);
+          } else {
+            val = ft.toExternal(f);
+          }
+        } catch (Exception e) {
+          // There is a chance of the underlying field not really matching the
+          // actual field type . So ,it can throw exception
+          LOG.warn("Error reading a field from document : " + solrDoc, e);
+          // if it happens log it and continue
+          continue;
+        }
+      }
+      if (sf != null && sf.multiValued() && !solrDoc.containsKey(fieldName)) {
+        ArrayList l = new ArrayList();
+        l.add(val);
+        solrDoc.addField(fieldName, l);
+      } else {
+        solrDoc.addField(fieldName, val);
+      }
+    }
+
+    return solrDoc;
+  }
+
+  public static class DocListInfo {
+    public long numFound = 0;
+    public long start = 0;
+    public Float maxScore = null;
+
+    public DocListInfo(long numFound, long start, Float maxScore) {
+      this.numFound = numFound;
+      this.start = start;
+      this.maxScore = maxScore;
+    }
+  }
+
+  /**
+   * 
+   * Users wanting to define custom {@link QueryResponseWriter}s that deal with
+   * {@link SolrInputDocument}s and {@link SolrDocumentList} should override the
+   * methods for this class. All the methods are w/o body because the user is left
+   * to choose which all methods are required for his purpose
+   */
+  public static abstract class SingleResponseWriter {
+
+    /**
+     * This method is called at the start of the {@link QueryResponseWriter}
+     * output. Override this method if you want to provide a header for your
+     * output, e.g., XML headers, etc.
+     * 
+     * @throws IOException
+     *           if any error occurs.
+     */
+    public void start() throws IOException { }
+
+    /**
+     * This method is called at the start of processing a
+     * {@link SolrDocumentList}. Those that override this method are provided
+     * with {@link DocListInfo} object to use to inspect the output
+     * {@link SolrDocumentList}.
+     * 
+     * @param info Information about the {@link SolrDocumentList} to output.
+     */
+    public void startDocumentList(DocListInfo info) throws IOException { }
+
+    /**
+     * This method writes out a {@link SolrDocument}, on a doc-by-doc basis.
+     * This method is only called when {@link #isStreamingDocs()} returns true.
+     * 
+     * @param solrDocument
+     *          The doc-by-doc {@link SolrDocument} to transform into output as
+     *          part of this {@link QueryResponseWriter}.
+     */
+    public void writeDoc(SolrDocument solrDocument) throws IOException { }
+
+    /**
+     * This method is called at the end of outputting a {@link SolrDocumentList}
+     * or on a doc-by-doc {@link SolrDocument} basis.
+     */
+    public void endDocumentList() throws IOException { } 
+    /**
+     * This method defines how to output the {@link SolrQueryResponse} header
+     * which is provided as a {@link NamedList} parameter.
+     * 
+     * @param responseHeader
+     *          The response header to output.
+     */
+    public void writeResponseHeader(NamedList responseHeader) throws IOException { }
+
+    /**
+     * This method is called at the end of the {@link QueryResponseWriter}
+     * lifecycle. Implement this method to add a footer to your output, e.g., in
+     * the case of XML, the outer tag for your tag set, etc.
+     * 
+     * @throws IOException
+     *           If any error occurs.
+     */
+    public void end() throws IOException { }
+
+    /**
+     * Define this method to control how output is written by this
+     * {@link QueryResponseWriter} if the output is not a
+     * {@link SolrInputDocument} or a {@link SolrDocumentList}.
+     * 
+     * @param name
+     *          The name of the object to output.
+     * @param other
+     *          The object to output.
+     * @throws IOException
+     *           If any error occurs.
+     */
+    public void writeOther(String name, Object other) throws IOException { }
+
+    /**
+     * Overriding this method to return false forces all
+     * {@link SolrInputDocument}s to be spit out as a {@link SolrDocumentList}
+     * so they can be processed as a whole, rather than on a doc-by-doc basis.
+     * If set to false, this method calls
+     * {@link #writeAllDocs(DocListInfo, List)}, else if set to true, then this
+     * method forces calling {@link #writeDoc(SolrDocument)} on a doc-by-doc
+     * basis. one
+     * 
+     * @return True to force {@link #writeDoc(SolrDocument)} to be called, False
+     *         to force {@link #writeAllDocs(DocListInfo, List)} to be called.
+     */
+    public boolean isStreamingDocs() { return true; }
+
+    /**
+     * Writes out all {@link SolrInputDocument}s . This is invoked only if
+     * {@link #isStreamingDocs()} returns false.
+     * 
+     * @param info
+     *          Information about the {@link List} of {@link SolrDocument}s to
+     *          output.
+     * @param allDocs
+     *          A {@link List} of {@link SolrDocument}s to output.
+     * @throws IOException
+     *           If any error occurs.
+     */
+    public void writeAllDocs(DocListInfo info, List<SolrDocument> allDocs) throws IOException { }
+
+  }
+
+}

Modified: lucene/solr/trunk/src/java/org/apache/solr/request/BinaryResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/BinaryResponseWriter.java?rev=884037&r1=884036&r2=884037&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/BinaryResponseWriter.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/BinaryResponseWriter.java Wed Nov 25 11:07:33 2009
@@ -35,7 +35,7 @@
 
 public class BinaryResponseWriter implements BinaryQueryResponseWriter {
   private static final Logger LOG = LoggerFactory.getLogger(BinaryResponseWriter.class);
-  private static final Set<Class> KNOWN_TYPES = new HashSet<Class>();
+  public static final Set<Class> KNOWN_TYPES = new HashSet<Class>();
 
   public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) throws IOException {
     Resolver resolver = new Resolver(req, response.getReturnFields());

Added: lucene/solr/trunk/src/java/org/apache/solr/request/GenericBinaryResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/GenericBinaryResponseWriter.java?rev=884037&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/GenericBinaryResponseWriter.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/GenericBinaryResponseWriter.java Wed Nov 25 11:07:33 2009
@@ -0,0 +1,78 @@
+/**
+ * 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.request;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrInputDocument;
+
+/**
+ * 
+ * 
+ * A generic {@link QueryResponseWriter} implementation that requires a user to
+ * implement the
+ * {@link #getSingleResponseWriter(OutputStream, SolrQueryRequest, SolrQueryResponse)}
+ * that defines a {@link SingleResponseWriter} to handle the binary output.
+ * 
+ * @since 1.5
+ * @version $Id$
+ * 
+ */
+public abstract class GenericBinaryResponseWriter extends BaseResponseWriter
+    implements BinaryQueryResponseWriter {
+
+  /**
+   * 
+   * Writes the binary output data using the {@link SingleResponseWriter}
+   * provided by a call to
+   * {@link #getSingleResponseWriter(OutputStream, SolrQueryRequest, SolrQueryResponse)}
+   * .
+   * 
+   * @param out
+   *          The {@link OutputStream} to write the binary data to.
+   * @param request
+   *          The provided {@link SolrQueryRequest}.
+   * @param response
+   *          The provided {@link SolrQueryResponse}.
+   */
+  public void write(OutputStream out, SolrQueryRequest request,
+      SolrQueryResponse response) throws IOException {
+    super.write(getSingleResponseWriter(out, request, response), request,
+        response);
+  }
+
+  /**
+   * Users of this class should implement this method to define a
+   * {@link SingleResponseWriter} responsible for writing the binary output
+   * given a {@link SolrDocumentList} or doc-by-doc, given a
+   * {@link SolrInputDocument}.
+   * 
+   * @param out
+   *          The {@link OutputStream} to write the binary data response to.
+   * @param request
+   *          The provided {@link SolrQueryRequest}.
+   * @param response
+   *          The provided {@link SolrQueryResponse}.
+   * @return A {@link SingleResponseWriter} that will be used to generate the
+   *         response output from this {@link QueryResponseWriter}.
+   */
+  public abstract SingleResponseWriter getSingleResponseWriter(
+      OutputStream out, SolrQueryRequest request, SolrQueryResponse response);
+}

Added: lucene/solr/trunk/src/java/org/apache/solr/request/GenericTextResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/GenericTextResponseWriter.java?rev=884037&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/GenericTextResponseWriter.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/GenericTextResponseWriter.java Wed Nov 25 11:07:33 2009
@@ -0,0 +1,77 @@
+/**
+ * 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.request;
+
+import java.io.Writer;
+import java.io.IOException;
+
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrInputDocument;
+
+/**
+ * 
+ * 
+ * A generic {@link QueryResponseWriter} implementation that requires a user to
+ * implement the
+ * {@link #getSingleResponseWriter(Writer, SolrQueryRequest, SolrQueryResponse)}
+ * that defines a {@link SingleResponseWriter} to handle plain ol' text output.
+ * 
+ * @since 1.5
+ * @version $Id$
+ * 
+ */
+public abstract class GenericTextResponseWriter extends BaseResponseWriter
+    implements QueryResponseWriter {
+
+  /**
+   * 
+   * Writes text output using the {@link SingleResponseWriter} provided by a
+   * call to
+   * {@link #getSingleResponseWriter(Writer, SolrQueryRequest, SolrQueryResponse)}
+   * .
+   * 
+   * @param out
+   *          The {@link Writer} to write the text output to.
+   * @param request
+   *          The provided {@link SolrQueryRequest}.
+   * @param response
+   *          The provided {@link SolrQueryResponse}.
+   */
+  public void write(Writer writer, SolrQueryRequest request,
+      SolrQueryResponse response) throws IOException {
+    super.write(getSingleResponseWriter(writer, request, response), request,
+        response);
+  }
+
+  /**
+   * Users of this class should implement this method to define a
+   * {@link SingleResponseWriter} responsible for writing text output given a
+   * {@link SolrDocumentList} or doc-by-doc, given a {@link SolrInputDocument}.
+   * 
+   * @param writer
+   *          The {@link Writer} to write the text data response to.
+   * @param request
+   *          The provided {@link SolrQueryRequest}.
+   * @param response
+   *          The provided {@link SolrQueryResponse}.
+   * @return A {@link SingleResponseWriter} that will be used to generate the
+   *         response output from this {@link QueryResponseWriter}.
+   */
+  protected abstract SingleResponseWriter getSingleResponseWriter(
+      Writer writer, SolrQueryRequest request, SolrQueryResponse response);
+}