diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -10,6 +10,9 @@ private $slugPrefix; private $statuses; + private $parentPaths; + private $ancestorPaths; + private $needContent; const ORDER_HIERARCHY = 'hierarchy'; @@ -34,7 +37,7 @@ return $this; } - public function withSlugPrefix($slug_prefix) { + public function withSlugPrefix($slug_prefix) { $this->slugPrefix = $slug_prefix; return $this; } @@ -44,6 +47,16 @@ 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 @@ $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; } diff --git a/src/applications/phriction/query/PhrictionDocumentSearchEngine.php b/src/applications/phriction/query/PhrictionDocumentSearchEngine.php --- a/src/applications/phriction/query/PhrictionDocumentSearchEngine.php +++ b/src/applications/phriction/query/PhrictionDocumentSearchEngine.php @@ -27,6 +27,14 @@ $query->withSlugs($map['paths']); } + if ($map['parentPaths']) { + $query->withParentPaths($map['parentPaths']); + } + + if ($map['ancestorPaths']) { + $query->withAncestorPaths($map['ancestorPaths']); + } + return $query; } @@ -40,6 +48,14 @@ ->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')), ); }