diff --git a/src/applications/phid/conduit/ConduitAPI_phid_lookup_Method.php b/src/applications/phid/conduit/ConduitAPI_phid_lookup_Method.php index 60b051b735..9fd73e7d31 100644 --- a/src/applications/phid/conduit/ConduitAPI_phid_lookup_Method.php +++ b/src/applications/phid/conduit/ConduitAPI_phid_lookup_Method.php @@ -1,62 +1,50 @@ 'required list', ); } public function defineReturnType() { return 'nonempty dict'; } public function defineErrorTypes() { return array(); } protected function execute(ConduitAPIRequest $request) { $names = $request->getValue('names'); - $phids = array(); - foreach ($names as $name) { - $phid = PhabricatorPHID::fromObjectName($name, $request->getUser()); - if ($phid) { - $phids[$name] = $phid; - } - } $query = id(new PhabricatorObjectQuery()) ->setViewer($request->getUser()) ->withNames($names); $query->execute(); - $objects = $query->getNamedResults(); - - foreach ($objects as $name => $object) { - $phids[$name] = $object->getPHID(); - } + $name_map = $query->getNamedResults(); - $handles = id(new PhabricatorObjectHandleData($phids)) + $handles = id(new PhabricatorObjectHandleData(mpull($name_map, 'getPHID'))) ->setViewer($request->getUser()) ->loadHandles(); $result = array(); - foreach ($phids as $name => $phid) { - if (isset($handles[$phid]) && $handles[$phid]->isComplete()) { - $result[$name] = $this->buildHandleInformationDictionary( - $handles[$phid]); - } + foreach ($name_map as $name => $object) { + $phid = $object->getPHID(); + $handle = $handles[$phid]; + $result[$phid] = $this->buildHandleInformationDictionary($handle); } return $result; } } diff --git a/src/applications/phid/query/PhabricatorObjectQuery.php b/src/applications/phid/query/PhabricatorObjectQuery.php index e9b069be43..d1a53a2d7e 100644 --- a/src/applications/phid/query/PhabricatorObjectQuery.php +++ b/src/applications/phid/query/PhabricatorObjectQuery.php @@ -1,85 +1,113 @@ phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function loadPage() { + if ($this->namedResults === null) { + $this->namedResults = array(); + } + $types = PhabricatorPHIDType::getAllTypes(); - $this->namedResults = $this->loadObjectsByName($types); + $names = $this->names; + $phids = $this->phids; + + // We allow objects to be named by their PHID in addition to their normal + // name so that, e.g., CLI tools which accept object names can also accept + // PHIDs and work as users expect. + $actually_phids = array(); + if ($names) { + foreach ($names as $key => $name) { + if (!strncmp($name, 'PHID-', 5)) { + $actually_phids[] = $name; + $phids[] = $name; + unset($names[$key]); + } + } + } - return $this->loadObjectsByPHID($types) + - mpull($this->namedResults, null, 'getPHID'); + if ($names) { + $name_results = $this->loadObjectsByName($types, $names); + } else { + $name_results = array(); + } + + if ($phids) { + $phid_results = $this->loadObjectsByPHID($types, $phids); + } else { + $phid_results = array(); + } + + foreach ($actually_phids as $phid) { + if (isset($phid_results[$phid])) { + $name_results[$phid] = $phid_results[$phid]; + } + } + + $this->namedResults += $name_results; + + return $phid_results + mpull($name_results, null, 'getPHID'); } public function getNamedResults() { if ($this->namedResults === null) { throw new Exception("Call execute() before getNamedResults()!"); } return $this->namedResults; } - private function loadObjectsByName(array $types) { - $names = $this->names; - if (!$names) { - return array(); - } - + private function loadObjectsByName(array $types, array $names) { $groups = array(); foreach ($names as $name) { foreach ($types as $type => $type_impl) { if (!$type_impl->canLoadNamedObject($name)) { continue; } $groups[$type][] = $name; break; } } $results = array(); foreach ($groups as $type => $group) { $results += $types[$type]->loadNamedObjects($this, $group); } return $results; } - private function loadObjectsByPHID(array $types) { - $phids = $this->phids; - if (!$phids) { - return array(); - } - + private function loadObjectsByPHID(array $types, array $phids) { $groups = array(); foreach ($phids as $phid) { $type = phid_get_type($phid); $groups[$type][] = $phid; } $results = array(); foreach ($groups as $type => $group) { if (isset($types[$type])) { $objects = $types[$type]->loadObjects($this, $group); $results += mpull($objects, null, 'getPHID'); } } return $results; } } diff --git a/src/applications/phid/storage/PhabricatorPHID.php b/src/applications/phid/storage/PhabricatorPHID.php index 13b476b79d..9c9018645b 100644 --- a/src/applications/phid/storage/PhabricatorPHID.php +++ b/src/applications/phid/storage/PhabricatorPHID.php @@ -1,53 +1,27 @@ setViewer($viewer) - ->withNames(array($name)); - $query->execute(); - - $objects = $query->getNamedResults(); - if ($objects) { - return head($objects)->getPHID(); - } - - /// TODO: Destroy this legacy stuff. - - $object = null; - $match = null; - if (preg_match('/^PHID-[A-Z]+-.{20}$/', $name)) { - // It's already a PHID! Yay. - return $name; - } - - if ($object) { - return $object->getPHID(); - } - - return null; - } } diff --git a/src/applications/search/controller/PhabricatorSearchController.php b/src/applications/search/controller/PhabricatorSearchController.php index 52009a106b..5dd0efd05b 100644 --- a/src/applications/search/controller/PhabricatorSearchController.php +++ b/src/applications/search/controller/PhabricatorSearchController.php @@ -1,282 +1,288 @@ key = idx($data, 'key'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($this->key) { $query = id(new PhabricatorSearchQuery())->loadOneWhere( 'queryKey = %s', $this->key); if (!$query) { return new Aphront404Response(); } } else { $query = new PhabricatorSearchQuery(); if ($request->isFormPost()) { $query_str = $request->getStr('query'); $pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP; if ($request->getStr('jump') != 'no' && $user && $user->loadPreferences()->getPreference($pref_jump, 1)) { $response = PhabricatorJumpNavHandler::jumpPostResponse($query_str); } else { $response = null; } if ($response) { return $response; } else { $query->setQuery($query_str); if ($request->getStr('scope')) { switch ($request->getStr('scope')) { case PhabricatorSearchScope::SCOPE_OPEN_REVISIONS: $query->setParameter('open', 1); $query->setParameter( 'type', DifferentialPHIDTypeRevision::TYPECONST); break; case PhabricatorSearchScope::SCOPE_OPEN_TASKS: $query->setParameter('open', 1); $query->setParameter( 'type', ManiphestPHIDTypeTask::TYPECONST); break; case PhabricatorSearchScope::SCOPE_WIKI: $query->setParameter( 'type', PhabricatorPHIDConstants::PHID_TYPE_WIKI); break; case PhabricatorSearchScope::SCOPE_COMMITS: $query->setParameter( 'type', PhabricatorRepositoryPHIDTypeCommit::TYPECONST); break; default: break; } } else { if (strlen($request->getStr('type'))) { $query->setParameter('type', $request->getStr('type')); } if ($request->getArr('author')) { $query->setParameter('author', $request->getArr('author')); } if ($request->getArr('owner')) { $query->setParameter('owner', $request->getArr('owner')); } if ($request->getArr('subscribers')) { $query->setParameter('subscribers', $request->getArr('subscribers')); } if ($request->getInt('open')) { $query->setParameter('open', $request->getInt('open')); } if ($request->getArr('project')) { $query->setParameter('project', $request->getArr('project')); } } $query->save(); return id(new AphrontRedirectResponse()) ->setURI('/search/'.$query->getQueryKey().'/'); } } } $options = array( '' => 'All Documents', ) + PhabricatorSearchAbstractDocument::getSupportedTypes(); $status_options = array( 0 => 'Open and Closed Documents', 1 => 'Open Documents', ); $phids = array_merge( $query->getParameter('author', array()), $query->getParameter('owner', array()), $query->getParameter('subscribers', array()), $query->getParameter('project', array())); $handles = $this->loadViewerHandles($phids); $author_value = array_select_keys( $handles, $query->getParameter('author', array())); $author_value = mpull($author_value, 'getFullName', 'getPHID'); $owner_value = array_select_keys( $handles, $query->getParameter('owner', array())); $owner_value = mpull($owner_value, 'getFullName', 'getPHID'); $subscribers_value = array_select_keys( $handles, $query->getParameter('subscribers', array())); $subscribers_value = mpull($subscribers_value, 'getFullName', 'getPHID'); $project_value = array_select_keys( $handles, $query->getParameter('project', array())); $project_value = mpull($project_value, 'getFullName', 'getPHID'); $search_form = new AphrontFormView(); $search_form ->setUser($user) ->setAction('/search/') ->setNoShading(true) ->appendChild( phutil_tag( 'input', array( 'type' => 'hidden', 'name' => 'jump', 'value' => 'no', ))) ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Search') ->setName('query') ->setValue($query->getQuery())) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Document Type') ->setName('type') ->setOptions($options) ->setValue($query->getParameter('type'))) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Document Status') ->setName('open') ->setOptions($status_options) ->setValue($query->getParameter('open'))) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('author') ->setLabel('Author') ->setDatasource('/typeahead/common/users/') ->setValue($author_value)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('owner') ->setLabel('Owner') ->setDatasource('/typeahead/common/searchowner/') ->setValue($owner_value) ->setCaption( 'Tip: search for "Up For Grabs" to find unowned documents.')) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('subscribers') ->setLabel('Subscribers') ->setDatasource('/typeahead/common/users/') ->setValue($subscribers_value)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('project') ->setLabel('Project') ->setDatasource('/typeahead/common/projects/') ->setValue($project_value)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Search')); $search_panel = new AphrontListFilterView(); $search_panel->appendChild($search_form); require_celerity_resource('phabricator-search-results-css'); if ($query->getID()) { $limit = 20; $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'page'); $pager->setPageSize($limit); $pager->setOffset($request->getInt('page')); $query->setParameter('limit', $limit + 1); $query->setParameter('offset', $pager->getOffset()); $engine = PhabricatorSearchEngineSelector::newSelector()->newEngine(); $results = $engine->executeSearch($query); $results = $pager->sliceResults($results); + // If there are any objects which match the query by name, and we're + // not paging through the results, prefix the results with the named + // objects. if (!$request->getInt('page')) { - $jump = PhabricatorPHID::fromObjectName($query->getQuery(), $user); - if ($jump) { - array_unshift($results, $jump); + $named = id(new PhabricatorObjectQuery()) + ->setViewer($user) + ->withNames(array($query->getQuery())) + ->execute(); + if ($named) { + $results = array_merge(array_keys($named), $results); } } if ($results) { $loader = id(new PhabricatorObjectHandleData($results)) ->setViewer($user); $handles = $loader->loadHandles(); $objects = $loader->loadObjects(); $results = array(); foreach ($handles as $phid => $handle) { $view = id(new PhabricatorSearchResultView()) ->setHandle($handle) ->setQuery($query) ->setObject(idx($objects, $phid)); $results[] = $view->render(); } $results = hsprintf( '
'. '%s'. '
%s
'. '
', phutil_implode_html("\n", $results), $pager->render()); } else { $results = hsprintf( '
'. '

No search results.

'. '
'); } $results = id(new PHUIBoxView()) ->addMargin(PHUI::MARGIN_LARGE) ->addPadding(PHUI::PADDING_LARGE) ->setShadow(true) ->appendChild($results) ->addClass('phabricator-search-result-box'); } else { $results = null; } return $this->buildApplicationPage( array( $search_panel, $results, ), array( 'title' => pht('Search Results'), 'device' => true, 'dust' => true, )); } } diff --git a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php index 10863b3c60..a41c06a2f3 100644 --- a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php +++ b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php @@ -1,138 +1,140 @@ setName('index') ->setSynopsis('Build or rebuild search indexes.') ->setExamples( "**index** D123\n". "**index** --type DREV\n". "**index** --all") ->setArguments( array( array( 'name' => 'all', 'help' => 'Reindex all documents.', ), array( 'name' => 'type', 'param' => 'TYPE', 'help' => 'PHID type to reindex, like "TASK" or "DREV".', ), array( 'name' => 'background', 'help' => 'Instead of indexing in this process, queue tasks for '. 'the daemons. This is better if you are indexing a lot '. 'of stuff, but less helpful for debugging.', ), array( 'name' => 'foreground', 'help' => 'Index in this process, even if there are many objects '. 'to index. This is helpful for debugging.', ), array( 'name' => 'objects', 'wildcard' => true, ), )); } public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $is_all = $args->getArg('all'); $is_type = $args->getArg('type'); $obj_names = $args->getArg('objects'); if ($obj_names && ($is_all || $is_type)) { throw new PhutilArgumentUsageException( "You can not name objects to index alongside the '--all' or '--type' ". "flags."); } else if (!$obj_names && !($is_all || $is_type)) { throw new PhutilArgumentUsageException( "Provide one of '--all', '--type' or a list of object names."); } if ($obj_names) { $phids = $this->loadPHIDsByNames($obj_names); } else { $phids = $this->loadPHIDsByTypes($is_type); } if (!$phids) { throw new PhutilArgumentUsageException( "Nothing to index!"); } $groups = phid_group_by_type($phids); foreach ($groups as $group_type => $group) { $console->writeOut( pht( "Indexing %d object(s) of type %s.", count($group), $group_type)."\n"); } $indexer = new PhabricatorSearchIndexer(); foreach ($phids as $phid) { $indexer->indexDocumentByPHID($phid); $console->writeOut(pht("Indexing '%s'...\n", $phid)); } $console->writeOut("Done.\n"); } private function loadPHIDsByNames(array $names) { - $phids = array(); + $query = id(new PhabricatorObjectQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withNames($names); + $query->execute(); + $objects = $query->getNamedResults(); + foreach ($names as $name) { - $phid = PhabricatorPHID::fromObjectName( - $name, - PhabricatorUser::getOmnipotentUser()); - if (!$phid) { + if (empty($objects[$name])) { throw new PhutilArgumentUsageException( "'{$name}' is not the name of a known object."); } - $phids[] = $phid; } - return $phids; + + return mpull($objects, 'getPHID'); } private function loadPHIDsByTypes($type) { $indexer_symbols = id(new PhutilSymbolLoader()) ->setAncestorClass('PhabricatorSearchDocumentIndexer') ->setConcreteOnly(true) ->setType('class') ->selectAndLoadSymbols(); $indexers = array(); foreach ($indexer_symbols as $symbol) { $indexers[] = newv($symbol['name'], array()); } $phids = array(); foreach ($indexers as $indexer) { $indexer_phid = $indexer->getIndexableObject()->generatePHID(); $indexer_type = phid_get_type($indexer_phid); if ($type && ($indexer_type != $type)) { continue; } $iterator = $indexer->getIndexIterator(); foreach ($iterator as $object) { $phids[] = $object->getPHID(); } } return $phids; } }