diff --git a/src/applications/search/compiler/PhutilSearchQueryCompiler.php b/src/applications/search/compiler/PhutilSearchQueryCompiler.php --- a/src/applications/search/compiler/PhutilSearchQueryCompiler.php +++ b/src/applications/search/compiler/PhutilSearchQueryCompiler.php @@ -284,11 +284,24 @@ $operator = self::OPERATOR_AND; break; case '': - // See T12995. If this query term contains Chinese, Japanese or - // Korean characters, treat the term as a substring term by default. - // These languages do not separate words with spaces, so the term - // search mode is normally useless. - if ($enable_functions && !$is_quoted && phutil_utf8_is_cjk($value)) { + $use_substring = false; + + if ($enable_functions && !$is_quoted) { + // See T12995. If this query term contains Chinese, Japanese or + // Korean characters, treat the term as a substring term by default. + // These languages do not separate words with spaces, so the term + // search mode is normally useless. + if (phutil_utf8_is_cjk($value)) { + $use_substring = true; + } else if (phutil_preg_match('/^_/', $value)) { + // See T13632. Assume users searching for any term that begins + // with an undescore intend to perform substring search if they + // don't provide an explicit search function. + $use_substring = true; + } + } + + if ($use_substring) { $operator = self::OPERATOR_SUBSTRING; } else { $operator = self::OPERATOR_AND; diff --git a/src/applications/search/compiler/__tests__/PhutilSearchQueryCompilerTestCase.php b/src/applications/search/compiler/__tests__/PhutilSearchQueryCompilerTestCase.php --- a/src/applications/search/compiler/__tests__/PhutilSearchQueryCompilerTestCase.php +++ b/src/applications/search/compiler/__tests__/PhutilSearchQueryCompilerTestCase.php @@ -205,6 +205,20 @@ 'xyz', ), ), + + // See T12995. Interpret CJK tokens as substring queries since these + // languages do not use spaces as word separators. + "\xE7\x8C\xAB" => array( + array(null, $op_sub, "\xE7\x8C\xAB"), + ), + + // See T13632. Interpret tokens that begin with "_" as substring tokens + // if no function is specified. + '_x _y_ "_z_"' => array( + array(null, $op_sub, '_x'), + array(null, $op_sub, '_y_'), + array(null, $op_and, '_z_'), + ), ); $this->assertCompileFunctionQueries($function_tests);