Differential D18539 Diff 44521 src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
Show First 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { | ||||
$alias = $this->getPrimaryTableAlias(); | $alias = $this->getPrimaryTableAlias(); | ||||
if ($alias) { | if ($alias) { | ||||
$select[] = qsprintf($conn, '%T.*', $alias); | $select[] = qsprintf($conn, '%T.*', $alias); | ||||
} else { | } else { | ||||
$select[] = '*'; | $select[] = '*'; | ||||
} | } | ||||
$select[] = $this->buildEdgeLogicSelectClause($conn); | $select[] = $this->buildEdgeLogicSelectClause($conn); | ||||
$select[] = $this->buildFerretSelectClause($conn); | |||||
return $select; | return $select; | ||||
} | } | ||||
/** | /** | ||||
* @task clauses | * @task clauses | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 502 Lines • ▼ Show 20 Lines | if ($object instanceof PhabricatorCustomFieldInterface) { | ||||
$orders['-'.$modern_key] = array( | $orders['-'.$modern_key] = array( | ||||
'vector' => array('-'.$modern_key, '-id'), | 'vector' => array('-'.$modern_key, '-id'), | ||||
'name' => pht('%s (Reversed)', $field->getFieldName()), | 'name' => pht('%s (Reversed)', $field->getFieldName()), | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
if ($this->supportsFerretEngine()) { | |||||
$orders['relevance'] = array( | |||||
'vector' => array('rank', 'id'), | |||||
'name' => pht('Relevence'), | |||||
); | |||||
} | |||||
return $orders; | return $orders; | ||||
} | } | ||||
public function getBuiltinOrderAliasMap() { | public function getBuiltinOrderAliasMap() { | ||||
$orders = $this->getBuiltinOrders(); | $orders = $this->getBuiltinOrders(); | ||||
$map = array(); | $map = array(); | ||||
foreach ($orders as $key => $order) { | foreach ($orders as $key => $order) { | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | if ($object instanceof PhabricatorCustomFieldInterface) { | ||||
'null' => 'tail', | 'null' => 'tail', | ||||
'customfield' => true, | 'customfield' => true, | ||||
'customfield.index.table' => $index->getTableName(), | 'customfield.index.table' => $index->getTableName(), | ||||
'customfield.index.key' => $digest, | 'customfield.index.key' => $digest, | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
if ($this->supportsFerretEngine()) { | |||||
$columns['rank'] = array( | |||||
'table' => null, | |||||
'column' => '_ft_rank', | |||||
'type' => 'int', | |||||
); | |||||
} | |||||
$cache->setKey($cache_key, $columns); | $cache->setKey($cache_key, $columns); | ||||
return $columns; | return $columns; | ||||
} | } | ||||
/** | /** | ||||
* @task order | * @task order | ||||
▲ Show 20 Lines • Show All 408 Lines • ▼ Show 20 Lines | protected function isCustomFieldOrderKey($key) { | ||||
$prefix = 'custom:'; | $prefix = 'custom:'; | ||||
return !strncmp($key, $prefix, strlen($prefix)); | return !strncmp($key, $prefix, strlen($prefix)); | ||||
} | } | ||||
/* -( Ferret )------------------------------------------------------------- */ | /* -( Ferret )------------------------------------------------------------- */ | ||||
public function supportsFerretEngine() { | |||||
$object = $this->newResultObject(); | |||||
return ($object instanceof PhabricatorFerretInterface); | |||||
} | |||||
public function withFerretConstraint( | public function withFerretConstraint( | ||||
PhabricatorFerretEngine $engine, | PhabricatorFerretEngine $engine, | ||||
array $fulltext_tokens) { | array $fulltext_tokens) { | ||||
if (!$this->supportsFerretEngine()) { | |||||
throw new Exception( | |||||
pht( | |||||
'Query ("%s") does not support the Ferret fulltext engine.', | |||||
get_class($this))); | |||||
} | |||||
if ($this->ferretEngine) { | if ($this->ferretEngine) { | ||||
throw new Exception( | throw new Exception( | ||||
pht( | pht( | ||||
'Query may not have multiple fulltext constraints.')); | 'Query may not have multiple fulltext constraints.')); | ||||
} | } | ||||
if (!$fulltext_tokens) { | if (!$fulltext_tokens) { | ||||
return $this; | return $this; | ||||
Show All 11 Lines | foreach ($this->ferretTokens as $fulltext_token) { | ||||
if ($function === null) { | if ($function === null) { | ||||
$function = $current_function; | $function = $current_function; | ||||
} | } | ||||
$raw_field = $engine->getFieldForFunction($function); | $raw_field = $engine->getFieldForFunction($function); | ||||
if (!isset($table_map[$function])) { | if (!isset($table_map[$function])) { | ||||
$alias = 'ftfield'.$idx++; | $alias = 'ftfield_'.$idx++; | ||||
$table_map[$function] = array( | $table_map[$function] = array( | ||||
'alias' => $alias, | 'alias' => $alias, | ||||
'key' => $raw_field, | 'key' => $raw_field, | ||||
); | ); | ||||
} | } | ||||
$current_function = $function; | $current_function = $function; | ||||
} | } | ||||
// Join the title field separately so we can rank results. | |||||
$table_map['rank'] = array( | |||||
'alias' => 'ft_rank', | |||||
'key' => PhabricatorSearchDocumentFieldType::FIELD_TITLE, | |||||
); | |||||
$this->ferretTables = $table_map; | $this->ferretTables = $table_map; | ||||
return $this; | return $this; | ||||
} | } | ||||
protected function buildFerretSelectClause(AphrontDatabaseConnection $conn) { | |||||
$select = array(); | |||||
if (!$this->supportsFerretEngine()) { | |||||
return $select; | |||||
} | |||||
if (!$this->ferretEngine) { | |||||
$select[] = '0 _ft_rank'; | |||||
return $select; | |||||
} | |||||
$engine = $this->ferretEngine; | |||||
$stemmer = $engine->newStemmer(); | |||||
$op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; | |||||
$op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; | |||||
$table_alias = 'ft_rank'; | |||||
$parts = array(); | |||||
foreach ($this->ferretTokens as $fulltext_token) { | |||||
$raw_token = $fulltext_token->getToken(); | |||||
$value = $raw_token->getValue(); | |||||
if ($raw_token->getOperator() == $op_not) { | |||||
// Ignore "not" terms when ranking, since they aren't useful. | |||||
continue; | |||||
} | |||||
if ($raw_token->getOperator() == $op_sub) { | |||||
$is_substring = true; | |||||
} else { | |||||
$is_substring = false; | |||||
} | |||||
if ($is_substring) { | |||||
$parts[] = qsprintf( | |||||
$conn, | |||||
'IF(%T.rawCorpus LIKE %~, 2, 0)', | |||||
$table_alias, | |||||
$value); | |||||
continue; | |||||
} | |||||
if ($raw_token->isQuoted()) { | |||||
$is_quoted = true; | |||||
$is_stemmed = false; | |||||
} else { | |||||
$is_quoted = false; | |||||
$is_stemmed = true; | |||||
} | |||||
$term_constraints = array(); | |||||
$term_value = $engine->newTermsCorpus($value); | |||||
$parts[] = qsprintf( | |||||
$conn, | |||||
'IF(%T.termCorpus LIKE %~, 2, 0)', | |||||
$table_alias, | |||||
$term_value); | |||||
if ($is_stemmed) { | |||||
$stem_value = $stemmer->stemToken($value); | |||||
$stem_value = $engine->newTermsCorpus($stem_value); | |||||
$parts[] = qsprintf( | |||||
$conn, | |||||
'IF(%T.normalCorpus LIKE %~, 1, 0)', | |||||
$table_alias, | |||||
$stem_value); | |||||
} | |||||
$parts[] = '0'; | |||||
} | |||||
$select[] = qsprintf( | |||||
$conn, | |||||
'%Q _ft_rank', | |||||
implode(' + ', $parts)); | |||||
return $select; | |||||
} | |||||
protected function buildFerretJoinClause(AphrontDatabaseConnection $conn) { | protected function buildFerretJoinClause(AphrontDatabaseConnection $conn) { | ||||
if (!$this->ferretEngine) { | if (!$this->ferretEngine) { | ||||
return array(); | return array(); | ||||
} | } | ||||
$op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; | $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; | ||||
$op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; | $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; | ||||
▲ Show 20 Lines • Show All 1,003 Lines • Show Last 20 Lines |