1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-22 20:51:10 +01:00

Support "phriction.document.search" queries by "parentPaths" or "ancestorPaths"

Summary: Ref T13090. Ref T13077. This adds `parentPaths` and `ancestorPaths` constraints to `phriction.document.query`. These should be a little more usable than the internal `slugPrefix` / `depth` stuff -- that's technically more powerful, but requires callers to know more slug normalization rules. We could perhaps expose `minDepth` / `maxDepth` in the future.

Test Plan: Ran valid and invalid `parentPaths` and `ancestorPaths` queries for `/`, `aaa/`, `AAA/`, etc. Got sensible-seeming results.

Maniphest Tasks: T13090, T13077

Differential Revision: https://secure.phabricator.com/D19125
This commit is contained in:
epriestley 2018-02-22 10:51:27 -08:00
parent ffcfc04652
commit 4cb62ca0d6
2 changed files with 118 additions and 1 deletions

View file

@ -10,6 +10,9 @@ final class PhrictionDocumentQuery
private $slugPrefix;
private $statuses;
private $parentPaths;
private $ancestorPaths;
private $needContent;
const ORDER_HIERARCHY = 'hierarchy';
@ -34,7 +37,7 @@ final class PhrictionDocumentQuery
return $this;
}
public function withSlugPrefix($slug_prefix) {
public function withSlugPrefix($slug_prefix) {
$this->slugPrefix = $slug_prefix;
return $this;
}
@ -44,6 +47,16 @@ final class PhrictionDocumentQuery
return $this;
}
public function withParentPaths(array $paths) {
$this->parentPaths = $paths;
return $this;
}
public function withAncestorPaths(array $paths) {
$this->ancestorPaths = $paths;
return $this;
}
public function needContent($need_content) {
$this->needContent = $need_content;
return $this;
@ -214,6 +227,94 @@ final class PhrictionDocumentQuery
$this->depths);
}
if ($this->parentPaths !== null || $this->ancestorPaths !== null) {
$sets = array(
array(
'paths' => $this->parentPaths,
'parents' => true,
),
array(
'paths' => $this->ancestorPaths,
'parents' => false,
),
);
$paths = array();
foreach ($sets as $set) {
$set_paths = $set['paths'];
if ($set_paths === null) {
continue;
}
if (!$set_paths) {
throw new PhabricatorEmptyQueryException(
pht('No parent/ancestor paths specified.'));
}
$is_parents = $set['parents'];
foreach ($set_paths as $path) {
$path_normal = PhabricatorSlug::normalize($path);
if ($path !== $path_normal) {
throw new Exception(
pht(
'Document path "%s" is not a valid path. The normalized '.
'form of this path is "%s".',
$path,
$path_normal));
}
$depth = PhabricatorSlug::getDepth($path_normal);
if ($is_parents) {
$min_depth = $depth + 1;
$max_depth = $depth + 1;
} else {
$min_depth = $depth + 1;
$max_depth = null;
}
$paths[] = array(
$path_normal,
$min_depth,
$max_depth,
);
}
}
$path_clauses = array();
foreach ($paths as $path) {
$parts = array();
list($prefix, $min, $max) = $path;
// If we're getting children or ancestors of the root document, they
// aren't actually stored with the leading "/" in the database, so
// just skip this part of the clause.
if ($prefix !== '/') {
$parts[] = qsprintf(
$conn,
'd.slug LIKE %>',
$prefix);
}
if ($min !== null) {
$parts[] = qsprintf(
$conn,
'd.depth >= %d',
$min);
}
if ($max !== null) {
$parts[] = qsprintf(
$conn,
'd.depth <= %d',
$max);
}
$path_clauses[] = '('.implode(') AND (', $parts).')';
}
$where[] = '('.implode(') OR (', $path_clauses).')';
}
return $where;
}

View file

@ -27,6 +27,14 @@ final class PhrictionDocumentSearchEngine
$query->withSlugs($map['paths']);
}
if ($map['parentPaths']) {
$query->withParentPaths($map['parentPaths']);
}
if ($map['ancestorPaths']) {
$query->withAncestorPaths($map['ancestorPaths']);
}
return $query;
}
@ -40,6 +48,14 @@ final class PhrictionDocumentSearchEngine
->setKey('paths')
->setIsHidden(true)
->setLabel(pht('Paths')),
id(new PhabricatorSearchStringListField())
->setKey('parentPaths')
->setIsHidden(true)
->setLabel(pht('Parent Paths')),
id(new PhabricatorSearchStringListField())
->setKey('ancestorPaths')
->setIsHidden(true)
->setLabel(pht('Ancestor Paths')),
);
}