Source for file Service.php

Documentation is available at Service.php

  1. <?php
  2. /**
  3.  * Copyright (c) 2007-2009, Conduit Internet Technologies, Inc.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions are met:
  8.  *
  9.  *  - Redistributions of source code must retain the above copyright notice,
  10.  *    this list of conditions and the following disclaimer.
  11.  *  - Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  *  - Neither the name of Conduit Internet Technologies, Inc. nor the names of
  15.  *    its contributors may be used to endorse or promote products derived from
  16.  *    this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28.  * POSSIBILITY OF SUCH DAMAGE.
  29.  *
  30.  * @copyright Copyright 2007-2009 Conduit Internet Technologies, Inc. (http://conduit-it.com)
  31.  * @license New BSD (http://solr-php-client.googlecode.com/svn/trunk/COPYING)
  32.  *
  33.  * @package Apache
  34.  * @subpackage Solr
  35.  * @author Donovan Jimenez <djimenez@conduit-it.com>
  36.  */
  37.  
  38. // See Issue #1 (http://code.google.com/p/solr-php-client/issues/detail?id=1)
  39. // Doesn't follow typical include path conventions, but is more convenient for users
  40. require_once(dirname(__FILE__'/Document.php');
  41. require_once(dirname(__FILE__'/Response.php');
  42.  
  43. /**
  44.  * Starting point for the Solr API. Represents a Solr server resource and has
  45.  * methods for pinging, adding, deleting, committing, optimizing and searching.
  46.  *
  47.  * Example Usage:
  48.  * <code>
  49.  * ...
  50.  * $solr = new Apache_Solr_Service(); //or explicitly new Apache_Solr_Service('localhost', 8180, '/solr')
  51.  *
  52.  * if ($solr->ping())
  53.  * {
  54.  *         $solr->deleteByQuery('*:*'); //deletes ALL documents - be careful :)
  55.  *
  56.  *         $document = new Apache_Solr_Document();
  57.  *         $document->id = uniqid(); //or something else suitably unique
  58.  *
  59.  *         $document->title = 'Some Title';
  60.  *         $document->content = 'Some content for this wonderful document. Blah blah blah.';
  61.  *
  62.  *         $solr->addDocument($document);     //if you're going to be adding documents in bulk using addDocuments
  63.  *                                         //with an array of documents is faster
  64.  *
  65.  *         $solr->commit(); //commit to see the deletes and the document
  66.  *         $solr->optimize(); //merges multiple segments into one
  67.  *
  68.  *         //and the one we all care about, search!
  69.  *         //any other common or custom parameters to the request handler can go in the
  70.  *         //optional 4th array argument.
  71.  *         $solr->search('content:blah', 0, 10, array('sort' => 'timestamp desc'));
  72.  * }
  73.  * ...
  74.  * </code>
  75.  *
  76.  * @todo Investigate using other HTTP clients other than file_get_contents built-in handler. Could provide performance
  77.  *  improvements when dealing with multiple requests by using HTTP's keep alive functionality
  78.  */
  79. {
  80.     /**
  81.      * Response version we support
  82.      */
  83.     const SOLR_VERSION '1.2';
  84.  
  85.     /**
  86.      * Response writer we support
  87.      *
  88.      * @todo Solr 1.3 release may change this to SerializedPHP or PHP implementation
  89.      */
  90.     const SOLR_WRITER 'json';
  91.  
  92.     /**
  93.      * NamedList Treatment constants
  94.      */
  95.     const NAMED_LIST_FLAT 'flat';
  96.     const NAMED_LIST_MAP 'map';
  97.  
  98.     /**
  99.      * Servlet mappings
  100.      */
  101.     const PING_SERVLET 'admin/ping';
  102.     const UPDATE_SERVLET 'update';
  103.     const SEARCH_SERVLET 'select';
  104.     const THREADS_SERVLET 'admin/threads';
  105.  
  106.     /**
  107.      * Server identification strings
  108.      *
  109.      * @var string 
  110.      */
  111.     protected $_host$_port$_path;
  112.  
  113.     /**
  114.      * Whether {@link Apache_Solr_Response} objects should create {@link Apache_Solr_Document}s in
  115.      * the returned parsed data
  116.      *
  117.      * @var boolean 
  118.      */
  119.     protected $_createDocuments = true;
  120.  
  121.     /**
  122.      * Whether {@link Apache_Solr_Response} objects should have multivalue fields with only a single value
  123.      * collapsed to appear as a single value would.
  124.      *
  125.      * @var boolean 
  126.      */
  127.     protected $_collapseSingleValueArrays = true;
  128.  
  129.     /**
  130.      * How NamedLists should be formatted in the output.  This specifically effects facet counts. Valid values
  131.      * are {@link Apache_Solr_Service::NAMED_LIST_MAP} (default) or {@link Apache_Solr_Service::NAMED_LIST_FLAT}.
  132.      *
  133.      * @var string 
  134.      */
  135.     protected $_namedListTreatment = self::NAMED_LIST_MAP;
  136.  
  137.     /**
  138.      * Query delimiters. Someone might want to be able to change
  139.      * these (to use &amp; instead of & for example), so I've provided them.
  140.      *
  141.      * @var string 
  142.      */
  143.     protected $_queryDelimiter = '?'$_queryStringDelimiter = '&';
  144.  
  145.     /**
  146.      * Constructed servlet full path URLs
  147.      *
  148.      * @var string 
  149.      */
  150.     protected $_updateUrl$_searchUrl$_threadsUrl;
  151.  
  152.     /**
  153.      * Keep track of whether our URLs have been constructed
  154.      *
  155.      * @var boolean 
  156.      */
  157.     protected $_urlsInited = false;
  158.  
  159.     /**
  160.      * Stream context for posting
  161.      *
  162.      * @var resource 
  163.      */
  164.     protected $_postContext;
  165.  
  166.     /**
  167.      * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
  168.      *
  169.      * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead
  170.      *
  171.      * @param string $value 
  172.      * @return string 
  173.      */
  174.     static public function escape($value)
  175.     {
  176.         //list taken from http://lucene.apache.org/java/docs/queryparsersyntax.html#Escaping%20Special%20Characters
  177.         $pattern '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
  178.         $replace '\\\$1';
  179.  
  180.         return preg_replace($pattern$replace$value);
  181.     }
  182.  
  183.     /**
  184.      * Escape a value meant to be contained in a phrase for special query characters
  185.      *
  186.      * @param string $value 
  187.      * @return string 
  188.      */
  189.     static public function escapePhrase($value)
  190.     {
  191.         $pattern '/("|\\\)/';
  192.         $replace '\\\$1';
  193.  
  194.         return preg_replace($pattern$replace$value);
  195.     }
  196.  
  197.     /**
  198.      * Convenience function for creating phrase syntax from a value
  199.      *
  200.      * @param string $value 
  201.      * @return string 
  202.      */
  203.     static public function phrase($value)
  204.     {
  205.         return '"' self::escapePhrase($value'"';
  206.     }
  207.  
  208.     /**
  209.      * Constructor. All parameters are optional and will take on default values
  210.      * if not specified.
  211.      *
  212.      * @param string $host 
  213.      * @param string $port 
  214.      * @param string $path 
  215.      */
  216.     public function __construct($host 'localhost'$port 8180$path '/solr/')
  217.     {
  218.         $this->setHost($host);
  219.         $this->setPort($port);
  220.         $this->setPath($path);
  221.  
  222.         $this->_initUrls();
  223.  
  224.         //set up the stream context for posting with file_get_contents
  225.         $contextOpts array(
  226.             'http' => array(
  227.                 'method' => 'POST',
  228.                 'header' => "Content-Type: text/xml; charset=UTF-8\r\n" //php.net example showed \r\n at the end
  229.             )
  230.         );
  231.  
  232.         $this->_postContext = stream_context_create($contextOpts);
  233.     }
  234.  
  235.     /**
  236.      * Return a valid http URL given this server's host, port and path and a provided servlet name
  237.      *
  238.      * @param string $servlet 
  239.      * @return string 
  240.      */
  241.     protected function _constructUrl($servlet$params array())
  242.     {
  243.         if (count($params))
  244.         {
  245.             //escape all parameters appropriately for inclusion in the query string
  246.             $escapedParams array();
  247.  
  248.             foreach ($params as $key => $value)
  249.             {
  250.                 $escapedParams[urlencode($key'=' urlencode($value);
  251.             }
  252.  
  253.             $queryString $this->_queryDelimiter . implode($this->_queryStringDelimiter$escapedParams);
  254.         }
  255.         else
  256.         {
  257.             $queryString '';
  258.         }
  259.  
  260.         return 'http://' $this->_host . ':' $this->_port . $this->_path . $servlet $queryString;
  261.     }
  262.  
  263.     /**
  264.      * Construct the Full URLs for the three servlets we reference
  265.      */
  266.     protected function _initUrls()
  267.     {
  268.         //Initialize our full servlet URLs now that we have server information
  269.         $this->_updateUrl = $this->_constructUrl(self::UPDATE_SERVLETarray('wt' => self::SOLR_WRITER ));
  270.         $this->_searchUrl = $this->_constructUrl(self::SEARCH_SERVLET);
  271.         $this->_threadsUrl = $this->_constructUrl(self::THREADS_SERVLETarray('wt' => self::SOLR_WRITER ));
  272.  
  273.         $this->_urlsInited = true;
  274.     }
  275.  
  276.     /**
  277.      * Central method for making a get operation against this Solr Server
  278.      *
  279.      * @param string $url 
  280.      * @param float $timeout Read timeout in seconds
  281.      * @return Apache_Solr_Response 
  282.      *
  283.      * @todo implement timeout ability
  284.      * @throws Exception If a non 200 response status is returned
  285.      */
  286.     protected function _sendRawGet($url$timeout FALSE)
  287.     {
  288.         //$http_response_header is set by file_get_contents
  289.         $response new Apache_Solr_Response(@file_get_contents($url)$http_response_header$this->_createDocuments$this->_collapseSingleValueArrays);
  290.  
  291.         if ($response->getHttpStatus(!= 200)
  292.         {
  293.             throw new Exception('"' $response->getHttpStatus('" Status: ' $response->getHttpStatusMessage()$response->getHttpStatus());
  294.         }
  295.  
  296.         return $response;
  297.     }
  298.  
  299.     /**
  300.      * Central method for making a post operation against this Solr Server
  301.      *
  302.      * @param string $url 
  303.      * @param string $rawPost 
  304.      * @param float $timeout Read timeout in seconds
  305.      * @param string $contentType 
  306.      * @return Apache_Solr_Response 
  307.      *
  308.      * @throws Exception If a non 200 response status is returned
  309.      */
  310.     protected function _sendRawPost($url$rawPost$timeout FALSE$contentType 'text/xml; charset=UTF-8')
  311.     {
  312.         //ensure content type is correct
  313.         stream_context_set_option($this->_postContext'http''header''Content-Type: ' $contentType);
  314.  
  315.         //set the read timeout if specified
  316.         if ($timeout !== FALSE)
  317.         {
  318.             stream_context_set_option($this->_postContext'http''timeout'$timeout);
  319.         }
  320.  
  321.         //set the content
  322.         stream_context_set_option($this->_postContext'http''content'$rawPost);
  323.  
  324.         //$http_response_header is set by file_get_contents
  325.         $response new Apache_Solr_Response(@file_get_contents($urlfalse$this->_postContext)$http_response_header$this->_createDocuments$this->_collapseSingleValueArrays);
  326.  
  327.         if ($response->getHttpStatus(!= 200)
  328.         {
  329.             throw new Exception('"' $response->getHttpStatus('" Status: ' $response->getHttpStatusMessage()$response->getHttpStatus());
  330.         }
  331.  
  332.         return $response;
  333.     }
  334.  
  335.     /**
  336.      * Returns the set host
  337.      *
  338.      * @return string 
  339.      */
  340.     public function getHost()
  341.     {
  342.         return $this->_host;
  343.     }
  344.  
  345.     /**
  346.      * Set the host used. If empty will fallback to constants
  347.      *
  348.      * @param string $host 
  349.      */
  350.     public function setHost($host)
  351.     {
  352.         //Use the provided host or use the default
  353.         if (empty($host))
  354.         {
  355.             throw new Exception('Host parameter is empty');
  356.         }
  357.         else
  358.         {
  359.             $this->_host = $host;
  360.         }
  361.  
  362.         if ($this->_urlsInited)
  363.         {
  364.             $this->_initUrls();
  365.         }
  366.     }
  367.  
  368.     /**
  369.      * Get the set port
  370.      *
  371.      * @return integer 
  372.      */
  373.     public function getPort()
  374.     {
  375.         return $this->_port;
  376.     }
  377.  
  378.     /**
  379.      * Set the port used. If empty will fallback to constants
  380.      *
  381.      * @param integer $port 
  382.      */
  383.     public function setPort($port)
  384.     {
  385.         //Use the provided port or use the default
  386.         $port = (int) $port;
  387.  
  388.         if ($port <= 0)
  389.         {
  390.             throw new Exception('Port is not a valid port number');
  391.         }
  392.         else
  393.         {
  394.             $this->_port = $port;
  395.         }
  396.  
  397.         if ($this->_urlsInited)
  398.         {
  399.             $this->_initUrls();
  400.         }
  401.     }
  402.  
  403.     /**
  404.      * Get the set path.
  405.      *
  406.      * @return string 
  407.      */
  408.     public function getPath()
  409.     {
  410.         return $this->_path;
  411.     }
  412.  
  413.     /**
  414.      * Set the path used. If empty will fallback to constants
  415.      *
  416.      * @param string $path 
  417.      */
  418.     public function setPath($path)
  419.     {
  420.         $path trim($path'/');
  421.  
  422.         $this->_path = '/' $path '/';
  423.  
  424.         if ($this->_urlsInited)
  425.         {
  426.             $this->_initUrls();
  427.         }
  428.     }
  429.  
  430.     /**
  431.      * Set the create documents flag. This determines whether {@link Apache_Solr_Response} objects will
  432.      * parse the response and create {@link Apache_Solr_Document} instances in place.
  433.      *
  434.      * @param unknown_type $createDocuments 
  435.      */
  436.     public function setCreateDocuments($createDocuments)
  437.     {
  438.         $this->_createDocuments = (bool) $createDocuments;
  439.     }
  440.  
  441.     /**
  442.      * Get the current state of teh create documents flag.
  443.      *
  444.      * @return boolean 
  445.      */
  446.     public function getCreateDocuments()
  447.     {
  448.         return $this->_createDocuments;
  449.     }
  450.  
  451.     /**
  452.      * Set the collapse single value arrays flag.
  453.      *
  454.      * @param boolean $collapseSingleValueArrays 
  455.      */
  456.     public function setCollapseSingleValueArrays($collapseSingleValueArrays)
  457.     {
  458.         $this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays;
  459.     }
  460.  
  461.     /**
  462.      * Get the current state of the collapse single value arrays flag.
  463.      *
  464.      * @return boolean 
  465.      */
  466.     public function getCollapseSingleValueArrays()
  467.     {
  468.         return $this->_collapseSingleValueArrays;
  469.     }
  470.  
  471.     /**
  472.      * Set how NamedLists should be formatted in the response data. This mainly effects
  473.      * the facet counts format.
  474.      *
  475.      * @param string $namedListTreatment 
  476.      * @throws Exception If invalid option is set
  477.      */
  478.     public function setNamedListTreatmet($namedListTreatment)
  479.     {
  480.         switch ((string) $namedListTreatment)
  481.         {
  482.             case Apache_Solr_Service::NAMED_LIST_FLAT:
  483.                 $this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_FLAT;
  484.                 break;
  485.  
  486.             case Apache_Solr_Service::NAMED_LIST_MAP:
  487.                 $this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_MAP;
  488.                 break;
  489.  
  490.             default:
  491.                 throw new Exception('Not a valid named list treatement option');
  492.         }
  493.     }
  494.  
  495.     /**
  496.      * Get the current setting for named list treatment.
  497.      *
  498.      * @return string 
  499.      */
  500.     public function getNamedListTreatment()
  501.     {
  502.         return $this->_namedListTreatment;
  503.     }
  504.  
  505.  
  506.     /**
  507.      * Set the string used to separate the path form the query string.
  508.      * Defaulted to '?'
  509.      *
  510.      * @param string $queryDelimiter 
  511.      */
  512.     public function setQueryDelimiter($queryDelimiter)
  513.     {
  514.         $this->_queryDelimiter = $queryDelimiter;
  515.     }
  516.  
  517.     /**
  518.      * Set the string used to separate the parameters in thequery string
  519.      * Defaulted to '&'
  520.      *
  521.      * @param string $queryStringDelimiter 
  522.      */
  523.     public function setQueryStringDelimiter($queryStringDelimiter)
  524.     {
  525.         $this->_queryStringDelimiter = $queryStringDelimiter;
  526.     }
  527.  
  528.     /**
  529.      * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
  530.      * server is able to be made.
  531.      *
  532.      * @param float $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
  533.      * @return float Actual time taken to ping the server, FALSE if timeout occurs
  534.      */
  535.     public function ping($timeout 2)
  536.     {
  537.         $timeout = (float) $timeout;
  538.  
  539.         if ($timeout <= 0)
  540.         {
  541.             $timeout = -1;
  542.         }
  543.  
  544.         $start microtime(true);
  545.  
  546.         //to prevent strict errors
  547.         $errno 0;
  548.         $errstr '';
  549.  
  550.         //try to connect to the host with timeout
  551.         $fp fsockopen($this->_host$this->_port$errno$errstr$timeout);
  552.  
  553.         if ($fp)
  554.         {
  555.             //If we have a timeout set, then determine the amount of time we have left
  556.             //in the request and set the stream timeout for the write operation
  557.             if ($timeout 0)
  558.             {
  559.                 //do the calculation
  560.                 $writeTimeout $timeout (microtime(true$start);
  561.  
  562.                 //check if we're out of time
  563.                 if ($writeTimeout <= 0)
  564.                 {
  565.                     fclose($fp);
  566.                     return false;
  567.                 }
  568.  
  569.                 //convert to microseconds and set the stream timeout
  570.                 $writeTimeoutInMicroseconds = (int) $writeTimeout 1000000;
  571.                 stream_set_timeout($fp0$writeTimeoutInMicroseconds);
  572.             }
  573.  
  574.             $request =     'HEAD ' $this->_path . self::PING_SERVLET ' HTTP/1.1' "\r\n" .
  575.                         'host: ' $this->_host . "\r\n" .
  576.                         'Connection: close' "\r\n" .
  577.                         "\r\n";
  578.  
  579.             fwrite($fp$request);
  580.  
  581.             //check the stream meta data to see if we timed out during the operation
  582.             $metaData stream_get_meta_data($fp);
  583.  
  584.             if (isset($metaData['timeout']&& $metaData['timeout'])
  585.             {
  586.                 fclose($fp);
  587.                 return false;
  588.             }
  589.  
  590.  
  591.             //if we have a timeout set and have made it this far, determine the amount of time
  592.             //still remaining and set the timeout appropriately before the read operation
  593.             if ($timeout 0)
  594.             {
  595.                 //do the calculation
  596.                 $readTimeout $timeout (microtime(true$start);
  597.  
  598.                 //check if we've run out of time
  599.                 if ($readTimeout <= 0)
  600.                 {
  601.                     fclose($fp);
  602.                     return false;
  603.                 }
  604.  
  605.                 //convert to microseconds and set the stream timeout
  606.                 $readTimeoutInMicroseconds $readTimeout 1000000;
  607.                 stream_set_timeout($fp0$readTimeoutInMicroseconds);
  608.             }
  609.  
  610.             //at the very least we should get a response header line of
  611.             //HTTP/1.1 200 OK
  612.             $response fread($fp15);
  613.  
  614.             //check the stream meta data to see if we timed out during the operation
  615.             $metaData stream_get_meta_data($fp);
  616.             fclose($fp)//we're done with the connection - ignore the rest
  617.  
  618.             if (isset($metaData['timeout']&& $metaData['timeout'])
  619.             {
  620.                 return false;
  621.             }
  622.  
  623.             //finally, check the response header line
  624.             if ($response != 'HTTP/1.1 200 OK')
  625.             {
  626.                 return false;
  627.             }
  628.  
  629.             //we made it, return the approximate ping time
  630.             return microtime(true$start;
  631.         }
  632.  
  633.         //we weren't able to make a connection
  634.         return false;
  635.     }
  636.  
  637.     /**
  638.      * Call the /admin/threads servlet and retrieve information about all threads in the
  639.      * Solr servlet's thread group. Useful for diagnostics.
  640.      *
  641.      * @return Apache_Solr_Response 
  642.      *
  643.      * @throws Exception If an error occurs during the service call
  644.      */
  645.     public function threads()
  646.     {
  647.         return $this->_sendRawGet($this->_threadsUrl);
  648.     }
  649.  
  650.     /**
  651.      * Raw Add Method. Takes a raw post body and sends it to the update service.  Post body
  652.      * should be a complete and well formed "add" xml document.
  653.      *
  654.      * @param string $rawPost 
  655.      * @return Apache_Solr_Response 
  656.      *
  657.      * @throws Exception If an error occurs during the service call
  658.      */
  659.     public function add($rawPost)
  660.     {
  661.         return $this->_sendRawPost($this->_updateUrl$rawPost);
  662.     }
  663.  
  664.     /**
  665.      * Add a Solr Document to the index
  666.      *
  667.      * @param Apache_Solr_Document $document 
  668.      * @param boolean $allowDups 
  669.      * @param boolean $overwritePending 
  670.      * @param boolean $overwriteCommitted 
  671.      * @return Apache_Solr_Response 
  672.      *
  673.      * @throws Exception If an error occurs during the service call
  674.      */
  675.     public function addDocument(Apache_Solr_Document $document$allowDups false$overwritePending true$overwriteCommitted true)
  676.     {
  677.         $dupValue $allowDups 'true' 'false';
  678.         $pendingValue $overwritePending 'true' 'false';
  679.         $committedValue $overwriteCommitted 'true' 'false';
  680.  
  681.         $rawPost '<add allowDups="' $dupValue '" overwritePending="' $pendingValue '" overwriteCommitted="' $committedValue '">';
  682.         $rawPost .= $this->_documentToXmlFragment($document);
  683.         $rawPost .= '</add>';
  684.  
  685.         return $this->add($rawPost);
  686.     }
  687.  
  688.     /**
  689.      * Add an array of Solr Documents to the index all at once
  690.      *
  691.      * @param array $documents Should be an array of Apache_Solr_Document instances
  692.      * @param boolean $allowDups 
  693.      * @param boolean $overwritePending 
  694.      * @param boolean $overwriteCommitted 
  695.      * @return Apache_Solr_Response 
  696.      *
  697.      * @throws Exception If an error occurs during the service call
  698.      */
  699.     public function addDocuments($documents$allowDups false$overwritePending true$overwriteCommitted true)
  700.     {
  701.         $dupValue $allowDups 'true' 'false';
  702.         $pendingValue $overwritePending 'true' 'false';
  703.         $committedValue $overwriteCommitted 'true' 'false';
  704.  
  705.         $rawPost '<add allowDups="' $dupValue '" overwritePending="' $pendingValue '" overwriteCommitted="' $committedValue '">';
  706.  
  707.         foreach ($documents as $document)
  708.         {
  709.             if ($document instanceof Apache_Solr_Document)
  710.             {
  711.                 $rawPost .= $this->_documentToXmlFragment($document);
  712.             }
  713.         }
  714.  
  715.         $rawPost .= '</add>';
  716.  
  717.         return $this->add($rawPost);
  718.     }
  719.  
  720.     /**
  721.      * Create an XML fragment from a {@link Apache_Solr_Document} instance appropriate for use inside a Solr add call
  722.      *
  723.      * @return string 
  724.      */
  725.     protected function _documentToXmlFragment(Apache_Solr_Document $document)
  726.     {
  727.         $xml '<doc';
  728.  
  729.         if ($document->getBoost(!== false)
  730.         {
  731.             $xml .= ' boost="' $document->getBoost('"';
  732.         }
  733.  
  734.         $xml .= '>';
  735.  
  736.         foreach ($document as $key => $value)
  737.         {
  738.             $key htmlspecialchars($keyENT_QUOTES'UTF-8');
  739.             $fieldBoost $document->getFieldBoost($key);
  740.  
  741.             if (is_array($value))
  742.             {
  743.                 foreach ($value as $multivalue)
  744.                 {
  745.                     $xml .= '<field name="' $key '"';
  746.  
  747.                     if ($fieldBoost !== false)
  748.                     {
  749.                         $xml .= ' boost="' $fieldBoost '"';
  750.  
  751.                         // only set the boost for the first field in the set
  752.                         $fieldBoost false;
  753.                     }
  754.  
  755.                     $multivalue htmlspecialchars($multivalueENT_NOQUOTES'UTF-8');
  756.  
  757.                     $xml .= '>' $multivalue '</field>';
  758.                 }
  759.             }
  760.             else
  761.             {
  762.                 $xml .= '<field name="' $key '"';
  763.  
  764.                 if ($fieldBoost !== false)
  765.                 {
  766.                     $xml .= ' boost="' $fieldBoost '"';
  767.                 }
  768.  
  769.                 $value htmlspecialchars($valueENT_NOQUOTES'UTF-8');
  770.  
  771.                 $xml .= '>' $value '</field>';
  772.             }
  773.         }
  774.  
  775.         $xml .= '</doc>';
  776.  
  777.         return $xml;
  778.     }
  779.  
  780.     /**
  781.      * Send a commit command.  Will be synchronous unless both wait parameters are set to false.
  782.      *
  783.      * @param boolean $optimize Defaults to true
  784.      * @param boolean $waitFlush Defaults to true
  785.      * @param boolean $waitSearcher Defaults to true
  786.      * @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
  787.      * @return Apache_Solr_Response 
  788.      *
  789.      * @throws Exception If an error occurs during the service call
  790.      */
  791.     public function commit($optimize true$waitFlush true$waitSearcher true$timeout 3600)
  792.     {
  793.         $optimizeValue $optimize 'true' 'false';
  794.         $flushValue $waitFlush 'true' 'false';
  795.         $searcherValue $waitSearcher 'true' 'false';
  796.  
  797.         $rawPost '<commit optimize="' $optimizeValue '" waitFlush="' $flushValue '" waitSearcher="' $searcherValue '" />';
  798.  
  799.         return $this->_sendRawPost($this->_updateUrl$rawPost$timeout);
  800.     }
  801.  
  802.     /**
  803.      * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
  804.      * a complete and well formed "delete" xml document
  805.      *
  806.      * @param string $rawPost Expected to be utf-8 encoded xml document
  807.      * @return Apache_Solr_Response 
  808.      *
  809.      * @throws Exception If an error occurs during the service call
  810.      */
  811.     public function delete($rawPost)
  812.     {
  813.         return $this->_sendRawPost($this->_updateUrl$rawPost);
  814.     }
  815.  
  816.     /**
  817.      * Create a delete document based on document ID
  818.      *
  819.      * @param string $id Expected to be utf-8 encoded
  820.      * @param boolean $fromPending 
  821.      * @param boolean $fromCommitted 
  822.      * @return Apache_Solr_Response 
  823.      *
  824.      * @throws Exception If an error occurs during the service call
  825.      */
  826.     public function deleteById($id$fromPending true$fromCommitted true)
  827.     {
  828.         $pendingValue $fromPending 'true' 'false';
  829.         $committedValue $fromCommitted 'true' 'false';
  830.  
  831.         //escape special xml characters
  832.         $id htmlspecialchars($idENT_NOQUOTES'UTF-8');
  833.  
  834.         $rawPost '<delete fromPending="' $pendingValue '" fromCommitted="' $committedValue '"><id>' $id '</id></delete>';
  835.  
  836.         return $this->delete($rawPost);
  837.     }
  838.  
  839.     /**
  840.      * Create a delete document based on a query and submit it
  841.      *
  842.      * @param string $rawQuery Expected to be utf-8 encoded
  843.      * @param boolean $fromPending 
  844.      * @param boolean $fromCommitted 
  845.      * @return Apache_Solr_Response 
  846.      *
  847.      * @throws Exception If an error occurs during the service call
  848.      */
  849.     public function deleteByQuery($rawQuery$fromPending true$fromCommitted true)
  850.     {
  851.         $pendingValue $fromPending 'true' 'false';
  852.         $committedValue $fromCommitted 'true' 'false';
  853.  
  854.         // escape special xml characters
  855.         $rawQuery htmlspecialchars($rawQueryENT_NOQUOTES'UTF-8');
  856.  
  857.         $rawPost '<delete fromPending="' $pendingValue '" fromCommitted="' $committedValue '"><query>' $rawQuery '</query></delete>';
  858.  
  859.         return $this->delete($rawPost);
  860.     }
  861.  
  862.     /**
  863.      * Send an optimize command.  Will be synchronous unless both wait parameters are set
  864.      * to false.
  865.      *
  866.      * @param boolean $waitFlush 
  867.      * @param boolean $waitSearcher 
  868.      * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception)
  869.      * @return Apache_Solr_Response 
  870.      *
  871.      * @throws Exception If an error occurs during the service call
  872.      */
  873.     public function optimize($waitFlush true$waitSearcher true$timeout 3600)
  874.     {
  875.         $flushValue $waitFlush 'true' 'false';
  876.         $searcherValue $waitSearcher 'true' 'false';
  877.  
  878.         $rawPost '<optimize waitFlush="' $flushValue '" waitSearcher="' $searcherValue '" />';
  879.  
  880.         return $this->_sendRawPost($this->_updateUrl$rawPost$timeout);
  881.     }
  882.  
  883.     /**
  884.      * Simple Search interface
  885.      *
  886.      * @param string $query The raw query string
  887.      * @param int $offset The starting offset for result documents
  888.      * @param int $limit The maximum number of result documents to return
  889.      * @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)
  890.      * @return Apache_Solr_Response 
  891.      *
  892.      * @throws Exception If an error occurs during the service call
  893.      */
  894.     public function search($query$offset 0$limit 10$params array())
  895.     {
  896.         if (!is_array($params))
  897.         {
  898.             $params array();
  899.         }
  900.  
  901.         // construct our full parameters
  902.         // sending the version is important in case the format changes
  903.         $params['version'self::SOLR_VERSION;
  904.  
  905.         // common parameters in this interface
  906.         $params['wt'self::SOLR_WRITER;
  907.         $params['json.nl'$this->_namedListTreatment;
  908.  
  909.         $params['q'$query;
  910.         $params['start'$offset;
  911.         $params['rows'$limit;
  912.  
  913.         // use http_build_query to encode our arguments because its faster
  914.         // than urlencoding all the parts ourselves in a loop
  915.         $queryString http_build_query($paramsnull$this->_queryStringDelimiter);
  916.  
  917.         // because http_build_query treats arrays differently than we want to, correct the query
  918.         // string by changing foo[#]=bar (# being an actual number) parameter strings to just
  919.         // multiple foo=bar strings. This regex should always work since '=' will be urlencoded
  920.         // anywhere else the regex isn't expecting it
  921.         $queryString preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/''='$queryString);
  922.  
  923.         return $this->_sendRawGet($this->_searchUrl . $this->_queryDelimiter . $queryString);
  924.     }
  925. }

Documentation generated on Wed, 11 Mar 2009 17:34:18 -0400 by phpDocumentor 1.4.2