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

Documentation generated on Mon, 09 Nov 2009 18:15:45 -0500 by phpDocumentor 1.4.2