@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add "title:..." support to the Ferret engine

Summary:
Ref T12819. Adds (hacky, hard-coded) field support (for now, only for "title").

I've written this so `title:quick ferret` is the same as `title:quick title:ferret`. I think this is what users probably mean.

You can do the other thing as `ferret title:quick`, or `title:quick all:ferret`.

Test Plan: Searched for `title:x`, `title:"x"`, `title:~"x"`, etc. Searched for "garbage:y", got an exception since that's not a recognized function. Searched for `title:x y`, saw both do title search.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12819

Differential Revision: https://secure.phabricator.com/D18503

+73 -10
+73 -10
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 29 29 private $ngrams = array(); 30 30 private $ferretEngine; 31 31 private $ferretTokens; 32 + private $ferretTables; 32 33 33 34 protected function getPageCursors(array $page) { 34 35 return array( ··· 1401 1402 $this->ferretEngine = $engine; 1402 1403 $this->ferretTokens = $fulltext_tokens; 1403 1404 1405 + 1406 + $function_map = array( 1407 + 'all' => PhabricatorSearchDocumentFieldType::FIELD_ALL, 1408 + 'title' => PhabricatorSearchDocumentFieldType::FIELD_TITLE, 1409 + ); 1410 + 1411 + $current_function = 'all'; 1412 + $table_map = array(); 1413 + $idx = 1; 1414 + foreach ($this->ferretTokens as $fulltext_token) { 1415 + $raw_token = $fulltext_token->getToken(); 1416 + $function = $raw_token->getFunction(); 1417 + 1418 + if ($function === null) { 1419 + $function = $current_function; 1420 + } 1421 + 1422 + if (!isset($function_map[$function])) { 1423 + throw new PhutilSearchQueryCompilerSyntaxException( 1424 + pht( 1425 + 'Unknown search function "%s".', 1426 + $function)); 1427 + } 1428 + 1429 + if (!isset($table_map[$function])) { 1430 + $alias = 'ftfield'.$idx++; 1431 + $table_map[$function] = array( 1432 + 'alias' => $alias, 1433 + 'key' => $function_map[$function], 1434 + ); 1435 + } 1436 + 1437 + $current_function = $function; 1438 + } 1439 + 1440 + $this->ferretTables = $table_map; 1441 + 1404 1442 return $this; 1405 1443 } 1406 1444 ··· 1525 1563 $ngram); 1526 1564 } 1527 1565 1528 - $joins[] = qsprintf( 1529 - $conn, 1530 - 'JOIN %T ftfield ON ftdoc.id = ftfield.documentID', 1531 - $field_table->getTableName()); 1566 + foreach ($this->ferretTables as $table) { 1567 + $alias = $table['alias']; 1568 + 1569 + $joins[] = qsprintf( 1570 + $conn, 1571 + 'JOIN %T %T ON ftdoc.id = %T.documentID 1572 + AND %T.fieldKey = %s', 1573 + $field_table->getTableName(), 1574 + $alias, 1575 + $alias, 1576 + $alias, 1577 + $table['key']); 1578 + } 1532 1579 1533 1580 return $joins; 1534 1581 } ··· 1540 1587 1541 1588 $ngram_engine = new PhabricatorNgramEngine(); 1542 1589 $stemmer = new PhutilSearchStemmer(); 1590 + $table_map = $this->ferretTables; 1543 1591 1544 1592 $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; 1545 1593 $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; 1546 1594 1547 1595 $where = array(); 1596 + $current_function = 'all'; 1548 1597 foreach ($this->ferretTokens as $fulltext_token) { 1549 1598 $raw_token = $fulltext_token->getToken(); 1550 1599 $value = $raw_token->getValue(); 1551 1600 1601 + $function = $raw_token->getFunction(); 1602 + if ($function === null) { 1603 + $function = $current_function; 1604 + } 1605 + $current_function = $function; 1606 + 1607 + $table_alias = $table_map[$function]['alias']; 1608 + 1552 1609 $is_not = ($raw_token->getOperator() == $op_not); 1553 1610 1554 1611 if ($raw_token->getOperator() == $op_sub) { ··· 1563 1620 if ($is_not) { 1564 1621 $where[] = qsprintf( 1565 1622 $conn, 1566 - '(ftfield.rawCorpus NOT LIKE %~)', 1623 + '(%T.rawCorpus NOT LIKE %~)', 1624 + $table_alias, 1567 1625 $value); 1568 1626 } else { 1569 1627 $where[] = qsprintf( 1570 1628 $conn, 1571 - '(ftfield.rawCorpus LIKE %~)', 1629 + '(%T.rawCorpus LIKE %~)', 1630 + $table_alias, 1572 1631 $value); 1573 1632 } 1574 1633 continue; ··· 1596 1655 if ($is_not) { 1597 1656 $term_constraints[] = qsprintf( 1598 1657 $conn, 1599 - '(ftfield.termCorpus NOT LIKE %~)', 1658 + '(%T.termCorpus NOT LIKE %~)', 1659 + $table_alias, 1600 1660 $term_value); 1601 1661 } else { 1602 1662 $term_constraints[] = qsprintf( 1603 1663 $conn, 1604 - '(ftfield.termCorpus LIKE %~)', 1664 + '(%T.termCorpus LIKE %~)', 1665 + $table_alias, 1605 1666 $term_value); 1606 1667 } 1607 1668 ··· 1612 1673 1613 1674 $term_constraints[] = qsprintf( 1614 1675 $conn, 1615 - '(ftfield.normalCorpus LIKE %~)', 1676 + '(%T.normalCorpus LIKE %~)', 1677 + $table_alias, 1616 1678 $stem_value); 1617 1679 } 1618 1680 ··· 1624 1686 } else if ($is_quoted) { 1625 1687 $where[] = qsprintf( 1626 1688 $conn, 1627 - '(ftfield.rawCorpus LIKE %~ AND (%Q))', 1689 + '(%T.rawCorpus LIKE %~ AND (%Q))', 1690 + $table_alias, 1628 1691 $value, 1629 1692 implode(' OR ', $term_constraints)); 1630 1693 } else {