? .svn
? Rich_Document.php
? SolrPhpClient.2008-11-25.zip
? apachesolr_338534_highlighting.diff
? apachesolr_drupal_http_request.diff
? apachesolr_drupal_http_request_2.diff
? highlighting.diff
? highlighting_body.diff
? minimal-spelling-303937-27.patch
? new_php_client_and_solr_trunk.diff
? spatch.diff
? splunk.acquiapipe.net
? SolrPhpClient/.svn
? SolrPhpClient/LICENSE
? SolrPhpClient/phpdocs
? SolrPhpClient/Apache/.svn
? SolrPhpClient/Apache/Solr/.svn
? SolrPhpClient/Apache/Solr/RichDocument.php
? SolrPhpClient/Apache/Solr/do.php
? SolrPhpClient/Apache/Solr/Service/.svn
? contrib/.svn
? contrib/apachesolr_attachments/.svn
? contrib/apachesolr_image/.svn
? contrib/apachesolr_lang/.svn
? contrib/apachesolr_mlt/.svn
? contrib/apachesolr_multisitesearch/.svn
? contrib/apachesolr_nodeaccess/.svn
? contrib/apachesolr_nodeaccess/tests/.svn
? tests/.svn
Index: apachesolr_search.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr_search.module,v
retrieving revision 1.1.2.6.2.45
diff -u -r1.1.2.6.2.45 apachesolr_search.module
--- apachesolr_search.module	11 Dec 2008 20:57:47 -0000	1.1.2.6.2.45
+++ apachesolr_search.module	14 Dec 2008 11:16:07 -0000
@@ -106,6 +106,11 @@
         // We default to getting snippets from the body.
         $hl_fl = is_null($params['hl.fl']) ? 'body' : $params['hl.fl'];
 
+        if (variable_get('apachesolr_search_spellcheck', FALSE)) {
+          //Add new parameter to the search request
+          $params['spellcheck.q'] = $query->get_query_basic();
+          $params['spellcheck'] = 'true';
+        }
 
         // TODO: This adds all of the possible facets to the query. Not all
         // of these facets have their blocks enabled, so the list should be
@@ -211,9 +216,9 @@
         $response = $solr->search($query->get_query_basic(), $params['start'], $params['rows'], $params);
         // The response is cached so that it is accessible to the blocks and anything
         // else that needs it beyond the initial search.
+        $total = $response->response->numFound;
         apachesolr_static_response_cache($response);
         apachesolr_has_searched(TRUE);
-        $total = $response->response->numFound;
         pager_query("SELECT %d", $params['rows'], 0, NULL, $total);
         if ($total > 0) {
           $extra = array();
@@ -441,6 +446,70 @@
   }
 }
 
+/**
+ * Implementation of hook_form_[form_id]_alter().
+ *
+ * This adds spelling suggestions to the search form.
+ */
+function apachesolr_search_form_search_form_alter(&$form, $form_state) {
+
+  if (($form['module']['#value'] == 'apachesolr_search') && variable_get('apachesolr_search_spellcheck', FALSE) && apachesolr_has_searched() && ($response = apachesolr_static_response_cache())) {
+    //Get spellchecker suggestions into an array.
+    $suggestions = get_object_vars($response->spellcheck->suggestions);
+
+    if ($suggestions) {
+      //Prepage original query and replace wrong words.
+      $query = apachesolr_drupal_query();
+      $new_keywords = $query->get_query_basic();
+      
+      foreach($suggestions as $word => $value) {
+        $new_keywords = substr_replace($new_keywords, $value->suggestion[0], $value->startOffset, $value->endOffset - $value->startOffset);
+      }
+
+      $form['basic']['suggestion'] = array(
+        '#prefix' => '<br/><div class="container-suggestions">',
+        '#suffix' => '</div>',
+        '#type' => 'item',
+        '#title' => t('Did you mean'),
+        '#value' => l($new_keywords, 'search/'. arg(1) .'/'. $new_keywords),
+      );
+    }
+  }
+}
+
+/**
+ * Implementation of hook_form_[form_id]_alter().
+ *
+ * This adds spelling suggestions to the search form.
+ */
+function apachesolr_search_form_apachesolr_settings_alter(&$form, $form_state) {
+  $form['apachesolr_search_spellcheck'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable Spellchecker and suggestions'),
+    '#default_value' => variable_get('apachesolr_search_spellcheck', FALSE),
+    '#description' => t('Enable spellchecker and get word suggestions. Also known as the "Did you mean" feature'),
+  );
+
+  $form['#submit'][] = 'apachesolr_search_build_specheck';
+  // Move buttons to the bottom.
+  $buttons = $form['buttons'];
+  unset($form['buttons']);
+  $form['buttons'] = $buttons;
+}
+
+function apachesolr_search_build_specheck() {
+  try {
+    $solr = apachesolr_get_solr();
+    $params['spellcheck'] = 'true';
+    $params['spellcheck.build'] = 'true';
+    $params['echoParams'] = 'all';
+    $response = $solr->search('solr', 0, 0, $params);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR);
+  }
+}
+
 function apachesolr_search_get_username($facet) {
   if ($facet == 0) {
     return variable_get('anonymous', t('Anonymous'));
Index: SolrPhpClient/ChangeLog
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/SolrPhpClient/Attic/ChangeLog,v
retrieving revision 1.1.4.2
diff -u -r1.1.4.2 ChangeLog
--- SolrPhpClient/ChangeLog	25 Oct 2008 14:47:29 -0000	1.1.4.2
+++ SolrPhpClient/ChangeLog	14 Dec 2008 11:16:08 -0000
@@ -1,3 +1,33 @@
+2008-11-24 18:14  djimenez
+
+	* Pieter Berkel requested the ability to set field and document
+	  boost values when adding documents to Solr: 
+	  
+	  https://issues.apache.org/jira/browse/SOLR-341?focusedCommentId=12650095#action_12650095
+	  
+	  Added the ability to do so in the Apache_Solr_Document class and
+	  updated the XML fragment generation code in Apache_Solr_Service to
+	  include the new boost values when available.
+
+2008-11-14 22:08  djimenez
+
+	* Apache/Solr/Document.php: Rich Robinson raised an issue on the
+	  Solr issue tracker where he found that documents with values =
+	  false were creating prematurely exiting foreach loops. I tracked
+	  it down to the valid() implementation of the Iterator iterface,
+	  and after fussing with it decided to just implement
+	  IteratorAggregator and use the pre-existing SPL ArrayIterator
+	  class. Simplifies the code and now all document values are looped
+	  even when some are false.
+
+2008-09-02 14:33  djimenez
+
+	* Apache/Solr/Document.php, Apache/Solr/Response.php,
+	  Apache/Solr/Service.php, Apache/Solr/Service/Balancer.php: At the
+	  request of several people using these classes I've changed the
+	  members and functions that were private to protected so that they
+	  can be overridden / used in subclasses
+
 2008-08-26 14:50  dwolfe
 
 	* Apache/Solr/Service/Balancer.php: Changed default value for
@@ -19,11 +49,6 @@
 	  to decide if we should retry the call. If the ping timesout then
 	  we select another read service (if one is available) and continue
 
-2008-04-11 20:46  djimenez
-
-	* Apache/Solr/Service/Balancer.php: doubled timeouts on balancer so
-	  I'll see less exceptions during high load
-
 2008-01-26 01:03  djimenez
 
 	* Apache/Solr/Response.php: HTTP 100 Continue response can also be
@@ -50,6 +75,12 @@
 	  build is not compiled with this options and we never would have
 	  found it ourselves.
 
+2008-01-23 02:53  djimenez
+
+	* Apache/Solr/Service.php, ChangeLog: 5 seconds seemed like too
+	  long a default timeout, set to 2. Added a Changelog that I'm
+	  packaging with the distribution.
+
 2008-01-14 19:12  djimenez
 
 	* Apache/Solr/Response.php, Apache/Solr/Service.php, Solr.clss:
@@ -76,11 +107,6 @@
 	  balancer so that it will try other services during a
 	  communication error on a call.
 
-2007-10-02 16:54  djimenez
-
-	* Apache/Solr/Response.php: Missed changing the package /
-	  subpackage pair. refs #29
-
 2007-10-02 02:52  djimenez
 
 	* Apache/Solr/Service.php: Got rid of the const defined defaults
Index: SolrPhpClient/Apache/Solr/Document.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/SolrPhpClient/Apache/Solr/Document.php,v
retrieving revision 1.1.4.4
diff -u -r1.1.4.4 Document.php
--- SolrPhpClient/Apache/Solr/Document.php	25 Oct 2008 16:49:25 -0000	1.1.4.4
+++ SolrPhpClient/Apache/Solr/Document.php	14 Dec 2008 11:16:08 -0000
@@ -1,5 +1,4 @@
 <?php
-// $Id: Document.php,v 1.1.4.4 2008/10/25 16:49:25 robertDouglass Exp $
 /**
  * @copyright Copyright 2007 Conduit Internet Technologies, Inc. (http://conduit-it.com)
  * @license Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
@@ -22,8 +21,8 @@
  */
 
 /**
- * Holds Key / Value pairs that represent a Solr Document. Field values can be accessed
- * by direct dereferencing such as:
+ * Holds Key / Value pairs that represent a Solr Document along with any associated boost
+ * values. Field values can be accessed by direct dereferencing such as:
  * <code>
  * ...
  * $document->title = 'Something';
@@ -40,11 +39,225 @@
  * }
  * </code>
  */
-class Apache_Solr_Document implements Iterator
+class Apache_Solr_Document implements IteratorAggregate
 {
+	/**
+	 * Document boost value
+	 *
+	 * @var float
+	 */
+	protected $_documentBoost = false;
+
+	/**
+	 * Document field values, indexed by name
+	 *
+	 * @var array
+	 */
 	protected $_fields = array();
 
 	/**
+	 * Document field boost values, indexed by name
+	 *
+	 * @var array array of floats
+	 */
+	protected $_fieldBoosts = array();
+
+	/**
+	 * Clear all boosts and fields from this document
+	 */
+	public function clear()
+	{
+		$this->_documentBoost = false;
+
+		$this->_fields = array();
+		$this->_fieldBoosts = array();
+	}
+
+	/**
+	 * Get current document boost
+	 *
+	 * @return mixed will be false for default, or else a float
+	 */
+	public function getBoost()
+	{
+		return $this->_documentBoost;
+	}
+
+	/**
+	 * Set document boost factor
+	 *
+	 * @param mixed $boost Use false for default boost, else cast to float
+	 */
+	public function setBoost($boost)
+	{
+		if ($boost !== false)
+		{
+			$this->_documentBoost = (float) $boost;
+		}
+		else
+		{
+			$this->_documentBoost = false;
+		}
+	}
+
+	/**
+	 * Add a value to a multi-valued field
+	 *
+	 * NOTE: the solr XML format allows you to specify boosts
+	 * PER value even though the underlying Lucene implementation
+	 * only allows a boost per field. To remedy this, the final
+	 * field boost value will be the product of all specified boosts
+	 * on field values - this is similar to SolrJ's functionality.
+	 *
+	 * <code>
+	 * $doc = new Apache_Solr_Document();
+	 *
+	 * $doc->addField('foo', 'bar', 2.0);
+	 * $doc->addField('foo', 'baz', 3.0);
+	 *
+	 * // resultant field boost will be 6!
+	 * echo $doc->getFieldBoost('foo');
+	 * </code>
+	 *
+	 * @param string $key
+	 * @param mixed $value
+	 * @param float $boost
+	 */
+	public function addField($key, $value, $boost = false)
+	{
+		if (!isset($this->_fields[$key]))
+		{
+			$this->_fields[$key] = array();
+		}
+
+		if (!isset($this->_fieldBoosts[$key]))
+		{
+			$this->setFieldBoost($key, $boost);
+		}
+		else if ($boost !== false)
+		{
+			if ($this->_fieldBoosts[$key] !== false)
+			{
+				$this->_fieldBoosts[$key] *= (float) $boost;
+			}
+			else
+			{
+				$this->_fieldBoosts[$key] = (float) $boost;
+			}
+		}
+
+		if (!is_array($this->_fields[$key]))
+		{
+			$this->_fields[$key] = array($this->_fields[$key]);
+		}
+
+		$this->_fields[$key][] = $value;
+	}
+
+	/**
+	 * Handle the array manipulation for a multi-valued field
+	 *
+	 * @param string $key
+	 * @param string $value
+	 *
+	 * @deprecated Use addField(...) instead
+	 */
+	public function setMultiValue($key, $value, $boost = false)
+	{
+		$this->addField($key, $value, $boost);
+	}
+
+	/**
+	 * Get field information
+	 *
+	 * @param string $key
+	 * @return mixed associative array of info if field exists, false otherwise
+	 */
+	public function getField($key)
+	{
+		if (isset($this->_fields[$key]))
+		{
+			return array(
+				'name' => $key,
+				'value' => $this->_fields[$key],
+				'boost' => $this->_fieldBoosts[$key]
+			);
+		}
+
+		return false;
+	}
+
+	/**
+	 * Set a field value. Multi-valued fields should be set as arrays
+	 * or instead use the addField(...) function which will automatically
+	 * make sure the field is an array.
+	 *
+	 * @param string $key
+	 * @param mixed $value
+	 * @param float $boost
+	 */
+	public function setField($key, $value, $boost = false)
+	{
+		$this->_fields[$key] = $value;
+		$this->setFieldBoost($key, $boost);
+	}
+
+	public function getFieldBoost($key)
+	{
+		return $this->_fieldBoosts[$key];
+	}
+
+	public function setFieldBoost($key, $boost)
+	{
+        //@note:JacobSingh changed this because of problem w/ multivalued fields
+		if ($boost !== false && $boost !== null)
+		{
+			$this->_fieldBoosts[$key] = (float) $boost;
+		}
+		else
+		{
+			$this->_fieldBoosts = false;
+		}
+	}
+
+	/**
+	 * Get the names of all fields in this document
+	 *
+	 * @return array
+	 */
+	public function getFieldNames()
+	{
+		return array_keys($this->_fields);
+	}
+
+	/**
+	 * Get the values of all fields in this document
+	 *
+	 * @return array
+	 */
+	public function getFieldValues()
+	{
+		return array_values($this->_fields);
+	}
+
+	/**
+	 * IteratorAggregate implementation function. Allows usage:
+	 *
+	 * <code>
+	 * foreach ($document as $key => $value)
+	 * {
+	 * 	...
+	 * }
+	 * </code>
+	 */
+	public function getIterator()
+	{
+		$arrayObject = new ArrayObject($this->_fields);
+
+		return $arrayObject->getIterator();
+	}
+
+	/**
 	 * Magic get for field values
 	 *
 	 * @param string $key
@@ -52,149 +265,54 @@
 	 */
 	public function __get($key)
 	{
-	  return $this->_fields[$key];
+		return $this->_fields[$key];
+	}
+
+	/**
+	 * Magic set for field values. Multi-valued fields should be set as arrays
+	 * or instead use the setMultiValue(...) function which will automatically
+	 * make sure the field is an array.
+	 *
+	 * @param string $key
+	 * @param mixed $value
+	 */
+	public function __set($key, $value)
+	{
+		$this->_fields[$key] = $value;
+
+		if (!isset($this->_fieldBoosts[$key]))
+		{
+			$this->_fieldBoosts[$key] = false;
+		}
+	}
+
+	/**
+	 * Magic isset for fields values.  Do not call directly. Allows usage:
+	 *
+	 * <code>
+	 * isset($document->some_field);
+	 * </code>
+	 *
+	 * @param string $key
+	 * @return boolean
+	 */
+	public function __isset($key)
+	{
+		return isset($this->_fields[$key]);
 	}
 
-  /**
-   * Magic set for field values. Multi-valued fields should be set as arrays
-   * or instead use the setMultiValue(...) function which will automatically
-   * make sure the field is an array.
-   *
-   * @param string $key
-   * @param mixed $value
-   */
-  public function __set($key, $value)
-  {
-    $this->_fields[$key] = $value;
-  }
-
-  /**
-   * Magic isset for fields values.  Do no call directly. Allows usage:
-   *
-   * <code>
-   * isset($document->some_field);
-   * </code>
-   *
-   * @param string $key
-   * @return boolean
-   */
-  public function __isset($key)
-  {
-    return isset($this->_fields[$key]);
-  }
-
-  /**
-   * Magic unset for field values. Do no call directly. Allows usage:
-   *
-   * <code>
-   * unset($document->some_field);
-   * </code>
-   *
-   * @param string $key
-   */
-  public function __unset($key)
-  {
-    unset($this->_fields[$key]);
-  }
-
-  /**
-   * Handle the array manipulation for a multi-valued field
-   *
-   * @param string $key
-   * @param string $value
-   */
-  public function setMultiValue($key, $value)
-  {
-    if (!isset($this->_fields[$key]))
-    {
-      $this->_fields[$key] = array();
-    }
-
-    if (!is_array($this->_fields[$key]))
-    {
-      $this->_fields[$key] = array($this->_fields[$key]);
-    }
-
-    $this->_fields[$key][] = $value;
-  }
-
-  /**
-   * Get the names of all fields in this document
-   *
-   * @return array
-   */
-  public function getFieldNames()
-  {
-    return array_keys($this->_fields);
-  }
-
-  /**
-   * Iterator implementation function, proxies to _fields. Allows usage:
-   *
-   * <code>
-   * foreach ($document as $key => $value)
-   * {
-   *   ...
-   * }
-   * </code>
-   */
-    public function rewind() {
-        reset($this->_fields);
-    }
-
-  /**
-   * Iterator implementation function, proxies to _fields. Allows usage:
-   *
-   * <code>
-   * foreach ($document as $key => $value)
-   * {
-   *   ...
-   * }
-   * </code>
-   */
-    public function current() {
-      return current($this->_fields);
-    }
-
-  /**
-   * Iterator implementation function, proxies to _fields. Allows usage:
-   *
-   * <code>
-   * foreach ($document as $key => $value)
-   * {
-   *   ...
-   * }
-   * </code>
-   */
-    public function key() {
-      return key($this->_fields);
-    }
-
-  /**
-   * Iterator implementation function, proxies to _fields. Allows usage:
-   *
-   * <code>
-   * foreach ($document as $key => $value)
-   * {
-   *   ...
-   * }
-   * </code>
-   */
-    public function next() {
-      return next($this->_fields);
-    }
-
-  /**
-   * Iterator implementation function, proxies to _fields. Allows usage:
-   *
-   * <code>
-   * foreach ($document as $key => $value)
-   * {
-   *   ...
-   * }
-   * </code>
-   */
-    public function valid() {
-      return current($this->_fields) !== false;
-    }
+	/**
+	 * Magic unset for field values. Do not call directly. Allows usage:
+	 *
+	 * <code>
+	 * unset($document->some_field);
+	 * </code>
+	 *
+	 * @param string $key
+	 */
+	public function __unset($key)
+	{
+		unset($this->_fields[$key]);
+		unset($this->_fieldBoosts[$key]);
+	}
 }
\ No newline at end of file
Index: SolrPhpClient/Apache/Solr/Response.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/SolrPhpClient/Apache/Solr/Response.php,v
retrieving revision 1.1.2.1.2.6
diff -u -r1.1.2.1.2.6 Response.php
--- SolrPhpClient/Apache/Solr/Response.php	25 Oct 2008 16:49:25 -0000	1.1.2.1.2.6
+++ SolrPhpClient/Apache/Solr/Response.php	14 Dec 2008 11:16:09 -0000
@@ -1,5 +1,4 @@
 <?php
-// $Id: Response.php,v 1.1.2.1.2.6 2008/10/25 16:49:25 robertDouglass Exp $
 /**
  * @copyright Copyright 2007 Conduit Internet Technologies, Inc. (http://conduit-it.com)
  * @license Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
@@ -32,234 +31,234 @@
  */
 class Apache_Solr_Response
 {
-  /**
-   * Holds the raw response used in construction
-   *
-   * @var string
-   */
-  protected $_rawResponse;
-
-  /**
-   * Parsed values from the passed in http headers
-   *
-   * @var string
-   */
-  protected $_httpStatus, $_httpStatusMessage, $_type, $_encoding;
-
-  /**
-   * Whether the raw response has been parsed
-   *
-   * @var boolean
-   */
-  protected $_isParsed = false;
-
-  /**
-   * Parsed representation of the data
-   *
-   * @var mixed
-   */
-  protected $_parsedData;
-
-  /**
-   * Data parsing flags.  Determines what extra processing should be done
-   * after the data is initially converted to a data structure.
-   *
-   * @var boolean
-   */
-  protected $_createDocuments = true,
-      $_collapseSingleValueArrays = true;
-
-  /**
-   * Constructor. Takes the raw HTTP response body and the exploded HTTP headers
-   *
-   * @param string $rawResponse
-   * @param array $httpHeaders
-   * @param boolean $createDocuments Whether to convert the documents json_decoded as stdClass instances to Apache_Solr_Document instances
-   * @param boolean $collapseSingleValueArrays Whether to make multivalued fields appear as single values
-   */
-  public function __construct($rawResponse, $httpHeaders = array(), $createDocuments = true, $collapseSingleValueArrays = true)
-  {
-    //Assume 0, 'Communication Error', utf-8, and  text/plain
-    $status = 0;
-    $statusMessage = 'Communication Error';
-    $type = 'text/plain';
-    $encoding = 'UTF-8';
-
-    //iterate through headers for real status, type, and encoding
-    if (is_array($httpHeaders) && count($httpHeaders) > 0)
-    {
-      //look at the first headers for the HTTP status code
-      //and message (errors are usually returned this way)
-      //
-      //HTTP 100 Continue response can also be returned before
-      //the REAL status header, so we need look until we find
-      //the last header starting with HTTP
-      //
-      //the spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1
-      //
-      //Thanks to Daniel Andersson for pointing out this oversight
-      while (isset($httpHeaders[0]) && substr($httpHeaders[0], 0, 4) == 'HTTP')
-      {
-        $parts = split(' ', substr($httpHeaders[0], 9), 2);
-
-        $status = $parts[0];
-        $statusMessage = trim($parts[1]);
-
-        array_shift($httpHeaders);
-      }
-
-      //Look for the Content-Type response header and determine type
-      //and encoding from it (if possible - such as 'Content-Type: text/plain; charset=UTF-8')
-      foreach ($httpHeaders as $header)
-      {
-        if (strncasecmp($header, 'Content-Type:', 13) == 0)
-        {
-          //split content type value into two parts if possible
-          $parts = split(';', substr($header, 13), 2);
-
-          $type = trim($parts[0]);
-
-          if ($parts[1])
-          {
-            //split the encoding section again to get the value
-            $parts = split('=', $parts[1], 2);
-
-            if ($parts[1])
-            {
-              $encoding = trim($parts[1]);
-            }
-          }
-
-          break;
-        }
-      }
-    }
-
-    $this->_rawResponse = $rawResponse;
-    $this->_type = $type;
-    $this->_encoding = $encoding;
-    $this->_httpStatus = $status;
-    $this->_httpStatusMessage = $statusMessage;
-    $this->_createDocuments = (bool) $createDocuments;
-    $this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays;
-  }
-
-  /**
-   * Get the HTTP status code
-   *
-   * @return integer
-   */
-  public function getHttpStatus()
-  {
-    return $this->_httpStatus;
-  }
-
-  /**
-   * Get the HTTP status message of the response
-   *
-   * @return string
-   */
-  public function getHttpStatusMessage()
-  {
-    return $this->_httpStatusMessage;
-  }
-
-  /**
-   * Get content type of this Solr response
-   *
-   * @return string
-   */
-  public function getType()
-  {
-    return $this->_type;
-  }
-
-  /**
-   * Get character encoding of this response. Should usually be utf-8, but just in case
-   *
-   * @return string
-   */
-  public function getEncoding()
-  {
-    return $this->_encoding;
-  }
-
-  /**
-   * Get the raw response as it was given to this object
-   *
-   * @return string
-   */
-  public function getRawResponse()
-  {
-    return $this->_rawResponse;
-  }
-
-  /**
-   * Magic get to expose the parsed data and to lazily load it
-   *
-   * @param unknown_type $key
-   * @return unknown
-   */
-  public function __get($key)
-  {
-    if (!$this->_isParsed)
-    {
-      $this->_parseData();
-      $this->_isParsed = true;
-    }
-
-    if (isset($this->_parsedData->$key))
-    {
-      return $this->_parsedData->$key;
-    }
-
-    return null;
-  }
-
-  /**
-   * Parse the raw response into the parsed_data array for access
-   */
-  protected function _parseData()
-  {
-    //An alternative would be to use Zend_Json::decode(...)
-    $data = json_decode($this->_rawResponse);
-
-    //if we're configured to collapse single valued arrays or to convert them to Apache_Solr_Document objects
-    //and we have response documents, then try to collapse the values and / or convert them now
-    if (($this->_createDocuments || $this->_collapseSingleValueArrays) && isset($data->response) && is_array($data->response->docs))
-    {
-      $documents = array();
-
-      foreach ($data->response->docs as $originalDocument)
-      {
-        if ($this->_createDocuments)
-        {
-          $document = new Apache_Solr_Document();
-        }
-        else
-        {
-          $document = $originalDocument;
-        }
-
-        foreach ($originalDocument as $key => $value)
-        {
-          //If a result is an array with only a single
-          //value then its nice to be able to access
-          //it as if it were always a single value
-          if ($this->_collapseSingleValueArrays && is_array($value) && count($value) <= 1)
-          {
-            $value = array_shift($value);
-          }
-
-          $document->$key = $value;
-        }
+	/**
+	 * Holds the raw response used in construction
+	 *
+	 * @var string
+	 */
+	protected $_rawResponse;
+
+	/**
+	 * Parsed values from the passed in http headers
+	 *
+	 * @var string
+	 */
+	protected $_httpStatus, $_httpStatusMessage, $_type, $_encoding;
+
+	/**
+	 * Whether the raw response has been parsed
+	 *
+	 * @var boolean
+	 */
+	protected $_isParsed = false;
+
+	/**
+	 * Parsed representation of the data
+	 *
+	 * @var mixed
+	 */
+	protected $_parsedData;
+
+	/**
+	 * Data parsing flags.  Determines what extra processing should be done
+	 * after the data is initially converted to a data structure.
+	 *
+	 * @var boolean
+	 */
+	protected $_createDocuments = true,
+			$_collapseSingleValueArrays = true;
+
+	/**
+	 * Constructor. Takes the raw HTTP response body and the exploded HTTP headers
+	 *
+	 * @param string $rawResponse
+	 * @param array $httpHeaders
+	 * @param boolean $createDocuments Whether to convert the documents json_decoded as stdClass instances to Apache_Solr_Document instances
+	 * @param boolean $collapseSingleValueArrays Whether to make multivalued fields appear as single values
+	 */
+	public function __construct($rawResponse, $httpHeaders = array(), $createDocuments = true, $collapseSingleValueArrays = true)
+	{
+		//Assume 0, 'Communication Error', utf-8, and  text/plain
+		$status = 0;
+		$statusMessage = 'Communication Error';
+		$type = 'text/plain';
+		$encoding = 'UTF-8';
+
+		//iterate through headers for real status, type, and encoding
+		if (is_array($httpHeaders) && count($httpHeaders) > 0)
+		{
+			//look at the first headers for the HTTP status code
+			//and message (errors are usually returned this way)
+			//
+			//HTTP 100 Continue response can also be returned before
+			//the REAL status header, so we need look until we find
+			//the last header starting with HTTP
+			//
+			//the spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1
+			//
+			//Thanks to Daniel Andersson for pointing out this oversight
+			while (isset($httpHeaders[0]) && substr($httpHeaders[0], 0, 4) == 'HTTP')
+			{
+				$parts = split(' ', substr($httpHeaders[0], 9), 2);
+
+				$status = $parts[0];
+				$statusMessage = trim($parts[1]);
+
+				array_shift($httpHeaders);
+			}
+
+			//Look for the Content-Type response header and determine type
+			//and encoding from it (if possible - such as 'Content-Type: text/plain; charset=UTF-8')
+			foreach ($httpHeaders as $header)
+			{
+				if (strncasecmp($header, 'Content-Type:', 13) == 0)
+				{
+					//split content type value into two parts if possible
+					$parts = split(';', substr($header, 13), 2);
+
+					$type = trim($parts[0]);
+
+					if ($parts[1])
+					{
+						//split the encoding section again to get the value
+						$parts = split('=', $parts[1], 2);
+
+						if ($parts[1])
+						{
+							$encoding = trim($parts[1]);
+						}
+					}
+
+					break;
+				}
+			}
+		}
+
+		$this->_rawResponse = $rawResponse;
+		$this->_type = $type;
+		$this->_encoding = $encoding;
+		$this->_httpStatus = $status;
+		$this->_httpStatusMessage = $statusMessage;
+		$this->_createDocuments = (bool) $createDocuments;
+		$this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays;
+	}
+
+	/**
+	 * Get the HTTP status code
+	 *
+	 * @return integer
+	 */
+	public function getHttpStatus()
+	{
+		return $this->_httpStatus;
+	}
+
+	/**
+	 * Get the HTTP status message of the response
+	 *
+	 * @return string
+	 */
+	public function getHttpStatusMessage()
+	{
+		return $this->_httpStatusMessage;
+	}
+
+	/**
+	 * Get content type of this Solr response
+	 *
+	 * @return string
+	 */
+	public function getType()
+	{
+		return $this->_type;
+	}
+
+	/**
+	 * Get character encoding of this response. Should usually be utf-8, but just in case
+	 *
+	 * @return string
+	 */
+	public function getEncoding()
+	{
+		return $this->_encoding;
+	}
+
+	/**
+	 * Get the raw response as it was given to this object
+	 *
+	 * @return string
+	 */
+	public function getRawResponse()
+	{
+		return $this->_rawResponse;
+	}
+
+	/**
+	 * Magic get to expose the parsed data and to lazily load it
+	 *
+	 * @param unknown_type $key
+	 * @return unknown
+	 */
+	public function __get($key)
+	{
+		if (!$this->_isParsed)
+		{
+			$this->_parseData();
+			$this->_isParsed = true;
+		}
+
+		if (isset($this->_parsedData->$key))
+		{
+			return $this->_parsedData->$key;
+		}
+
+		return null;
+	}
+
+	/**
+	 * Parse the raw response into the parsed_data array for access
+	 */
+	protected function _parseData()
+	{
+		//An alternative would be to use Zend_Json::decode(...)
+		$data = json_decode($this->_rawResponse);
+
+		//if we're configured to collapse single valued arrays or to convert them to Apache_Solr_Document objects
+		//and we have response documents, then try to collapse the values and / or convert them now
+		if (($this->_createDocuments || $this->_collapseSingleValueArrays) && isset($data->response) && is_array($data->response->docs))
+		{
+			$documents = array();
+
+			foreach ($data->response->docs as $originalDocument)
+			{
+				if ($this->_createDocuments)
+				{
+					$document = new Apache_Solr_Document();
+				}
+				else
+				{
+					$document = $originalDocument;
+				}
+
+				foreach ($originalDocument as $key => $value)
+				{
+					//If a result is an array with only a single
+					//value then its nice to be able to access
+					//it as if it were always a single value
+					if ($this->_collapseSingleValueArrays && is_array($value) && count($value) <= 1)
+					{
+						$value = array_shift($value);
+					}
+
+					$document->$key = $value;
+				}
 
-        $documents[] = $document;
-      }
+				$documents[] = $document;
+			}
 
-      $data->response->docs = $documents;
-    }
+			$data->response->docs = $documents;
+		}
 
-    $this->_parsedData = $data;
-  }
+		$this->_parsedData = $data;
+	}
 }
\ No newline at end of file
Index: SolrPhpClient/Apache/Solr/Service.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/SolrPhpClient/Apache/Solr/Service.php,v
retrieving revision 1.1.2.1.2.10
diff -u -r1.1.2.1.2.10 Service.php
--- SolrPhpClient/Apache/Solr/Service.php	18 Nov 2008 16:40:12 -0000	1.1.2.1.2.10
+++ SolrPhpClient/Apache/Solr/Service.php	14 Dec 2008 11:16:09 -0000
@@ -1,5 +1,4 @@
 <?php
-// $Id: Service.php,v 1.1.2.1.2.10 2008/11/18 16:40:12 jacobsingh Exp $
 /**
  * @copyright Copyright 2007 Conduit Internet Technologies, Inc. (http://conduit-it.com)
  * @license Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
@@ -21,6 +20,7 @@
  * @author Donovan Jimenez <djimenez@conduit-it.com>
  */
 
+//@note:JacobSingh changed this so class location was not hardcoded, but relative
 require_once(dirname(__FILE__) . '/Document.php');
 require_once(dirname(__FILE__) . '/Response.php');
 
@@ -35,24 +35,24 @@
  *
  * if ($solr->ping())
  * {
- *     $solr->deleteByQuery('*:*'); //deletes ALL documents - be careful :)
+ * 		$solr->deleteByQuery('*:*'); //deletes ALL documents - be careful :)
  *
- *     $document = new Apache_Solr_Document();
- *     $document->id = uniqid(); //or something else suitably unique
+ * 		$document = new Apache_Solr_Document();
+ * 		$document->id = uniqid(); //or something else suitably unique
  *
- *     $document->title = 'Some Title';
- *     $document->content = 'Some content for this wonderful document. Blah blah blah.';
+ * 		$document->title = 'Some Title';
+ * 		$document->content = 'Some content for this wonderful document. Blah blah blah.';
  *
- *     $solr->addDocument($document);   //if you're going to be adding documents in bulk using addDocuments
- *                     //with an array of documents is faster
+ * 		$solr->addDocument($document); 	//if you're going to be adding documents in bulk using addDocuments
+ * 										//with an array of documents is faster
  *
- *     $solr->commit(); //commit to see the deletes and the document
- *     $solr->optimize(); //merges multiple segments into one
+ * 		$solr->commit(); //commit to see the deletes and the document
+ * 		$solr->optimize(); //merges multiple segments into one
  *
- *     //and the one we all care about, search!
- *     //any other common or custom parameters to the request handler can go in the
- *     //optional 4th array argument.
- *     $solr->search('content:blah', 0, 10, array('sort' => 'timestamp desc'));
+ * 		//and the one we all care about, search!
+ * 		//any other common or custom parameters to the request handler can go in the
+ * 		//optional 4th array argument.
+ * 		$solr->search('content:blah', 0, 10, array('sort' => 'timestamp desc'));
  * }
  * ...
  * </code>
@@ -62,825 +62,848 @@
  */
 class Apache_Solr_Service
 {
-  /**
-   * Response version we support
-   */
-  const SOLR_VERSION = '1.2';
-
-  /**
-   * Response writer we support
-   *
-   * @todo Solr 1.3 release may change this to SerializedPHP or PHP implementation
-   */
-  const SOLR_WRITER = 'json';
-
-  /**
-   * NamedList Treatment constants
-   */
-  const NAMED_LIST_FLAT = 'flat';
-  const NAMED_LIST_MAP = 'map';
-
-  /**
-   * Servlet mappings
-   */
-  const PING_SERVLET = 'admin/ping';
-  const UPDATE_SERVLET = 'update';
-  const SEARCH_SERVLET = 'select';
-  const THREADS_SERVLET = 'admin/threads';
-
-  /**
-   * Server identification strings
-   *
-   * @var string
-   */
-  protected $_host, $_port, $_path;
-
-  /**
-   * Whether {@link Apache_Solr_Response} objects should create {@link Apache_Solr_Document}s in
-   * the returned parsed data
-   *
-   * @var boolean
-   */
-  protected $_createDocuments = true;
-
-  /**
-   * Whether {@link Apache_Solr_Response} objects should have multivalue fields with only a single value
-   * collapsed to appear as a single value would.
-   *
-   * @var boolean
-   */
-  protected $_collapseSingleValueArrays = true;
-
-  /**
-   * How NamedLists should be formatted in the output.  This specifically effects facet counts. Valid values
-   * are {@link Apache_Solr_Service::NAMED_LIST_MAP} (default) or {@link Apache_Solr_Service::NAMED_LIST_FLAT}.
-   *
-   * @var string
-   */
-  protected $_namedListTreatment = self::NAMED_LIST_MAP;
-
-  /**
-   * Query delimiters. Someone might want to be able to change
-   * these (to use &amp; instead of & for example), so I've provided them.
-   *
-   * @var string
-   */
-  protected $_queryDelimiter = '?', $_queryStringDelimiter = '&';
-
-  /**
-   * Constructed servlet full path URLs
-   *
-   * @var string
-   */
-  protected $_updateUrl, $_searchUrl, $_threadsUrl;
-
-  /**
-   * Keep track of whether our URLs have been constructed
-   *
-   * @var boolean
-   */
-  protected $_urlsInited = false;
-
-  /**
-   * Stream context for posting
-   *
-   * @var resource
-   */
-  protected $_postContext;
-
-  /**
-   * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
-   *
-   * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead
-   *
-   * @param string $value
-   * @return string
-   */
-  static public function escape($value)
-  {
-    //list taken from http://lucene.apache.org/java/docs/queryparsersyntax.html#Escaping%20Special%20Characters
-    $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
-    $replace = '\\\$1';
-
-    return preg_replace($pattern, $replace, $value);
-  }
-
-  /**
-   * Escape a value meant to be contained in a phrase for special query characters
-   *
-   * @param string $value
-   * @return string
-   */
-  static public function escapePhrase($value)
-  {
-    $pattern = '/("|\\\)/';
-    $replace = '\\\$1';
-
-    return preg_replace($pattern, $replace, $value);
-  }
-
-  /**
-   * Convenience function for creating phrase syntax from a value
-   *
-   * @param string $value
-   * @return string
-   */
-  static public function phrase($value)
-  {
-    return '"' . self::escapePhrase($value) . '"';
-  }
-
-  /**
-   * Constructor. All parameters are optional and will take on default values
-   * if not specified.
-   *
-   * @param string $host
-   * @param string $port
-   * @param string $path
-   */
-  public function __construct($host = 'localhost', $port = 8180, $path = '/solr/')
-  {
-    $this->setHost($host);
-    $this->setPort($port);
-    $this->setPath($path);
-
-    $this->_initUrls();
-
-    //set up the stream context for posting with file_get_contents
-    $contextOpts = array(
-      'http' => array(
-        'method' => 'POST',
-        'header' => "Content-Type: text/xml; charset=UTF-8\r\n" //php.net example showed \r\n at the end
-      )
-    );
-
-    $this->_postContext = stream_context_create($contextOpts);
-  }
-
-  /**
-   * Return a valid http URL given this server's host, port and path and a provided servlet name
-   *
-   * @param string $servlet
-   * @return string
-   */
-  protected function _constructUrl($servlet, $params = array())
-  {
-    if (count($params))
-    {
-      //escape all parameters appropriately for inclusion in the query string
-      $escapedParams = array();
-
-      foreach ($params as $key => $value)
-      {
-        $escapedParams[] = urlencode($key) . '=' . urlencode($value);
-      }
-
-      $queryString = $this->_queryDelimiter . implode($this->_queryStringDelimiter, $escapedParams);
-    }
-    else
-    {
-      $queryString = '';
-    }
-
-    return 'http://' . $this->_host . ':' . $this->_port . $this->_path . $servlet . $queryString;
-  }
-
-  /**
-   * Construct the Full URLs for the three servlets we reference
-   */
-  protected function _initUrls()
-  {
-    //Initialize our full servlet URLs now that we have server information
-    $this->_updateUrl = $this->_constructUrl(self::UPDATE_SERVLET, array('wt' => self::SOLR_WRITER ));
-    $this->_searchUrl = $this->_constructUrl(self::SEARCH_SERVLET);
-    $this->_threadsUrl = $this->_constructUrl(self::THREADS_SERVLET, array('wt' => self::SOLR_WRITER ));
-
-    $this->_urlsInited = true;
-  }
-
-  /**
-   * Central method for making a get operation against this Solr Server
-   *
-   * @param string $url
-   * @param float $timeout Read timeout in seconds
-   * @return Apache_Solr_Response
-   *
-   * @todo implement timeout ability
-   * @throws Exception If a non 200 response status is returned
-   */
-  protected function _sendRawGet($url, $timeout = FALSE)
-  {
-    //$http_response_header is set by file_get_contents
-    $http_response_header = NULL;
-    $response = new Apache_Solr_Response(@file_get_contents($url), $http_response_header, $this->_createDocuments, $this->_collapseSingleValueArrays);
-
-    if ($response->getHttpStatus() != 200)
-    {
-      throw new Exception('"' . $response->getHttpStatus() . '" Status: ' . $response->getHttpStatusMessage(), $response->getHttpStatus());
-    }
-
-    return $response;
-  }
-
-  /**
-   * Central method for making a post operation against this Solr Server
-   *
-   * @param string $url
-   * @param string $rawPost
-   * @param float $timeout Read timeout in seconds
-   * @param string $contentType
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If a non 200 response status is returned
-   */
-  protected function _sendRawPost($url, $rawPost, $timeout = FALSE, $contentType = 'text/xml; charset=UTF-8')
-  {
-    //ensure content type is correct
-    stream_context_set_option($this->_postContext, 'http', 'header', 'Content-Type: ' . $contentType);
-
-    //set the read timeout if specified
-    if ($timeout !== FALSE)
-    {
-      stream_context_set_option($this->_postContext, 'http', 'timeout', $timeout);
-    }
-
-    //set the content
-    stream_context_set_option($this->_postContext, 'http', 'content', $rawPost);
-
-    //$http_response_header is set by file_get_contents
-    $response = new Apache_Solr_Response(@file_get_contents($url, false, $this->_postContext), $http_response_header, $this->_createDocuments, $this->_collapseSingleValueArrays);
-
-    if ($response->getHttpStatus() != 200)
-    {
-      throw new Exception('"' . $response->getHttpStatus() . '" Status: ' . $response->getHttpStatusMessage(), $response->getHttpStatus());
-    }
-
-    return $response;
-  }
-
-  /**
-   * Returns the set host
-   *
-   * @return string
-   */
-  public function getHost()
-  {
-    return $this->_host;
-  }
-
-  /**
-   * Set the host used. If empty will fallback to constants
-   *
-   * @param string $host
-   */
-  public function setHost($host)
-  {
-    //Use the provided host or use the default
-    if (empty($host))
-    {
-      throw new Exception('Host parameter is empty');
-    }
-    else
-    {
-      $this->_host = $host;
-    }
-
-    if ($this->_urlsInited)
-    {
-      $this->_initUrls();
-    }
-  }
-
-  /**
-   * Get the set port
-   *
-   * @return integer
-   */
-  public function getPort()
-  {
-    return $this->_port;
-  }
-
-  /**
-   * Set the port used. If empty will fallback to constants
-   *
-   * @param integer $port
-   */
-  public function setPort($port)
-  {
-    //Use the provided port or use the default
-    $port = (int) $port;
-
-    if ($port <= 0)
-    {
-      throw new Exception('Port is not a valid port number');
-    }
-    else
-    {
-      $this->_port = $port;
-    }
-
-    if ($this->_urlsInited)
-    {
-      $this->_initUrls();
-    }
-  }
-
-  /**
-   * Get the set path.
-   *
-   * @return string
-   */
-  public function getPath()
-  {
-    return $this->_path;
-  }
-
-  /**
-   * Set the path used. If empty will fallback to constants
-   *
-   * @param string $path
-   */
-  public function setPath($path)
-  {
-    $path = trim($path, '/');
-
-    $this->_path = '/' . $path . '/';
-
-    if ($this->_urlsInited)
-    {
-      $this->_initUrls();
-    }
-  }
-
-  /**
-   * Set the create documents flag. This determines whether {@link Apache_Solr_Response} objects will
-   * parse the response and create {@link Apache_Solr_Document} instances in place.
-   *
-   * @param unknown_type $createDocuments
-   */
-  public function setCreateDocuments($createDocuments)
-  {
-    $this->_createDocuments = (bool) $createDocuments;
-  }
-
-  /**
-   * Get the current state of teh create documents flag.
-   *
-   * @return boolean
-   */
-  public function getCreateDocuments()
-  {
-    return $this->_createDocuments;
-  }
-
-  /**
-   * Set the collapse single value arrays flag.
-   *
-   * @param boolean $collapseSingleValueArrays
-   */
-  public function setCollapseSingleValueArrays($collapseSingleValueArrays)
-  {
-    $this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays;
-  }
-
-  /**
-   * Get the current state of the collapse single value arrays flag.
-   *
-   * @return boolean
-   */
-  public function getCollapseSingleValueArrays()
-  {
-    return $this->_collapseSingleValueArrays;
-  }
-
-  /**
-   * Set how NamedLists should be formatted in the response data. This mainly effects
-   * the facet counts format.
-   *
-   * @param string $namedListTreatment
-   * @throws Exception If invalid option is set
-   */
-  public function setNamedListTreatmet($namedListTreatment)
-  {
-    switch ((string) $namedListTreatment)
-    {
-      case Apache_Solr_Service::NAMED_LIST_FLAT:
-        $this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_FLAT;
-        break;
-
-      case Apache_Solr_Service::NAMED_LIST_MAP:
-        $this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_MAP;
-        break;
-
-      default:
-        throw new Exception('Not a valid named list treatement option');
-    }
-  }
-
-  /**
-   * Get the current setting for named list treatment.
-   *
-   * @return string
-   */
-  public function getNamedListTreatment()
-  {
-    return $this->_namedListTreatment;
-  }
-
-
-  /**
-   * Set the string used to separate the path form the query string.
-   * Defaulted to '?'
-   *
-   * @param string $queryDelimiter
-   */
-  public function setQueryDelimiter($queryDelimiter)
-  {
-    $this->_queryDelimiter = $queryDelimiter;
-  }
-
-  /**
-   * Set the string used to separate the parameters in thequery string
-   * Defaulted to '&'
-   *
-   * @param string $queryStringDelimiter
-   */
-  public function setQueryStringDelimiter($queryStringDelimiter)
-  {
-    $this->_queryStringDelimiter = $queryStringDelimiter;
-  }
-
-  /**
-   * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
-   * server is able to be made.
-   *
-   * @param float $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
-   * @return float Actual time taken to ping the server, FALSE if timeout occurs
-   */
-  public function ping($timeout = 2)
-  {
-    $timeout = (float) $timeout;
-
-    if ($timeout <= 0)
-    {
-      $timeout = -1;
-    }
-
-    $start = microtime(true);
-
-    //to prevent strict errors
-    $errno = 0;
-    $errstr = '';
-
-    //try to connect to the host with timeout
-    $fp = fsockopen($this->_host, $this->_port, $errno, $errstr, $timeout);
-
-    if ($fp)
-    {
-      //If we have a timeout set, then determine the amount of time we have left
-      //in the request and set the stream timeout for the write operation
-      if ($timeout > 0)
-      {
-        //do the calculation
-        $writeTimeout = $timeout - (microtime(true) - $start);
-
-        //check if we're out of time
-        if ($writeTimeout <= 0)
-        {
-          fclose($fp);
-          return false;
-        }
-
-        //convert to microseconds and set the stream timeout
-        $writeTimeoutInMicroseconds = (int) $writeTimeout * 1000000;
-        stream_set_timeout($fp, 0, $writeTimeoutInMicroseconds);
-      }
-
-      $request =   'HEAD ' . $this->_path . self::PING_SERVLET . ' HTTP/1.1' . "\r\n" .
-            'host: ' . $this->_host . "\r\n" .
-            'Connection: close' . "\r\n" .
-            "\r\n";
-
-      fwrite($fp, $request);
-
-      //check the stream meta data to see if we timed out during the operation
-      $metaData = stream_get_meta_data($fp);
-
-      if (isset($metaData['timeout']) && $metaData['timeout'])
-      {
-        fclose($fp);
-        return false;
-      }
-
-
-      //if we have a timeout set and have made it this far, determine the amount of time
-      //still remaining and set the timeout appropriately before the read operation
-      if ($timeout > 0)
-      {
-        //do the calculation
-        $readTimeout = $timeout - (microtime(true) - $start);
-
-        //check if we've run out of time
-        if ($readTimeout <= 0)
-        {
-          fclose($fp);
-          return false;
-        }
-
-        //convert to microseconds and set the stream timeout
-        $readTimeoutInMicroseconds = $readTimeout * 1000000;
-        stream_set_timeout($fp, 0, $readTimeoutInMicroseconds);
-      }
-
-      //at the very least we should get a response header line of
-      //HTTP/1.1 200 OK
-      $response = fread($fp, 15);
-
-      //check the stream meta data to see if we timed out during the operation
-      $metaData = stream_get_meta_data($fp);
-      fclose($fp); //we're done with the connection - ignore the rest
-
-      if (isset($metaData['timeout']) && $metaData['timeout'])
-      {
-        return false;
-      }
-
-      //finally, check the response header line
-      if ($response != 'HTTP/1.1 200 OK')
-      {
-        return false;
-      }
-
-      //we made it, return the approximate ping time
-      return microtime(true) - $start;
-    }
-
-    //we weren't able to make a connection
-    return false;
-  }
-
-  /**
-   * Call the /admin/threads servlet and retrieve information about all threads in the
-   * Solr servlet's thread group. Useful for diagnostics.
-   *
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function threads()
-  {
-    return $this->_sendRawGet($this->_threadsUrl);
-  }
-
-  /**
-   * Raw Add Method. Takes a raw post body and sends it to the update service.  Post body
-   * should be a complete and well formed "add" xml document.
-   *
-   * @param string $rawPost
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function add($rawPost)
-  {
-    return $this->_sendRawPost($this->_updateUrl, $rawPost);
-  }
-
-  /**
-   * Add a Solr Document to the index
-   *
-   * @param Apache_Solr_Document $document
-   * @param boolean $allowDups
-   * @param boolean $overwritePending
-   * @param boolean $overwriteCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
-  {
-    $dupValue = $allowDups ? 'true' : 'false';
-    $pendingValue = $overwritePending ? 'true' : 'false';
-    $committedValue = $overwriteCommitted ? 'true' : 'false';
-
-    $rawPost = '<add allowDups="' . $dupValue . '" overwritePending="' . $pendingValue . '" overwriteCommitted="' . $committedValue . '">';
-    $rawPost .= $this->_documentToXmlFragment($document);
-    $rawPost .= '</add>';
-
-    return $this->add($rawPost);
-  }
-
-  /**
-   * Add an array of Solr Documents to the index all at once
-   *
-   * @param array $documents Should be an array of Apache_Solr_Document instances
-   * @param boolean $allowDups
-   * @param boolean $overwritePending
-   * @param boolean $overwriteCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
-  {
-    $dupValue = $allowDups ? 'true' : 'false';
-    $pendingValue = $overwritePending ? 'true' : 'false';
-    $committedValue = $overwriteCommitted ? 'true' : 'false';
-
-    $rawPost = '<add allowDups="' . $dupValue . '" overwritePending="' . $pendingValue . '" overwriteCommitted="' . $committedValue . '">';
-
-    foreach ($documents as $document)
-    {
-      if ($document instanceof Apache_Solr_Document)
-      {
-        $rawPost .= $this->_documentToXmlFragment($document);
-      }
-    }
-
-    $rawPost .= '</add>';
-
-    return $this->add($rawPost);
-  }
-
-  /**
-   * Create an XML fragment from a {@link Apache_Solr_Document} instance appropriate for use inside a Solr add call
-   *
-   * @return string
-   */
-  protected function _documentToXmlFragment(Apache_Solr_Document $document)
-  {
-    $xml = '<doc>';
-
-    foreach ($document as $key => $value)
-    {
-      $key = htmlspecialchars($key, ENT_QUOTES, 'UTF-8');
-
-      if (is_array($value))
-      {
-        foreach ($value as $multivalue)
-        {
-          $multivalue = htmlspecialchars($multivalue, ENT_NOQUOTES, 'UTF-8');
-
-          $xml .= '<field name="' . $key . '">' . $multivalue . '</field>';
-        }
-      }
-      else
-      {
-        $value = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
-
-        $xml .= '<field name="' . $key . '">' . $value . '</field>';
-      }
-    }
-
-    $xml .= '</doc>';
-
-    return $xml;
-  }
-
-  /**
-   * Send a commit command.  Will be synchronous unless both wait parameters are set to false.
-   *
-   * @param boolean $optimize Defaults to true
-   * @param boolean $waitFlush Defaults to true
-   * @param boolean $waitSearcher Defaults to true
-   * @param float $timeout Maximum expected duration (in seconds) of the commit operation on the server (otherwise, will throw a communication exception). Defaults to 1 hour
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function commit($optimize = true, $waitFlush = true, $waitSearcher = true, $timeout = 3600)
-  {
-    $optimizeValue = $optimize ? 'true' : 'false';
-    $flushValue = $waitFlush ? 'true' : 'false';
-    $searcherValue = $waitSearcher ? 'true' : 'false';
-
-    $rawPost = '<commit optimize="' . $optimizeValue . '" waitFlush="' . $flushValue . '" waitSearcher="' . $searcherValue . '" />';
-
-    return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
-  }
-
-  /**
-   * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
-   * a complete and well formed "delete" xml document
-   *
-   * @param string $rawPost Expected to be utf-8 encoded xml document
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function delete($rawPost)
-  {
-    return $this->_sendRawPost($this->_updateUrl, $rawPost);
-  }
-
-  /**
-   * Create a delete document based on document ID
-   *
-   * @param string $id Expected to be utf-8 encoded
-   * @param boolean $fromPending
-   * @param boolean $fromCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function deleteById($id, $fromPending = true, $fromCommitted = true)
-  {
-    $pendingValue = $fromPending ? 'true' : 'false';
-    $committedValue = $fromCommitted ? 'true' : 'false';
-
-    //escape special xml characters
-    $id = htmlspecialchars($id, ENT_NOQUOTES, 'UTF-8');
-
-    $rawPost = '<delete fromPending="' . $pendingValue . '" fromCommitted="' . $committedValue . '"><id>' . $id . '</id></delete>';
-
-    return $this->delete($rawPost);
-  }
-
-  /**
-   * Create a delete document based on a query and submit it
-   *
-   * @param string $rawQuery Expected to be utf-8 encoded
-   * @param boolean $fromPending
-   * @param boolean $fromCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true)
-  {
-    $pendingValue = $fromPending ? 'true' : 'false';
-    $committedValue = $fromCommitted ? 'true' : 'false';
-
-    // escape special xml characters
-    $rawQuery = htmlspecialchars($rawQuery, ENT_NOQUOTES, 'UTF-8');
-
-    $rawPost = '<delete fromPending="' . $pendingValue . '" fromCommitted="' . $committedValue . '"><query>' . $rawQuery . '</query></delete>';
-
-    return $this->delete($rawPost);
-  }
-
-  /**
-   * Send an optimize command.  Will be synchronous unless both wait parameters are set
-   * to false.
-   *
-   * @param boolean $waitFlush
-   * @param boolean $waitSearcher
-   * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception)
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function optimize($waitFlush = true, $waitSearcher = true, $timeout = 3600)
-  {
-    $flushValue = $waitFlush ? 'true' : 'false';
-    $searcherValue = $waitSearcher ? 'true' : 'false';
-
-    $rawPost = '<optimize waitFlush="' . $flushValue . '" waitSearcher="' . $searcherValue . '" />';
-
-    return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
-  }
-
-  /**
-   * Simple Search interface
-   *
-   * @param string $query The raw query string
-   * @param int $offset The starting offset for result documents
-   * @param int $limit The maximum number of result documents to return
-   * @param array $params key / value pairs for other query parameters (see Solr documentation), use arrays for parameter keys used more than once (e.g. facet.field)
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function search($query, $offset = 0, $limit = 10, $params = array())
-  {
-    if (!is_array($params))
-    {
-      $params = array();
-    }
-
-    // construct our full parameters
-    // sending the version is important in case the format changes
-    $params['version'] = self::SOLR_VERSION;
-
-    // common parameters in this interface
-    $params['wt'] = self::SOLR_WRITER;
-    $params['json.nl'] = $this->_namedListTreatment;
-
-    $params['q'] = $query;
-    $params['start'] = $offset;
-    $params['rows'] = $limit;
-
-    // use http_build_query to encode our arguments because its faster
-    // than urlencoding all the parts ourselves in a loop
-    $queryString = http_build_query($params, null, $this->_queryStringDelimiter);
-
-    // because http_build_query treats arrays differently than we want to, correct the query
-    // string by changing foo[#]=bar (# being an actual number) parameter strings to just
-    // multiple foo=bar strings. This regex should always work since '=' will be urlencoded
-    // anywhere else the regex isn't expecting it
-    $queryString = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $queryString);
+	/**
+	 * Response version we support
+	 */
+	const SOLR_VERSION = '1.2';
+
+	/**
+	 * Response writer we support
+	 *
+	 * @todo Solr 1.3 release may change this to SerializedPHP or PHP implementation
+	 */
+	const SOLR_WRITER = 'json';
+
+	/**
+	 * NamedList Treatment constants
+	 */
+	const NAMED_LIST_FLAT = 'flat';
+	const NAMED_LIST_MAP = 'map';
+
+	/**
+	 * Servlet mappings
+	 */
+	const PING_SERVLET = 'admin/ping';
+	const UPDATE_SERVLET = 'update';
+	const SEARCH_SERVLET = 'select';
+	const THREADS_SERVLET = 'admin/threads';
+
+	/**
+	 * Server identification strings
+	 *
+	 * @var string
+	 */
+	protected $_host, $_port, $_path;
+
+	/**
+	 * Whether {@link Apache_Solr_Response} objects should create {@link Apache_Solr_Document}s in
+	 * the returned parsed data
+	 *
+	 * @var boolean
+	 */
+	protected $_createDocuments = true;
+
+	/**
+	 * Whether {@link Apache_Solr_Response} objects should have multivalue fields with only a single value
+	 * collapsed to appear as a single value would.
+	 *
+	 * @var boolean
+	 */
+	protected $_collapseSingleValueArrays = true;
+
+	/**
+	 * How NamedLists should be formatted in the output.  This specifically effects facet counts. Valid values
+	 * are {@link Apache_Solr_Service::NAMED_LIST_MAP} (default) or {@link Apache_Solr_Service::NAMED_LIST_FLAT}.
+	 *
+	 * @var string
+	 */
+	protected $_namedListTreatment = self::NAMED_LIST_MAP;
+
+	/**
+	 * Query delimiters. Someone might want to be able to change
+	 * these (to use &amp; instead of & for example), so I've provided them.
+	 *
+	 * @var string
+	 */
+	protected $_queryDelimiter = '?', $_queryStringDelimiter = '&';
+
+	/**
+	 * Constructed servlet full path URLs
+	 *
+	 * @var string
+	 */
+	protected $_updateUrl, $_searchUrl, $_threadsUrl;
+
+	/**
+	 * Keep track of whether our URLs have been constructed
+	 *
+	 * @var boolean
+	 */
+	protected $_urlsInited = false;
+
+	/**
+	 * Stream context for posting
+	 *
+	 * @var resource
+	 */
+	protected $_postContext;
+
+	/**
+	 * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
+	 *
+	 * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead
+	 *
+	 * @param string $value
+	 * @return string
+	 */
+	static public function escape($value)
+	{
+		//list taken from http://lucene.apache.org/java/docs/queryparsersyntax.html#Escaping%20Special%20Characters
+		$pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
+		$replace = '\\\$1';
+
+		return preg_replace($pattern, $replace, $value);
+	}
+
+	/**
+	 * Escape a value meant to be contained in a phrase for special query characters
+	 *
+	 * @param string $value
+	 * @return string
+	 */
+	static public function escapePhrase($value)
+	{
+		$pattern = '/("|\\\)/';
+		$replace = '\\\$1';
+
+		return preg_replace($pattern, $replace, $value);
+	}
+
+	/**
+	 * Convenience function for creating phrase syntax from a value
+	 *
+	 * @param string $value
+	 * @return string
+	 */
+	static public function phrase($value)
+	{
+		return '"' . self::escapePhrase($value) . '"';
+	}
+
+	/**
+	 * Constructor. All parameters are optional and will take on default values
+	 * if not specified.
+	 *
+	 * @param string $host
+	 * @param string $port
+	 * @param string $path
+	 */
+	public function __construct($host = 'localhost', $port = 8180, $path = '/solr/')
+	{
+		$this->setHost($host);
+		$this->setPort($port);
+		$this->setPath($path);
+
+		$this->_initUrls();
+
+		//set up the stream context for posting with file_get_contents
+		$contextOpts = array(
+			'http' => array(
+				'method' => 'POST',
+				'header' => "Content-Type: text/xml; charset=UTF-8\r\n" //php.net example showed \r\n at the end
+			)
+		);
+
+		$this->_postContext = stream_context_create($contextOpts);
+	}
+
+	/**
+	 * Return a valid http URL given this server's host, port and path and a provided servlet name
+	 *
+	 * @param string $servlet
+	 * @return string
+	 */
+	protected function _constructUrl($servlet, $params = array())
+	{
+		if (count($params))
+		{
+			//escape all parameters appropriately for inclusion in the query string
+			$escapedParams = array();
+
+			foreach ($params as $key => $value)
+			{
+				$escapedParams[] = urlencode($key) . '=' . urlencode($value);
+			}
+
+			$queryString = $this->_queryDelimiter . implode($this->_queryStringDelimiter, $escapedParams);
+		}
+		else
+		{
+			$queryString = '';
+		}
+
+		return 'http://' . $this->_host . ':' . $this->_port . $this->_path . $servlet . $queryString;
+	}
+
+	/**
+	 * Construct the Full URLs for the three servlets we reference
+	 */
+	protected function _initUrls()
+	{
+		//Initialize our full servlet URLs now that we have server information
+		$this->_updateUrl = $this->_constructUrl(self::UPDATE_SERVLET, array('wt' => self::SOLR_WRITER ));
+		$this->_searchUrl = $this->_constructUrl(self::SEARCH_SERVLET);
+		$this->_threadsUrl = $this->_constructUrl(self::THREADS_SERVLET, array('wt' => self::SOLR_WRITER ));
+
+		$this->_urlsInited = true;
+	}
+
+	/**
+	 * Central method for making a get operation against this Solr Server
+	 *
+	 * @param string $url
+	 * @param float $timeout Read timeout in seconds
+	 * @return Apache_Solr_Response
+	 *
+	 * @todo implement timeout ability
+	 * @throws Exception If a non 200 response status is returned
+	 */
+	protected function _sendRawGet($url, $timeout = FALSE)
+	{
+		//$http_response_header is set by file_get_contents
+		$response = new Apache_Solr_Response(@file_get_contents($url), $http_response_header, $this->_createDocuments, $this->_collapseSingleValueArrays);
+
+		if ($response->getHttpStatus() != 200)
+		{
+			throw new Exception('"' . $response->getHttpStatus() . '" Status: ' . $response->getHttpStatusMessage(), $response->getHttpStatus());
+		}
+
+		return $response;
+	}
+
+	/**
+	 * Central method for making a post operation against this Solr Server
+	 *
+	 * @param string $url
+	 * @param string $rawPost
+	 * @param float $timeout Read timeout in seconds
+	 * @param string $contentType
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If a non 200 response status is returned
+	 */
+	protected function _sendRawPost($url, $rawPost, $timeout = FALSE, $contentType = 'text/xml; charset=UTF-8')
+	{
+		//ensure content type is correct
+		stream_context_set_option($this->_postContext, 'http', 'header', 'Content-Type: ' . $contentType);
+
+		//set the read timeout if specified
+		if ($timeout !== FALSE)
+		{
+			stream_context_set_option($this->_postContext, 'http', 'timeout', $timeout);
+		}
+
+		//set the content
+		stream_context_set_option($this->_postContext, 'http', 'content', $rawPost);
+
+		//$http_response_header is set by file_get_contents
+		$response = new Apache_Solr_Response(@file_get_contents($url, false, $this->_postContext), $http_response_header, $this->_createDocuments, $this->_collapseSingleValueArrays);
+
+		if ($response->getHttpStatus() != 200)
+		{
+			throw new Exception('"' . $response->getHttpStatus() . '" Status: ' . $response->getHttpStatusMessage(), $response->getHttpStatus());
+		}
+
+		return $response;
+	}
+
+	/**
+	 * Returns the set host
+	 *
+	 * @return string
+	 */
+	public function getHost()
+	{
+		return $this->_host;
+	}
+
+	/**
+	 * Set the host used. If empty will fallback to constants
+	 *
+	 * @param string $host
+	 */
+	public function setHost($host)
+	{
+		//Use the provided host or use the default
+		if (empty($host))
+		{
+			throw new Exception('Host parameter is empty');
+		}
+		else
+		{
+			$this->_host = $host;
+		}
+
+		if ($this->_urlsInited)
+		{
+			$this->_initUrls();
+		}
+	}
+
+	/**
+	 * Get the set port
+	 *
+	 * @return integer
+	 */
+	public function getPort()
+	{
+		return $this->_port;
+	}
+
+	/**
+	 * Set the port used. If empty will fallback to constants
+	 *
+	 * @param integer $port
+	 */
+	public function setPort($port)
+	{
+		//Use the provided port or use the default
+		$port = (int) $port;
+
+		if ($port <= 0)
+		{
+			throw new Exception('Port is not a valid port number');
+		}
+		else
+		{
+			$this->_port = $port;
+		}
+
+		if ($this->_urlsInited)
+		{
+			$this->_initUrls();
+		}
+	}
+
+	/**
+	 * Get the set path.
+	 *
+	 * @return string
+	 */
+	public function getPath()
+	{
+		return $this->_path;
+	}
+
+	/**
+	 * Set the path used. If empty will fallback to constants
+	 *
+	 * @param string $path
+	 */
+	public function setPath($path)
+	{
+		$path = trim($path, '/');
+
+		$this->_path = '/' . $path . '/';
+
+		if ($this->_urlsInited)
+		{
+			$this->_initUrls();
+		}
+	}
+
+	/**
+	 * Set the create documents flag. This determines whether {@link Apache_Solr_Response} objects will
+	 * parse the response and create {@link Apache_Solr_Document} instances in place.
+	 *
+	 * @param unknown_type $createDocuments
+	 */
+	public function setCreateDocuments($createDocuments)
+	{
+		$this->_createDocuments = (bool) $createDocuments;
+	}
+
+	/**
+	 * Get the current state of teh create documents flag.
+	 *
+	 * @return boolean
+	 */
+	public function getCreateDocuments()
+	{
+		return $this->_createDocuments;
+	}
+
+	/**
+	 * Set the collapse single value arrays flag.
+	 *
+	 * @param boolean $collapseSingleValueArrays
+	 */
+	public function setCollapseSingleValueArrays($collapseSingleValueArrays)
+	{
+		$this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays;
+	}
+
+	/**
+	 * Get the current state of the collapse single value arrays flag.
+	 *
+	 * @return boolean
+	 */
+	public function getCollapseSingleValueArrays()
+	{
+		return $this->_collapseSingleValueArrays;
+	}
+
+	/**
+	 * Set how NamedLists should be formatted in the response data. This mainly effects
+	 * the facet counts format.
+	 *
+	 * @param string $namedListTreatment
+	 * @throws Exception If invalid option is set
+	 */
+	public function setNamedListTreatmet($namedListTreatment)
+	{
+		switch ((string) $namedListTreatment)
+		{
+			case Apache_Solr_Service::NAMED_LIST_FLAT:
+				$this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_FLAT;
+				break;
+
+			case Apache_Solr_Service::NAMED_LIST_MAP:
+				$this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_MAP;
+				break;
+
+			default:
+				throw new Exception('Not a valid named list treatement option');
+		}
+	}
+
+	/**
+	 * Get the current setting for named list treatment.
+	 *
+	 * @return string
+	 */
+	public function getNamedListTreatment()
+	{
+		return $this->_namedListTreatment;
+	}
+
+
+	/**
+	 * Set the string used to separate the path form the query string.
+	 * Defaulted to '?'
+	 *
+	 * @param string $queryDelimiter
+	 */
+	public function setQueryDelimiter($queryDelimiter)
+	{
+		$this->_queryDelimiter = $queryDelimiter;
+	}
+
+	/**
+	 * Set the string used to separate the parameters in thequery string
+	 * Defaulted to '&'
+	 *
+	 * @param string $queryStringDelimiter
+	 */
+	public function setQueryStringDelimiter($queryStringDelimiter)
+	{
+		$this->_queryStringDelimiter = $queryStringDelimiter;
+	}
+
+	/**
+	 * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
+	 * server is able to be made.
+	 *
+	 * @param float $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
+	 * @return float Actual time taken to ping the server, FALSE if timeout occurs
+	 */
+	public function ping($timeout = 2)
+	{
+		$timeout = (float) $timeout;
+
+		if ($timeout <= 0)
+		{
+			$timeout = -1;
+		}
+
+		$start = microtime(true);
+
+		//to prevent strict errors
+		$errno = 0;
+		$errstr = '';
+
+		//try to connect to the host with timeout
+		$fp = fsockopen($this->_host, $this->_port, $errno, $errstr, $timeout);
+
+		if ($fp)
+		{
+			//If we have a timeout set, then determine the amount of time we have left
+			//in the request and set the stream timeout for the write operation
+			if ($timeout > 0)
+			{
+				//do the calculation
+				$writeTimeout = $timeout - (microtime(true) - $start);
+
+				//check if we're out of time
+				if ($writeTimeout <= 0)
+				{
+					fclose($fp);
+					return false;
+				}
+
+				//convert to microseconds and set the stream timeout
+				$writeTimeoutInMicroseconds = (int) $writeTimeout * 1000000;
+				stream_set_timeout($fp, 0, $writeTimeoutInMicroseconds);
+			}
+
+			$request = 	'HEAD ' . $this->_path . self::PING_SERVLET . ' HTTP/1.1' . "\r\n" .
+						'host: ' . $this->_host . "\r\n" .
+						'Connection: close' . "\r\n" .
+						"\r\n";
+
+			fwrite($fp, $request);
+
+			//check the stream meta data to see if we timed out during the operation
+			$metaData = stream_get_meta_data($fp);
+
+			if (isset($metaData['timeout']) && $metaData['timeout'])
+			{
+				fclose($fp);
+				return false;
+			}
+
+
+			//if we have a timeout set and have made it this far, determine the amount of time
+			//still remaining and set the timeout appropriately before the read operation
+			if ($timeout > 0)
+			{
+				//do the calculation
+				$readTimeout = $timeout - (microtime(true) - $start);
+
+				//check if we've run out of time
+				if ($readTimeout <= 0)
+				{
+					fclose($fp);
+					return false;
+				}
+
+				//convert to microseconds and set the stream timeout
+				$readTimeoutInMicroseconds = $readTimeout * 1000000;
+				stream_set_timeout($fp, 0, $readTimeoutInMicroseconds);
+			}
+
+			//at the very least we should get a response header line of
+			//HTTP/1.1 200 OK
+			$response = fread($fp, 15);
+
+			//check the stream meta data to see if we timed out during the operation
+			$metaData = stream_get_meta_data($fp);
+			fclose($fp); //we're done with the connection - ignore the rest
+
+			if (isset($metaData['timeout']) && $metaData['timeout'])
+			{
+				return false;
+			}
+
+			//finally, check the response header line
+			if ($response != 'HTTP/1.1 200 OK')
+			{
+				return false;
+			}
+
+			//we made it, return the approximate ping time
+			return microtime(true) - $start;
+		}
+
+		//we weren't able to make a connection
+		return false;
+	}
+
+	/**
+	 * Call the /admin/threads servlet and retrieve information about all threads in the
+	 * Solr servlet's thread group. Useful for diagnostics.
+	 *
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function threads()
+	{
+		return $this->_sendRawGet($this->_threadsUrl);
+	}
+
+	/**
+	 * Raw Add Method. Takes a raw post body and sends it to the update service.  Post body
+	 * should be a complete and well formed "add" xml document.
+	 *
+	 * @param string $rawPost
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function add($rawPost)
+	{
+		return $this->_sendRawPost($this->_updateUrl, $rawPost);
+	}
+
+	/**
+	 * Add a Solr Document to the index
+	 *
+	 * @param Apache_Solr_Document $document
+	 * @param boolean $allowDups
+	 * @param boolean $overwritePending
+	 * @param boolean $overwriteCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
+	{
+		$dupValue = $allowDups ? 'true' : 'false';
+		$pendingValue = $overwritePending ? 'true' : 'false';
+		$committedValue = $overwriteCommitted ? 'true' : 'false';
+
+		$rawPost = '<add allowDups="' . $dupValue . '" overwritePending="' . $pendingValue . '" overwriteCommitted="' . $committedValue . '">';
+		$rawPost .= $this->_documentToXmlFragment($document);
+		$rawPost .= '</add>';
+
+		return $this->add($rawPost);
+	}
+
+	/**
+	 * Add an array of Solr Documents to the index all at once
+	 *
+	 * @param array $documents Should be an array of Apache_Solr_Document instances
+	 * @param boolean $allowDups
+	 * @param boolean $overwritePending
+	 * @param boolean $overwriteCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
+	{
+		$dupValue = $allowDups ? 'true' : 'false';
+		$pendingValue = $overwritePending ? 'true' : 'false';
+		$committedValue = $overwriteCommitted ? 'true' : 'false';
+
+		$rawPost = '<add allowDups="' . $dupValue . '" overwritePending="' . $pendingValue . '" overwriteCommitted="' . $committedValue . '">';
+
+		foreach ($documents as $document)
+		{
+			if ($document instanceof Apache_Solr_Document)
+			{
+				$rawPost .= $this->_documentToXmlFragment($document);
+			}
+		}
+
+		$rawPost .= '</add>';
+
+		return $this->add($rawPost);
+	}
+
+	/**
+	 * Create an XML fragment from a {@link Apache_Solr_Document} instance appropriate for use inside a Solr add call
+	 *
+	 * @return string
+	 */
+	protected function _documentToXmlFragment(Apache_Solr_Document $document)
+	{
+		$xml = '<doc';
+
+		if ($document->getBoost() !== false)
+		{
+			$xml .= ' boost="' . $document->getBoost() . '"';
+		}
+
+		$xml .= '>';
+
+		foreach ($document as $key => $value)
+		{
+			$key = htmlspecialchars($key, ENT_QUOTES, 'UTF-8');
+			$fieldBoost = $document->getFieldBoost($key);
+
+			if (is_array($value))
+			{
+				foreach ($value as $multivalue)
+				{
+					$xml .= '<field name="' . $key . '"';
+//@note: JacobSingh changed this to is_int because off null / false confusion
+					if (is_int($fieldBoost))
+					{
+						$xml .= ' boost="' . $fieldBoost . '"';
+						// only set the boost for the first field in the set
+						$fieldBoost = false;
+					}
+
+					$multivalue = htmlspecialchars($multivalue, ENT_NOQUOTES, 'UTF-8');
+
+					$xml .= '>' . $multivalue . '</field>' . "\n";
+				}
+			}
+			else
+			{
+				$xml .= '<field name="' . $key . '"';
+//@note: JacobSingh changed this to is_int because off null / false confusion
+				if (is_int($fieldBoost))
+				{
+					$xml .= ' boost="' . $fieldBoost . '"';
+				}
+
+				$value = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
+
+				$xml .= '>' . $value . '</field>' . "\n";
+			}
+		}
+
+		$xml .= '</doc>' . "\n";
+
+		return $xml;
+	}
+
+	/**
+	 * Send a commit command.  Will be synchronous unless both wait parameters are set to false.
+	 *
+	 * @param boolean $optimize Defaults to true
+	 * @param boolean $waitFlush Defaults to true
+	 * @param boolean $waitSearcher Defaults to true
+	 * @param float $timeout Maximum expected duration (in seconds) of the commit operation on the server (otherwise, will throw a communication exception). Defaults to 1 hour
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function commit($optimize = true, $waitFlush = true, $waitSearcher = true, $timeout = 3600)
+	{
+		$optimizeValue = $optimize ? 'true' : 'false';
+		$flushValue = $waitFlush ? 'true' : 'false';
+		$searcherValue = $waitSearcher ? 'true' : 'false';
+
+		$rawPost = '<commit optimize="' . $optimizeValue . '" waitFlush="' . $flushValue . '" waitSearcher="' . $searcherValue . '" />';
+
+		return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
+	}
+
+	/**
+	 * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
+	 * a complete and well formed "delete" xml document
+	 *
+	 * @param string $rawPost Expected to be utf-8 encoded xml document
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function delete($rawPost)
+	{
+		return $this->_sendRawPost($this->_updateUrl, $rawPost);
+	}
+
+	/**
+	 * Create a delete document based on document ID
+	 *
+	 * @param string $id Expected to be utf-8 encoded
+	 * @param boolean $fromPending
+	 * @param boolean $fromCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function deleteById($id, $fromPending = true, $fromCommitted = true)
+	{
+		$pendingValue = $fromPending ? 'true' : 'false';
+		$committedValue = $fromCommitted ? 'true' : 'false';
+
+		//escape special xml characters
+		$id = htmlspecialchars($id, ENT_NOQUOTES, 'UTF-8');
+
+		$rawPost = '<delete fromPending="' . $pendingValue . '" fromCommitted="' . $committedValue . '"><id>' . $id . '</id></delete>';
+
+		return $this->delete($rawPost);
+	}
+
+	/**
+	 * Create a delete document based on a query and submit it
+	 *
+	 * @param string $rawQuery Expected to be utf-8 encoded
+	 * @param boolean $fromPending
+	 * @param boolean $fromCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true)
+	{
+		$pendingValue = $fromPending ? 'true' : 'false';
+		$committedValue = $fromCommitted ? 'true' : 'false';
+
+		// escape special xml characters
+		$rawQuery = htmlspecialchars($rawQuery, ENT_NOQUOTES, 'UTF-8');
+
+		$rawPost = '<delete fromPending="' . $pendingValue . '" fromCommitted="' . $committedValue . '"><query>' . $rawQuery . '</query></delete>';
+
+		return $this->delete($rawPost);
+	}
+
+	/**
+	 * Send an optimize command.  Will be synchronous unless both wait parameters are set
+	 * to false.
+	 *
+	 * @param boolean $waitFlush
+	 * @param boolean $waitSearcher
+	 * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception)
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function optimize($waitFlush = true, $waitSearcher = true, $timeout = 3600)
+	{
+		$flushValue = $waitFlush ? 'true' : 'false';
+		$searcherValue = $waitSearcher ? 'true' : 'false';
+
+		$rawPost = '<optimize waitFlush="' . $flushValue . '" waitSearcher="' . $searcherValue . '" />';
+
+		return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
+	}
+
+	/**
+	 * Simple Search interface
+	 *
+	 * @param string $query The raw query string
+	 * @param int $offset The starting offset for result documents
+	 * @param int $limit The maximum number of result documents to return
+	 * @param array $params key / value pairs for other query parameters (see Solr documentation), use arrays for parameter keys used more than once (e.g. facet.field)
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function search($query, $offset = 0, $limit = 10, $params = array())
+	{
+		if (!is_array($params))
+		{
+			$params = array();
+		}
+
+		// construct our full parameters
+		// sending the version is important in case the format changes
+		$params['version'] = self::SOLR_VERSION;
+
+		// common parameters in this interface
+		$params['wt'] = self::SOLR_WRITER;
+		$params['json.nl'] = $this->_namedListTreatment;
+
+		$params['q'] = $query;
+		$params['start'] = $offset;
+		$params['rows'] = $limit;
+
+		// use http_build_query to encode our arguments because its faster
+		// than urlencoding all the parts ourselves in a loop
+		$queryString = http_build_query($params, null, $this->_queryStringDelimiter);
+
+		// because http_build_query treats arrays differently than we want to, correct the query
+		// string by changing foo[#]=bar (# being an actual number) parameter strings to just
+		// multiple foo=bar strings. This regex should always work since '=' will be urlencoded
+		// anywhere else the regex isn't expecting it
+		$queryString = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $queryString);
 
-    return $this->_sendRawGet($this->_searchUrl . $this->_queryDelimiter . $queryString);
-  }
+		return $this->_sendRawGet($this->_searchUrl . $this->_queryDelimiter . $queryString);
+	}
 }
\ No newline at end of file
Index: SolrPhpClient/Apache/Solr/Service/Balancer.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/SolrPhpClient/Apache/Solr/Service/Balancer.php,v
retrieving revision 1.1.4.4
diff -u -r1.1.4.4 Balancer.php
--- SolrPhpClient/Apache/Solr/Service/Balancer.php	25 Oct 2008 16:49:25 -0000	1.1.4.4
+++ SolrPhpClient/Apache/Solr/Service/Balancer.php	14 Dec 2008 11:16:09 -0000
@@ -1,5 +1,4 @@
 <?php
-// $Id: Balancer.php,v 1.1.4.4 2008/10/25 16:49:25 robertDouglass Exp $
 /**
  * @copyright Copyright 2007 Conduit Internet Technologies, Inc. (http://conduit-it.com)
  * @license Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
@@ -26,722 +25,722 @@
 /**
  * Reference Implementation for using multiple Solr services in a distribution. Functionality
  * includes:
- *   routing of read / write operations
- *   failover (on selection) for multiple read servers
+ * 	routing of read / write operations
+ * 	failover (on selection) for multiple read servers
  */
 class Apache_Solr_Service_Balancer
 {
-  protected $_createDocuments = true;
+	protected $_createDocuments = true;
 
-  protected $_readableServices = array();
-  protected $_writeableServices = array();
+	protected $_readableServices = array();
+	protected $_writeableServices = array();
 
-  protected $_currentReadService = null;
-  protected $_currentWriteService = null;
+	protected $_currentReadService = null;
+	protected $_currentWriteService = null;
 
-  protected $_readPingTimeout = 2;
-  protected $_writePingTimeout = 4;
-  
-  // Configuration for server selection backoff intervals
-  protected $_useBackoff = false;    // Set to true to use more resillient write server selection
-  protected $_backoffLimit = 600;    // 10 minute default maximum
-  protected $_backoffEscalation = 2.0;   // Rate at which to increase backoff period
-  protected $_defaultBackoff = 2.0;    // Default backoff interval
-
-  /**
-   * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
-   *
-   * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead
-   *
-   * @param string $value
-   * @return string
-   */
-  static public function escape($value)
-  {
-    return Apache_Solr_Service::escape($value);
-  }
-
-  /**
-   * Escape a value meant to be contained in a phrase for special query characters
-   *
-   * @param string $value
-   * @return string
-   */
-  static public function escapePhrase($value)
-  {
-    return Apache_Solr_Service::escapePhrase($value);
-  }
-
-  /**
-   * Convenience function for creating phrase syntax from a value
-   *
-   * @param string $value
-   * @return string
-   */
-  static public function phrase($value)
-  {
-    return Apache_Solr_Service::phrase($value);
-  }
-
-  /**
-   * Constructor. Takes arrays of read and write service instances or descriptions
-   *
-   * @param array $readableServices
-   * @param array $writeableServices
-   */
-  public function __construct($readableServices = array(), $writeableServices = array())
-  {
-    //setup readable services
-    foreach ($readableServices as $service)
-    {
-      $this->addReadService($service);
-    }
-
-    //setup writeable services
-    foreach ($writeableServices as $service)
-    {
-      $this->addWriteService($service);
-    }
-  }
-
-  public function setReadPingTimeout($timeout)
-  {
-    $this->_readPingTimeout = $timeout;
-  }
-
-  public function setWritePingTimeout($timeout)
-  {
-    $this->_writePingTimeout = $timeout;
-  }
-  
-  public function setUseBackoff($enable)
-  {
-    $this->_useBackoff = $enable;
-  }
-
-  /**
-   * Generates a service ID
-   *
-   * @param string $host
-   * @param integer $port
-   * @param string $path
-   * @return string
-   */
-  protected function _getServiceId($host, $port, $path)
-  {
-    return $host . ':' . $port . $path;
-  }
-
-  /**
-   * Adds a service instance or service descriptor (if it is already
-   * not added)
-   *
-   * @param mixed $service
-   *
-   * @throws Exception If service descriptor is not valid
-   */
-  public function addReadService($service)
-  {
-    if ($service instanceof Apache_Solr_Service)
-    {
-      $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
-
-      $this->_readableServices[$id] = $service;
-    }
-    else if (is_array($service))
-    {
-      if (isset($service['host']) && isset($service['port']) && isset($service['path']))
-      {
-        $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
-
-        $this->_readableServices[$id] = $service;
-      }
-      else
-      {
-        throw new Exception('A Readable Service description array does not have all required elements of host, port, and path');
-      }
-    }
-  }
-
-  /**
-   * Removes a service instance or descriptor from the available services
-   *
-   * @param mixed $service
-   *
-   * @throws Exception If service descriptor is not valid
-   */
-  public function removeReadService($service)
-  {
-    $id = '';
-
-    if ($service instanceof Apache_Solr_Service)
-    {
-      $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
-    }
-    else if (is_array($service))
-    {
-      if (isset($service['host']) && isset($service['port']) && isset($service['path']))
-      {
-        $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
-      }
-      else
-      {
-        throw new Exception('A Readable Service description array does not have all required elements of host, port, and path');
-      }
-    }
-    else if (is_string($service))
-    {
-      $id = $service;
-    }
-
-    if ($id && isset($this->_readableServices[$id]))
-    {
-      unset($this->_readableServices[$id]);
-    }
-  }
-
-  /**
-   * Adds a service instance or service descriptor (if it is already
-   * not added)
-   *
-   * @param mixed $service
-   *
-   * @throws Exception If service descriptor is not valid
-   */
-  public function addWriteService($service)
-  {
-    if ($service instanceof Apache_Solr_Service)
-    {
-      $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
-
-      $this->_writeableServices[$id] = $service;
-    }
-    else if (is_array($service))
-    {
-      if (isset($service['host']) && isset($service['port']) && isset($service['path']))
-      {
-        $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
-
-        $this->_writeableServices[$id] = $service;
-      }
-      else
-      {
-        throw new Exception('A Writeable Service description array does not have all required elements of host, port, and path');
-      }
-    }
-  }
-
-  /**
-   * Removes a service instance or descriptor from the available services
-   *
-   * @param mixed $service
-   *
-   * @throws Exception If service descriptor is not valid
-   */
-  public function removeWriteService($service)
-  {
-    $id = '';
-
-    if ($service instanceof Apache_Solr_Service)
-    {
-      $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
-    }
-    else if (is_array($service))
-    {
-      if (isset($service['host']) && isset($service['port']) && isset($service['path']))
-      {
-        $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
-      }
-      else
-      {
-        throw new Exception('A Readable Service description array does not have all required elements of host, port, and path');
-      }
-    }
-    else if (is_string($service))
-    {
-      $id = $service;
-    }
-
-    if ($id && isset($this->_writeableServices[$id]))
-    {
-      unset($this->_writeableServices[$id]);
-    }
-  }
-
-  /**
-   * Iterate through available read services and select the first with a ping
-   * that satisfies configured timeout restrictions (or the default)
-   *
-   * @return Apache_Solr_Service
-   *
-   * @throws Exception If there are no read services that meet requirements
-   */
-  protected function _selectReadService($forceSelect = false)
-  {
-    if (!$this->_currentReadService || !isset($this->_readableServices[$this->_currentReadService]) || $forceSelect)
-    {
-      if ($this->_currentReadService && isset($this->_readableServices[$this->_currentReadService]) && $forceSelect)
-      {
-        // we probably had a communication error, ping the current read service, remove it if it times out
-        if ($this->_readableServices[$this->_currentReadService]->ping($this->_readPingTimeout) === false)
-        {
-          $this->removeReadService($this->_currentReadService);
-        }
-      }
-      
-      if (count($this->_readableServices))
-      {
-        // select one of the read services at random
-        $ids = array_keys($this->_readableServices);
-        
-        $id = $ids[rand(0, count($ids) - 1)];
-        $service = $this->_readableServices[$id];
-      
-        if (is_array($service))
-        {
-          //convert the array definition to a client object
-          $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']);
-          $this->_readableServices[$id] = $service;
-        }
-        
-        $service->setCreateDocuments($this->_createDocuments);
-        $this->_currentReadService = $id;
-      }
-      else
-      {
-        throw new Exception('No read services were available');
-      }
-    }
-
-    return $this->_readableServices[$this->_currentReadService];
-  }
-
-  /**
-   * Iterate through available write services and select the first with a ping
-   * that satisfies configured timeout restrictions (or the default)
-   *
-   * @return Apache_Solr_Service
-   *
-   * @throws Exception If there are no write services that meet requirements
-   */
-  protected function _selectWriteService($forceSelect = false)
-  {
-    if($this->_useBackoff)
-    {
-      return $this->_selectWriteServiceSafe($forceSelect);
-    }    
-    
-    if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect)
-    {
-      if ($this->_currentWriteService && isset($this->_writeableServices[$this->_currentWriteService]) && $forceSelect)
-      {
-        // we probably had a communication error, ping the current read service, remove it if it times out
-        if ($this->_writeableServices[$this->_currentWriteService]->ping($this->_writePingTimeout) === false)
-        {
-          $this->removeWriteService($this->_currentWriteService);
-        }
-      }
-      
-      if (count($this->_writeableServices))
-      {
-        // select one of the read services at random
-        $ids = array_keys($this->_writeableServices);
-        
-        $id = $ids[rand(0, count($ids) - 1)];
-        $service = $this->_writeableServices[$id];
-      
-        if (is_array($service))
-        {
-          //convert the array definition to a client object
-          $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']);
-          $this->_writeableServices[$id] = $service;
-        }
-
-        $this->_currentWriteService = $id;
-      }
-      else
-      {
-        throw new Exception('No write services were available');
-      }
-    }
-
-    return $this->_writeableServices[$this->_currentWriteService];
-  }
-  
-  /**
-   * Iterate through available write services and select the first with a ping
-   * that satisfies configured timeout restrictions (or the default).  The 
-   * timeout period will increase until a connection is made or the limit is 
-   * reached.   This will allow for increased reliability with heavily loaded 
-   * server(s).
-   *
-   * @return Apache_Solr_Service
-   *
-   * @throws Exception If there are no write services that meet requirements
-   */  
-  
-  protected function _selectWriteServiceSafe($forceSelect = false)
-  {    
-    if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect)
-    {
-      if (count($this->_writeableServices))
-      {
-        $backoff = $this->_defaultBackoff;
-        
-        do {
-          // select one of the read services at random
-          $ids = array_keys($this->_writeableServices);
-          
-          $id = $ids[rand(0, count($ids) - 1)];
-          $service = $this->_writeableServices[$id];
-        
-          if (is_array($service))
-          {
-            //convert the array definition to a client object
-            $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']);
-            $this->_writeableServices[$id] = $service;
-          }
-  
-          $this->_currentWriteService = $id;
-                    
-          $backoff *= $this->_backoffEscalation;
-          
-          if($backoff > $this->_backoffLimit)
-          {
-            throw new Exception('No write services were available.  All timeouts exceeded.');
-          }
-          
-        } while($this->_writeableServices[$this->_currentWriteService]->ping($backoff) === false);
-      }
-      else
-      {
-        throw new Exception('No write services were available');
-      }
-    }
-
-    return $this->_writeableServices[$this->_currentWriteService];
-  }
-
-  public function setCreateDocuments($createDocuments)
-  {
-    $this->_createDocuments = (bool) $createDocuments;
-
-    if ($this->_currentReadService)
-    {
-      $service = $this->_selectReadService();
-      $service->setCreateDocuments($createDocuments);
-    }
-  }
-
-  public function getCreateDocuments()
-  {
-    return $this->_createDocuments;
-  }
-
-  /**
-   * Raw Add Method. Takes a raw post body and sends it to the update service.  Post body
-   * should be a complete and well formed "add" xml document.
-   *
-   * @param string $rawPost
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function add($rawPost)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->add($rawPost);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Add a Solr Document to the index
-   *
-   * @param Apache_Solr_Document $document
-   * @param boolean $allowDups
-   * @param boolean $overwritePending
-   * @param boolean $overwriteCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->addDocument($document, $allowDups, $overwritePending, $overwriteCommitted);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Add an array of Solr Documents to the index all at once
-   *
-   * @param array $documents Should be an array of Apache_Solr_Document instances
-   * @param boolean $allowDups
-   * @param boolean $overwritePending
-   * @param boolean $overwriteCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->addDocuments($documents, $allowDups, $overwritePending, $overwriteCommitted);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Send a commit command.  Will be synchronous unless both wait parameters are set
-   * to false.
-   *
-   * @param boolean $waitFlush
-   * @param boolean $waitSearcher
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function commit($optimize = true, $waitFlush = true, $waitSearcher = true, $timeout = 3600)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->commit($optimize, $waitFlush, $waitSearcher, $timeout);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
-   * a complete and well formed "delete" xml document
-   *
-   * @param string $rawPost
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function delete($rawPost)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->delete($rawPost);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Create a delete document based on document ID
-   *
-   * @param string $id
-   * @param boolean $fromPending
-   * @param boolean $fromCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function deleteById($id, $fromPending = true, $fromCommitted = true)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->deleteById($id, $fromPending, $fromCommitted);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Create a delete document based on a query and submit it
-   *
-   * @param string $rawQuery
-   * @param boolean $fromPending
-   * @param boolean $fromCommitted
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->deleteByQuery($rawQuery, $fromPending, $fromCommitted);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Send an optimize command.  Will be synchronous unless both wait parameters are set
-   * to false.
-   *
-   * @param boolean $waitFlush
-   * @param boolean $waitSearcher
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function optimize($waitFlush = true, $waitSearcher = true)
-  {
-    $service = $this->_selectWriteService();
-
-    do
-    {
-      try
-      {
-        return $service->optimize($waitFlush, $waitSearcher);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectWriteService(true);
-    } while ($service);
-    
-    return false;
-  }
-
-  /**
-   * Simple Search interface
-   *
-   * @param string $query The raw query string
-   * @param int $offset The starting offset for result documents
-   * @param int $limit The maximum number of result documents to return
-   * @param array $params key / value pairs for query parameters, use arrays for multivalued parameters
-   * @return Apache_Solr_Response
-   *
-   * @throws Exception If an error occurs during the service call
-   */
-  public function search($query, $offset = 0, $limit = 10, $params = array())
-  {
-    $service = $this->_selectReadService();
-
-    do
-    {
-      try
-      {
-        return $service->search($query, $offset, $limit, $params);
-      }
-      catch (Exception $e)
-      {
-        if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
-        {
-          throw $e;
-        }
-      }
-
-      $service = $this->_selectReadService(true);
-    } while ($service);
-    
-    return false;
-  }
+	protected $_readPingTimeout = 2;
+	protected $_writePingTimeout = 4;
+	
+	// Configuration for server selection backoff intervals
+	protected $_useBackoff = false;		// Set to true to use more resillient write server selection
+	protected $_backoffLimit = 600;		// 10 minute default maximum
+	protected $_backoffEscalation = 2.0; 	// Rate at which to increase backoff period
+	protected $_defaultBackoff = 2.0;		// Default backoff interval
+
+	/**
+	 * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
+	 *
+	 * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead
+	 *
+	 * @param string $value
+	 * @return string
+	 */
+	static public function escape($value)
+	{
+		return Apache_Solr_Service::escape($value);
+	}
+
+	/**
+	 * Escape a value meant to be contained in a phrase for special query characters
+	 *
+	 * @param string $value
+	 * @return string
+	 */
+	static public function escapePhrase($value)
+	{
+		return Apache_Solr_Service::escapePhrase($value);
+	}
+
+	/**
+	 * Convenience function for creating phrase syntax from a value
+	 *
+	 * @param string $value
+	 * @return string
+	 */
+	static public function phrase($value)
+	{
+		return Apache_Solr_Service::phrase($value);
+	}
+
+	/**
+	 * Constructor. Takes arrays of read and write service instances or descriptions
+	 *
+	 * @param array $readableServices
+	 * @param array $writeableServices
+	 */
+	public function __construct($readableServices = array(), $writeableServices = array())
+	{
+		//setup readable services
+		foreach ($readableServices as $service)
+		{
+			$this->addReadService($service);
+		}
+
+		//setup writeable services
+		foreach ($writeableServices as $service)
+		{
+			$this->addWriteService($service);
+		}
+	}
+
+	public function setReadPingTimeout($timeout)
+	{
+		$this->_readPingTimeout = $timeout;
+	}
+
+	public function setWritePingTimeout($timeout)
+	{
+		$this->_writePingTimeout = $timeout;
+	}
+	
+	public function setUseBackoff($enable)
+	{
+		$this->_useBackoff = $enable;
+	}
+
+	/**
+	 * Generates a service ID
+	 *
+	 * @param string $host
+	 * @param integer $port
+	 * @param string $path
+	 * @return string
+	 */
+	protected function _getServiceId($host, $port, $path)
+	{
+		return $host . ':' . $port . $path;
+	}
+
+	/**
+	 * Adds a service instance or service descriptor (if it is already
+	 * not added)
+	 *
+	 * @param mixed $service
+	 *
+	 * @throws Exception If service descriptor is not valid
+	 */
+	public function addReadService($service)
+	{
+		if ($service instanceof Apache_Solr_Service)
+		{
+			$id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
+
+			$this->_readableServices[$id] = $service;
+		}
+		else if (is_array($service))
+		{
+			if (isset($service['host']) && isset($service['port']) && isset($service['path']))
+			{
+				$id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
+
+				$this->_readableServices[$id] = $service;
+			}
+			else
+			{
+				throw new Exception('A Readable Service description array does not have all required elements of host, port, and path');
+			}
+		}
+	}
+
+	/**
+	 * Removes a service instance or descriptor from the available services
+	 *
+	 * @param mixed $service
+	 *
+	 * @throws Exception If service descriptor is not valid
+	 */
+	public function removeReadService($service)
+	{
+		$id = '';
+
+		if ($service instanceof Apache_Solr_Service)
+		{
+			$id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
+		}
+		else if (is_array($service))
+		{
+			if (isset($service['host']) && isset($service['port']) && isset($service['path']))
+			{
+				$id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
+			}
+			else
+			{
+				throw new Exception('A Readable Service description array does not have all required elements of host, port, and path');
+			}
+		}
+		else if (is_string($service))
+		{
+			$id = $service;
+		}
+
+		if ($id && isset($this->_readableServices[$id]))
+		{
+			unset($this->_readableServices[$id]);
+		}
+	}
+
+	/**
+	 * Adds a service instance or service descriptor (if it is already
+	 * not added)
+	 *
+	 * @param mixed $service
+	 *
+	 * @throws Exception If service descriptor is not valid
+	 */
+	public function addWriteService($service)
+	{
+		if ($service instanceof Apache_Solr_Service)
+		{
+			$id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
+
+			$this->_writeableServices[$id] = $service;
+		}
+		else if (is_array($service))
+		{
+			if (isset($service['host']) && isset($service['port']) && isset($service['path']))
+			{
+				$id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
+
+				$this->_writeableServices[$id] = $service;
+			}
+			else
+			{
+				throw new Exception('A Writeable Service description array does not have all required elements of host, port, and path');
+			}
+		}
+	}
+
+	/**
+	 * Removes a service instance or descriptor from the available services
+	 *
+	 * @param mixed $service
+	 *
+	 * @throws Exception If service descriptor is not valid
+	 */
+	public function removeWriteService($service)
+	{
+		$id = '';
+
+		if ($service instanceof Apache_Solr_Service)
+		{
+			$id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath());
+		}
+		else if (is_array($service))
+		{
+			if (isset($service['host']) && isset($service['port']) && isset($service['path']))
+			{
+				$id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']);
+			}
+			else
+			{
+				throw new Exception('A Readable Service description array does not have all required elements of host, port, and path');
+			}
+		}
+		else if (is_string($service))
+		{
+			$id = $service;
+		}
+
+		if ($id && isset($this->_writeableServices[$id]))
+		{
+			unset($this->_writeableServices[$id]);
+		}
+	}
+
+	/**
+	 * Iterate through available read services and select the first with a ping
+	 * that satisfies configured timeout restrictions (or the default)
+	 *
+	 * @return Apache_Solr_Service
+	 *
+	 * @throws Exception If there are no read services that meet requirements
+	 */
+	protected function _selectReadService($forceSelect = false)
+	{
+		if (!$this->_currentReadService || !isset($this->_readableServices[$this->_currentReadService]) || $forceSelect)
+		{
+			if ($this->_currentReadService && isset($this->_readableServices[$this->_currentReadService]) && $forceSelect)
+			{
+				// we probably had a communication error, ping the current read service, remove it if it times out
+				if ($this->_readableServices[$this->_currentReadService]->ping($this->_readPingTimeout) === false)
+				{
+					$this->removeReadService($this->_currentReadService);
+				}
+			}
+			
+			if (count($this->_readableServices))
+			{
+				// select one of the read services at random
+				$ids = array_keys($this->_readableServices);
+				
+				$id = $ids[rand(0, count($ids) - 1)];
+				$service = $this->_readableServices[$id];
+			
+				if (is_array($service))
+				{
+					//convert the array definition to a client object
+					$service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']);
+					$this->_readableServices[$id] = $service;
+				}
+				
+				$service->setCreateDocuments($this->_createDocuments);
+				$this->_currentReadService = $id;
+			}
+			else
+			{
+				throw new Exception('No read services were available');
+			}
+		}
+
+		return $this->_readableServices[$this->_currentReadService];
+	}
+
+	/**
+	 * Iterate through available write services and select the first with a ping
+	 * that satisfies configured timeout restrictions (or the default)
+	 *
+	 * @return Apache_Solr_Service
+	 *
+	 * @throws Exception If there are no write services that meet requirements
+	 */
+	protected function _selectWriteService($forceSelect = false)
+	{
+		if($this->_useBackoff)
+		{
+			return $this->_selectWriteServiceSafe($forceSelect);
+		}		
+		
+		if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect)
+		{
+			if ($this->_currentWriteService && isset($this->_writeableServices[$this->_currentWriteService]) && $forceSelect)
+			{
+				// we probably had a communication error, ping the current read service, remove it if it times out
+				if ($this->_writeableServices[$this->_currentWriteService]->ping($this->_writePingTimeout) === false)
+				{
+					$this->removeWriteService($this->_currentWriteService);
+				}
+			}
+			
+			if (count($this->_writeableServices))
+			{
+				// select one of the read services at random
+				$ids = array_keys($this->_writeableServices);
+				
+				$id = $ids[rand(0, count($ids) - 1)];
+				$service = $this->_writeableServices[$id];
+			
+				if (is_array($service))
+				{
+					//convert the array definition to a client object
+					$service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']);
+					$this->_writeableServices[$id] = $service;
+				}
+
+				$this->_currentWriteService = $id;
+			}
+			else
+			{
+				throw new Exception('No write services were available');
+			}
+		}
+
+		return $this->_writeableServices[$this->_currentWriteService];
+	}
+	
+	/**
+	 * Iterate through available write services and select the first with a ping
+	 * that satisfies configured timeout restrictions (or the default).  The 
+	 * timeout period will increase until a connection is made or the limit is 
+	 * reached.   This will allow for increased reliability with heavily loaded 
+	 * server(s).
+	 *
+	 * @return Apache_Solr_Service
+	 *
+	 * @throws Exception If there are no write services that meet requirements
+	 */	
+	
+	protected function _selectWriteServiceSafe($forceSelect = false)
+	{		
+		if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect)
+		{
+			if (count($this->_writeableServices))
+			{
+				$backoff = $this->_defaultBackoff;
+				
+				do {
+					// select one of the read services at random
+					$ids = array_keys($this->_writeableServices);
+					
+					$id = $ids[rand(0, count($ids) - 1)];
+					$service = $this->_writeableServices[$id];
+				
+					if (is_array($service))
+					{
+						//convert the array definition to a client object
+						$service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']);
+						$this->_writeableServices[$id] = $service;
+					}
+	
+					$this->_currentWriteService = $id;
+										
+					$backoff *= $this->_backoffEscalation;
+					
+					if($backoff > $this->_backoffLimit)
+					{
+						throw new Exception('No write services were available.  All timeouts exceeded.');
+					}
+					
+				} while($this->_writeableServices[$this->_currentWriteService]->ping($backoff) === false);
+			}
+			else
+			{
+				throw new Exception('No write services were available');
+			}
+		}
+
+		return $this->_writeableServices[$this->_currentWriteService];
+	}
+
+	public function setCreateDocuments($createDocuments)
+	{
+		$this->_createDocuments = (bool) $createDocuments;
+
+		if ($this->_currentReadService)
+		{
+			$service = $this->_selectReadService();
+			$service->setCreateDocuments($createDocuments);
+		}
+	}
+
+	public function getCreateDocuments()
+	{
+		return $this->_createDocuments;
+	}
+
+	/**
+	 * Raw Add Method. Takes a raw post body and sends it to the update service.  Post body
+	 * should be a complete and well formed "add" xml document.
+	 *
+	 * @param string $rawPost
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function add($rawPost)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->add($rawPost);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Add a Solr Document to the index
+	 *
+	 * @param Apache_Solr_Document $document
+	 * @param boolean $allowDups
+	 * @param boolean $overwritePending
+	 * @param boolean $overwriteCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->addDocument($document, $allowDups, $overwritePending, $overwriteCommitted);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Add an array of Solr Documents to the index all at once
+	 *
+	 * @param array $documents Should be an array of Apache_Solr_Document instances
+	 * @param boolean $allowDups
+	 * @param boolean $overwritePending
+	 * @param boolean $overwriteCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->addDocuments($documents, $allowDups, $overwritePending, $overwriteCommitted);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Send a commit command.  Will be synchronous unless both wait parameters are set
+	 * to false.
+	 *
+	 * @param boolean $waitFlush
+	 * @param boolean $waitSearcher
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function commit($optimize = true, $waitFlush = true, $waitSearcher = true, $timeout = 3600)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->commit($optimize, $waitFlush, $waitSearcher, $timeout);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
+	 * a complete and well formed "delete" xml document
+	 *
+	 * @param string $rawPost
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function delete($rawPost)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->delete($rawPost);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Create a delete document based on document ID
+	 *
+	 * @param string $id
+	 * @param boolean $fromPending
+	 * @param boolean $fromCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function deleteById($id, $fromPending = true, $fromCommitted = true)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->deleteById($id, $fromPending, $fromCommitted);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Create a delete document based on a query and submit it
+	 *
+	 * @param string $rawQuery
+	 * @param boolean $fromPending
+	 * @param boolean $fromCommitted
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->deleteByQuery($rawQuery, $fromPending, $fromCommitted);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Send an optimize command.  Will be synchronous unless both wait parameters are set
+	 * to false.
+	 *
+	 * @param boolean $waitFlush
+	 * @param boolean $waitSearcher
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function optimize($waitFlush = true, $waitSearcher = true)
+	{
+		$service = $this->_selectWriteService();
+
+		do
+		{
+			try
+			{
+				return $service->optimize($waitFlush, $waitSearcher);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectWriteService(true);
+		} while ($service);
+		
+		return false;
+	}
+
+	/**
+	 * Simple Search interface
+	 *
+	 * @param string $query The raw query string
+	 * @param int $offset The starting offset for result documents
+	 * @param int $limit The maximum number of result documents to return
+	 * @param array $params key / value pairs for query parameters, use arrays for multivalued parameters
+	 * @return Apache_Solr_Response
+	 *
+	 * @throws Exception If an error occurs during the service call
+	 */
+	public function search($query, $offset = 0, $limit = 10, $params = array())
+	{
+		$service = $this->_selectReadService();
+
+		do
+		{
+			try
+			{
+				return $service->search($query, $offset, $limit, $params);
+			}
+			catch (Exception $e)
+			{
+				if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR
+				{
+					throw $e;
+				}
+			}
+
+			$service = $this->_selectReadService(true);
+		} while ($service);
+		
+		return false;
+	}
 }
