[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorSearchEngineElastic extends PhabricatorSearchEngine { 4 private $uri; 5 private $index; 6 private $timeout; 7 8 public function __construct($uri, $index) { 9 $this->uri = $uri; 10 $this->index = $index; 11 } 12 13 public function setTimeout($timeout) { 14 $this->timeout = $timeout; 15 return $this; 16 } 17 18 public function getTimeout() { 19 return $this->timeout; 20 } 21 22 public function reindexAbstractDocument( 23 PhabricatorSearchAbstractDocument $doc) { 24 25 $type = $doc->getDocumentType(); 26 $phid = $doc->getPHID(); 27 $handle = id(new PhabricatorHandleQuery()) 28 ->setViewer(PhabricatorUser::getOmnipotentUser()) 29 ->withPHIDs(array($phid)) 30 ->executeOne(); 31 32 // URL is not used internally but it can be useful externally. 33 $spec = array( 34 'title' => $doc->getDocumentTitle(), 35 'url' => PhabricatorEnv::getProductionURI($handle->getURI()), 36 'dateCreated' => $doc->getDocumentCreated(), 37 '_timestamp' => $doc->getDocumentModified(), 38 'field' => array(), 39 'relationship' => array(), 40 ); 41 42 foreach ($doc->getFieldData() as $field) { 43 $spec['field'][] = array_combine(array('type', 'corpus', 'aux'), $field); 44 } 45 46 foreach ($doc->getRelationshipData() as $relationship) { 47 list($rtype, $to_phid, $to_type, $time) = $relationship; 48 $spec['relationship'][$rtype][] = array( 49 'phid' => $to_phid, 50 'phidType' => $to_type, 51 'when' => $time, 52 ); 53 } 54 55 $this->executeRequest( 56 "/{$type}/{$phid}/", 57 $spec, 58 $is_write = true); 59 } 60 61 public function reconstructDocument($phid) { 62 $type = phid_get_type($phid); 63 64 $response = $this->executeRequest("/{$type}/{$phid}", array()); 65 66 if (empty($response['exists'])) { 67 return null; 68 } 69 70 $hit = $response['_source']; 71 72 $doc = new PhabricatorSearchAbstractDocument(); 73 $doc->setPHID($phid); 74 $doc->setDocumentType($response['_type']); 75 $doc->setDocumentTitle($hit['title']); 76 $doc->setDocumentCreated($hit['dateCreated']); 77 $doc->setDocumentModified($hit['_timestamp']); 78 79 foreach ($hit['field'] as $fdef) { 80 $doc->addField($fdef['type'], $fdef['corpus'], $fdef['aux']); 81 } 82 83 foreach ($hit['relationship'] as $rtype => $rships) { 84 foreach ($rships as $rship) { 85 $doc->addRelationship( 86 $rtype, 87 $rship['phid'], 88 $rship['phidType'], 89 $rship['when']); 90 } 91 } 92 93 return $doc; 94 } 95 96 private function buildSpec(PhabricatorSavedQuery $query) { 97 $spec = array(); 98 $filter = array(); 99 100 if (strlen($query->getParameter('query'))) { 101 $spec[] = array( 102 'match' => array( 103 'field.corpus' => array( 104 'operator' => 'and', 105 'query' => $query->getParameter('query'), 106 ), 107 ), 108 ); 109 } 110 111 $exclude = $query->getParameter('exclude'); 112 if ($exclude) { 113 $filter[] = array( 114 'not' => array( 115 'ids' => array( 116 'values' => array($exclude), 117 ), 118 ), 119 ); 120 } 121 122 $relationship_map = array( 123 PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR => 124 $query->getParameter('authorPHIDs', array()), 125 PhabricatorSearchRelationship::RELATIONSHIP_OWNER => 126 $query->getParameter('ownerPHIDs', array()), 127 PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER => 128 $query->getParameter('subscriberPHIDs', array()), 129 PhabricatorSearchRelationship::RELATIONSHIP_PROJECT => 130 $query->getParameter('projectPHIDs', array()), 131 PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY => 132 $query->getParameter('repositoryPHIDs', array()), 133 ); 134 135 $statuses = $query->getParameter('statuses', array()); 136 $statuses = array_fuse($statuses); 137 138 $rel_open = PhabricatorSearchRelationship::RELATIONSHIP_OPEN; 139 $rel_closed = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED; 140 $rel_unowned = PhabricatorSearchRelationship::RELATIONSHIP_UNOWNED; 141 142 $include_open = !empty($statuses[$rel_open]); 143 $include_closed = !empty($statuses[$rel_closed]); 144 145 if ($include_open && !$include_closed) { 146 $relationship_map[$rel_open] = true; 147 } else if (!$include_open && $include_closed) { 148 $relationship_map[$rel_closed] = true; 149 } 150 151 if ($query->getParameter('withUnowned')) { 152 $relationship_map[$rel_unowned] = true; 153 } 154 155 foreach ($relationship_map as $field => $param) { 156 if (is_array($param) && $param) { 157 $should = array(); 158 foreach ($param as $val) { 159 $should[] = array( 160 'match' => array( 161 "relationship.{$field}.phid" => array( 162 'query' => $val, 163 'type' => 'phrase', 164 ), 165 ), 166 ); 167 } 168 // We couldn't solve it by minimum_number_should_match because it can 169 // match multiple owners without matching author. 170 $spec[] = array('bool' => array('should' => $should)); 171 } else if ($param) { 172 $filter[] = array( 173 'exists' => array( 174 'field' => "relationship.{$field}.phid", 175 ), 176 ); 177 } 178 } 179 180 if ($spec) { 181 $spec = array('query' => array('bool' => array('must' => $spec))); 182 } 183 184 if ($filter) { 185 $filter = array('filter' => array('and' => $filter)); 186 if (!$spec) { 187 $spec = array('query' => array('match_all' => new stdClass())); 188 } 189 $spec = array( 190 'query' => array( 191 'filtered' => $spec + $filter, 192 ), 193 ); 194 } 195 196 if (!$query->getParameter('query')) { 197 $spec['sort'] = array( 198 array('dateCreated' => 'desc'), 199 ); 200 } 201 202 $spec['from'] = (int)$query->getParameter('offset', 0); 203 $spec['size'] = (int)$query->getParameter('limit', 25); 204 205 return $spec; 206 } 207 208 public function executeSearch(PhabricatorSavedQuery $query) { 209 $types = $query->getParameter('types'); 210 if (!$types) { 211 $types = array_keys( 212 PhabricatorSearchApplicationSearchEngine::getIndexableDocumentTypes()); 213 } 214 215 // Don't use '/_search' for the case that there is something 216 // else in the index (for example if 'phabricator' is only an alias to 217 // some bigger index). Use '/$types/_search' instead. 218 $uri = '/'.implode(',', $types).'/_search'; 219 220 try { 221 $response = $this->executeRequest($uri, $this->buildSpec($query)); 222 } catch (HTTPFutureHTTPResponseStatus $ex) { 223 // elasticsearch probably uses Lucene query syntax: 224 // http://lucene.apache.org/core/3_6_1/queryparsersyntax.html 225 // Try literal search if operator search fails. 226 if (!strlen($query->getParameter('query'))) { 227 throw $ex; 228 } 229 $query = clone $query; 230 $query->setParameter( 231 'query', 232 addcslashes( 233 $query->getParameter('query'), '+-&|!(){}[]^"~*?:\\')); 234 $response = $this->executeRequest($uri, $this->buildSpec($query)); 235 } 236 237 $phids = ipull($response['hits']['hits'], '_id'); 238 return $phids; 239 } 240 241 private function executeRequest($path, array $data, $is_write = false) { 242 $uri = new PhutilURI($this->uri); 243 $uri->setPath($this->index); 244 $uri->appendPath($path); 245 $data = json_encode($data); 246 247 $future = new HTTPSFuture($uri, $data); 248 if ($is_write) { 249 $future->setMethod('PUT'); 250 } 251 if ($this->getTimeout()) { 252 $future->setTimeout($this->getTimeout()); 253 } 254 list($body) = $future->resolvex(); 255 256 if ($is_write) { 257 return null; 258 } 259 260 $body = json_decode($body, true); 261 if (!is_array($body)) { 262 throw new Exception('elasticsearch server returned invalid JSON!'); 263 } 264 265 return $body; 266 } 267 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |