? .solrconfig.xml.swp
? .svn
? 352430.facet_limit_default.diff
? first_run_attachments.diff
? mlt-nodeaccess-348150-12.patch
? new_php_client_and_solr_trunk_0.diff
? solr_14_and_new_solr_php_client.diff
? subquery_test.diff
? SolrPhpClient/.svn
? SolrPhpClient/Apache/.svn
? SolrPhpClient/Apache/Solr/.svn
? SolrPhpClient/Apache/Solr/Service/.svn
? blocks/.svn
? contrib/.svn
? contrib/apachesolr_attachments/.svn
? contrib/apachesolr_image/.svn
? contrib/apachesolr_mlt/.svn
? contrib/apachesolr_multisitesearch/.svn
? contrib/apachesolr_nodeaccess/.svn
? contrib/apachesolr_nodeaccess/tests/.svn
? tests/.svn
Index: solrconfig.xml
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/Attic/solrconfig.xml,v
retrieving revision 1.1.2.10
diff -u -r1.1.2.10 solrconfig.xml
--- solrconfig.xml	19 Dec 2008 01:23:09 -0000	1.1.2.10
+++ solrconfig.xml	31 Dec 2008 23:24:21 -0000
@@ -17,8 +17,17 @@
 -->
 
 <config>
-  <!-- Set this to 'false' if you want solr to continue working after it has
-       encountered an severe configuration error.  In a production environment,
+
+<requestHandler name="/update/extract" class="org.apache.solr.handler.extraction.ExtractingRequestHandler">
+    <lst name="defaults">
+      <str name="ext.map.Last-Modified">last_modified</str>
+      <bool name="ext.ignore.und.fl">true</bool>
+    </lst>
+</requestHandler>
+
+
+  <!-- Set this to 'false' if you want solr to continue working after it has 
+       encountered an severe configuration error.  In a production environment, 
        you may want solr to keep working even if one handler is mis-configured.
 
        You may also set this to false using by setting the system property:
@@ -84,7 +93,7 @@
 
     <!--
       This option specifies which Lucene LockFactory implementation to use.
-
+      
       single = SingleInstanceLockFactory - suggested for a read-only index
                or when there is no possibility of another process trying
                to modify the index.
@@ -107,16 +116,44 @@
     <maxMergeDocs>2147483647</maxMergeDocs>
     <maxFieldLength>10000</maxFieldLength>
 
-    <!-- If true, unlock any held write or commit locks on startup.
+    <!-- If true, unlock any held write or commit locks on startup. 
          This defeats the locking mechanism that allows multiple
          processes to safely access a lucene index, and should be
          used with care.
          This is not needed if lock type is 'none' or 'single'
      -->
     <unlockOnStartup>false</unlockOnStartup>
+
+    <!--
+        Custom deletion policies can specified here. The class must
+        implement org.apache.lucene.index.IndexDeletionPolicy.
+
+        http://lucene.apache.org/java/2_3_2/api/org/apache/lucene/index/IndexDeletionPolicy.html
+
+        The standard Solr IndexDeletionPolicy implementation supports deleting
+        index commit points on number of commits, age of commit point and
+        optimized status.
+
+        The latest commit point should always be preserved regardless
+        of the criteria.
+    -->
+    <deletionPolicy class="solr.SolrDeletionPolicy">
+      <!-- Keep only optimized commit points -->
+      <str name="keepOptimizedOnly">false</str>
+      <!-- The maximum number of commit points to be kept -->
+      <str name="maxCommitsToKeep">1</str>
+      <!--
+          Delete all commit points once they have reached the given age.
+          Supports DateMathParser syntax e.g.
+          
+          <str name="maxCommitAge">30MINUTES</str>
+          <str name="maxCommitAge">1DAY</str>
+      -->
+    </deletionPolicy>
+
   </mainIndex>
 
-  <!--	Enables JMX if and only if an existing MBeanServer is found, use
+  <!--	Enables JMX if and only if an existing MBeanServer is found, use 
   		this if you want to configure JMX through JVM parameters. Remove
   		this to disable exposing Solr configuration and statistics to JMX.
 
@@ -124,7 +161,7 @@
 		e.g. <jmx agentId="myAgent" />
 
 		If you want to start a new MBeanServer, specify the serviceUrl
-		e.g <jmx serviceurl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr" />
+		e.g <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr" />
 
 		For more details see http://wiki.apache.org/solr/SolrJmx
   -->
@@ -141,9 +178,9 @@
     <!-- Perform a <commit/> automatically under certain conditions:
          maxDocs - number of updates since last commit is greater than this
          maxTime - oldest uncommited update (in ms) is this long ago
-    <autoCommit>
+    <autoCommit> 
       <maxDocs>10000</maxDocs>
-      <maxTime>1000</maxTime>
+      <maxTime>1000</maxTime> 
     </autoCommit>
     -->
 
@@ -164,7 +201,7 @@
     </listener>
     -->
     <!-- A postOptimize event is fired only after every optimize command, useful
-         in conjunction with index distribution to only distribute optimized indicies
+         in conjunction with index distribution to only distribute optimized indicies 
     <listener event="postOptimize" class="solr.RunExecutableListener">
       <str name="exe">snapshooter</str>
       <str name="dir">solr/bin</str>
@@ -182,6 +219,12 @@
     <maxBooleanClauses>1024</maxBooleanClauses>
 
 
+    <!-- There are two implementations of cache available for Solr,
+         LRUCache, based on a synchronized LinkedHashMap, and
+         FastLRUCache, based on a ConcurrentHashMap.  FastLRUCache has faster gets
+         and slower puts in single threaded operation and thus is generally faster
+         than LRUCache when the hit ratio of the cache is high (> 75%), and may be
+         faster under other scenarios on multi-cpu systems. -->
     <!-- Cache used by SolrIndexSearcher for filters (DocSets),
          unordered sets of *all* documents that match a query.
          When a new searcher is opened, its caches may be prepopulated
@@ -189,7 +232,7 @@
          autowarmCount is the number of items to prepopulate.  For LRUCache,
          the autowarmed items will be the most recently accessed items.
        Parameters:
-         class - the SolrCache implementation (currently only LRUCache)
+         class - the SolrCache implementation LRUCache or FastLRUCache
          size - the maximum number of entries in the cache
          initialSize - the initial capacity (number of entries) of
            the cache.  (seel java.util.HashMap)
@@ -197,11 +240,22 @@
            and old cache.
          -->
     <filterCache
-      class="solr.LRUCache"
+      class="solr.FastLRUCache"
       size="512"
       initialSize="512"
       autowarmCount="128"/>
 
+    <!-- Cache used to hold field values that are quickly accessible
+         by document id.  The fieldValueCache is created by default
+         even if not configured here.
+      <fieldValueCache
+        class="solr.FastLRUCache"
+        size="512"
+        autowarmCount="128"
+        showItems="32"
+      />
+    -->
+
    <!-- queryResultCache caches results of searches - ordered lists of
          document ids (DocList) based on a query, a sort, and the range
          of documents requested.  -->
@@ -303,7 +357,7 @@
 
   </query>
 
-  <!--
+  <!-- 
     Let the dispatch filter handler /select?qt=XXX
     handleSelect=true will use consistent error handling for /select and /update
     handleSelect=false will use solr1.1 style error formatting
@@ -313,7 +367,7 @@
     <requestParsers enableRemoteStreaming="false" multipartUploadLimitInKB="2048" />
 
     <!-- Set HTTP caching related parameters (for proxy caches and clients).
-
+          
          To get the behaviour of Solr 1.2 (ie: no caching related headers)
          use the never304="true" option and do not specify a value for
          <cacheControl>
@@ -352,7 +406,7 @@
 
   <!-- requestHandler plugins... incoming queries will be dispatched to the
      correct handler based on the path or the qt (query type) param.
-     Names starting with a '/' are accessed with the a path equal to the
+     Names starting with a '/' are accessed with the a path equal to the 
      registered name.  Names without a leading '/' are accessed with:
       http://host/app/select?qt=name
      If no qt is defined, the requestHandler that declares default="true"
@@ -370,14 +424,33 @@
      </lst>
   </requestHandler>
 
+<!-- Please refer to http://wiki.apache.org/solr/SolrReplication for details on configuring replication -->
+<!--Master config-->
+<!--
+<requestHandler name="/replication" class="solr.ReplicationHandler" >
+    <lst name="master">
+        <str name="replicateAfter">commit</str>
+         <str name="confFiles">schema.xml,stopwords.txt</str>
+    </lst>
+</requestHandler>
+-->
+<!-- Slave config-->
+<!--
+<requestHandler name="/replication" class="solr.ReplicationHandler">
+    <lst name="slave">
+        <str name="masterUrl">http://localhost:8983/solr/replication</str>
+        <str name="pollInterval">00:00:60</str>  
+     </lst>
+</requestHandler>
+-->
 
   <!-- DisMaxRequestHandler allows easy searching across multiple fields
        for simple user-entered phrases.  It's implementation is now
        just the standard SearchHandler with a default query type
-       of "dismax".
+       of "dismax". 
        see http://wiki.apache.org/solr/DisMaxRequestHandler
    -->
-  <requestHandler name="dismax" class="solr.SearchHandler" >
+  <requestHandler name="dismax" class="solr.SearchHandler">
     <lst name="defaults">
      <str name="defType">dismax</str>
      <str name="echoParams">explicit</str>
@@ -387,7 +460,7 @@
   <!-- Note how you can register the same handler multiple times with
        different names (and different init parameters)
     -->
-  <requestHandler name="drupal" class="solr.SearchHandler" default="true">
+  <requestHandler name="partitioned" class="solr.SearchHandler" default="true">
     <lst name="defaults">
      <str name="defType">dismax</str>
      <str name="echoParams">explicit</str>
@@ -445,33 +518,36 @@
    </lst>
   </requestHandler>
 
+
   <!--
    Search components are registered to SolrCore and used by Search Handlers
-
+   
    By default, the following components are avaliable:
-
+    
    <searchComponent name="query"     class="org.apache.solr.handler.component.QueryComponent" />
    <searchComponent name="facet"     class="org.apache.solr.handler.component.FacetComponent" />
    <searchComponent name="mlt"       class="org.apache.solr.handler.component.MoreLikeThisComponent" />
    <searchComponent name="highlight" class="org.apache.solr.handler.component.HighlightComponent" />
+   <searchComponent name="stats"     class="org.apache.solr.handler.component.StatsComponent" />
    <searchComponent name="debug"     class="org.apache.solr.handler.component.DebugComponent" />
-
+   
    Default configuration in a requestHandler would look like:
     <arr name="components">
       <str>query</str>
       <str>facet</str>
       <str>mlt</str>
       <str>highlight</str>
+      <str>stats</str>
       <str>debug</str>
     </arr>
 
     If you register a searchComponent to one of the standard names, that will be used instead.
-    To insert handlers before or after the 'standard' components, use:
-
+    To insert components before or after the 'standard' components, use:
+    
     <arr name="first-components">
       <str>myFirstComponentName</str>
     </arr>
-
+    
     <arr name="last-components">
       <str>myLastComponentName</str>
     </arr>
@@ -487,7 +563,7 @@
       <str name="name">default</str>
       <str name="field">spell</str>
       <str name="spellcheckIndexDir">./spellchecker1</str>
-      <str name="buildOnCommit">true</str>
+
     </lst>
     <lst name="spellchecker">
       <str name="name">jarowinkler</str>
@@ -495,7 +571,7 @@
       <!-- Use a different Distance Measure -->
       <str name="distanceMeasure">org.apache.lucene.search.spell.JaroWinklerDistance</str>
       <str name="spellcheckIndexDir">./spellchecker2</str>
-      <str name="buildOnCommit">true</str>
+
     </lst>
 
   </searchComponent>
@@ -519,7 +595,7 @@
   </requestHandler>
 
 
-  <!-- Update request handler.
+  <!-- Update request handler.  
 
        Note: Since solr1.1 requestHandlers requires a valid content type header if posted in
        the body. For example, curl now requires: -H 'Content-type:text/xml; charset=utf-8'
@@ -540,22 +616,22 @@
   <requestHandler name="/update/csv" class="solr.CSVRequestHandler" startup="lazy" />
 
 
-  <!--
-   Admin Handlers - This will register all the standard admin RequestHandlers.  Adding
-   this single handler is equivolent to registering:
-
+  <!-- 
+   Admin Handlers - This will register all the standard admin RequestHandlers.  Adding 
+   this single handler is equivalent to registering:
+   
   <requestHandler name="/admin/luke"       class="org.apache.solr.handler.admin.LukeRequestHandler" />
   <requestHandler name="/admin/system"     class="org.apache.solr.handler.admin.SystemInfoHandler" />
   <requestHandler name="/admin/plugins"    class="org.apache.solr.handler.admin.PluginInfoHandler" />
   <requestHandler name="/admin/threads"    class="org.apache.solr.handler.admin.ThreadDumpHandler" />
   <requestHandler name="/admin/properties" class="org.apache.solr.handler.admin.PropertiesRequestHandler" />
   <requestHandler name="/admin/file"       class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
-
+  
   If you wish to hide files under ${solr.home}/conf, explicitly register the ShowFileRequestHandler using:
   <requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
     <lst name="invariants">
-     <str name="hidden">synonyms.txt</str>
-     <str name="hidden">anotherfile.txt</str>
+     <str name="hidden">synonyms.txt</str> 
+     <str name="hidden">anotherfile.txt</str> 
     </lst>
   </requestHandler>
   -->
@@ -612,7 +688,7 @@
   <!-- queryResponseWriter plugins... query responses will be written using the
     writer specified by the 'wt' request parameter matching the name of a registered
     writer.
-    The "default" writer is the default and will be used if 'wt' is not specified
+    The "default" writer is the default and will be used if 'wt' is not specified 
     in the request. XMLResponseWriter will be used if nothing is specified here.
     The json, python, and ruby writers are also available by default.
 
@@ -628,7 +704,7 @@
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for
-       every xsltCacheLifetimeSeconds.
+       every xsltCacheLifetimeSeconds.  
    -->
   <queryResponseWriter name="xslt" class="org.apache.solr.request.XSLTResponseWriter">
     <int name="xsltCacheLifetimeSeconds">5</int>
@@ -639,7 +715,7 @@
   <queryParser name="lucene" class="org.apache.solr.search.LuceneQParserPlugin"/>
   -->
 
-  <!-- example of registering a custom function parser
+  <!-- example of registering a custom function parser 
   <valueSourceParser name="myfunc" class="com.mycompany.MyValueSourceParser" />
   -->
 
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	31 Dec 2008 23:24:21 -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	31 Dec 2008 23:24:21 -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	31 Dec 2008 23:24:21 -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	31 Dec 2008 23:24:21 -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	31 Dec 2008 23:24:21 -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;
+	}
 }
