diff -Nur /tmp/sphinx/includes/api/sphinxapi.php sphinx/includes/api/sphinxapi.php --- /tmp/sphinx/includes/api/sphinxapi.php 1970-01-01 03:00:00.000000000 +0300 +++ sphinx/includes/api/sphinxapi.php 2008-07-15 04:33:22.000000000 +0400 @@ -0,0 +1,1189 @@ +=8 ) + { + $i = (int)$v; + return pack ( "NN", $i>>32, $i&((1<<32)-1) ); + } + + // x32 route, bcmath + $x = "4294967296"; + if ( function_exists("bcmul") ) + { + $h = bcdiv ( $v, $x, 0 ); + $l = bcmod ( $v, $x ); + return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit + } + + // x32 route, 15 or less decimal digits + // we can use float, because its actually double and has 52 precision bits + if ( strlen($v)<=15 ) + { + $f = (float)$v; + $h = (int)($f/$x); + $l = (int)($f-$x*$h); + return pack ( "NN", $h, $l ); + } + + // x32 route, 16 or more decimal digits + // well, let me know if you *really* need this + die ( "INTERNAL ERROR: packing more than 15-digit numeric on 32-bit PHP is not implemented yet (contact support)" ); +} + + +/// portably unpack 64 unsigned bits, network order to numeric +function sphUnpack64 ( $v ) +{ + list($h,$l) = array_values ( unpack ( "N*N*", $v ) ); + + // x64 route + if ( PHP_INT_SIZE>=8 ) + { + if ( $h<0 ) $h += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again + if ( $l<0 ) $l += (1<<32); + return ($h<<32) + $l; + } + + // x32 route + $h = sprintf ( "%u", $h ); + $l = sprintf ( "%u", $l ); + $x = "4294967296"; + + // bcmath + if ( function_exists("bcmul") ) + return bcadd ( $l, bcmul ( $x, $h ) ); + + // no bcmath, 15 or less decimal digits + // we can use float, because its actually double and has 52 precision bits + if ( $h<1048576 ) + { + $f = ((float)$h)*$x + (float)$l; + return sprintf ( "%.0f", $f ); // builtin conversion is only about 39-40 bits precise! + } + + // x32 route, 16 or more decimal digits + // well, let me know if you *really* need this + die ( "INTERNAL ERROR: unpacking more than 15-digit numeric on 32-bit PHP is not implemented yet (contact support)" ); +} + + +/// sphinx searchd client class +class SphinxClient +{ + var $_host; ///< searchd host (default is "localhost") + var $_port; ///< searchd port (default is 3312) + var $_offset; ///< how many records to seek from result-set start (default is 0) + var $_limit; ///< how many records to return from result-set starting at offset (default is 20) + var $_mode; ///< query matching mode (default is SPH_MATCH_ALL) + var $_weights; ///< per-field weights (default is 1 for all fields) + var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE) + var $_sortby; ///< attribute to sort by (defualt is "") + var $_min_id; ///< min ID to match (default is 0, which means no limit) + var $_max_id; ///< max ID to match (default is 0, which means no limit) + var $_filters; ///< search filters + var $_groupby; ///< group-by attribute name + var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with) + var $_groupsort; ///< group-by sorting clause (to sort groups in result set with) + var $_groupdistinct;///< group-by count-distinct attribute + var $_maxmatches; ///< max matches to retrieve + var $_cutoff; ///< cutoff to stop searching at (default is 0) + var $_retrycount; ///< distributed retries count + var $_retrydelay; ///< distributed retries delay + var $_anchor; ///< geographical anchor point + var $_indexweights; ///< per-index weights + var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25) + var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit) + var $_fieldweights; ///< per-field-name weights + + var $_error; ///< last error message + var $_warning; ///< last warning message + + var $_reqs; ///< requests array for multi-query + var $_mbenc; ///< stored mbstring encoding + var $_arrayresult; ///< whether $result["matches"] should be a hash or an array + var $_timeout; ///< connect timeout + + ///////////////////////////////////////////////////////////////////////////// + // common stuff + ///////////////////////////////////////////////////////////////////////////// + + /// create a new client object and fill defaults + function SphinxClient () + { + // per-client-object settings + $this->_host = "localhost"; + $this->_port = 3312; + + // per-query settings + $this->_offset = 0; + $this->_limit = 20; + $this->_mode = SPH_MATCH_ALL; + $this->_weights = array (); + $this->_sort = SPH_SORT_RELEVANCE; + $this->_sortby = ""; + $this->_min_id = 0; + $this->_max_id = 0; + $this->_filters = array (); + $this->_groupby = ""; + $this->_groupfunc = SPH_GROUPBY_DAY; + $this->_groupsort = "@group desc"; + $this->_groupdistinct= ""; + $this->_maxmatches = 1000; + $this->_cutoff = 0; + $this->_retrycount = 0; + $this->_retrydelay = 0; + $this->_anchor = array (); + $this->_indexweights= array (); + $this->_ranker = SPH_RANK_PROXIMITY_BM25; + $this->_maxquerytime= 0; + $this->_fieldweights= array(); + + $this->_error = ""; // per-reply fields (for single-query case) + $this->_warning = ""; + $this->_reqs = array (); // requests storage (for multi-query case) + $this->_mbenc = ""; + $this->_arrayresult = false; + $this->_timeout = 0; + } + + /// get last error message (string) + function GetLastError () + { + return $this->_error; + } + + /// get last warning message (string) + function GetLastWarning () + { + return $this->_warning; + } + + /// set searchd host name (string) and port (integer) + function SetServer ( $host, $port ) + { + assert ( is_string($host) ); + assert ( is_int($port) ); + $this->_host = $host; + $this->_port = $port; + } + + /// set server connection timeout (0 to remove) + function SetConnectTimeout ( $timeout ) + { + assert ( is_numeric($timeout) ); + $this->_timeout = $timeout; + } + + ///////////////////////////////////////////////////////////////////////////// + + /// enter mbstring workaround mode + function _MBPush () + { + $this->_mbenc = ""; + if ( ini_get ( "mbstring.func_overload" ) & 2 ) + { + $this->_mbenc = mb_internal_encoding(); + mb_internal_encoding ( "latin1" ); + } + } + + /// leave mbstring workaround mode + function _MBPop () + { + if ( $this->_mbenc ) + mb_internal_encoding ( $this->_mbenc ); + } + + /// connect to searchd server + function _Connect () + { + $errno = 0; + $errstr = ""; + if ( $this->_timeout<=0 ) + $fp = @fsockopen ( $this->_host, $this->_port, $errno, $errstr ); + else + $fp = @fsockopen ( $this->_host, $this->_port, $errno, $errstr, $this->_timeout ); + + if ( !$fp ) + { + $errstr = trim ( $errstr ); + $this->_error = "connection to {$this->_host}:{$this->_port} failed (errno=$errno, msg=$errstr)"; + return false; + } + + // check version + list(,$v) = unpack ( "N*", fread ( $fp, 4 ) ); + $v = (int)$v; + if ( $v<1 ) + { + fclose ( $fp ); + $this->_error = "expected searchd protocol version 1+, got version '$v'"; + return false; + } + + // all ok, send my version + fwrite ( $fp, pack ( "N", 1 ) ); + return $fp; + } + + /// get and check response packet from searchd server + function _GetResponse ( $fp, $client_ver ) + { + $response = ""; + $len = 0; + + $header = fread ( $fp, 8 ); + if ( strlen($header)==8 ) + { + list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) ); + $left = $len; + while ( $left>0 && !feof($fp) ) + { + $chunk = fread ( $fp, $left ); + if ( $chunk ) + { + $response .= $chunk; + $left -= strlen($chunk); + } + } + } + fclose ( $fp ); + + // check response + $read = strlen ( $response ); + if ( !$response || $read!=$len ) + { + $this->_error = $len + ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)" + : "received zero-sized searchd response"; + return false; + } + + // check status + if ( $status==SEARCHD_WARNING ) + { + list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) ); + $this->_warning = substr ( $response, 4, $wlen ); + return substr ( $response, 4+$wlen ); + } + if ( $status==SEARCHD_ERROR ) + { + $this->_error = "searchd error: " . substr ( $response, 4 ); + return false; + } + if ( $status==SEARCHD_RETRY ) + { + $this->_error = "temporary searchd error: " . substr ( $response, 4 ); + return false; + } + if ( $status!=SEARCHD_OK ) + { + $this->_error = "unknown status code '$status'"; + return false; + } + + // check version + if ( $ver<$client_ver ) + { + $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work", + $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff ); + } + + return $response; + } + + ///////////////////////////////////////////////////////////////////////////// + // searching + ///////////////////////////////////////////////////////////////////////////// + + /// set offset and count into result set, + /// and optionally set max-matches and cutoff limits + function SetLimits ( $offset, $limit, $max=0, $cutoff=0 ) + { + assert ( is_int($offset) ); + assert ( is_int($limit) ); + assert ( $offset>=0 ); + assert ( $limit>0 ); + assert ( $max>=0 ); + $this->_offset = $offset; + $this->_limit = $limit; + if ( $max>0 ) + $this->_maxmatches = $max; + if ( $cutoff>0 ) + $this->_cutoff = $cutoff; + } + + /// set maximum query time, in milliseconds, per-index + /// integer, 0 means "do not limit" + function SetMaxQueryTime ( $max ) + { + assert ( is_int($max) ); + assert ( $max>=0 ); + $this->_maxquerytime = $max; + } + + /// set matching mode + function SetMatchMode ( $mode ) + { + assert ( $mode==SPH_MATCH_ALL + || $mode==SPH_MATCH_ANY + || $mode==SPH_MATCH_PHRASE + || $mode==SPH_MATCH_BOOLEAN + || $mode==SPH_MATCH_EXTENDED + || $mode==SPH_MATCH_FULLSCAN + || $mode==SPH_MATCH_EXTENDED2 ); + $this->_mode = $mode; + } + + /// set ranking mode + function SetRankingMode ( $ranker ) + { + assert ( $ranker==SPH_RANK_PROXIMITY_BM25 + || $ranker==SPH_RANK_BM25 + || $ranker==SPH_RANK_NONE + || $ranker==SPH_RANK_WORDCOUNT ); + $this->_ranker = $ranker; + } + + /// set matches sorting mode + function SetSortMode ( $mode, $sortby="" ) + { + assert ( + $mode==SPH_SORT_RELEVANCE || + $mode==SPH_SORT_ATTR_DESC || + $mode==SPH_SORT_ATTR_ASC || + $mode==SPH_SORT_TIME_SEGMENTS || + $mode==SPH_SORT_EXTENDED || + $mode==SPH_SORT_EXPR ); + assert ( is_string($sortby) ); + assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 ); + + $this->_sort = $mode; + $this->_sortby = $sortby; + } + + /// bind per-field weights by order + /// DEPRECATED; use SetFieldWeights() instead + function SetWeights ( $weights ) + { + assert ( is_array($weights) ); + foreach ( $weights as $weight ) + assert ( is_int($weight) ); + + $this->_weights = $weights; + } + + /// bind per-field weights by name + function SetFieldWeights ( $weights ) + { + assert ( is_array($weights) ); + foreach ( $weights as $name=>$weight ) + { + assert ( is_string($name) ); + assert ( is_int($weight) ); + } + $this->_fieldweights = $weights; + } + + /// bind per-index weights by name + function SetIndexWeights ( $weights ) + { + assert ( is_array($weights) ); + foreach ( $weights as $index=>$weight ) + { + assert ( is_string($index) ); + assert ( is_int($weight) ); + } + $this->_indexweights = $weights; + } + + /// set IDs range to match + /// only match records if document ID is beetwen $min and $max (inclusive) + function SetIDRange ( $min, $max ) + { + assert ( is_numeric($min) ); + assert ( is_numeric($max) ); + assert ( $min<=$max ); + $this->_min_id = $min; + $this->_max_id = $max; + } + + /// set values set filter + /// only match records where $attribute value is in given set + function SetFilter ( $attribute, $values, $exclude=false ) + { + assert ( is_string($attribute) ); + assert ( is_array($values) ); + assert ( count($values) ); + + if ( is_array($values) && count($values) ) + { + foreach ( $values as $value ) + assert ( is_numeric($value) ); + + $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values ); + } + } + + /// set range filter + /// only match records if $attribute value is beetwen $min and $max (inclusive) + function SetFilterRange ( $attribute, $min, $max, $exclude=false ) + { + assert ( is_string($attribute) ); + assert ( is_int($min) ); + assert ( is_int($max) ); + assert ( $min<=$max ); + + $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max ); + } + + /// set float range filter + /// only match records if $attribute value is beetwen $min and $max (inclusive) + function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false ) + { + assert ( is_string($attribute) ); + assert ( is_float($min) ); + assert ( is_float($max) ); + assert ( $min<=$max ); + + $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max ); + } + + /// setup anchor point for geosphere distance calculations + /// required to use @geodist in filters and sorting + /// latitude and longitude must be in radians + function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long ) + { + assert ( is_string($attrlat) ); + assert ( is_string($attrlong) ); + assert ( is_float($lat) ); + assert ( is_float($long) ); + + $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long ); + } + + /// set grouping attribute and function + function SetGroupBy ( $attribute, $func, $groupsort="@group desc" ) + { + assert ( is_string($attribute) ); + assert ( is_string($groupsort) ); + assert ( $func==SPH_GROUPBY_DAY + || $func==SPH_GROUPBY_WEEK + || $func==SPH_GROUPBY_MONTH + || $func==SPH_GROUPBY_YEAR + || $func==SPH_GROUPBY_ATTR + || $func==SPH_GROUPBY_ATTRPAIR ); + + $this->_groupby = $attribute; + $this->_groupfunc = $func; + $this->_groupsort = $groupsort; + } + + /// set count-distinct attribute for group-by queries + function SetGroupDistinct ( $attribute ) + { + assert ( is_string($attribute) ); + $this->_groupdistinct = $attribute; + } + + /// set distributed retries count and delay + function SetRetries ( $count, $delay=0 ) + { + assert ( is_int($count) && $count>=0 ); + assert ( is_int($delay) && $delay>=0 ); + $this->_retrycount = $count; + $this->_retrydelay = $delay; + } + + /// set result set format (hash or array; hash by default) + /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs + function SetArrayResult ( $arrayresult ) + { + assert ( is_bool($arrayresult) ); + $this->_arrayresult = $arrayresult; + } + + ////////////////////////////////////////////////////////////////////////////// + + /// clear all filters (for multi-queries) + function ResetFilters () + { + $this->_filters = array(); + $this->_anchor = array(); + } + + /// clear groupby settings (for multi-queries) + function ResetGroupBy () + { + $this->_groupby = ""; + $this->_groupfunc = SPH_GROUPBY_DAY; + $this->_groupsort = "@group desc"; + $this->_groupdistinct= ""; + } + + ////////////////////////////////////////////////////////////////////////////// + + /// connect to searchd server, run given search query through given indexes, + /// and return the search results + function Query ( $query, $index="*", $comment="" ) + { + assert ( empty($this->_reqs) ); + + $this->AddQuery ( $query, $index, $comment ); + $results = $this->RunQueries (); + $this->_reqs = array (); // just in case it failed too early + + if ( !is_array($results) ) + return false; // probably network error; error message should be already filled + + $this->_error = $results[0]["error"]; + $this->_warning = $results[0]["warning"]; + if ( $results[0]["status"]==SEARCHD_ERROR ) + return false; + else + return $results[0]; + } + + /// helper to pack floats in network byte order + function _PackFloat ( $f ) + { + $t1 = pack ( "f", $f ); // machine order + list(,$t2) = unpack ( "L*", $t1 ); // int in machine order + return pack ( "N", $t2 ); + } + + /// add query to multi-query batch + /// returns index into results array from RunQueries() call + function AddQuery ( $query, $index="*", $comment="" ) + { + // mbstring workaround + $this->_MBPush (); + + // build request + $req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits + $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby; + $req .= pack ( "N", strlen($query) ) . $query; // query itself + $req .= pack ( "N", count($this->_weights) ); // weights + foreach ( $this->_weights as $weight ) + $req .= pack ( "N", (int)$weight ); + $req .= pack ( "N", strlen($index) ) . $index; // indexes + $req .= pack ( "N", 1 ); // id64 range marker + $req .= sphPack64 ( $this->_min_id ) . sphPack64 ( $this->_max_id ); // id64 range + + // filters + $req .= pack ( "N", count($this->_filters) ); + foreach ( $this->_filters as $filter ) + { + $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"]; + $req .= pack ( "N", $filter["type"] ); + switch ( $filter["type"] ) + { + case SPH_FILTER_VALUES: + $req .= pack ( "N", count($filter["values"]) ); + foreach ( $filter["values"] as $value ) + $req .= pack ( "N", floatval($value) ); // this uberhack is to workaround 32bit signed int limit on x32 platforms + break; + + case SPH_FILTER_RANGE: + $req .= pack ( "NN", $filter["min"], $filter["max"] ); + break; + + case SPH_FILTER_FLOATRANGE: + $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] ); + break; + + default: + assert ( 0 && "internal error: unhandled filter type" ); + } + $req .= pack ( "N", $filter["exclude"] ); + } + + // group-by clause, max-matches count, group-sort clause, cutoff count + $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby; + $req .= pack ( "N", $this->_maxmatches ); + $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort; + $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay ); + $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct; + + // anchor point + if ( empty($this->_anchor) ) + { + $req .= pack ( "N", 0 ); + } else + { + $a =& $this->_anchor; + $req .= pack ( "N", 1 ); + $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"]; + $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"]; + $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] ); + } + + // per-index weights + $req .= pack ( "N", count($this->_indexweights) ); + foreach ( $this->_indexweights as $idx=>$weight ) + $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight ); + + // max query time + $req .= pack ( "N", $this->_maxquerytime ); + + // per-field weights + $req .= pack ( "N", count($this->_fieldweights) ); + foreach ( $this->_fieldweights as $field=>$weight ) + $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight ); + + // comment + $req .= pack ( "N", strlen($comment) ) . $comment; + + // mbstring workaround + $this->_MBPop (); + + // store request to requests array + $this->_reqs[] = $req; + return count($this->_reqs)-1; + } + + /// connect to searchd, run queries batch, and return an array of result sets + function RunQueries () + { + if ( empty($this->_reqs) ) + { + $this->_error = "no queries defined, issue AddQuery() first"; + return false; + } + + // mbstring workaround + $this->_MBPush (); + + if (!( $fp = $this->_Connect() )) + { + $this->_MBPop (); + return false; + } + + //////////////////////////// + // send query, get response + //////////////////////////// + + $nreqs = count($this->_reqs); + $req = join ( "", $this->_reqs ); + $len = 4+strlen($req); + $req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header + + fwrite ( $fp, $req, $len+8 ); + if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) )) + { + $this->_MBPop (); + return false; + } + + $this->_reqs = array (); + + ////////////////// + // parse response + ////////////////// + + $p = 0; // current position + $max = strlen($response); // max position for checks, to protect against broken responses + + $results = array (); + for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ ) + { + $results[] = array(); + $result =& $results[$ires]; + + $result["error"] = ""; + $result["warning"] = ""; + + // extract status + list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $result["status"] = $status; + if ( $status!=SEARCHD_OK ) + { + list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $message = substr ( $response, $p, $len ); $p += $len; + + if ( $status==SEARCHD_WARNING ) + { + $result["warning"] = $message; + } else + { + $result["error"] = $message; + continue; + } + } + + // read schema + $fields = array (); + $attrs = array (); + + list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + while ( $nfields-->0 && $p<$max ) + { + list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $fields[] = substr ( $response, $p, $len ); $p += $len; + } + $result["fields"] = $fields; + + list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + while ( $nattrs-->0 && $p<$max ) + { + list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $attr = substr ( $response, $p, $len ); $p += $len; + list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $attrs[$attr] = $type; + } + $result["attrs"] = $attrs; + + // read match count + list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + + // read matches + $idx = -1; + while ( $count-->0 && $p<$max ) + { + // index into result array + $idx++; + + // parse document id and weight + if ( $id64 ) + { + $doc = sphUnpack64 ( substr ( $response, $p, 8 ) ); $p += 8; + list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + } else + { + list ( $doc, $weight ) = array_values ( unpack ( "N*N*", + substr ( $response, $p, 8 ) ) ); + $p += 8; + + if ( PHP_INT_SIZE>=8 ) + { + // x64 route, workaround broken unpack() in 5.2.2+ + if ( $doc<0 ) $doc += (1<<32); + } else + { + // x32 route, workaround php signed/unsigned braindamage + $doc = sprintf ( "%u", $doc ); + } + } + $weight = sprintf ( "%u", $weight ); + + // create match entry + if ( $this->_arrayresult ) + $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight ); + else + $result["matches"][$doc]["weight"] = $weight; + + // parse and create attributes + $attrvals = array (); + foreach ( $attrs as $attr=>$type ) + { + // handle floats + if ( $type==SPH_ATTR_FLOAT ) + { + list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + list(,$fval) = unpack ( "f*", pack ( "L", $uval ) ); + $attrvals[$attr] = $fval; + continue; + } + + // handle everything else as unsigned ints + list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + if ( $type & SPH_ATTR_MULTI ) + { + $attrvals[$attr] = array (); + $nvalues = $val; + while ( $nvalues-->0 && $p<$max ) + { + list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $attrvals[$attr][] = sprintf ( "%u", $val ); + } + } else + { + $attrvals[$attr] = sprintf ( "%u", $val ); + } + } + + if ( $this->_arrayresult ) + $result["matches"][$idx]["attrs"] = $attrvals; + else + $result["matches"][$doc]["attrs"] = $attrvals; + } + + list ( $total, $total_found, $msecs, $words ) = + array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) ); + $result["total"] = sprintf ( "%u", $total ); + $result["total_found"] = sprintf ( "%u", $total_found ); + $result["time"] = sprintf ( "%.3f", $msecs/1000 ); + $p += 16; + + while ( $words-->0 && $p<$max ) + { + list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; + $word = substr ( $response, $p, $len ); $p += $len; + list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8; + $result["words"][$word] = array ( + "docs"=>sprintf ( "%u", $docs ), + "hits"=>sprintf ( "%u", $hits ) ); + } + } + + $this->_MBPop (); + return $results; + } + + ///////////////////////////////////////////////////////////////////////////// + // excerpts generation + ///////////////////////////////////////////////////////////////////////////// + + /// connect to searchd server, and generate exceprts (snippets) + /// of given documents for given query. returns false on failure, + /// an array of snippets on success + function BuildExcerpts ( $docs, $index, $words, $opts=array() ) + { + assert ( is_array($docs) ); + assert ( is_string($index) ); + assert ( is_string($words) ); + assert ( is_array($opts) ); + + $this->_MBPush (); + + if (!( $fp = $this->_Connect() )) + { + $this->_MBPop(); + return false; + } + + ///////////////// + // fixup options + ///////////////// + + if ( !isset($opts["before_match"]) ) $opts["before_match"] = ""; + if ( !isset($opts["after_match"]) ) $opts["after_match"] = ""; + if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... "; + if ( !isset($opts["limit"]) ) $opts["limit"] = 256; + if ( !isset($opts["around"]) ) $opts["around"] = 5; + if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false; + if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false; + if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false; + if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false; + + ///////////////// + // build request + ///////////////// + + // v.1.0 req + $flags = 1; // remove spaces + if ( $opts["exact_phrase"] ) $flags |= 2; + if ( $opts["single_passage"] ) $flags |= 4; + if ( $opts["use_boundaries"] ) $flags |= 8; + if ( $opts["weight_order"] ) $flags |= 16; + $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags + $req .= pack ( "N", strlen($index) ) . $index; // req index + $req .= pack ( "N", strlen($words) ) . $words; // req words + + // options + $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"]; + $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"]; + $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"]; + $req .= pack ( "N", (int)$opts["limit"] ); + $req .= pack ( "N", (int)$opts["around"] ); + + // documents + $req .= pack ( "N", count($docs) ); + foreach ( $docs as $doc ) + { + assert ( is_string($doc) ); + $req .= pack ( "N", strlen($doc) ) . $doc; + } + + //////////////////////////// + // send query, get response + //////////////////////////// + + $len = strlen($req); + $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header + $wrote = fwrite ( $fp, $req, $len+8 ); + if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) )) + { + $this->_MBPop (); + return false; + } + + ////////////////// + // parse response + ////////////////// + + $pos = 0; + $res = array (); + $rlen = strlen($response); + for ( $i=0; $i $rlen ) + { + $this->_error = "incomplete reply"; + $this->_MBPop (); + return false; + } + $res[] = $len ? substr ( $response, $pos, $len ) : ""; + $pos += $len; + } + + $this->_MBPop (); + return $res; + } + + + ///////////////////////////////////////////////////////////////////////////// + // keyword generation + ///////////////////////////////////////////////////////////////////////////// + + /// connect to searchd server, and generate keyword list for a given query + /// returns false on failure, + /// an array of words on success + function BuildKeywords ( $query, $index, $hits ) + { + assert ( is_string($query) ); + assert ( is_string($index) ); + assert ( is_bool($hits) ); + + $this->_MBPush (); + + if (!( $fp = $this->_Connect() )) + { + $this->_MBPop(); + return false; + } + + ///////////////// + // build request + ///////////////// + + // v.1.0 req + $req = pack ( "N", strlen($query) ) . $query; // req query + $req .= pack ( "N", strlen($index) ) . $index; // req index + $req .= pack ( "N", (int)$hits ); + + //////////////////////////// + // send query, get response + //////////////////////////// + + $len = strlen($req); + $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header + $wrote = fwrite ( $fp, $req, $len+8 ); + if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) )) + { + $this->_MBPop (); + return false; + } + + ////////////////// + // parse response + ////////////////// + + $pos = 0; + $res = array (); + $rlen = strlen($response); + list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) ); + $pos += 4; + for ( $i=0; $i<$nwords; $i++ ) + { + list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4; + $tokenized = $len ? substr ( $response, $pos, $len ) : ""; + $pos += $len; + + list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4; + $normalized = $len ? substr ( $response, $pos, $len ) : ""; + $pos += $len; + + $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized ); + + if ( $hits ) + { + list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) ); + $pos += 8; + $res [$i]["docs"] = $ndocs; + $res [$i]["hits"] = $nhits; + } + + if ( $pos > $rlen ) + { + $this->_error = "incomplete reply"; + $this->_MBPop (); + return false; + } + } + + $this->_MBPop (); + return $res; + } + + function EscapeString ( $string ) + { + $from = array ( '(',')','|','-','!','@','~','"','&', '/' ); + $to = array ( '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/' ); + + return str_replace ( $from, $to, $string ); + } + + ///////////////////////////////////////////////////////////////////////////// + // attribute updates + ///////////////////////////////////////////////////////////////////////////// + + /// update given attribute values on given documents in given indexes + /// returns amount of updated documents (0 or more) on success, or -1 on failure + function UpdateAttributes ( $index, $attrs, $values ) + { + // verify everything + assert ( is_string($index) ); + + assert ( is_array($attrs) ); + foreach ( $attrs as $attr ) + assert ( is_string($attr) ); + + assert ( is_array($values) ); + foreach ( $values as $id=>$entry ) + { + assert ( is_numeric($id) ); + assert ( is_array($entry) ); + assert ( count($entry)==count($attrs) ); + foreach ( $entry as $v ) + assert ( is_int($v) ); + } + + // build request + $req = pack ( "N", strlen($index) ) . $index; + + $req .= pack ( "N", count($attrs) ); + foreach ( $attrs as $attr ) + $req .= pack ( "N", strlen($attr) ) . $attr; + + $req .= pack ( "N", count($values) ); + foreach ( $values as $id=>$entry ) + { + $req .= sphPack64 ( $id ); + foreach ( $entry as $v ) + $req .= pack ( "N", $v ); + } + + // mbstring workaround + $this->_MBPush (); + + // connect, send query, get response + if (!( $fp = $this->_Connect() )) + { + $this->_MBPop (); + return -1; + } + + $len = strlen($req); + $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header + fwrite ( $fp, $req, $len+8 ); + + if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) )) + { + $this->_MBPop (); + return -1; + } + + // parse response + list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) ); + $this->_MBPop (); + return $updated; + } +} + +// +// $Id: sphinxapi.php 1365 2008-07-15 00:33:22Z shodan $ +// + +?> diff -Nur /tmp/sphinx/sphinx_admin.inc.php sphinx/sphinx_admin.inc.php --- /tmp/sphinx/sphinx_admin.inc.php 2008-09-25 23:26:19.000000000 +0400 +++ sphinx/sphinx_admin.inc.php 2008-10-07 16:07:39.366702487 +0400 @@ -25,31 +25,31 @@ case 'delete': return drupal_get_form('sphinx_index_confirm_delete', $iid); break; - + case 'edit': return drupal_get_form('sphinx_index_edit', $iid); break; - + case 'enable': db_query('UPDATE {sphinx_indexes} SET active = 1 WHERE iid =%d', $iid); drupal_goto('admin/settings/sphinx/indexes'); break; - + case 'disable': db_query('UPDATE {sphinx_indexes} SET active = 0 WHERE iid =%d', $iid); drupal_goto('admin/settings/sphinx/indexes'); break; - + case 'fields': return _sphinx_manage_fields($iid); break; - + case 'attributes': return _sphinx_manage_attributes($iid); break; } } - + $header = array( array('data' => t('Index'), 'field' => 'index_name'), array('data' => t('Display name'), 'field' => 'display_name'), @@ -67,12 +67,12 @@ array('data' => t('')), array('data' => t('')), ); - + $sql = 'SELECT {sphinx_indexes}.*, {sphinx_attributes}.display_name AS sort_field FROM {sphinx_indexes} LEFT JOIN {sphinx_attributes} ON default_sort_key_fid = aid '. tablesort_sql($header); $result = pager_query($sql, 10); $client = new SphinxClient(); $connect = $client->_Connect(); - + while ($indexes = db_fetch_object($result)) { if ($indexes->active) { $client->SetServer($indexes->server, (int)$indexes->port); @@ -108,11 +108,11 @@ l(t('Edit'), 'admin/settings/sphinx/indexes/edit/'. $indexes->iid), ); } - + $output .= theme('table', $header, $rows); $output .= drupal_get_form('sphinx_admin_add_index_form'); $output .= theme('pager', null, 10, 10); - + return $output; } @@ -123,8 +123,8 @@ * @ingroup forms * @see sphinx_admin_form_submit() */ -function sphinx_admin_form() { - +function sphinx_admin_form($form_id) { + $options = array(); for ($n = 1; $n < 10; $n++) { $options[$n] = $n; @@ -136,9 +136,9 @@ $result = db_query($sql); $index_options = array(); while ($indexes = db_fetch_object($result)) { - $index_options[$indexes->iid] = $indexes->display_name .' ('. $indexes->index_name .')'; + $index_options[$indexes->index_name] = $indexes->display_name .' ('. $indexes->index_name .')'; } - + $form['sphinx']['sphinx_offline_message'] = array( '#type' => 'textarea', '#title' => t('Offline message'), @@ -146,7 +146,7 @@ '#description' => t('Type the message you users will see, if the search daemon is not running'), '#required' => true, ); - + $form['sphinx']['sphinx_default_server'] = array( '#type' => 'textfield', '#title' => t('Default server'), @@ -176,23 +176,24 @@ return system_settings_form($form); } -function sphinx_admin_form_validate($form_id, $form_values) { +function sphinx_admin_form_validate($form_id, &$form_state) { //TODO insert check for portnum and server ip } -function sphinx_admin_form_submit($form_id, $form_values) { - - variable_set('sphinx_offline_message', $form_values['sphinx_offline_message']); - variable_set('sphinx_default_server', $form_values['sphinx_default_server']); - variable_set('sphinx_default_port', $form_values['sphinx_default_port']); - variable_set('sphinx_default_index', $form_values['sphinx_default_index']); - variable_set('sphinx_results_per_page', $form_values['sphinx_results_per_page']); +function sphinx_admin_form_submit($form_id, &$form_state) { +/* Not need, because settings_form has own submit + variable_set('sphinx_offline_message', $form_state['values']['sphinx_offline_message']); + variable_set('sphinx_default_server', $form_state['values']['sphinx_default_server']); + variable_set('sphinx_default_port', $form_state['values']['sphinx_default_port']); + variable_set('sphinx_default_index', $form_state['values']['sphinx_default_index']); + variable_set('sphinx_results_per_page', $form_state['values']['sphinx_results_per_page']); drupal_set_message(t('Your Sphinx settings are saved')); +*/ } -function sphinx_admin_add_index_form() { - - +function sphinx_admin_add_index_form($form_id) { + + $form['sphinx']['addnew'] = array( '#type' => 'fieldset', '#title' => t('Add new index'), @@ -200,7 +201,7 @@ '#collapsible' => true, '#collapsed' => true, ); - + $form['sphinx']['addnew']['index_name'] = array( '#type' => 'textfield', '#title' => t('Index name'), @@ -252,13 +253,13 @@ return $form; } -function sphinx_admin_add_index_form_submit($form_id, $form_values) { - db_query("INSERT INTO {sphinx_indexes} (index_name, display_name, path, server, port, excerpt, multiquery, active) VALUES ('%s','%s','%s',%d,%d,%d,%d,%d)", $form_values['index_name'], $form_values['display_name'], $form_values['path'], $form_values['server'], $form_values['port'], $form_values['excerpt'], $form_values['multiquery'], $form_values['active']); +function sphinx_admin_add_index_form_submit($form_id, &$form_state) { + db_query("INSERT INTO {sphinx_indexes} (index_name, display_name, path, server, port, excerpt, multiquery, active) VALUES ('%s','%s','%s',%d,%d,%d,%d,%d)", $form_state['values']['index_name'], $form_state['values']['display_name'], $form_state['values']['path'], $form_state['values']['server'], $form_state['values']['port'], $form_state['values']['excerpt'], $form_state['values']['multiquery'], $form_state['values']['active']); } function sphinx_index_confirm_delete($iid = '') { - - + + if (!empty($iid)) { $result = db_query('SELECT index_name, display_name FROM {sphinx_indexes} WHERE iid = %d;', $iid); $names = db_fetch_object($result); @@ -269,34 +270,34 @@ $form['index'] = array('#type' => 'hidden', '#value' => $iid); $form['submit'] = array('#type' => 'submit', '#value' => t('Delete')); $form['cancel'] = array('#type' => 'submit', '#value' => t('Cancel')); - + return $form; } } -function sphinx_index_confirm_delete_submit($form_id, $form_values) { - if ($form_values['op'] == 'Delete') { - db_query('DELETE FROM {sphinx_indexes} WHERE iid = %d;', $form_values['index']); - db_query('DELETE FROM {sphinx_fields} WHERE iid = %d;', $form_values['index']); - db_query('DELETE FROM {sphinx_attributes} WHERE iid = %d;', $form_values['index']); +function sphinx_index_confirm_delete_submit($form_id, &$form_state) { + if ($form_state['values']['op'] == 'Delete') { + db_query('DELETE FROM {sphinx_indexes} WHERE iid = %d;', $form_state['values']['index']); + db_query('DELETE FROM {sphinx_fields} WHERE iid = %d;', $form_state['values']['index']); + db_query('DELETE FROM {sphinx_attributes} WHERE iid = %d;', $form_state['values']['index']); } drupal_goto('admin/settings/sphinx/indexes'); } -function sphinx_index_edit($iid = '') { - - +function sphinx_index_edit($form_id, $iid = '') { + + if (!empty($iid)) { - $result = db_query('SELECT * FROM {sphinx_indexes} WHERE iid = %d;', $iid); + $result = db_query('SELECT * FROM {sphinx_indexes} WHERE iid = %d', $iid); $index = db_fetch_object($result); - + $sql = 'SELECT aid, attribute_name, display_name FROM {sphinx_attributes} WHERE iid=%d AND active=1'; $result = db_query($sql, $iid); $options = array(); while ($attributes = db_fetch_object($result)) { $options[$attributes->aid] = $attributes->display_name; } - + $form['sphinx']['index_name'] = array( '#type' => 'textfield', '#title' => t('Index name'), @@ -370,25 +371,25 @@ } } -function sphinx_index_edit_submit($form_id, $form_values) { +function sphinx_index_edit_submit($form_id, &$form_state) { db_query("UPDATE {sphinx_indexes} SET index_name='%s', display_name='%s', path='%s', default_sort_key_fid=%s, default_sort_order='%s', server='%s', port=%d, excerpt=%d, multiquery=%d, active=%d WHERE iid=%d;", - $form_values['index_name'], - $form_values['display_name'], - $form_values['path'], - $form_values['default_sort_key_fid'], - $form_values['default_sort_order'], - $form_values['server'], - $form_values['port'], - $form_values['excerpt'], - $form_values['multiquery'], - $form_values['active'], - $form_values['index'] + $form_state['values']['index_name'], + $form_state['values']['display_name'], + $form_state['values']['path'], + $form_state['values']['default_sort_key_fid'], + $form_state['values']['default_sort_order'], + $form_state['values']['server'], + $form_state['values']['port'], + $form_state['values']['excerpt'], + $form_state['values']['multiquery'], + $form_state['values']['active'], + $form_state['values']['index'] ); drupal_goto('admin/settings/sphinx/indexes'); } function _sphinx_manage_fields($iid) { - + $output = ''; $sql = 'SELECT * FROM {sphinx_indexes} WHERE iid=%d;'; $result = db_query($sql, $iid); @@ -412,7 +413,7 @@ array(), array(), ); - + $sql = 'SELECT * FROM {sphinx_fields} WHERE iid=%d '. tablesort_sql($header); $result = db_query($sql, $iid); $counter = 0; @@ -442,7 +443,7 @@ $output .= theme('table', $header, $rows); } if (count($serving)) { - + $add_header = array( array('data' => t('Field')), array(), @@ -460,11 +461,11 @@ case 'delete': return drupal_get_form('sphinx_field_confirm_delete', $iid, $field); break; - + case 'edit': return drupal_get_form('sphinx_field_edit', $iid, $field); break; - + case 'add': if (!empty($field)) { return drupal_get_form('sphinx_field_add', $iid, $field); @@ -473,12 +474,12 @@ return; } break; - + case 'enable': $res = db_query('UPDATE {sphinx_fields} SET active = 1 WHERE fid =%d', $field); drupal_goto('admin/settings/sphinx/indexes/fields/'. $iid); break; - + case 'disable': $res = db_query('UPDATE {sphinx_fields} SET active = 0 WHERE fid =%d', $field); drupal_goto('admin/settings/sphinx/indexes/fields/'. $iid); @@ -486,9 +487,9 @@ } } -function sphinx_field_confirm_delete($iid, $fid) { - - +function sphinx_field_confirm_delete($form_id, $iid, $fid) { + + $result = db_query('SELECT field_name, display_name FROM {sphinx_fields} WHERE iid = %d && fid= %d;', $iid, $fid); $names = db_fetch_object($result); $form = array(); @@ -499,25 +500,25 @@ $form['fid'] = array('#type' => 'hidden', '#value' => $fid); $form['submit'] = array('#type' => 'submit', '#value' => t('Delete')); $form['cancel'] = array('#type' => 'submit', '#value' => t('Cancel')); - + return $form; } -function sphinx_field_confirm_delete_submit($form_id, $form_values) { - if ($form_values['op'] == 'Delete') { - db_query('DELETE FROM {sphinx_fields} WHERE iid=%d && fid=%d;', $form_values['iid'], $form_values['fid']); +function sphinx_field_confirm_delete_submit($form_id, &$form_state) { + if ($form_state['values']['op'] == 'Delete') { + db_query('DELETE FROM {sphinx_fields} WHERE iid=%d && fid=%d;', $form_state['values']['iid'], $form_state['values']['fid']); } - drupal_goto('admin/settings/sphinx/indexes/fields/'. $form_values['iid']); + drupal_goto('admin/settings/sphinx/indexes/fields/'. $form_state['values']['iid']); } -function sphinx_field_add($iid, $field) { - - +function sphinx_field_add($form_id, $iid, $field) { + + if (!empty($iid)) { $result = db_query('SELECT index_name, display_name FROM {sphinx_indexes} WHERE iid = %d;', $iid); $names = db_fetch_object($result); } - + $form['sphinx']['addnew']['text'] = array( '#value' => t('You are about to add the field %field to the index %title (%display)', array('%field' => $field, '%title' => $names->index_name, '%display' => $names->display_name)), ); @@ -548,15 +549,15 @@ return $form; } -function sphinx_field_add_submit($form_id, $form_values) { - db_query("INSERT INTO {sphinx_fields} (iid, field_name, display_name, excerpt, weight, active) VALUES (%d, '%s','%s',%d,%d,%d)", $form_values['iid'], $form_values['field_name'], $form_values['display_name'], $form_values['excerpt'], $form_values['weight'], $form_values['active']); +function sphinx_field_add_submit($form_id, &$form_state) { + db_query("INSERT INTO {sphinx_fields} (iid, field_name, display_name, excerpt, weight, active) VALUES (%d, '%s','%s',%d,%d,%d)", $form_state['values']['iid'], $form_state['values']['field_name'], $form_state['values']['display_name'], $form_state['values']['excerpt'], $form_state['values']['weight'], $form_state['values']['active']); //exit; - drupal_goto('admin/settings/sphinx/indexes/fields/'. $form_values['iid']); + drupal_goto('admin/settings/sphinx/indexes/fields/'. $form_state['values']['iid']); } -function sphinx_field_edit($iid, $fid) { - - +function sphinx_field_edit($form_id, $iid, $fid) { + + if (!empty($iid)) { $result = db_query('SELECT index_name, display_name FROM {sphinx_indexes} WHERE iid = %d;', $iid); $index = db_fetch_object($result); @@ -565,11 +566,11 @@ $result = db_query('SELECT field_name, display_name, excerpt, weight, active FROM {sphinx_fields} WHERE fid = %d;', $fid); $field = db_fetch_object($result); } - + $form['sphinx']['addnew']['text'] = array( '#value' => t('You are about to edit the field %field in the index %title (%display)', array('%field' => $field->field_name, '%title' => $index->index_name, '%display' => $index->display_name)), ); - + $form['sphinx']['addnew']['display_name'] = array( '#type' => 'textfield', '#title' => t('Display name'), @@ -601,9 +602,9 @@ return $form; } -function sphinx_field_edit_submit($form_id, $form_values) { - db_query("UPDATE {sphinx_fields} SET display_name = '%s', excerpt=%d, weight=%d, active=%d WHERE fid=%d AND iid=%d", $form_values['display_name'], $form_values['excerpt'], $form_values['weight'], $form_values['active'], $form_values['fid'], $form_values['iid']); - drupal_goto('admin/settings/sphinx/indexes/fields/'. $form_values['iid']); +function sphinx_field_edit_submit($form_id, &$form_state) { + db_query("UPDATE {sphinx_fields} SET display_name = '%s', excerpt=%d, weight=%d, active=%d WHERE fid=%d AND iid=%d", $form_state['values']['display_name'], $form_state['values']['excerpt'], $form_state['values']['weight'], $form_state['values']['active'], $form_state['values']['fid'], $form_state['values']['iid']); + drupal_goto('admin/settings/sphinx/indexes/fields/'. $form_state['values']['iid']); } function _sphinx_manage_attributes($iid) { @@ -613,7 +614,7 @@ $index = db_fetch_object($result); $client = new SphinxClient(); $client->SetServer($index->server, (int)$index->port); - + $connect = $client->_Connect(); if (!$connect) { return 'Index not available'; @@ -643,9 +644,9 @@ array(), array(), ); - + } - + $sql = 'SELECT * FROM {sphinx_attributes} WHERE iid=%d '. tablesort_sql($header); $result = db_query($sql, $iid); $counter = 0; @@ -684,14 +685,14 @@ l(t('Edit'), 'admin/settings/sphinx/attributes/edit/'. $iid .'/'. $attributes->aid), ); - + } } if ($counter) { $output .= theme('table', $header, $rows); } if (count($serving)) { - + $add_header = array( array('data' => t('Attribute')), array(), @@ -709,11 +710,11 @@ case 'delete': return drupal_get_form('sphinx_attribute_confirm_delete', $iid, $attribute); break; - + case 'edit': return drupal_get_form('sphinx_attribute_edit', $iid, $attribute); break; - + case 'add': if (!empty($attribute)) { return drupal_get_form('sphinx_attribute_add', $iid, $attribute); @@ -722,12 +723,12 @@ return; } break; - + case 'enable': $res = db_query('UPDATE {sphinx_attributes} SET active = 1 WHERE aid =%d', $attribute); drupal_goto('admin/settings/sphinx/indexes/attributes/'. $iid); break; - + case 'disable': $res = db_query('UPDATE {sphinx_attributes} SET active = 0 WHERE aid =%d', $attribute); drupal_goto('admin/settings/sphinx/indexes/attributes/'. $iid); @@ -735,9 +736,9 @@ } } -function sphinx_attribute_confirm_delete($iid, $aid) { - - +function sphinx_attribute_confirm_delete($form_id, $iid, $aid) { + + $result = db_query('SELECT attribute_name, display_name FROM {sphinx_attributes} WHERE iid = %d && aid= %d;', $iid, $aid); $names = db_fetch_object($result); $form = array(); @@ -748,25 +749,25 @@ $form['aid'] = array('#type' => 'hidden', '#value' => $aid); $form['submit'] = array('#type' => 'submit', '#value' => t('Delete')); $form['cancel'] = array('#type' => 'submit', '#value' => t('Cancel')); - + return $form; } -function sphinx_attribute_confirm_delete_submit($form_id, $form_values) { - if ($form_values['op'] == 'Delete') { - db_query('DELETE FROM {sphinx_attributes} WHERE iid=%d && aid=%d;', $form_values['iid'], $form_values['aid']); +function sphinx_attribute_confirm_delete_submit($form_id, &$form_state) { + if ($form_state['values']['op'] == 'Delete') { + db_query('DELETE FROM {sphinx_attributes} WHERE iid=%d && aid=%d;', $form_state['values']['iid'], $form_state['values']['aid']); } - drupal_goto('admin/settings/sphinx/indexes/attributes/'. $form_values['iid']); + drupal_goto('admin/settings/sphinx/indexes/attributes/'. $form_state['values']['iid']); } -function sphinx_attribute_add($iid, $attribute) { - - +function sphinx_attribute_add($form_id, $iid, $attribute) { + + if (!empty($iid)) { $result = db_query('SELECT index_name, display_name FROM {sphinx_indexes} WHERE iid = %d;', $iid); $names = db_fetch_object($result); } - + $form['sphinx']['addnew']['text'] = array( '#value' => t('You are about to add the attribute %attribute to the index %title (%display)', array('%attribute' => $attribute, '%title' => $names->index_name, '%display' => $names->display_name)), ); @@ -777,7 +778,7 @@ '#description' => t('Type the name you want the users to see when using this attribute'), '#required' => true, ); - + $form['sphinx']['addnew']['active'] = array( '#type' => 'checkbox', '#title' => t('Enable'), @@ -801,16 +802,15 @@ return $form; } -function sphinx_attribute_add_submit($form_id, $form_values) { - $facet = (empty($form_values['facet']))?0:$form_values['facet']; - $type = (empty($form_values['type']))?'':$form_values['type']; - db_query("INSERT INTO {sphinx_attributes} (iid, attribute_name, display_name, active, facet, type) VALUES (%d, '%s','%s',%d, %d, '%s')", $form_values['iid'], $form_values['attribute_name'], $form_values['display_name'], $form_values['active'], $facet, $type); - drupal_goto('admin/settings/sphinx/indexes/attributes/'. $form_values['iid']); +function sphinx_attribute_add_submit($form_id, &$form_state) { + $facet = (empty($form_state['values']['facet']))?0:$form_state['values']['facet']; + $type = (empty($form_state['values']['type']))?'':$form_state['values']['type']; + db_query("INSERT INTO {sphinx_attributes} (iid, attribute_name, display_name, active, facet, type) VALUES (%d, '%s','%s',%d, %d, '%s')", $form_state['values']['iid'], $form_state['values']['attribute_name'], $form_state['values']['display_name'], $form_state['values']['active'], $facet, $type); + drupal_goto('admin/settings/sphinx/indexes/attributes/'. $form_state['values']['iid']); } -function sphinx_attribute_edit($iid, $aid) { - - +function sphinx_attribute_edit($form_id, $iid, $aid) { + if (!empty($iid)) { $result = db_query('SELECT index_name, display_name FROM {sphinx_indexes} WHERE iid = %d;', $iid); $index = db_fetch_object($result); @@ -819,11 +819,11 @@ $result = db_query('SELECT attribute_name, display_name, active, facet, type FROM {sphinx_attributes} WHERE aid = %d;', $aid); $attribute = db_fetch_object($result); } - + $form['sphinx']['addnew']['text'] = array( '#value' => t('You are about to edit the attribute %attribute in the index %title (%display)', array('%attribute' => $attribute->attribute_name, '%title' => $index->index_name, '%display' => $index->display_name)), ); - + $form['sphinx']['addnew']['display_name'] = array( '#type' => 'textfield', '#title' => t('Display name'), @@ -831,7 +831,7 @@ '#default_value' => $attribute->display_name, '#required' => true, ); - + $form['sphinx']['addnew']['active'] = array( '#type' => 'checkbox', '#title' => t('Enable'), @@ -860,13 +860,13 @@ return $form; } -function sphinx_attribute_edit_submit($form_id, $form_values) { +function sphinx_attribute_edit_submit($form_id, &$form_state) { if(module_exists('sphinx_facet')) { - db_query("UPDATE {sphinx_attributes} SET display_name = '%s', active=%d, facet=%d, type='%s' WHERE aid=%d AND iid=%d", $form_values['display_name'], $form_values['active'], $form_values['facet'], $form_values['type'], $form_values['aid'], $form_values['iid']); + db_query("UPDATE {sphinx_attributes} SET display_name = '%s', active=%d, facet=%d, type='%s' WHERE aid=%d AND iid=%d", $form_state['values']['display_name'], $form_state['values']['active'], $form_state['values']['facet'], $form_state['values']['type'], $form_state['values']['aid'], $form_state['values']['iid']); } else { - db_query("UPDATE {sphinx_attributes} SET display_name = '%s', active=%d WHERE aid=%d AND iid=%d", $form_values['display_name'], $form_values['active'], $form_values['aid'], $form_values['iid']); + db_query("UPDATE {sphinx_attributes} SET display_name = '%s', active=%d WHERE aid=%d AND iid=%d", $form_state['values']['display_name'], $form_state['values']['active'], $form_state['values']['aid'], $form_state['values']['iid']); } - drupal_goto('admin/settings/sphinx/indexes/attributes/'. $form_values['iid']); + drupal_goto('admin/settings/sphinx/indexes/attributes/'. $form_state['values']['iid']); } function _sphinx_get_field_display_name_by_iid($fid = null) { diff -Nur /tmp/sphinx/sphinx_facet.info sphinx/sphinx_facet.info --- /tmp/sphinx/sphinx_facet.info 2008-09-25 23:35:23.000000000 +0400 +++ sphinx/sphinx_facet.info 2008-09-29 22:19:26.252395225 +0400 @@ -1,11 +1,8 @@ -; $Id: sphinx_facet.info,v 1.1.2.2 2008/09/25 19:26:19 johsw Exp $ +; $Id$ name = Sphinx facets description = "This module provides blocks with facets" package = "Search" -dependencies = sphinx -version = "0.01" -; Information added by drupal.org packaging script on 2008-09-25 -version = "5.x-1.1" -project = "sphinx" -datestamp = "1222371323" +dependencies[] = sphinx +version = "6.x-1.0-dev" +core = 6.x diff -Nur /tmp/sphinx/sphinx_facet.module sphinx/sphinx_facet.module --- /tmp/sphinx/sphinx_facet.module 2008-09-25 23:26:19.000000000 +0400 +++ sphinx/sphinx_facet.module 2008-09-29 22:20:57.997367571 +0400 @@ -2,8 +2,7 @@ function sphinx_facet_menu() { if (!$may_cache) { - $items[] = array( - 'path' => 'admin/settings/sphinx/facet', + $items['admin/settings/sphinx/facet'] = array( 'title' => t('Facets'), 'description' => t('Configure the Sphinx facets'), 'callback' => 'drupal_get_form', @@ -30,8 +29,9 @@ } -function sphinx_facet_administration_submit($form_id, $form_values){ - variable_set('sphinx_facet_number_of_facets', $form_values['no_facets']); + +function sphinx_facet_administration_submit($form_id, &$form_state){ + variable_set('sphinx_facet_number_of_facets', $form_state['values']['no_facets']); drupal_set_message(t('Facet settings updated!')); } @@ -39,8 +39,8 @@ if ($op == 'list') { $blocks['facet'] = array( 'info' => t('Sphinx - facets'), - 'weight' => 0, - 'enabled' => 1, + 'weight' => 0, + 'enabled' => 1, 'region' => 'sidebar_wide' ); return $blocks; @@ -52,7 +52,7 @@ $index = _sphinx_get_index_by_path(arg(1)); $attributes = _sphinx_facet_get_attributes_by_iid($index); $facets = _sphinx_facet_build_facets($index, $attributes); - return array('subject' => 'Facets', 'content' => $facets); + return array('subject' => 'Facets', 'content' => $facets); } } } @@ -71,14 +71,14 @@ function _sphinx_facet_build_facets($index, $attributes){ //TODO: Don't display currently active filters - + $response = ''; $url = explode('?', $_SERVER['REQUEST_URI']); $query = _sphinx_facet_build_query($index); $index_name = _sphinx_facet_get_index_name_by_id($index); $client = new SphinxClient(); $connect = $client->_Connect(); - $display_names = array(); + $display_names = array(); if (!empty($_GET['as_filter'])) { $filter_codes = explode('-', $_GET['as_filter']); $filters = array(); @@ -86,19 +86,19 @@ $filter_list = array(); for($z=0; $zattribute_name] = array($parts[1]); - if($attribute->type == "term"){ + if($attribute->type == "term"){ $term = taxonomy_get_term($parts[1]); $name = $term->name; } else if ($attribute->type == "user") { $u = user_load(array('uid' => $parts[1])); - $name = $u->profile_navn; + $name = $u->profile_navn; } - + if(count($filter_codes)>2){ $query_string = str_replace($filter_codes[$z].'-', '',$url[1]); } else { @@ -126,23 +126,23 @@ for($n=0; $n'; - + $lines = array(); foreach($results[$n]['matches'] as $tid => $value) { - - if($attributes[$n]->type == "term"){ + + if($attributes[$n]->type == "term"){ $term = taxonomy_get_term($value['attrs']['@groupby']); $link = $term->name; } else if ($attributes[$n]->type == "user") { $u = user_load(array('uid' => $value['attrs']['@groupby'])); - $link = $u->profile_navn; + $link = $u->profile_navn; } else if ($attributes[$n]->type == "node") { - + } if(empty($_GET['as_filter'])) { - $qstring = 'as_filter='.$attributes[$n]->aid.'_'.$value['attrs']['@groupby'].'-'; + $qstring = 'as_filter='.$attributes[$n]->aid.'_'.$value['attrs']['@groupby'].'-'; $query_string = (empty($url[1]))?$qstring:$url[1].'&'.$qstring; } else { $pieces = explode('&',$url[1]); @@ -150,24 +150,24 @@ $frag = explode('=', $pieces[$x]); //print_r($frag); if($frag[0]=='as_filter') { - $pieces[$x] = $frag[0].'='.$frag[1].$attributes[$n]->aid.'_'.$value['attrs']['@groupby'].'-'; - } + $pieces[$x] = $frag[0].'='.$frag[1].$attributes[$n]->aid.'_'.$value['attrs']['@groupby'].'-'; + } } $query_string = implode('&', $pieces); } - + $lines[] = l($link. ' ('.$value['attrs']['@count'].') ', substr($url[0],1), null, $query_string).'
'; } - + $response .= theme('item_list', $lines); } } return $response; - + } function _sphinx_facet_build_query($index) { - + $fields = _sphinx_get_active_fields_by_iid($index); $needle = arg(2).' '; foreach ($fields as $fid => $name) { @@ -177,10 +177,10 @@ } } if (!empty($needle)) { - return $needle; + return $needle; } return; -} +} function _sphinx_facet_get_attribute_data_by_aid($aid = null) { if (!empty($aid)) { diff -Nur /tmp/sphinx/sphinx.info sphinx/sphinx.info --- /tmp/sphinx/sphinx.info 2008-09-25 23:35:23.000000000 +0400 +++ sphinx/sphinx.info 2008-09-29 18:48:31.896239080 +0400 @@ -1,10 +1,6 @@ -; $Id: sphinx.info,v 1.2.2.1 2008/09/25 19:26:19 johsw Exp $ +; $Id$ name = Sphinx description = "This module provides interfaces Drupal with the Sphinx search daemon (http://sphinxsearch.com)" package = "Search" -version = "0.01" -; Information added by drupal.org packaging script on 2008-09-25 -version = "5.x-1.1" -project = "sphinx" -datestamp = "1222371323" - +version = "6.x-1.0-dev" +core = 6.x diff -Nur /tmp/sphinx/sphinx.install sphinx/sphinx.install --- /tmp/sphinx/sphinx.install 2008-09-25 23:26:19.000000000 +0400 +++ sphinx/sphinx.install 2008-09-29 20:06:51.907611147 +0400 @@ -1,91 +1,89 @@ array( + 'iid' => array('type' => 'serial', 'size' => 'small', 'unsigned' => TRUE, 'not null' => TRUE), + 'index_name' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'display_name' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'path' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'default_sort_key_fid' => array('type' => 'int', 'size' => 'small', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'default_sort_order' => array('type' => 'varchar', 'length' => 10, 'not null' => TRUE, 'default' => ''), + 'server' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'port' => array('type' => 'int', 'unsigned' => TRUE, 'default' => 3312), // XXX move magic number to constant + 'excerpt' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0), + 'multiquery' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0), + 'active' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0), + ), + 'primary key' => array('iid'), + 'indexes' => array('path' => array('path')), + 'description' => 'Indexes for SPHINX.' + ); + + $schema['sphinx_fields'] = array( + 'fields' => array( + 'fid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'iid' => array('type' => 'int', 'size' => 'small', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'field_name' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'display_name' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'active' => array('type' => 'int', 'unsigned' => TRUE, 'default' => 0), + 'excerpt' => array('type' => 'int', 'unsigned' => TRUE, 'default' => 0), + 'weight' => array('type' => 'int', 'size' => 'small', 'default' => 0), + ), + 'primary key' => array('fid'), + 'index' => array('iid' => array('iid')), + 'description' => 'Fields for SPHINX.' + ); + + $schema['sphinx_attributes'] = array( + 'fields' => array( + 'aid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'iid' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + 'attribute_name' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => 0), + 'display_name' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'default_sort' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'type' => array('type' => 'varchar', 'length' => 60, 'not null' => TRUE, 'default' => ''), + 'active' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0), + 'facet' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0), + ), + 'primary key' => array('aid'), + 'indexes' => array('iid' => array('iid')), + 'description' => 'Attributes for SPHINX.' + ); + + return $schema; +} + +/** + * Implementation of hook_install(). + */ function sphinx_install() { - switch ($GLOBALS['db_type']) { - case 'mysql': - case 'mysqli': - $sql1 = " - CREATE TABLE IF NOT EXISTS sphinx_indexes ( - iid int(3) unsigned NOT NULL auto_increment, - index_name varchar(60) NOT NULL, - display_name varchar(60) NOT NULL, - path varchar(60) NOT NULL, - default_sort_key_fid int(3) unsigned NOT NULL, - default_sort_order varchar(10) NOT NULL, - server varchar(255) NOT NULL, - port int(5) NOT NULL, - excerpt int(1) unsigned NOT NULL, - multiquery int(1) unsigned NOT NULL, - active int(1) unsigned NOT NULL, - PRIMARY KEY (iid), - KEY url (path) - );"; - - $sql2 = " - CREATE TABLE IF NOT EXISTS sphinx_fields ( - fid int(11) unsigned NOT NULL auto_increment, - iid int(11) unsigned NOT NULL, - field_name varchar(60) NOT NULL, - display_name varchar(60) NOT NULL, - active int(11) unsigned NOT NULL, - excerpt int(11) unsigned NOT NULL, - weight tinyint(4) NOT NULL, - PRIMARY KEY (fid), - KEY iid (iid) - );"; - $sql3 = " - CREATE TABLE IF NOT EXISTS sphinx_attributes ( - aid int(10) unsigned NOT NULL auto_increment, - iid int(3) unsigned NOT NULL, - attribute_name varchar(60) NOT NULL, - display_name varchar(60) NOT NULL, - type varchar(60) NOT NULL, - default_sort varchar(60) NOT NULL, - active int(1) unsigned NOT NULL, - facet int(1) unsigned NOT NULL, - PRIMARY KEY (aid), - KEY iid (iid) - );"; - $res1 = db_query($sql1); - $res2 = db_query($sql2); - $res3 = db_query($sql3); - if($res1 && $res2 && $res3) { - drupal_set_message(t('The Sphinx module is now installed!')); - } else { - drupal_set_message(t('Installation errror'),'error'); - } - break; - - case 'pgsql': - drupal_set_message(t('No pg support at the moment'),'error' ); - break; - } -} - - + drupal_install_schema('sphinx'); +} + + /** * Implementation of hook_uninstall(). */ function sphinx_uninstall() { - drupal_set_message(t("The Sphinx module is now uninstalled!")); + drupal_uninstall_schema('sphinx'); + variable_del('sphinx_default_index'); } + /** * Implementation of hook_requirements(). */ function sphinx_requirements($phase) { - $t = get_t(); - $path = './'. drupal_get_path('module', 'sphinx') ."/includes/api"; - $api_file = $path ."/sphinxapi.php"; - if (file_exists($api_file)) { - include_once ($api_file); - } + $t = get_t(); + $path = drupal_get_path('module', 'sphinx') .'/includes/api/'; + @include_once($path. 'sphinxapi.php'); $api_exists = class_exists('SphinxClient'); $requirements['sphinx_api'] = array( 'title' => $t('Sphinx api'), diff -Nur /tmp/sphinx/sphinx.module sphinx/sphinx.module --- /tmp/sphinx/sphinx.module 2008-09-25 23:26:19.000000000 +0400 +++ sphinx/sphinx.module 2008-10-07 16:36:34.019475392 +0400 @@ -1,95 +1,92 @@ 'admin/settings/sphinx', + $items['admin/settings/sphinx'] = array( 'title' => t('Sphinx settings'), 'description' => t('Configure the Sphinx search and result pages'), - 'callback' => '_sphinx_admin', - 'access' => user_access('administer sphinx search'), + 'page callback' => '_sphinx_admin', + 'access callback' => user_access('administer sphinx search'), 'type' => MENU_NORMAL_ITEM, ); - $items[] = array( - 'path' => 'admin/settings/sphinx/main', + $items['admin/settings/sphinx/main'] = array( 'title' => t('Sphinx settings'), 'description' => t('Configure the Sphinx search and result pages'), - 'callback' => '_sphinx_admin', - 'access' => user_access('administer sphinx search'), + 'page callback' => '_sphinx_admin', + 'access callback' => user_access('administer sphinx search'), 'weight' => -1, 'type' => MENU_DEFAULT_LOCAL_TASK, ); - - $items[] = array( - 'path' => 'admin/settings/sphinx/indexes', + + $items['admin/settings/sphinx/indexes'] = array( 'title' => t('Indexes'), 'description' => t('Configure the Sphinx indexes'), - 'callback' => '_sphinx_admin_indexes', - 'access' => user_access('administer sphinx search'), + 'page callback' => '_sphinx_admin_indexes', + 'access callback' => user_access('administer sphinx search'), 'type' => MENU_LOCAL_TASK, ); - $items[] = array( - 'path' => 'admin/settings/sphinx/fields', + $items['admin/settings/sphinx/fields'] = array( 'title' => t('Fields'), 'description' => t('Configure the Sphinx fields'), - 'callback' => '_sphinx_alter_fields', - 'access' => user_access('administer sphinx search'), + 'page callback' => '_sphinx_alter_fields', + 'access callback' => user_access('administer sphinx search'), 'type' => MENU_CALLBACK, ); - $items[] = array( - 'path' => 'admin/settings/sphinx/attributes', + $items['admin/settings/sphinx/attributes'] = array( 'title' => t('Attributes'), 'description' => t('Configure the Sphinx attributes'), - 'callback' => '_sphinx_alter_attributes', - 'access' => user_access('administer sphinx search'), + 'page callback' => '_sphinx_alter_attributes', + 'access callback' => user_access('administer sphinx search'), 'type' => MENU_CALLBACK, ); - - $default = _sphinx_get_path_by_iid(variable_get('sphinx_default_index', '')); - if(!empty($default)) { - $items[] = array( - 'path' => 'search', - 'title' => utf8_encode('Search'), - 'description' => t('Search'), - 'callback' => 'drupal_goto', - 'callback arguments' => array('search/'.$default), - 'access' => user_access('use sphinx search'), - 'type' => MENU_NORMAL_ITEM, - ); - } - $res = db_query('SELECT iid, display_name, path FROM {sphinx_indexes} WHERE active=1'); + + $items['search'] = array( + 'title' => t('Search'), + 'description' => t('Search'), + 'page callback' => '_sphinx_search_page', + 'access callback' => user_access('use sphinx search'), + 'type' => MENU_NORMAL_ITEM, + ); + $res = db_query('SELECT iid, display_name, path FROM {sphinx_indexes} WHERE active = 1'); while ($indexes = db_fetch_object($res)) { - $items[] = array( - 'path' => 'search/'. $indexes->path, + $items['search/'. $indexes->path] = array( 'title' => t('Search !index', array('!index' => $indexes->display_name)), 'description' => t('Search !index', array('!index' => $indexes->display_name)), - 'callback' => '_sphinx_search_page', - 'callback arguments' => array(arg(1), arg(2)), - 'access' => user_access('use index '. $indexes->display_name .'('. $indexes->iid .')'), - 'type' => MENU_LOCAL_TASK, + 'page callback' => '_sphinx_search_page', + 'page arguments' => array(1, ''), + 'access callback' => user_access('use index '. $indexes->display_name .'('. $indexes->iid .')'), + 'type' => ($indexes->path == variable_get('sphinx_default_index', '')) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, + ); + $items['search/'. $indexes->path. '/%'] = array( + 'title' => t('Search !index', array('!index' => $indexes->display_name)), + 'description' => t('Search !index', array('!index' => $indexes->display_name)), + 'page callback' => '_sphinx_search_page', + 'page arguments' => array(1, 2), + 'access callback' => user_access('use index '. $indexes->display_name .'('. $indexes->iid .')'), + 'type' => ($indexes->path == variable_get('sphinx_default_index', '')) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); } } @@ -100,12 +97,7 @@ * Implementation of hook_perm(). */ function sphinx_perm() { - $permissions = array('use sphinx search', 'administer sphinx search'); - $res = db_query('SELECT iid, display_name, path FROM {sphinx_indexes} WHERE active=1'); - while ($indexes = db_fetch_object($res)) { - $permissions[] = 'use index '. $indexes->display_name .'('. $indexes->iid .')'; - } - return $permissions; + return array('use sphinx search', 'administer sphinx search'); } /** @@ -156,7 +148,7 @@ function _sphinx_get_active_fields_by_iid($iid = null) { if (!empty($iid)) { $output = array(); - $res = db_query('SELECT fid, field_name FROM {sphinx_fields} WHERE iid=%d && active=1', $iid); + $res = db_query('SELECT fid, field_name FROM {sphinx_fields} WHERE iid=%d AND active=1', $iid); while ($fields = db_fetch_object($res)) { $output[$fields->fid] = $fields->field_name; } @@ -167,15 +159,28 @@ /** * The search page callback */ -function _sphinx_search_page($index_name, $needle = null) { - +function _sphinx_search_page($path = null, $needle = null) { + // TODO check search engine availability + drupal_add_css(drupal_get_path('module', 'sphinx') .'/sphinx.css'); - $iid = _sphinx_get_index_by_path($index_name); + + if ($path) { + $iid = _sphinx_get_index_by_path($path); + } + else { + $iid = _sphinx_get_index_by_name(variable_get('sphinx_default_index', null)); + } + + if (!$iid) { + return t('Default index not defined. Go to !sphinxadm and define default index.', array('!sphinxadm' => l(t('Sphinx settings'), 'admin/settings/sphinx'))); + } + $output = drupal_get_form('sphinx_search_form', $iid, $needle); - $sql = 'SELECT {sphinx_indexes}.*, {sphinx_attributes}.attribute_name AS sort_field FROM {sphinx_indexes} LEFT JOIN {sphinx_attributes} ON default_sort_key_fid = aid WHERE {sphinx_indexes}.iid=%d'; - + $sql = 'SELECT i.*, a.attribute_name AS sort_field FROM {sphinx_indexes} i LEFT JOIN {sphinx_attributes} a ON i.default_sort_key_fid = a.aid WHERE i.iid=%d'; + $res = db_query($sql, $iid); $index = db_fetch_object($res); + $client = new SphinxClient(); $connect = $client->_Connect(); if (!$connect) { @@ -185,7 +190,7 @@ if (!empty($_GET['as_sk'])) { $sort = _sphinx_get_attr_by_aid($_GET['as_sk']); } - else if (!empty($index->sort_field)) { + else if ($index->sort_field) { $sort = $index->sort_field; } else { @@ -195,37 +200,24 @@ if (!empty($_GET['as_sd'])) { $order = ($_GET['as_sd'] == 'asc') ? 'asc' : 'desc'; } - elseif (!empty($index->default_sort_order)) { + elseif ($index->default_sort_order) { $order = $index->default_sort_order; } else { $order = 'desc'; } } + $fields = _sphinx_get_active_fields_by_iid($iid); - - foreach ($fields as $fid => $name) { - $term = $_GET['as_f'. $fid]; - if (!empty($term)) { - $needle .= '@'. $name .' '. $term .' '; - } - } - if (!empty($_GET['as_filter'])) { - $filter_codes = explode('-', $_GET['as_filter']); - $filters = array(); - for($z=0; $z $name) { + $term = $_GET['as_f'. $fid]; + if (!empty($term)) { + $needle .= '@'. $name .' '. $term .' '; } } } + if (!empty($needle)) { if (isset($_GET['pr_page'])) { $limit = (int)$_GET['pr_page']; @@ -233,7 +225,8 @@ else { $limit = variable_get('sphinx_results_per_page', '10'); } - $results = _sphinx_do_search($client, $needle, $index->index_name, $sort, $order, $filters, $limit); + // NB + $results = _sphinx_do_search($client, $needle, $index->index_name, $sort .' '. $order, $limit); $output .= theme_sphinx_results($results[0], $index->index_name, $needle, $index->excerpt); $output .= _sphinx_pager($results[0], $limit); } @@ -256,18 +249,9 @@ * @return * true if the address is in a valid format. */ -function _sphinx_do_search($client, $needle, $index, $sort, $order, $filters = null, $limit) { - if (!empty($sort)) { - $client->SetSortMode(SPH_SORT_EXTENDED, $sort.' '.$order); - } else { - $client->SetSortMode(SPH_SORT_RELEVANCE); - } - if(is_array($filters)){ - foreach($filters as $attribute => $values){ - for($x=0; $xSetFilter( $attribute, array($values[$x])); - } - } +function _sphinx_do_search($client, $needle, $index, $sort, $limit) { + if (trim($sort)) { + $client->SetSortMode(SPH_SORT_EXTENDED, $sort); } $client->SetMatchMode(SPH_MATCH_EXTENDED); if (isset($_GET['page'])) { @@ -279,7 +263,7 @@ } $client->AddQuery($needle, $index); $response = $client->RunQueries(); - + return $response; } @@ -300,7 +284,8 @@ $output = ''; $output .= theme_sphinx_feedback($results, $needle); if ($results['total_found'] > 0) { - $lines = array(); + //TODO: Use theme item-list + $output .= '
    '; foreach ($results['matches'] as $k => $v) { $node = node_load($k); if ($node->nid) { @@ -308,17 +293,16 @@ $fields = _sphinx_excerpt_fields($index); //$content = _sphinx_excerpt_content($index, $node); //TODO: need to figure out how db-fields are translated to arrays in order to retrieve the relevant fields - $excerpt = $client->BuildExcerpts(array($node->title, $node->field_brdtekst), $index, $needle, $opts = array()); - //print_r($fields); - $lines[] = theme_sphinx_result($node, $excerpt, $fields); + $excerpt = $client->BuildExcerpts(array($node->title, $node->body), $index, $needle, $opts = array()); + $output .= theme_sphinx_result($node, $excerpt, $fields); } else { - $lines[] = theme_sphinx_result($node); + $output .= theme_sphinx_result($node); } } } } - $output = theme('item_list', $lines); + $output .= '
'; return $output; } @@ -326,7 +310,7 @@ $output = ''; $output .= '
'; $output .= $results['total_found'] .' '; - $output .= ($results['total_found'] == 1) ? t('result') : t('results'); + $output .= ($results['total_found'] == 1) ? t('result') : t('results'); $output .= ' '. t('for'); $output .= ' '. $needle .' ('. $results['time'] .' '. t('secs.') .')'; $output .= '
'; @@ -372,8 +356,7 @@ * @see sphinx_search_form_validate() * @see sphinx_search_form_submit() */ -function sphinx_search_form($iid, $needle) { - +function sphinx_search_form($form_id, $iid, $needle) { $sql = 'SELECT * FROM {sphinx_indexes} WHERE iid=%d AND active=1'; $result = db_query($sql, $iid); $index = db_fetch_object($result); @@ -404,19 +387,19 @@ '#collapsed' => ($_GET['as'] == 1) ? false : true, '#weight' => -6, ); - + $sql = 'SELECT aid, attribute_name, display_name FROM {sphinx_attributes} WHERE iid=%d AND active=1'; $result = db_query($sql, $iid); $options = array(0 => ''); while ($attributes = db_fetch_object($result)) { - + $options[$attributes->aid] = $attributes->display_name; } $sql = 'SELECT fid, field_name, display_name FROM {sphinx_fields} WHERE iid=%d AND active=1 ORDER BY weight'; $result = db_query($sql, $iid); - $count = -40; + $count = -40; while ($fields = db_fetch_object($result)) { - + $form['sphinx_search']['advanced']['field'][$fields->field_name] = array( '#title' => $fields->display_name, '#type' => 'textfield', @@ -441,45 +424,45 @@ '#default_value' => (!empty($_GET['as_sd'])) ? $_GET['as_sd'] : '', '#weight' => 60, ); - + $form['sphinx_search']['submit'] = array('#type' => 'submit', '#value' => t('Search'), '#weight' => 2,); - - + + return $form; } -function sphinx_search_form_validate($form_id, $form_values) { - $search = $form_values['searchstring']; +function sphinx_search_form_validate($form_id, &$form_state) { + $search = $form_state['values']['searchstring']; $sql = 'SELECT fid,field_name FROM {sphinx_fields} WHERE iid=%d AND active=1 ORDER BY weight DESC'; - $res = db_query($sql, $form_values['iid']); + $res = db_query($sql, $form_state['values']['iid']); while ($fields = db_fetch_object($res)) { - $field = $form_values[$fields->field_name]; + $field = $form_state['values'][$fields->field_name]; if (!empty($field)) { $search .= $field; } } if (empty($search)) { - form_set_error($form_values['searchstring'], t('You must enter a search string')); + form_set_error($form_state['values']['searchstring'], t('You must enter a search string')); } } -function sphinx_search_form_submit($form_id, $form_values) { - $query = _sphinx_build_advanced_query($form_values); - drupal_goto('search/'. $form_values['index_path'] .'/'. $form_values['searchstring'], $query); +function sphinx_search_form_submit($form_id, &$form_state) { + $query = _sphinx_build_advanced_query($form_state['values']); + drupal_goto('search/'. $form_state['values']['index_path'] .'/'. $form_state['values']['searchstring'], $query); } -function _sphinx_build_advanced_query($form_values) { +function _sphinx_build_advanced_query($form_state) { $output = ''; $sql = 'SELECT fid,field_name FROM {sphinx_fields} WHERE iid=%d AND active=1 ORDER BY weight DESC'; - $res = db_query($sql, $form_values['iid']); + $res = db_query($sql, $form_state['values']['iid']); while ($fields = db_fetch_object($res)) { - $field = $form_values[$fields->field_name]; + $field = $form_state['values'][$fields->field_name]; if (!empty($field)) { $output .= 'as_f'. $fields->fid .'='. $field .'&'; } } - if ($form_values['sort_key']) { - $output .= 'as_sk='. $form_values['sort_key'] .'&as_sd='. $form_values['sort_direction'] .'&'; + if ($form_state['values']['sort_key']) { + $output .= 'as_sk='. $form_state['values']['sort_key'] .'&as_sd='. $form_state['values']['sort_direction'] .'&'; } if (!empty($output)) { return 'as=1&'. substr($output, 0, -1); diff -Nur /tmp/sphinx/sphinx_suggestion.info sphinx/sphinx_suggestion.info --- /tmp/sphinx/sphinx_suggestion.info 2008-09-25 23:35:23.000000000 +0400 +++ sphinx/sphinx_suggestion.info 2008-09-29 22:18:57.383809191 +0400 @@ -1,10 +1,8 @@ -; $Id: sphinx_suggestion.info,v 1.1.2.1 2008/09/25 19:26:19 johsw Exp $ -name = Sphinx suggestion module +; $Id$ +name = "Sphinx suggestion module" description = "This module provides search term suggestions" package = "Search" -version = "0.01" -; Information added by drupal.org packaging script on 2008-09-25 -version = "5.x-1.1" -project = "sphinx" -datestamp = "1222371323" +version = "6.x-1.0-dev" +core = "6.x" +dependencies[] = sphinx diff -Nur /tmp/sphinx/sphinx_suggestion.install sphinx/sphinx_suggestion.install --- /tmp/sphinx/sphinx_suggestion.install 2008-08-21 16:14:27.000000000 +0400 +++ sphinx/sphinx_suggestion.install 2008-09-24 12:56:00.457760588 +0400 @@ -2,10 +2,10 @@ function sphinx_suggestion_requirements($phase) { $t = get_t(); - $pspell = function_exists('pspell_suggest'); - $requirements['pspell'] = array( + $pspell = function_exists('pspell_suggest'); + $requirements['pspell'] = array( 'title' => $t('PSPELL'), - 'value' => $pspell ? 'pspell installed correctly' : 'pspell not installed ('.l('http://www.php.net/manual/en/pspell.installation.php', 'http://www.php.net/manual/en/pspell.installation.php').')', + 'value' => $pspell ? 'pspell installed correctly' : 'pspell not installed ('.l('http://www.php.net/manual/en/pspell.installation.php', 'http://www.php.net/manual/en/pspell.installation.php').')', 'severity' => $pspell ? REQUIREMENT_OK : REQUIREMENT_ERROR, ); return $requirements; diff -Nur /tmp/sphinx/sphinx_suggestion.module sphinx/sphinx_suggestion.module --- /tmp/sphinx/sphinx_suggestion.module 2008-09-25 23:26:19.000000000 +0400 +++ sphinx/sphinx_suggestion.module 2008-09-29 22:16:59.906771482 +0400 @@ -1,10 +1,9 @@ 'admin/settings/sphinx/suggestions', + $items['admin/settings/sphinx/suggestions'] = array( 'title' => t('Suggestions'), 'description' => t('Configure the Sphinx suggestions'), 'callback' => 'drupal_get_form', @@ -112,11 +111,11 @@ return system_settings_form($form); } -function sphinx_suggestion_administration_validate($form_id, $form_values) {} +function sphinx_suggestion_administration_validate($form_id, &$form_state) {} -function sphinx_suggestion_administration_submit($form_id, $form_values) { - variable_set('sphinx_suggestion_default_language', $form_values['language']); - variable_set('sphinx_suggestion_default_spelling', $form_values['spelling']); +function sphinx_suggestion_administration_submit($form_id, &$form_state) { + variable_set('sphinx_suggestion_default_language', $form_state['values']['language']); + variable_set('sphinx_suggestion_default_spelling', $form_state['values']['spelling']); drupal_set_message(t('Language settings updated!')); }