mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-04 19:08:27 +01:00
44bbdec9ac
Summary: I thought that this will be fun but the elasticsearch API is horrible and the documentation is poor. Test Plan: Search for: - string - author - author, owner - string, author - open - string, open, author - string, exclude - several authors, several owners - nothing - probably all other combinations Normally, such an exhaustive test plan wouldn't be required but each combination requires a completely different query. Reviewers: epriestley, jungejason Reviewed By: epriestley CC: aran, Koolvin, btrahan Differential Revision: https://secure.phabricator.com/D2298
216 lines
5.9 KiB
PHP
216 lines
5.9 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright 2012 Facebook, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
final class PhabricatorSearchEngineElastic extends PhabricatorSearchEngine {
|
|
|
|
public function reindexAbstractDocument(
|
|
PhabricatorSearchAbstractDocument $doc) {
|
|
|
|
$type = $doc->getDocumentType();
|
|
$phid = $doc->getPHID();
|
|
$handle = PhabricatorObjectHandleData::loadOneHandle($phid);
|
|
|
|
$spec = array(
|
|
'title' => $doc->getDocumentTitle(),
|
|
'url' => PhabricatorEnv::getProductionURI($handle->getURI()),
|
|
'dateCreated' => $doc->getDocumentCreated(),
|
|
'_timestamp' => $doc->getDocumentModified(),
|
|
'field' => array(),
|
|
'relationship' => array(),
|
|
);
|
|
|
|
foreach ($doc->getFieldData() as $field) {
|
|
$spec['field'][] = array_combine(array('type', 'corpus', 'aux'), $field);
|
|
}
|
|
|
|
foreach ($doc->getRelationshipData() as $relationship) {
|
|
list($rtype, $to_phid, $to_type, $time) = $relationship;
|
|
$spec['relationship'][$rtype][] = array(
|
|
'phid' => $to_phid,
|
|
'phidType' => $to_type,
|
|
'when' => $time,
|
|
);
|
|
}
|
|
|
|
$this->executeRequest(
|
|
"/phabricator/{$type}/{$phid}/",
|
|
$spec,
|
|
$is_write = true);
|
|
}
|
|
|
|
public function reconstructDocument($phid) {
|
|
$type = phid_get_type($phid);
|
|
|
|
$response = $this->executeRequest("/phabricator/{$type}/{$phid}", array());
|
|
|
|
if (empty($response['exists'])) {
|
|
return null;
|
|
}
|
|
|
|
$hit = $response['_source'];
|
|
|
|
$doc = new PhabricatorSearchAbstractDocument();
|
|
$doc->setPHID($phid);
|
|
$doc->setDocumentType($response['_type']);
|
|
$doc->setDocumentTitle($hit['title']);
|
|
$doc->setDocumentCreated($hit['dateCreated']);
|
|
$doc->setDocumentModified($hit['_timestamp']);
|
|
|
|
foreach ($hit['field'] as $fdef) {
|
|
$doc->addField($fdef['type'], $fdef['corpus'], $fdef['aux']);
|
|
}
|
|
|
|
foreach ($hit['relationship'] as $rtype => $rships) {
|
|
foreach ($rships as $rship) {
|
|
$doc->addRelationship(
|
|
$rtype,
|
|
$rship['phid'],
|
|
$rship['phidType'],
|
|
$rship['when']);
|
|
}
|
|
}
|
|
|
|
return $doc;
|
|
}
|
|
|
|
public function executeSearch(PhabricatorSearchQuery $query) {
|
|
|
|
$spec = array();
|
|
$filter = array();
|
|
|
|
if ($query->getQuery()) {
|
|
$spec[] = array(
|
|
'field' => array(
|
|
'field.corpus' => $query->getQuery(),
|
|
),
|
|
);
|
|
}
|
|
|
|
$exclude = $query->getParameter('exclude');
|
|
if ($exclude) {
|
|
$filter[] = array(
|
|
'not' => array(
|
|
'ids' => array(
|
|
'values' => array($exclude),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
$type = $query->getParameter('type');
|
|
if ($type) {
|
|
$uri = "/phabricator/{$type}/_search";
|
|
} else {
|
|
// Don't use '/phabricator/_search' for the case that there is something
|
|
// else in the index (for example if 'phabricator' is only an alias to
|
|
// some bigger index).
|
|
$types = PhabricatorSearchAbstractDocument::getSupportedTypes();
|
|
$uri = '/phabricator/' . implode(',', array_keys($types)) . '/_search';
|
|
}
|
|
|
|
$rel_mapping = array(
|
|
'author' => PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
|
'open' => PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
|
|
'owner' => PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
|
|
'project' => PhabricatorSearchRelationship::RELATIONSHIP_PROJECT,
|
|
'repository' => PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY,
|
|
);
|
|
foreach ($rel_mapping as $name => $field) {
|
|
$param = $query->getParameter($name);
|
|
if (is_array($param)) {
|
|
$should = array();
|
|
foreach ($param as $val) {
|
|
$should[] = array(
|
|
'text' => array(
|
|
"relationship.{$field}.phid" => array(
|
|
'query' => $val,
|
|
'type' => 'phrase',
|
|
),
|
|
),
|
|
);
|
|
}
|
|
$spec[] = array('bool' => array('should' => $should));
|
|
} else if ($param) {
|
|
$filter[] = array(
|
|
'exists' => array(
|
|
'field' => "relationship.{$field}.phid",
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($spec) {
|
|
$spec = array('query' => array('bool' => array('must' => $spec)));
|
|
}
|
|
|
|
if ($filter) {
|
|
$filter = array('filter' => array('and' => $filter));
|
|
if ($spec) {
|
|
$spec = array(
|
|
'query' => array(
|
|
'filtered' => $spec + $filter,
|
|
),
|
|
);
|
|
} else {
|
|
$spec = $filter;
|
|
}
|
|
}
|
|
|
|
$spec['from'] = (int)$query->getParameter('offset', 0);
|
|
$spec['size'] = (int)$query->getParameter('limit', 0);
|
|
$response = $this->executeRequest($uri, $spec);
|
|
|
|
$phids = ipull($response['hits']['hits'], '_id');
|
|
return $phids;
|
|
}
|
|
|
|
private function executeRequest($path, array $data, $is_write = false) {
|
|
$uri = PhabricatorEnv::getEnvConfig('search.elastic.host');
|
|
$uri = new PhutilURI($uri);
|
|
$data = json_encode($data);
|
|
|
|
$uri->setPath($path);
|
|
|
|
$protocol = $uri->getProtocol();
|
|
if ($protocol == 'https') {
|
|
$future = new HTTPSFuture($uri, $data);
|
|
} else {
|
|
$future = new HTTPFuture($uri, $data);
|
|
}
|
|
|
|
if ($is_write) {
|
|
$future->setMethod('PUT');
|
|
} else {
|
|
$future->setMethod('GET');
|
|
}
|
|
|
|
list($body) = $future->resolvex();
|
|
|
|
if ($is_write) {
|
|
return null;
|
|
}
|
|
|
|
$body = json_decode($body, true);
|
|
if (!is_array($body)) {
|
|
throw new Exception("elasticsearch server returned invalid JSON!");
|
|
}
|
|
|
|
return $body;
|
|
}
|
|
|
|
}
|