@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.

Support "-term" in Ferret engine queries

Summary:
Ref T12819. Supports negating search terms, e.g. "apple -honeycrisp".

When negating a term, we're a little more strict about what can match (that is, what can //prevent// a document from being returned) since it's easy for a user to type "apple -honeycrisp -honey -crisp -crispies -olcrispers -honeyyums" to keep refining their search, but hard/impossible to split apart an overboard term.

Test Plan:
- Ran `apple -smith`, `apple -"granny smith"`, etc.
- Verified `phone -tact` does not exclude `phone contact`.
- (In theory, `phone -~tact` would, but the parser currently doesn't support this, and I'm not champing at the bit to add support.)

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12819

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

+48 -9
+48 -9
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 1410 1410 } 1411 1411 1412 1412 $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; 1413 + $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; 1413 1414 1414 1415 $engine = $this->ferretEngine; 1415 1416 $ngram_engine = new PhabricatorNgramEngine(); ··· 1421 1422 $flat = array(); 1422 1423 foreach ($this->ferretTokens as $fulltext_token) { 1423 1424 $raw_token = $fulltext_token->getToken(); 1425 + 1426 + // If this is a negated term like "-pomegranate", don't join the ngram 1427 + // table since we aren't looking for documents with this term. (We could 1428 + // LEFT JOIN the table and require a NULL row, but this is probably more 1429 + // trouble than it's worth.) 1430 + if ($raw_token->getOperator() == $op_not) { 1431 + continue; 1432 + } 1433 + 1424 1434 $value = $raw_token->getValue(); 1425 1435 1426 1436 $length = count(phutil_utf8v($value)); ··· 1530 1540 1531 1541 $ngram_engine = new PhabricatorNgramEngine(); 1532 1542 $stemmer = new PhutilSearchStemmer(); 1543 + 1533 1544 $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; 1545 + $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; 1534 1546 1535 1547 $where = array(); 1536 1548 foreach ($this->ferretTokens as $fulltext_token) { 1537 1549 $raw_token = $fulltext_token->getToken(); 1538 1550 $value = $raw_token->getValue(); 1551 + 1552 + $is_not = ($raw_token->getOperator() == $op_not); 1539 1553 1540 1554 if ($raw_token->getOperator() == $op_sub) { 1541 1555 $is_substring = true; ··· 1546 1560 // If we're doing substring search, we just match against the raw corpus 1547 1561 // and we're done. 1548 1562 if ($is_substring) { 1549 - $where[] = qsprintf( 1550 - $conn, 1551 - '(ftfield.rawCorpus LIKE %~)', 1552 - $value); 1563 + if ($is_not) { 1564 + $where[] = qsprintf( 1565 + $conn, 1566 + '(ftfield.rawCorpus NOT LIKE %~)', 1567 + $value); 1568 + } else { 1569 + $where[] = qsprintf( 1570 + $conn, 1571 + '(ftfield.rawCorpus LIKE %~)', 1572 + $value); 1573 + } 1553 1574 continue; 1554 1575 } 1555 1576 ··· 1563 1584 $is_stemmed = true; 1564 1585 } 1565 1586 1587 + // Never stem negated queries, since this can exclude results users 1588 + // did not mean to exclude and generally confuse things. 1589 + if ($is_not) { 1590 + $is_stemmed = false; 1591 + } 1592 + 1566 1593 $term_constraints = array(); 1567 1594 1568 1595 $term_value = ' '.$ngram_engine->newTermsCorpus($value).' '; 1569 - $term_constraints[] = qsprintf( 1570 - $conn, 1571 - '(ftfield.termCorpus LIKE %~)', 1572 - $term_value); 1596 + if ($is_not) { 1597 + $term_constraints[] = qsprintf( 1598 + $conn, 1599 + '(ftfield.termCorpus NOT LIKE %~)', 1600 + $term_value); 1601 + } else { 1602 + $term_constraints[] = qsprintf( 1603 + $conn, 1604 + '(ftfield.termCorpus LIKE %~)', 1605 + $term_value); 1606 + } 1573 1607 1574 1608 if ($is_stemmed) { 1575 1609 $stem_value = $stemmer->stemToken($value); ··· 1582 1616 $stem_value); 1583 1617 } 1584 1618 1585 - if ($is_quoted) { 1619 + if ($is_not) { 1620 + $where[] = qsprintf( 1621 + $conn, 1622 + '(%Q)', 1623 + implode(' AND ', $term_constraints)); 1624 + } else if ($is_quoted) { 1586 1625 $where[] = qsprintf( 1587 1626 $conn, 1588 1627 '(ftfield.rawCorpus LIKE %~ AND (%Q))',